1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2016 EDuke32 developers and contributors
4
5 This file is part of EDuke32.
6
7 EDuke32 is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License version 2
9 as published by the Free Software Foundation.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 //-------------------------------------------------------------------------
22
23 #include "anim.h"
24 #include "cmdline.h"
25 #include "colmatch.h"
26 #include "communityapi.h"
27 #include "compat.h"
28 #include "duke3d.h"
29 #include "gamestructures.h"
30 #include "input.h"
31 #include "menus.h"
32 #include "microprofile.h"
33 #include "osdcmds.h"
34 #include "savegame.h"
35 #include "scriplib.h"
36 #include "vfs.h"
37
38 #if KRANDDEBUG
39 # define GAMEEXEC_INLINE
40 # define GAMEEXEC_STATIC
41 #else
42 # define GAMEEXEC_INLINE inline
43 # define GAMEEXEC_STATIC static
44 #endif
45
46 #if MICROPROFILE_ENABLED != 0
47 extern MicroProfileToken g_eventTokens[MAXEVENTS];
48 extern MicroProfileToken g_eventCounterTokens[MAXEVENTS];
49 extern MicroProfileToken g_actorTokens[MAXTILES];
50 extern MicroProfileToken g_statnumTokens[MAXSTATUS];
51 #if 0
52 extern MicroProfileToken g_instTokens[CON_END];
53 #endif
54 #endif
55
56 vmstate_t vm;
57
58 int32_t g_tw;
59 int32_t g_currentEvent = -1;
60
61 intptr_t const *insptr;
62
63 int32_t g_returnVarID = -1; // var ID of "RETURN"
64 int32_t g_weaponVarID = -1; // var ID of "WEAPON"
65 int32_t g_worksLikeVarID = -1; // var ID of "WORKSLIKE"
66 int32_t g_zRangeVarID = -1; // var ID of "ZRANGE"
67 int32_t g_angRangeVarID = -1; // var ID of "ANGRANGE"
68 int32_t g_aimAngleVarID = -1; // var ID of "AUTOAIMANGLE"
69 int32_t g_lotagVarID = -1; // var ID of "LOTAG"
70 int32_t g_hitagVarID = -1; // var ID of "HITAG"
71 int32_t g_textureVarID = -1; // var ID of "TEXTURE"
72 int32_t g_thisActorVarID = -1; // var ID of "THISACTOR"
73 int32_t g_structVarIDs = -1;
74
75 GAMEEXEC_STATIC void VM_Execute(int const loop = false);
76
VM_ScriptInfo(intptr_t const * const ptr,int const range)77 void VM_ScriptInfo(intptr_t const * const ptr, int const range)
78 {
79 if (!apScript || !ptr || g_currentEvent == -1)
80 return;
81
82 initprintf("\n");
83
84 for (auto pScript = max<intptr_t const *>(ptr - (range >> 1), apScript),
85 p_end = min<intptr_t const *>(ptr + (range >> 1), apScript + g_scriptSize);
86 pScript < p_end;
87 ++pScript)
88 {
89 initprintf("%5d: %3d: ", (int32_t)(pScript - apScript), (int32_t)(pScript - ptr));
90
91 auto &v = *pScript;
92 int const lineNum = VM_DECODE_LINE_NUMBER(v);
93 int const vmInst = VM_DECODE_INST(v);
94
95 if (lineNum && lineNum != VM_IFELSE_MAGIC && vmInst < CON_OPCODE_END)
96 initprintf("%5d %s (%d)\n", lineNum, VM_GetKeywordForID(vmInst), vmInst);
97 else
98 initprintf("%d\n", (int32_t)*pScript);
99 }
100
101 initprintf("\n");
102
103 if (ptr == insptr)
104 {
105 if (vm.pUSprite)
106 initprintf("current actor: %d (%d)\n", vm.spriteNum, vm.pUSprite->picnum);
107
108 initprintf("g_errorLineNum: %d, g_tw: %d\n", VM_DECODE_LINE_NUMBER(g_tw), VM_DECODE_INST(g_tw));
109 }
110 }
111
VM_DeleteSprite(int const spriteNum,int const playerNum)112 static void VM_DeleteSprite(int const spriteNum, int const playerNum)
113 {
114 if (EDUKE32_PREDICT_FALSE((unsigned) spriteNum >= MAXSPRITES))
115 return;
116
117 // if player was set to squish, first stop that...
118 if (EDUKE32_PREDICT_FALSE(playerNum >= 0 && g_player[playerNum].ps->actorsqu == spriteNum))
119 g_player[playerNum].ps->actorsqu = -1;
120
121 A_DeleteSprite(spriteNum);
122 }
123
124 intptr_t apScriptEvents[MAXEVENTS];
125
126 static uspritetype dummy_sprite;
127 static actor_t dummy_actor;
128
VM_DummySprite(void)129 static inline void VM_DummySprite(void)
130 {
131 vm.pUSprite = &dummy_sprite;
132 vm.pActor = &dummy_actor;
133 vm.pData = &dummy_actor.t_data[0];
134 }
135
136 // verification that the event actually exists happens elsewhere
VM_EventInlineInternal__(int const eventNum,int const spriteNum,int const playerNum,int const playerDist=-1,int32_t returnValue=0)137 static FORCE_INLINE int32_t VM_EventInlineInternal__(int const eventNum, int const spriteNum, int const playerNum,
138 int const playerDist = -1, int32_t returnValue = 0)
139 {
140 MICROPROFILE_SCOPE_TOKEN(g_eventTokens[eventNum]);
141 MicroProfileCounterAdd(g_eventCounterTokens[eventNum], 1);
142
143 vmstate_t const newVMstate = { spriteNum, playerNum, playerDist, 0,
144 &sprite[spriteNum&(MAXSPRITES-1)],
145 &actor[spriteNum&(MAXSPRITES-1)].t_data[0],
146 g_player[playerNum&(MAXPLAYERS-1)].ps,
147 &actor[spriteNum&(MAXSPRITES-1)] };
148
149 auto &globalReturn = aGameVars[g_returnVarID].global;
150
151 struct
152 {
153 vmstate_t vm;
154 intptr_t globalReturn;
155 int eventNum;
156 intptr_t const *insptr;
157 } const saved = { vm, globalReturn, g_currentEvent, insptr };
158
159 vm = newVMstate;
160 g_currentEvent = eventNum;
161 insptr = apScript + apScriptEvents[eventNum];
162 globalReturn = returnValue;
163
164 if ((unsigned)spriteNum >= MAXSPRITES)
165 VM_DummySprite();
166
167 if ((unsigned)playerNum >= (unsigned)g_mostConcurrentPlayers)
168 vm.pPlayer = g_player[0].ps;
169
170 VM_Execute(true);
171
172 if (vm.flags & VM_KILL)
173 VM_DeleteSprite(vm.spriteNum, vm.playerNum);
174
175 // restoring these needs to happen after VM_DeleteSprite() due to event recursion
176 returnValue = globalReturn;
177
178 vm = saved.vm;
179 globalReturn = saved.globalReturn;
180 g_currentEvent = saved.eventNum;
181 insptr = saved.insptr;
182
183 return returnValue;
184 }
185
186 // the idea here is that the compiler inlines the call to VM_EventInlineInternal__() and gives us a set of
187 // functions which are optimized further based on distance/return having values known at compile time
188
VM_ExecuteEvent(int const nEventID,int const spriteNum,int const playerNum,int const nDist,int32_t const nReturn)189 int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist, int32_t const nReturn)
190 {
191 return VM_EventInlineInternal__(nEventID, spriteNum, playerNum, nDist, nReturn);
192 }
193
VM_ExecuteEvent(int const nEventID,int const spriteNum,int const playerNum,int const nDist)194 int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist)
195 {
196 return VM_EventInlineInternal__(nEventID, spriteNum, playerNum, nDist);
197 }
198
VM_ExecuteEvent(int const nEventID,int const spriteNum,int const playerNum)199 int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum)
200 {
201 return VM_EventInlineInternal__(nEventID, spriteNum, playerNum);
202 }
203
VM_ExecuteEventWithValue(int const nEventID,int const spriteNum,int const playerNum,int32_t const nReturn)204 int32_t VM_ExecuteEventWithValue(int const nEventID, int const spriteNum, int const playerNum, int32_t const nReturn)
205 {
206 return VM_EventInlineInternal__(nEventID, spriteNum, playerNum, -1, nReturn);
207 }
208
209
VM_CheckSquished(void)210 static int VM_CheckSquished(void)
211 {
212 auto const pSector = (usectorptr_t)§or[vm.pSprite->sectnum];
213
214 if (pSector->lotag == ST_23_SWINGING_DOOR || (vm.pSprite->picnum == APLAYER && ud.noclip) ||
215 (pSector->lotag == ST_1_ABOVE_WATER && !A_CheckNoSE7Water(vm.pUSprite, vm.pSprite->sectnum, pSector->lotag, NULL)))
216 return 0;
217
218 int32_t floorZ = pSector->floorz;
219 int32_t ceilZ = pSector->ceilingz;
220 #ifdef YAX_ENABLE
221 int16_t cb, fb;
222
223 yax_getbunches(vm.pSprite->sectnum, &cb, &fb);
224
225 if (cb >= 0 && (pSector->ceilingstat&512)==0) // if ceiling non-blocking...
226 ceilZ -= ZOFFSET5; // unconditionally don't squish... yax_getneighborsect is slowish :/
227 if (fb >= 0 && (pSector->floorstat&512)==0)
228 floorZ += ZOFFSET5;
229 #endif
230
231 if (vm.pSprite->pal == 1 ? (floorZ - ceilZ >= ZOFFSET5 || (pSector->lotag & 32768u)) : (floorZ - ceilZ >= ZOFFSET4))
232 return 0;
233
234 P_DoQuote(QUOTE_SQUISHED, vm.pPlayer);
235
236 if (A_CheckEnemySprite(vm.pSprite))
237 vm.pSprite->xvel = 0;
238
239 #ifndef EDUKE32_STANDALONE
240 if (EDUKE32_PREDICT_FALSE(vm.pSprite->pal == 1)) // frozen
241 {
242 vm.pActor->picnum = SHOTSPARK1;
243 vm.pActor->extra = 1;
244 return 0;
245 }
246 #endif
247
248 return 1;
249 }
250
P_ForceAngle(DukePlayer_t * pPlayer)251 GAMEEXEC_STATIC GAMEEXEC_INLINE void P_ForceAngle(DukePlayer_t *pPlayer)
252 {
253 int const nAngle = 128-(krand()&255);
254
255 pPlayer->q16horiz += F16(64);
256 pPlayer->return_to_center = 9;
257 pPlayer->rotscrnang = nAngle >> 1;
258 pPlayer->look_ang = pPlayer->rotscrnang;
259 }
260
261 // wow, this function sucks
262 #ifdef __cplusplus
263 extern "C"
264 #endif
265 int A_Dodge(spritetype * const);
A_Dodge(spritetype * const pSprite)266 int A_Dodge(spritetype * const pSprite)
267 {
268 if (A_CheckEnemySprite(pSprite) && pSprite->extra <= 0) // hack
269 return 0;
270
271 vec2_t const msin = { sintable[(pSprite->ang + 512) & 2047], sintable[pSprite->ang & 2047] };
272
273 for (native_t nexti, SPRITES_OF_STAT_SAFE(STAT_PROJECTILE, i, nexti)) //weapons list
274 {
275 if (OW(i) == i)
276 continue;
277
278 vec2_t const b = { SX(i) - pSprite->x, SY(i) - pSprite->y };
279 vec2_t const v = { sintable[(SA(i) + 512) & 2047], sintable[SA(i) & 2047] };
280
281 if (((msin.x * b.x) + (msin.y * b.y) >= 0) && ((v.x * b.x) + (v.y * b.y) < 0))
282 {
283 if (klabs((v.x * b.y) - (v.y * b.x)) < 65536 << 6)
284 {
285 pSprite->ang -= 512+(krand()&1024);
286 return 1;
287 }
288 }
289 }
290
291 return 0;
292 }
293
A_GetFurthestAngle(int const spriteNum,int const angDiv)294 int A_GetFurthestAngle(int const spriteNum, int const angDiv)
295 {
296 auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
297
298 if (pSprite->picnum != APLAYER && (AC_COUNT(actor[spriteNum].t_data)&63) > 2)
299 return (pSprite->ang + 1024) & 2047;
300
301 int furthestAngle = 0;
302 int const angIncs = tabledivide32_noinline(2048, angDiv);
303 int32_t greatestDist = INT32_MIN;
304 hitdata_t hit;
305
306 for (native_t j = pSprite->ang; j < (2048 + pSprite->ang); j += angIncs)
307 {
308 vec3_t origin = *(const vec3_t *)pSprite;
309 origin.z -= ZOFFSET3;
310 hitscan(&origin, pSprite->sectnum, sintable[(j + 512) & 2047], sintable[j & 2047], 0, &hit, CLIPMASK1);
311
312 int const hitDist = klabs(hit.pos.x-pSprite->x) + klabs(hit.pos.y-pSprite->y);
313
314 if (hitDist > greatestDist)
315 {
316 greatestDist = hitDist;
317 furthestAngle = j;
318 }
319 }
320
321 return furthestAngle & 2047;
322 }
323
A_FurthestVisiblePoint(int const spriteNum,uspriteptr_t const ts,vec2_t * const vect)324 int A_FurthestVisiblePoint(int const spriteNum, uspriteptr_t const ts, vec2_t * const vect)
325 {
326 if (AC_COUNT(actor[spriteNum].t_data)&63)
327 return -1;
328
329 auto const pnSprite = (uspriteptr_t)&sprite[spriteNum];
330
331 hitdata_t hit;
332 int const angincs = 128;
333 // ((!g_netServer && ud.multimode < 2) && ud.player_skill < 3) ? 2048 / 2 : tabledivide32_noinline(2048, 1 + (krand() & 1));
334
335 for (native_t j = ts->ang; j < (2048 + ts->ang); j += (angincs /*-(krand()&511)*/))
336 {
337 vec3_t origin = *(const vec3_t *)ts;
338 origin.z -= ZOFFSET2;
339 hitscan(&origin, ts->sectnum, sintable[(j + 512) & 2047], sintable[j & 2047], 16384 - (krand() & 32767), &hit, CLIPMASK1);
340
341 if (hit.sect < 0)
342 continue;
343
344 int const d = FindDistance2D(hit.pos.x - ts->x, hit.pos.y - ts->y);
345 int const da = FindDistance2D(hit.pos.x - pnSprite->x, hit.pos.y - pnSprite->y);
346
347 if (d < da)
348 {
349 if (cansee(hit.pos.x, hit.pos.y, hit.pos.z, hit.sect, pnSprite->x, pnSprite->y, pnSprite->z - ZOFFSET2, pnSprite->sectnum))
350 {
351 vect->x = hit.pos.x;
352 vect->y = hit.pos.y;
353 return hit.sect;
354 }
355 }
356 }
357
358 return -1;
359 }
360
getlzsum(uspriteptr_t pSprite,uspriteptr_t pTouched,int const wallDist)361 static inline uint16_t getlzsum(uspriteptr_t pSprite, uspriteptr_t pTouched, int const wallDist)
362 {
363 uint16_t lzsum = (uint16_t)pSprite->x ^ (uint16_t)pSprite->y ^ (uint16_t)pSprite->z ^ (uint16_t)sector[pSprite->sectnum].floorz
364 ^ (uint16_t)sector[pSprite->sectnum].ceilingz ^ wallDist ^ (uint16_t)pSprite->xvel ^ (uint16_t)pSprite->zvel;
365 if (pTouched) lzsum ^= (uint16_t)pTouched->x ^ (uint16_t)pTouched->y ^ (uint16_t)pTouched->z ^ (uint16_t)pTouched->xvel ^ (uint16_t)pTouched->zvel;
366 return lzsum;
367 }
368
VM_GetZRange(int const spriteNum,int32_t * const ceilhit,int32_t * const florhit,int const wallDist)369 void VM_GetZRange(int const spriteNum, int32_t* const ceilhit, int32_t* const florhit, int const wallDist)
370 {
371 auto const pSprite = &sprite[spriteNum];
372 auto pTouched = ((actor[spriteNum].florhit & 49152) == 49152) ? (uspriteptr_t)&sprite[actor[spriteNum].florhit & (MAXSPRITES-1)] : nullptr;
373
374 if (actor[spriteNum].lzsum == getlzsum((uspriteptr_t)pSprite, pTouched, wallDist))
375 return;
376
377 int const ocstat = pSprite->cstat;
378
379 pSprite->cstat = 0;
380 pSprite->z -= ACTOR_FLOOR_OFFSET;
381
382 getzrange(&pSprite->pos, pSprite->sectnum, &actor[spriteNum].ceilingz, ceilhit, &actor[spriteNum].floorz, florhit, wallDist, CLIPMASK0);
383
384 pSprite->z += ACTOR_FLOOR_OFFSET;
385 pSprite->cstat = ocstat;
386
387 actor[spriteNum].florhit = *florhit;
388 pTouched = ((actor[spriteNum].florhit & 49152) == 49152) ? (uspriteptr_t)&sprite[actor[spriteNum].florhit & (MAXSPRITES-1)] : nullptr;
389
390 actor[spriteNum].lzsum = getlzsum((uspriteptr_t)pSprite, pTouched, wallDist);
391 }
392
A_GetZLimits(int const spriteNum)393 void A_GetZLimits(int const spriteNum)
394 {
395 auto const pSprite = &sprite[spriteNum];
396 int32_t ceilhit = 0, florhit = actor[spriteNum].florhit;
397 int const clipDist = A_GetClipdist(spriteNum, -1);
398 auto const oceilz = actor[spriteNum].ceilingz;
399
400 VM_GetZRange(spriteNum, &ceilhit, &florhit, pSprite->statnum == STAT_PROJECTILE ? clipDist << 3 : clipDist);
401 actor[spriteNum].flags &= ~SFLAG_NOFLOORSHADOW;
402
403 if ((florhit&49152) == 49152 && (sprite[florhit&(MAXSPRITES-1)].cstat&48) == 0)
404 {
405 auto const hitspr = (uspriteptr_t)&sprite[florhit&(MAXSPRITES-1)];
406
407 florhit &= (MAXSPRITES-1);
408
409 // If a non-projectile would fall onto non-frozen enemy OR an enemy onto a player...
410 if ((A_CheckEnemySprite(hitspr) && hitspr->pal != 1 && pSprite->statnum != STAT_PROJECTILE)
411 || (hitspr->picnum == APLAYER && A_CheckEnemySprite(pSprite)))
412 {
413 actor[spriteNum].flags |= SFLAG_NOFLOORSHADOW; // No shadows on actors
414 pSprite->xvel = -256; // SLIDE_ABOVE_ENEMY
415 A_SetSprite(spriteNum, CLIPMASK0);
416 }
417 else if (pSprite->statnum == STAT_PROJECTILE && hitspr->picnum == APLAYER && pSprite->owner==florhit)
418 {
419 actor[spriteNum].ceilingz = sector[pSprite->sectnum].ceilingz;
420 actor[spriteNum].floorz = sector[pSprite->sectnum].floorz;
421 }
422 }
423
424 // in E1L1, the dumpster fire sprites break after calling this function because the cardboard boxes
425 // are a few units higher than the fire and are detected as the "ceiling"
426 // unfortunately, this trips the "ifgapzl 16 break" in "state firestate"
427 if ((ceilhit&49152) == 49152 && (sprite[ceilhit&(MAXSPRITES-1)].cstat&48) == 0
428 #ifndef EDUKE32_STANDALONE
429 // exclude squish sprites as they are sometimes used as pseudo-ladders in old usermaps
430 && (FURY || (actor[spriteNum].picnum != OOZ && actor[spriteNum].picnum != OOZ2))
431 #endif
432 )
433 {
434 if (pSprite->z >= actor[spriteNum].floorz)
435 actor[spriteNum].ceilingz = oceilz;
436 }
437 }
438
A_Fall(int const spriteNum)439 void A_Fall(int const spriteNum)
440 {
441 MICROPROFILE_SCOPEI("VM", EDUKE32_FUNCTION, MP_AUTO);
442
443 auto const pSprite = &sprite[spriteNum];
444 int spriteGravity = g_spriteGravity;
445
446 if (EDUKE32_PREDICT_FALSE(G_CheckForSpaceFloor(pSprite->sectnum)))
447 spriteGravity = 0;
448 else if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER || EDUKE32_PREDICT_FALSE(G_CheckForSpaceCeiling(pSprite->sectnum)))
449 spriteGravity = g_spriteGravity/6;
450
451 int32_t ceilhit, florhit;
452 VM_GetZRange(spriteNum, &ceilhit, &florhit, A_GetClipdist(spriteNum, -1));
453
454 #ifdef YAX_ENABLE
455 int fbunch = (sector[pSprite->sectnum].floorstat&512) ? -1 : yax_getbunch(pSprite->sectnum, YAX_FLOOR);
456 #endif
457
458 if (pSprite->z < actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET
459 #ifdef YAX_ENABLE
460 || fbunch >= 0
461 #endif
462 )
463 {
464 if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER && pSprite->zvel > 3122)
465 pSprite->zvel = 3144;
466 pSprite->z += pSprite->zvel = min(ACTOR_MAXFALLINGZVEL, pSprite->zvel+spriteGravity);
467 }
468
469 #ifdef YAX_ENABLE
470 if (fbunch >= 0)
471 setspritez(spriteNum, &pSprite->pos);
472 else
473 #endif
474 if (pSprite->z >= actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET)
475 {
476 pSprite->z = actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET;
477 pSprite->zvel = 0;
478 }
479 }
480
G_GetAngleDelta(int currAngle,int newAngle)481 int __fastcall G_GetAngleDelta(int currAngle, int newAngle)
482 {
483 currAngle &= 2047;
484 newAngle &= 2047;
485
486 if (klabs(currAngle-newAngle) < 1024)
487 {
488 // OSD_Printf("G_GetAngleDelta() returning %d\n",na-a);
489 return newAngle-currAngle;
490 }
491
492 if (newAngle > 1024)
493 newAngle -= 2048;
494 if (currAngle > 1024)
495 currAngle -= 2048;
496
497 // OSD_Printf("G_GetAngleDelta() returning %d\n",na-a);
498 return newAngle-currAngle;
499 }
500
VM_AlterAng(int32_t const moveFlags)501 GAMEEXEC_STATIC void VM_AlterAng(int32_t const moveFlags)
502 {
503 int const elapsedTics = (AC_COUNT(vm.pData))&31;
504
505 if (EDUKE32_PREDICT_FALSE((unsigned)AC_MOVE_ID(vm.pData) >= (unsigned)g_scriptSize-1))
506
507 {
508 AC_MOVE_ID(vm.pData) = 0;
509 OSD_Printf(OSD_ERROR "bad moveptr for actor %d (%d)!\n", vm.spriteNum, vm.pUSprite->picnum);
510 return;
511 }
512
513 auto const moveptr = apScript + AC_MOVE_ID(vm.pData);
514 auto &hvel = moveptr[0];
515 auto &vvel = moveptr[1];
516
517 vm.pSprite->xvel += (hvel - vm.pSprite->xvel)/5;
518 if (vm.pSprite->zvel < 648)
519 vm.pSprite->zvel += ((vvel<<4) - vm.pSprite->zvel)/5;
520
521 if (A_CheckEnemySprite(vm.pSprite) && vm.pSprite->extra <= 0) // hack
522 return;
523
524 if (moveFlags&seekplayer)
525 {
526 int const spriteAngle = vm.pSprite->ang;
527 int const holoDukeSprite = vm.pPlayer->holoduke_on;
528
529 // NOTE: looks like 'owner' is set to target sprite ID...
530
531 vm.pSprite->owner = (holoDukeSprite >= 0
532 && cansee(sprite[holoDukeSprite].x, sprite[holoDukeSprite].y, sprite[holoDukeSprite].z, sprite[holoDukeSprite].sectnum,
533 vm.pSprite->x, vm.pSprite->y, vm.pSprite->z, vm.pSprite->sectnum))
534 ? holoDukeSprite
535 : vm.pPlayer->i;
536
537 int const goalAng = (sprite[vm.pSprite->owner].picnum == APLAYER)
538 ? getangle(vm.pActor->lastv.x - vm.pSprite->x, vm.pActor->lastv.y - vm.pSprite->y)
539 : getangle(sprite[vm.pSprite->owner].x - vm.pSprite->x, sprite[vm.pSprite->owner].y - vm.pSprite->y);
540
541 if (vm.pSprite->xvel && vm.pSprite->picnum != DRONE)
542 {
543 int const angDiff = G_GetAngleDelta(spriteAngle, goalAng);
544
545 if (elapsedTics < 2)
546 {
547 if (klabs(angDiff) < 256)
548 {
549 int const angInc = 128-(krand()&256);
550 vm.pSprite->ang += angInc;
551 if (A_GetHitscanRange(vm.spriteNum) < 844)
552 vm.pSprite->ang -= angInc;
553 }
554 }
555 else if (elapsedTics > 18 && elapsedTics < GAMETICSPERSEC) // choose
556 {
557 if (klabs(angDiff >> 2) < 128)
558 vm.pSprite->ang = goalAng;
559 else
560 vm.pSprite->ang += angDiff >> 2;
561 }
562 }
563 else
564 vm.pSprite->ang = goalAng;
565 }
566
567 if (elapsedTics < 1)
568 {
569 if (moveFlags&furthestdir)
570 {
571 vm.pSprite->ang = A_GetFurthestAngle(vm.spriteNum, 2);
572 vm.pSprite->owner = vm.pPlayer->i;
573 }
574
575 if (moveFlags&fleeenemy)
576 vm.pSprite->ang = A_GetFurthestAngle(vm.spriteNum, 2);
577 }
578 }
579
VM_AddAngle(int const shift,int const goalAng)580 static inline void VM_AddAngle(int const shift, int const goalAng)
581 {
582 int angDiff = G_GetAngleDelta(vm.pSprite->ang, goalAng) >> shift;
583
584 if ((angDiff > -8 && angDiff < 0) || (angDiff < 8 && angDiff > 0))
585 angDiff <<= 1;
586
587 vm.pSprite->ang += angDiff;
588 }
589
VM_FacePlayer(int const shift)590 static inline void VM_FacePlayer(int const shift)
591 {
592 VM_AddAngle(shift, (vm.pPlayer->newowner >= 0) ? getangle(vm.pPlayer->opos.x - vm.pSprite->x, vm.pPlayer->opos.y - vm.pSprite->y)
593 : getangle(vm.pPlayer->pos.x - vm.pSprite->x, vm.pPlayer->pos.y - vm.pSprite->y));
594 }
595
VM_GetCeilZOfSlope(void)596 static inline int32_t VM_GetCeilZOfSlope(void)
597 {
598 vec2_t const vect = vm.pSprite->pos.vec2;
599 int const sectnum = vm.pSprite->sectnum;
600
601 return yax_getceilzofslope(sectnum, vect);
602 }
603
604 #ifndef EDUKE32_STANDALONE
VM_GetFlorZOfSlope(void)605 static inline int32_t VM_GetFlorZOfSlope(void)
606 {
607 vec2_t const vect = vm.pSprite->pos.vec2;
608 int const sectnum = vm.pSprite->sectnum;
609
610 return yax_getflorzofslope(sectnum, vect);
611 }
612 #endif
613
614 static int32_t A_GetWaterZOffset(int spritenum);
615
VM_Move(void)616 GAMEEXEC_STATIC void VM_Move(void)
617 {
618 MICROPROFILE_SCOPEI("VM", EDUKE32_FUNCTION, MP_AUTO);
619
620 auto const movflagsptr = &AC_MOVFLAGS(vm.pSprite, &actor[vm.spriteNum]);
621 // NOTE: test against -1 commented out and later revived in source history
622 // XXX: Does its presence/absence break anything? Where are movflags with all bits set created?
623 int const movflags = (*movflagsptr == (remove_pointer_t<decltype(movflagsptr)>)-1) ? 0 : *movflagsptr;
624 int const deadflag = (A_CheckEnemySprite(vm.pSprite) && vm.pSprite->extra <= 0);
625
626 AC_COUNT(vm.pData)++;
627
628 if (AC_MOVE_ID(vm.pData) == 0 || movflags == 0)
629 {
630 if (deadflag || (vm.pActor->bpos.x != vm.pSprite->x) || (vm.pActor->bpos.y != vm.pSprite->y))
631 setsprite(vm.spriteNum, &vm.pSprite->pos);
632
633 return;
634 }
635
636 if (!deadflag)
637 {
638 if (movflags & face_player)
639 VM_FacePlayer(2);
640
641 if (movflags & spin)
642 vm.pSprite->ang += sintable[((AC_COUNT(vm.pData) << 3) & 2047)] >> 6;
643
644 if (movflags & face_player_slow)
645 VM_FacePlayer(4);
646
647 if ((movflags & jumptoplayer_bits) == jumptoplayer_bits)
648 {
649 if (AC_COUNT(vm.pData) < 16)
650 vm.pSprite->zvel -= (sintable[(512 + (AC_COUNT(vm.pData) << 4)) & 2047] >> 5);
651 }
652
653 if (movflags & face_player_smart)
654 {
655 vec2_t const vect = { vm.pPlayer->pos.x + (vm.pPlayer->vel.x / 768), vm.pPlayer->pos.y + (vm.pPlayer->vel.y / 768) };
656 VM_AddAngle(2, getangle(vect.x - vm.pSprite->x, vect.y - vm.pSprite->y));
657 }
658 }
659
660 if (EDUKE32_PREDICT_FALSE((unsigned)AC_MOVE_ID(vm.pData) >= (unsigned)g_scriptSize-1))
661 {
662 AC_MOVE_ID(vm.pData) = 0;
663 OSD_Printf(OSD_ERROR "clearing bad moveptr for actor %d (%d)\n", vm.spriteNum, vm.pUSprite->picnum);
664 return;
665 }
666
667 auto const moveptr = apScript + AC_MOVE_ID(vm.pData);
668 auto &hvel = moveptr[0];
669 auto &vvel = moveptr[1];
670
671 if (movflags & geth)
672 vm.pSprite->xvel += (hvel - vm.pSprite->xvel) >> 1;
673
674 if (movflags & getv)
675 vm.pSprite->zvel += (16 * vvel - vm.pSprite->zvel) >> 1;
676
677 if (movflags&dodgebullet && !deadflag)
678 A_Dodge(vm.pSprite);
679
680 if (vm.pSprite->picnum != APLAYER)
681 VM_AlterAng(movflags);
682
683 if (vm.pSprite->xvel > -6 && vm.pSprite->xvel < 6)
684 vm.pSprite->xvel = 0;
685
686 int badguyp = A_CheckEnemySprite(vm.pSprite);
687
688 if (vm.pSprite->xvel || vm.pSprite->zvel)
689 {
690 int spriteXvel = vm.pSprite->xvel;
691 int angDiff = vm.pSprite->ang;
692
693 #ifndef EDUKE32_STANDALONE
694 if (badguyp && (FURY || vm.pSprite->picnum != ROTATEGUN))
695 {
696 if (!FURY && (vm.pSprite->picnum == DRONE || vm.pSprite->picnum == COMMANDER) && vm.pSprite->extra > 0)
697 {
698 if (vm.pSprite->picnum == COMMANDER)
699 {
700 int32_t nSectorZ;
701 // NOTE: COMMANDER updates both actor[].floorz and
702 // .ceilingz regardless of its zvel.
703 vm.pActor->floorz = nSectorZ = VM_GetFlorZOfSlope();
704 if (vm.pSprite->z > nSectorZ-ZOFFSET3)
705 {
706 vm.pSprite->z = nSectorZ-ZOFFSET3;
707 vm.pSprite->zvel = 0;
708 }
709
710 vm.pActor->ceilingz = nSectorZ = VM_GetCeilZOfSlope();
711 if (vm.pSprite->z < nSectorZ+(80<<8))
712 {
713 vm.pSprite->z = nSectorZ+(80<<8);
714 vm.pSprite->zvel = 0;
715 }
716 }
717 else
718 {
719 int32_t nSectorZ;
720 // The DRONE updates either .floorz or .ceilingz, not both.
721 if (vm.pSprite->zvel > 0)
722 {
723 vm.pActor->floorz = nSectorZ = VM_GetFlorZOfSlope();
724 if (vm.pSprite->z > nSectorZ-(30<<8))
725 vm.pSprite->z = nSectorZ-(30<<8);
726 }
727 else
728 {
729 vm.pActor->ceilingz = nSectorZ = VM_GetCeilZOfSlope();
730 if (vm.pSprite->z < nSectorZ+(50<<8))
731 {
732 vm.pSprite->z = nSectorZ+(50<<8);
733 vm.pSprite->zvel = 0;
734 }
735 }
736 }
737 }
738 else if ((FURY && badguyp) || vm.pSprite->picnum != ORGANTIC)
739 #else
740 if (badguyp)
741 {
742 #endif
743 {
744 // All other actors besides ORGANTIC don't update .floorz or
745 // .ceilingz here.
746 if (vm.pSprite->zvel > 0)
747 {
748 if (vm.pSprite->z > vm.pActor->floorz)
749 vm.pSprite->z = vm.pActor->floorz;
750 vm.pSprite->z += A_GetWaterZOffset(vm.spriteNum);
751 }
752 else if (vm.pSprite->zvel < 0)
753 {
754 int const l = VM_GetCeilZOfSlope();
755
756 if (vm.pSprite->z < l+(66<<8))
757 {
758 vm.pSprite->z = l+(66<<8);
759 vm.pSprite->zvel >>= 1;
760 }
761 }
762 }
763
764 if (vm.playerDist < 960 && vm.pSprite->xrepeat > 16)
765 {
766 spriteXvel = -(1024 - vm.playerDist);
767 angDiff = getangle(vm.pPlayer->pos.x - vm.pSprite->x, vm.pPlayer->pos.y - vm.pSprite->y);
768
769 if (vm.playerDist < 512)
770 {
771 vm.pPlayer->vel.x = 0;
772 vm.pPlayer->vel.y = 0;
773 }
774 else
775 {
776 vm.pPlayer->vel.x = mulscale16(vm.pPlayer->vel.x, vm.pPlayer->runspeed - 0x2000);
777 vm.pPlayer->vel.y = mulscale16(vm.pPlayer->vel.y, vm.pPlayer->runspeed - 0x2000);
778 }
779 }
780 else
781 #ifndef EDUKE32_STANDALONE
782 if (FURY || (vm.pSprite->picnum != DRONE && vm.pSprite->picnum != SHARK && vm.pSprite->picnum != COMMANDER))
783 #endif
784 {
785 if (vm.pPlayer->actorsqu == vm.spriteNum)
786 return;
787
788 if (!A_CheckSpriteFlags(vm.spriteNum, SFLAG_SMOOTHMOVE))
789 {
790 if (AC_COUNT(vm.pData) & 1)
791 return;
792 spriteXvel <<= 1;
793 }
794 }
795 }
796 else if (vm.pSprite->picnum == APLAYER)
797 if (vm.pSprite->z < vm.pActor->ceilingz+ZOFFSET5)
798 vm.pSprite->z = vm.pActor->ceilingz+ZOFFSET5;
799
800 vec3_t const vect
801 = { (spriteXvel * (sintable[(angDiff + 512) & 2047])) >> 14, (spriteXvel * (sintable[angDiff & 2047])) >> 14, vm.pSprite->zvel };
802
803 vm.pActor->movflag = A_MoveSprite(vm.spriteNum, &vect, (A_CheckSpriteFlags(vm.spriteNum, SFLAG_NOCLIP) ? 0 : CLIPMASK0));
804 }
805
806 if (!badguyp)
807 return;
808
809 vm.pSprite->shade += (sector[vm.pSprite->sectnum].ceilingstat & 1) ? (sector[vm.pSprite->sectnum].ceilingshade - vm.pSprite->shade) >> 1
810 : (sector[vm.pSprite->sectnum].floorshade - vm.pSprite->shade) >> 1;
811 }
812
813 static void P_AddWeaponMaybeSwitch(DukePlayer_t * const ps, int const weaponNum)
814 {
815 if ((ps->weaponswitch & (1|4)) == (1|4))
816 {
817 int const playerNum = P_Get(ps->i);
818 int new_wchoice = -1;
819 int curr_wchoice = -1;
820
821 for (native_t i=0; i<=FREEZE_WEAPON && (new_wchoice < 0 || curr_wchoice < 0); i++)
822 {
823 int w = g_player[playerNum].wchoice[i];
824
825 if (w == KNEE_WEAPON)
826 w = FREEZE_WEAPON;
827 else
828 w--;
829
830 if (w == ps->curr_weapon)
831 curr_wchoice = i;
832 if (w == weaponNum)
833 new_wchoice = i;
834 }
835
836 P_AddWeapon(ps, weaponNum, (new_wchoice < curr_wchoice));
837 }
838 else
839 {
840 P_AddWeapon(ps, weaponNum, (ps->weaponswitch & 1));
841 }
842 }
843
844 static void P_AddWeaponAmmoCommon(DukePlayer_t * const pPlayer, int const weaponNum, int const nAmount)
845 {
846 P_AddAmmo(pPlayer, weaponNum, nAmount);
847
848 if (PWEAPON(vm.playerNum, pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON && (pPlayer->gotweapon & (1 << weaponNum)))
849 P_AddWeaponMaybeSwitch(pPlayer, weaponNum);
850 }
851
852 static void VM_AddWeapon(DukePlayer_t * const pPlayer, int const weaponNum, int const nAmount)
853 {
854 if (EDUKE32_PREDICT_FALSE((unsigned)weaponNum >= MAX_WEAPONS))
855 {
856 CON_ERRPRINTF("invalid weapon %d\n", weaponNum);
857 return;
858 }
859
860 if ((pPlayer->gotweapon & (1 << weaponNum)) == 0)
861 {
862 P_AddWeaponMaybeSwitch(pPlayer, weaponNum);
863 }
864 else if (pPlayer->ammo_amount[weaponNum] >= pPlayer->max_ammo_amount[weaponNum])
865 {
866 vm.flags |= VM_NOEXECUTE;
867 return;
868 }
869
870 P_AddWeaponAmmoCommon(pPlayer, weaponNum, nAmount);
871 }
872
873 static void VM_AddAmmo(DukePlayer_t * const pPlayer, int const weaponNum, int const nAmount)
874 {
875 if (EDUKE32_PREDICT_FALSE((unsigned)weaponNum >= MAX_WEAPONS))
876 {
877 CON_ERRPRINTF("invalid weapon %d\n", weaponNum);
878 return;
879 }
880
881 if (pPlayer->ammo_amount[weaponNum] >= pPlayer->max_ammo_amount[weaponNum])
882 {
883 vm.flags |= VM_NOEXECUTE;
884 return;
885 }
886
887 P_AddWeaponAmmoCommon(pPlayer, weaponNum, nAmount);
888 }
889
890 static void VM_AddInventory(DukePlayer_t * const pPlayer, int const itemNum, int const nAmount)
891 {
892 switch (itemNum)
893 {
894 case GET_STEROIDS:
895 case GET_SCUBA:
896 case GET_HOLODUKE:
897 case GET_JETPACK:
898 case GET_HEATS:
899 case GET_FIRSTAID:
900 case GET_BOOTS:
901 pPlayer->inven_icon = inv_to_icon[itemNum];
902 pPlayer->inv_amount[itemNum] = nAmount;
903 break;
904
905 case GET_SHIELD:
906 {
907 int16_t & shield_amount = pPlayer->inv_amount[GET_SHIELD];
908 shield_amount = min(shield_amount + nAmount, pPlayer->max_shield_amount);
909 break;
910 }
911
912 case GET_ACCESS:
913 switch (vm.pSprite->pal)
914 {
915 case 0: pPlayer->got_access |= 1; break;
916 case 21: pPlayer->got_access |= 2; break;
917 case 23: pPlayer->got_access |= 4; break;
918 }
919 break;
920
921 default: CON_ERRPRINTF("invalid inventory item %d\n", itemNum); break;
922 }
923 }
924
925 static int A_GetVerticalVel(actor_t const * const pActor)
926 {
927 int32_t moveScriptOfs = AC_MOVE_ID(pActor->t_data);
928
929 return ((unsigned) moveScriptOfs < (unsigned) g_scriptSize - 1) ? apScript[moveScriptOfs + 1] : 0;
930 }
931
932 static int32_t A_GetWaterZOffset(int const spriteNum)
933 {
934 auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
935 auto const pActor = &actor[spriteNum];
936
937 if (sector[pSprite->sectnum].lotag == ST_1_ABOVE_WATER)
938 {
939 if (A_CheckSpriteFlags(spriteNum, SFLAG_NOWATERDIP))
940 return 0;
941
942 // fix for flying/jumping monsters getting stuck in water
943 if ((AC_MOVFLAGS(pSprite, pActor) & jumptoplayer_only) || (G_TileHasActor(pSprite->picnum) && A_GetVerticalVel(pActor) != 0))
944 return 0;
945
946 return ACTOR_ONWATER_ADDZ;
947 }
948
949 return 0;
950 }
951
952 static void VM_Fall(int const spriteNum, spritetype * const pSprite)
953 {
954 MICROPROFILE_SCOPEI("VM", EDUKE32_FUNCTION, MP_AUTO);
955
956 int spriteGravity = g_spriteGravity;
957
958 pSprite->xoffset = pSprite->yoffset = 0;
959
960 if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER || EDUKE32_PREDICT_FALSE(G_CheckForSpaceCeiling(pSprite->sectnum)))
961 spriteGravity = g_spriteGravity/6;
962 else if (EDUKE32_PREDICT_FALSE(G_CheckForSpaceFloor(pSprite->sectnum)))
963 spriteGravity = 0;
964
965 if (!actor[spriteNum].cgg-- || (sector[pSprite->sectnum].floorstat&2))
966 actor[spriteNum].cgg = 3;
967
968 A_GetZLimits(spriteNum);
969
970 if (pSprite->z < actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET)
971 {
972 // Free fall.
973 pSprite->zvel = min(pSprite->zvel+spriteGravity, ACTOR_MAXFALLINGZVEL);
974 int newZ = pSprite->z + pSprite->zvel;
975
976 #ifdef YAX_ENABLE
977 if (yax_getbunch(pSprite->sectnum, YAX_FLOOR) >= 0 && (sector[pSprite->sectnum].floorstat & 512) == 0)
978 setspritez(spriteNum, &pSprite->pos);
979 else
980 #endif
981 if (newZ > actor[spriteNum].floorz - ACTOR_FLOOR_OFFSET)
982 newZ = actor[spriteNum].floorz - ACTOR_FLOOR_OFFSET;
983
984 pSprite->z = newZ;
985 return;
986 }
987
988 // Preliminary new z position of the actor.
989 int newZ = actor[spriteNum].floorz - ACTOR_FLOOR_OFFSET;
990
991 if (A_CheckEnemySprite(pSprite) || (pSprite->picnum == APLAYER && pSprite->owner >= 0))
992 {
993 if (pSprite->zvel > 3084 && pSprite->extra <= 1)
994 {
995 // I'm guessing this DRONE check is from a beta version of the game
996 // where they crashed into the ground when killed
997 #ifndef EDUKE32_STANDALONE
998 if (!FURY && !(pSprite->picnum == APLAYER && pSprite->extra > 0) && pSprite->pal != 1 && pSprite->picnum != DRONE)
999 {
1000 A_DoGuts(spriteNum,JIBS6,15);
1001 A_PlaySound(SQUISHED,spriteNum);
1002 A_Spawn(spriteNum,BLOODPOOL);
1003 }
1004 #endif
1005 actor[spriteNum].picnum = SHOTSPARK1;
1006 actor[spriteNum].extra = 1;
1007 pSprite->zvel = 0;
1008 }
1009 else if (pSprite->zvel > 2048 && sector[pSprite->sectnum].lotag != ST_1_ABOVE_WATER)
1010 {
1011 int16_t newsect = pSprite->sectnum;
1012
1013 pushmove(&pSprite->pos, &newsect, 128, 4<<8, 4<<8, CLIPMASK0);
1014 if ((unsigned)newsect < MAXSECTORS)
1015 changespritesect(spriteNum, newsect);
1016
1017 #ifndef EDUKE32_STANDALONE
1018 if (!FURY)
1019 A_PlaySound(THUD, spriteNum);
1020 #endif
1021 }
1022 }
1023
1024 if (sector[pSprite->sectnum].lotag == ST_1_ABOVE_WATER && actor[spriteNum].floorz == yax_getflorzofslope(pSprite->sectnum, pSprite->pos.vec2))
1025 {
1026 pSprite->z = newZ + A_GetWaterZOffset(spriteNum);
1027 return;
1028 }
1029
1030 pSprite->z = newZ;
1031 pSprite->zvel = 0;
1032 }
1033
1034 static int32_t VM_ResetPlayer(int const playerNum, int32_t vmFlags, int32_t const resetFlags)
1035 {
1036 //AddLog("resetplayer");
1037 if (!g_netServer && ud.multimode < 2 && !(resetFlags & 2))
1038 {
1039 if (g_quickload && g_quickload->isValid() && ud.recstat != 2 && !(resetFlags & 8))
1040 {
1041 if (resetFlags & 4)
1042 {
1043 KB_FlushKeyboardQueue();
1044 KB_ClearKeysDown();
1045 FX_StopAllSounds();
1046 S_ClearSoundLocks();
1047 if (G_LoadPlayerMaybeMulti(*g_quickload) != 0)
1048 {
1049 g_quickload->reset();
1050 goto QuickLoadFailure;
1051 }
1052 }
1053 else if (!(resetFlags & 1))
1054 {
1055 Menu_Open(playerNum);
1056 KB_ClearKeyDown(sc_Space);
1057 I_AdvanceTriggerClear();
1058 Menu_Change(MENU_RESETPLAYER);
1059 }
1060 }
1061 else
1062 {
1063 QuickLoadFailure:
1064 g_player[playerNum].ps->gm = MODE_RESTART;
1065 }
1066 vmFlags |= VM_NOEXECUTE;
1067 }
1068 else
1069 {
1070 if (playerNum == myconnectindex)
1071 {
1072 CAMERADIST = 0;
1073 CAMERACLOCK = (int32_t) totalclock;
1074 }
1075
1076 if (g_fakeMultiMode)
1077 P_ResetMultiPlayer(playerNum);
1078 #ifndef NETCODE_DISABLE
1079 if (g_netServer)
1080 {
1081 P_ResetMultiPlayer(playerNum);
1082 Net_SpawnPlayer(playerNum);
1083 }
1084 #endif
1085 }
1086
1087 P_UpdateScreenPal(g_player[playerNum].ps);
1088 //AddLog("EOF: resetplayer");
1089
1090 return vmFlags;
1091 }
1092
1093 void G_GetTimeDate(int32_t * const pValues)
1094 {
1095 time_t timeStruct;
1096 time(&timeStruct);
1097 struct tm *pTime = localtime(&timeStruct);
1098
1099 // initprintf("Time&date: %s\n",asctime (ti));
1100
1101 pValues[0] = pTime->tm_sec;
1102 pValues[1] = pTime->tm_min;
1103 pValues[2] = pTime->tm_hour;
1104 pValues[3] = pTime->tm_mday;
1105 pValues[4] = pTime->tm_mon;
1106 pValues[5] = pTime->tm_year+1900;
1107 pValues[6] = pTime->tm_wday;
1108 pValues[7] = pTime->tm_yday;
1109 }
1110
1111 static int G_StartTrackSlot(int const volumeNum, int const levelNum)
1112 {
1113 if ((unsigned)volumeNum <= MAXVOLUMES && (unsigned)levelNum < MAXLEVELS)
1114 {
1115 int trackNum = MAXLEVELS*volumeNum + levelNum;
1116
1117 return S_TryPlaySpecialMusic(trackNum);
1118 }
1119
1120 return 1;
1121 }
1122
1123 static int G_StartTrackSlotWrap(int const volumeNum, int const levelNum)
1124 {
1125 if (EDUKE32_PREDICT_FALSE(G_StartTrackSlot(volumeNum, levelNum)))
1126 {
1127 CON_ERRPRINTF("invalid level %d or null music for volume %d level %d\n", levelNum, volumeNum, levelNum);
1128 return 1;
1129 }
1130
1131 return 0;
1132 }
1133
1134 static void G_ShowView(vec3_t vec, fix16_t a, fix16_t horiz, int sect, int ix1, int iy1, int ix2, int iy2, int unbiasedp)
1135 {
1136 int x1 = min(ix1, ix2);
1137 int x2 = max(ix1, ix2);
1138 int y1 = min(iy1, iy2);
1139 int y2 = max(iy1, iy2);
1140
1141 if (!unbiasedp)
1142 {
1143 // The showview command has a rounding bias towards zero,
1144 // e.g. floor((319*1680)/320) == 1674
1145 x1 = scale(x1,xdim,320);
1146 y1 = scale(y1,ydim,200);
1147 x2 = scale(x2,xdim,320);
1148 y2 = scale(y2,ydim,200);
1149 }
1150 else
1151 {
1152 // This will map the maximum 320-based coordinate to the
1153 // maximum real screen coordinate:
1154 // floor((319*1679)/319) == 1679
1155 x1 = scale(x1,xdim-1,319);
1156 y1 = scale(y1,ydim-1,199);
1157 x2 = scale(x2,xdim-1,319);
1158 y2 = scale(y2,ydim-1,199);
1159 }
1160
1161 horiz = fix16_clamp(horiz, F16(HORIZ_MIN), F16(HORIZ_MAX));
1162
1163 int const viewingRange = viewingrange;
1164 int const yxAspect = yxaspect;
1165
1166 videoSetViewableArea(x1,y1,x2,y2);
1167 renderSetAspect(viewingRange, yxAspect);
1168 int const smoothratio = calc_smoothratio(totalclock, ototalclock);
1169 G_DoInterpolations(smoothratio);
1170 if (!display_mirror)
1171 G_HandleMirror(vec.x, vec.y, vec.z, a, horiz, smoothratio);
1172 #ifdef POLYMER
1173 if (videoGetRenderMode() == REND_POLYMER)
1174 polymer_setanimatesprites(G_DoSpriteAnimations, vec.x, vec.y, vec.z, fix16_to_int(a), smoothratio);
1175 #endif
1176 yax_preparedrawrooms();
1177 renderDrawRoomsQ16(vec.x, vec.y, vec.z, a, horiz, sect);
1178 yax_drawrooms(G_DoSpriteAnimations, sect, 0, smoothratio);
1179
1180 display_mirror = 2;
1181 G_DoSpriteAnimations(vec.x, vec.y, vec.z, fix16_to_int(a), smoothratio);
1182 display_mirror = 0;
1183 renderDrawMasks();
1184 G_RestoreInterpolations();
1185 G_UpdateScreenArea();
1186 renderSetAspect(viewingRange, yxAspect);
1187 }
1188
1189 void Screen_Play(void)
1190 {
1191 bool running = true;
1192
1193 I_ClearAllInput();
1194
1195 do
1196 {
1197 gameHandleEvents();
1198
1199 ototalclock = totalclock + 1; // pause game like ANMs
1200
1201 if (!engineFPSLimit())
1202 continue;
1203
1204 videoClearScreen(0);
1205
1206 if (VM_OnEventWithReturn(EVENT_SCREEN, -1, myconnectindex, I_CheckAllInput()))
1207 running = false;
1208
1209 videoNextPage();
1210 I_ClearAllInput();
1211 } while (running);
1212 }
1213
1214 static inline void SetArray(int const arrayNum, int const arrayIndex, int const newValue)
1215 {
1216 auto &arr = aGameArrays[arrayNum];
1217
1218 #if 0 // this needs to be a compile time check, not something done at runtime...
1219 if (EDUKE32_PREDICT_FALSE(arr.flags & GAMEARRAY_READONLY))
1220 {
1221 OSD_Printf(OSD_ERROR "Tried to set value in read-only array `%s'", arr.szLabel);
1222 vm.flags |= VM_RETURN;
1223 return;
1224 }
1225 #endif
1226
1227 switch (arr.flags & GAMEARRAY_TYPE_MASK)
1228 {
1229 case 0: arr.pValues[arrayIndex] = newValue; break;
1230 case GAMEARRAY_INT16: ((int16_t *)arr.pValues)[arrayIndex] = newValue; break;
1231 case GAMEARRAY_INT8: ((int8_t *)arr.pValues)[arrayIndex] = newValue; break;
1232 case GAMEARRAY_UINT16: ((uint16_t *)arr.pValues)[arrayIndex] = newValue; break;
1233 case GAMEARRAY_UINT8: ((int8_t *)arr.pValues)[arrayIndex] = newValue; break;
1234 case GAMEARRAY_BITMAP:
1235 {
1236 uint32_t const mask = pow2char[arrayIndex&7];
1237 uint8_t &value = ((uint8_t *)arr.pValues)[arrayIndex>>3];
1238 value = (value & ~mask) | (-!!newValue & mask);
1239 break;
1240 }
1241 }
1242 }
1243
1244 static void ResizeArray(int const arrayNum, int const newSize)
1245 {
1246 auto &arr = aGameArrays[arrayNum];
1247
1248 int const oldSize = arr.size;
1249
1250 if (newSize == oldSize || newSize < 0)
1251 return;
1252 #if 0
1253 OSD_Printf(OSDTEXT_GREEN "CON_RESIZEARRAY: resizing array %s from %d to %d\n",
1254 array.szLabel, array.size, newSize);
1255 #endif
1256 if (newSize == 0)
1257 {
1258 Xaligned_free(arr.pValues);
1259 arr.pValues = nullptr;
1260 arr.size = 0;
1261 return;
1262 }
1263
1264 size_t const oldBytes = Gv_GetArrayAllocSizeForCount(arrayNum, oldSize);
1265 size_t const newBytes = Gv_GetArrayAllocSizeForCount(arrayNum, newSize);
1266
1267 auto const oldArray = arr.pValues;
1268 auto const newArray = (intptr_t *)Xaligned_alloc(ARRAY_ALIGNMENT, newBytes);
1269
1270 if (oldSize != 0)
1271 Bmemcpy(newArray, oldArray, min(oldBytes, newBytes));
1272
1273 if (newSize > oldSize)
1274 Bmemset((char *)newArray + oldBytes, 0, newBytes - oldBytes);
1275
1276 arr.pValues = newArray;
1277 arr.size = newSize;
1278
1279 Xaligned_free(oldArray);
1280 }
1281
1282 #if defined __GNUC__ || defined __clang__
1283 # define CON_USE_COMPUTED_GOTO
1284 #endif
1285
1286 #ifdef CON_USE_COMPUTED_GOTO
1287 # define vInstruction(KEYWORDID) VINST_ ## KEYWORDID
1288 # define vmErrorCase VINST_CON_OPCODE_END
1289 # define eval(INSTRUCTION) { goto *jumpTable[min<uint16_t>(INSTRUCTION, CON_OPCODE_END)]; }
1290 # define dispatch_unconditionally(...) { g_tw = tw = *insptr; eval((VM_DECODE_INST(tw))) }
1291 # define dispatch(...) { if (!vm_execution_depth || vm.flags & (VM_RETURN|VM_KILL|VM_NOEXECUTE)) return; dispatch_unconditionally(__VA_ARGS__); }
1292 # define abort_after_error(...) return
1293 # define vInstructionPointer(KEYWORDID) &&VINST_ ## KEYWORDID
1294 # define COMMA ,
1295 # define JUMP_TABLE_ARRAY_LITERAL { TRANSFORM_SCRIPT_KEYWORDS_LIST(vInstructionPointer, COMMA) }
1296 #else
1297 # define vInstruction(KEYWORDID) case KEYWORDID
1298 # define vmErrorCase default
1299 # define dispatch_unconditionally(...) continue
1300 # define dispatch(...) continue
1301 # define eval(INSTRUCTION) switch(INSTRUCTION)
1302 # define abort_after_error(...) continue // non-threaded dispatch handles this in the loop condition in VM_Execute()
1303 #endif
1304
1305 #if defined _MSC_VER
1306 #define VM_ASSERT(condition, fmt, ...) \
1307 if (EDUKE32_PREDICT_FALSE(!(condition))) \
1308 { \
1309 CON_ERRPRINTF(fmt, __VA_ARGS__); \
1310 abort_after_error(); \
1311 }
1312 #else
1313 #define VM_ASSERT(condition, ...) \
1314 if (EDUKE32_PREDICT_FALSE(!(condition))) \
1315 { \
1316 CON_ERRPRINTF(__VA_ARGS__); \
1317 abort_after_error(); \
1318 }
1319 #endif
1320
1321 GAMEEXEC_STATIC void VM_Execute(int const loop /*= false*/)
1322 {
1323 // be careful when changing this--the assignment used as a condition doubles as the nullptr check!
1324 auto branch = [&](int const x) {
1325 if (x || ((insptr = (intptr_t *)insptr[1]) && (VM_DECODE_INST(*insptr) == CON_ELSE)))
1326 {
1327 insptr += 2;
1328 VM_Execute();
1329 }
1330 };
1331
1332 int vm_execution_depth = loop;
1333 #ifdef CON_USE_COMPUTED_GOTO
1334 static void *const jumpTable[] = JUMP_TABLE_ARRAY_LITERAL;
1335 #else
1336 do
1337 {
1338 #endif
1339 int32_t tw = *insptr;
1340 g_tw = tw;
1341
1342 int const decoded = VM_DECODE_INST(tw);
1343 #if 0 && defined CON_USE_COMPUTED_GOTO
1344 // this is broken without CON_USE_COMPUTED_GOTO because it never goes out of scope
1345 MICROPROFILE_SCOPE_TOKEN(g_instTokens[decoded]);
1346 #endif
1347 eval(decoded)
1348 {
1349 vInstruction(CON_LEFTBRACE):
1350 {
1351 insptr++, vm_execution_depth++;
1352 dispatch_unconditionally();
1353 }
1354
1355 vInstruction(CON_RIGHTBRACE):
1356 {
1357 insptr++, vm_execution_depth--;
1358 dispatch();
1359 }
1360
1361 vInstruction(CON_ELSE):
1362 {
1363 insptr = (intptr_t *)insptr[1];
1364 dispatch_unconditionally();
1365 }
1366
1367 vInstruction(CON_STATE):
1368 {
1369 auto tempscrptr = &insptr[2];
1370 insptr = (intptr_t *)insptr[1];
1371 VM_Execute(true);
1372 insptr = tempscrptr;
1373 }
1374 dispatch();
1375
1376 vInstruction(CON_SETVAR_GLOBAL):
1377 insptr++;
1378 aGameVars[*insptr].global = insptr[1];
1379 insptr += 2;
1380 dispatch();
1381 vInstruction(CON_SETVAR_ACTOR):
1382 insptr++;
1383 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] = insptr[1];
1384 insptr += 2;
1385 dispatch();
1386 vInstruction(CON_SETVAR_PLAYER):
1387 insptr++;
1388 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] = insptr[1];
1389 insptr += 2;
1390 dispatch();
1391
1392 #ifdef CON_DISCRETE_VAR_ACCESS
1393 vInstruction(CON_IFVARE_GLOBAL):
1394 insptr++;
1395 tw = aGameVars[*insptr++].global;
1396 branch(tw == *insptr);
1397 dispatch();
1398 vInstruction(CON_IFVARN_GLOBAL):
1399 insptr++;
1400 tw = aGameVars[*insptr++].global;
1401 branch(tw != *insptr);
1402 dispatch();
1403 vInstruction(CON_IFVARAND_GLOBAL):
1404 insptr++;
1405 tw = aGameVars[*insptr++].global;
1406 branch(tw & *insptr);
1407 dispatch();
1408 vInstruction(CON_IFVAROR_GLOBAL):
1409 insptr++;
1410 tw = aGameVars[*insptr++].global;
1411 branch(tw | *insptr);
1412 dispatch();
1413 vInstruction(CON_IFVARXOR_GLOBAL):
1414 insptr++;
1415 tw = aGameVars[*insptr++].global;
1416 branch(tw ^ *insptr);
1417 dispatch();
1418 vInstruction(CON_IFVAREITHER_GLOBAL):
1419 insptr++;
1420 tw = aGameVars[*insptr++].global;
1421 branch(tw || *insptr);
1422 dispatch();
1423 vInstruction(CON_IFVARBOTH_GLOBAL):
1424 insptr++;
1425 tw = aGameVars[*insptr++].global;
1426 branch(tw && *insptr);
1427 dispatch();
1428 vInstruction(CON_IFVARG_GLOBAL):
1429 insptr++;
1430 tw = aGameVars[*insptr++].global;
1431 branch(tw > *insptr);
1432 dispatch();
1433 vInstruction(CON_IFVARGE_GLOBAL):
1434 insptr++;
1435 tw = aGameVars[*insptr++].global;
1436 branch(tw >= *insptr);
1437 dispatch();
1438 vInstruction(CON_IFVARL_GLOBAL):
1439 insptr++;
1440 tw = aGameVars[*insptr++].global;
1441 branch(tw < *insptr);
1442 dispatch();
1443 vInstruction(CON_IFVARLE_GLOBAL):
1444 insptr++;
1445 tw = aGameVars[*insptr++].global;
1446 branch(tw <= *insptr);
1447 dispatch();
1448 vInstruction(CON_IFVARA_GLOBAL):
1449 insptr++;
1450 tw = aGameVars[*insptr++].global;
1451 branch((uint32_t)tw > (uint32_t)*insptr);
1452 dispatch();
1453 vInstruction(CON_IFVARAE_GLOBAL):
1454 insptr++;
1455 tw = aGameVars[*insptr++].global;
1456 branch((uint32_t)tw >= (uint32_t)*insptr);
1457 dispatch();
1458 vInstruction(CON_IFVARB_GLOBAL):
1459 insptr++;
1460 tw = aGameVars[*insptr++].global;
1461 branch((uint32_t)tw < (uint32_t)*insptr);
1462 dispatch();
1463 vInstruction(CON_IFVARBE_GLOBAL):
1464 insptr++;
1465 tw = aGameVars[*insptr++].global;
1466 branch((uint32_t)tw <= (uint32_t)*insptr);
1467 dispatch();
1468
1469 vInstruction(CON_ADDVAR_GLOBAL):
1470 insptr++;
1471 aGameVars[*insptr].global += insptr[1];
1472 insptr += 2;
1473 dispatch();
1474 vInstruction(CON_SUBVAR_GLOBAL):
1475 insptr++;
1476 aGameVars[*insptr].global -= insptr[1];
1477 insptr += 2;
1478 dispatch();
1479 vInstruction(CON_MULVAR_GLOBAL):
1480 insptr++;
1481 aGameVars[*insptr].global *= insptr[1];
1482 insptr += 2;
1483 dispatch();
1484 vInstruction(CON_ANDVAR_GLOBAL):
1485 insptr++;
1486 aGameVars[*insptr].global &= insptr[1];
1487 insptr += 2;
1488 dispatch();
1489 vInstruction(CON_XORVAR_GLOBAL):
1490 insptr++;
1491 aGameVars[*insptr].global ^= insptr[1];
1492 insptr += 2;
1493 dispatch();
1494 vInstruction(CON_ORVAR_GLOBAL):
1495 insptr++;
1496 aGameVars[*insptr].global |= insptr[1];
1497 insptr += 2;
1498 dispatch();
1499 vInstruction(CON_SHIFTVARL_GLOBAL):
1500 insptr++;
1501 aGameVars[*insptr].global <<= insptr[1];
1502 insptr += 2;
1503 dispatch();
1504 vInstruction(CON_SHIFTVARR_GLOBAL):
1505 insptr++;
1506 aGameVars[*insptr].global >>= insptr[1];
1507 insptr += 2;
1508 dispatch();
1509
1510 vInstruction(CON_IFVARE_ACTOR):
1511 insptr++;
1512 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1513 branch(tw == *insptr);
1514 dispatch();
1515 vInstruction(CON_IFVARN_ACTOR):
1516 insptr++;
1517 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1518 branch(tw != *insptr);
1519 dispatch();
1520 vInstruction(CON_IFVARAND_ACTOR):
1521 insptr++;
1522 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1523 branch(tw & *insptr);
1524 dispatch();
1525 vInstruction(CON_IFVAROR_ACTOR):
1526 insptr++;
1527 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1528 branch(tw | *insptr);
1529 dispatch();
1530 vInstruction(CON_IFVARXOR_ACTOR):
1531 insptr++;
1532 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1533 branch(tw ^ *insptr);
1534 dispatch();
1535 vInstruction(CON_IFVAREITHER_ACTOR):
1536 insptr++;
1537 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1538 branch(tw || *insptr);
1539 dispatch();
1540 vInstruction(CON_IFVARBOTH_ACTOR):
1541 insptr++;
1542 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1543 branch(tw && *insptr);
1544 dispatch();
1545 vInstruction(CON_IFVARG_ACTOR):
1546 insptr++;
1547 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1548 branch(tw > *insptr);
1549 dispatch();
1550 vInstruction(CON_IFVARGE_ACTOR):
1551 insptr++;
1552 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1553 branch(tw >= *insptr);
1554 dispatch();
1555 vInstruction(CON_IFVARL_ACTOR):
1556 insptr++;
1557 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1558 branch(tw < *insptr);
1559 dispatch();
1560 vInstruction(CON_IFVARLE_ACTOR):
1561 insptr++;
1562 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1563 branch(tw <= *insptr);
1564 dispatch();
1565 vInstruction(CON_IFVARA_ACTOR):
1566 insptr++;
1567 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1568 branch((uint32_t)tw > (uint32_t)*insptr);
1569 dispatch();
1570 vInstruction(CON_IFVARAE_ACTOR):
1571 insptr++;
1572 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1573 branch((uint32_t)tw >= (uint32_t)*insptr);
1574 dispatch();
1575 vInstruction(CON_IFVARB_ACTOR):
1576 insptr++;
1577 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1578 branch((uint32_t)tw < (uint32_t)*insptr);
1579 dispatch();
1580 vInstruction(CON_IFVARBE_ACTOR):
1581 insptr++;
1582 tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)];
1583 branch((uint32_t)tw <= (uint32_t)*insptr);
1584 dispatch();
1585
1586 vInstruction(CON_ADDVAR_ACTOR):
1587 insptr++;
1588 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] += insptr[1];
1589 insptr += 2;
1590 dispatch();
1591 vInstruction(CON_SUBVAR_ACTOR):
1592 insptr++;
1593 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] -= insptr[1];
1594 insptr += 2;
1595 dispatch();
1596 vInstruction(CON_MULVAR_ACTOR):
1597 insptr++;
1598 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] *= insptr[1];
1599 insptr += 2;
1600 dispatch();
1601 vInstruction(CON_ANDVAR_ACTOR):
1602 insptr++;
1603 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] &= insptr[1];
1604 insptr += 2;
1605 dispatch();
1606 vInstruction(CON_XORVAR_ACTOR):
1607 insptr++;
1608 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] ^= insptr[1];
1609 insptr += 2;
1610 dispatch();
1611 vInstruction(CON_ORVAR_ACTOR):
1612 insptr++;
1613 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] |= insptr[1];
1614 insptr += 2;
1615 dispatch();
1616 vInstruction(CON_SHIFTVARL_ACTOR):
1617 insptr++;
1618 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] <<= insptr[1];
1619 insptr += 2;
1620 dispatch();
1621 vInstruction(CON_SHIFTVARR_ACTOR):
1622 insptr++;
1623 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] >>= insptr[1];
1624 insptr += 2;
1625 dispatch();
1626
1627 vInstruction(CON_IFVARE_PLAYER):
1628 insptr++;
1629 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1630 branch(tw == *insptr);
1631 dispatch();
1632 vInstruction(CON_IFVARN_PLAYER):
1633 insptr++;
1634 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1635 branch(tw != *insptr);
1636 dispatch();
1637 vInstruction(CON_IFVARAND_PLAYER):
1638 insptr++;
1639 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1640 branch(tw & *insptr);
1641 dispatch();
1642 vInstruction(CON_IFVAROR_PLAYER):
1643 insptr++;
1644 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1645 branch(tw | *insptr);
1646 dispatch();
1647 vInstruction(CON_IFVARXOR_PLAYER):
1648 insptr++;
1649 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1650 branch(tw ^ *insptr);
1651 dispatch();
1652 vInstruction(CON_IFVAREITHER_PLAYER):
1653 insptr++;
1654 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1655 branch(tw || *insptr);
1656 dispatch();
1657 vInstruction(CON_IFVARBOTH_PLAYER):
1658 insptr++;
1659 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1660 branch(tw && *insptr);
1661 dispatch();
1662 vInstruction(CON_IFVARG_PLAYER):
1663 insptr++;
1664 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1665 branch(tw > *insptr);
1666 dispatch();
1667 vInstruction(CON_IFVARGE_PLAYER):
1668 insptr++;
1669 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1670 branch(tw >= *insptr);
1671 dispatch();
1672 vInstruction(CON_IFVARL_PLAYER):
1673 insptr++;
1674 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1675 branch(tw < *insptr);
1676 dispatch();
1677 vInstruction(CON_IFVARLE_PLAYER):
1678 insptr++;
1679 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1680 branch(tw <= *insptr);
1681 dispatch();
1682 vInstruction(CON_IFVARA_PLAYER):
1683 insptr++;
1684 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1685 branch((uint32_t)tw > (uint32_t)*insptr);
1686 dispatch();
1687 vInstruction(CON_IFVARAE_PLAYER):
1688 insptr++;
1689 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1690 branch((uint32_t)tw >= (uint32_t)*insptr);
1691 dispatch();
1692 vInstruction(CON_IFVARB_PLAYER):
1693 insptr++;
1694 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1695 branch((uint32_t)tw < (uint32_t)*insptr);
1696 dispatch();
1697 vInstruction(CON_IFVARBE_PLAYER):
1698 insptr++;
1699 tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)];
1700 branch((uint32_t)tw <= (uint32_t)*insptr);
1701 dispatch();
1702
1703 vInstruction(CON_ADDVAR_PLAYER):
1704 insptr++;
1705 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] += insptr[1];
1706 insptr += 2;
1707 dispatch();
1708 vInstruction(CON_SUBVAR_PLAYER):
1709 insptr++;
1710 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] -= insptr[1];
1711 insptr += 2;
1712 dispatch();
1713 vInstruction(CON_MULVAR_PLAYER):
1714 insptr++;
1715 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] *= insptr[1];
1716 insptr += 2;
1717 dispatch();
1718 vInstruction(CON_ANDVAR_PLAYER):
1719 insptr++;
1720 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] &= insptr[1];
1721 insptr += 2;
1722 dispatch();
1723 vInstruction(CON_XORVAR_PLAYER):
1724 insptr++;
1725 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] ^= insptr[1];
1726 insptr += 2;
1727 dispatch();
1728 vInstruction(CON_ORVAR_PLAYER):
1729 insptr++;
1730 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] |= insptr[1];
1731 insptr += 2;
1732 dispatch();
1733 vInstruction(CON_SHIFTVARL_PLAYER):
1734 insptr++;
1735 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] <<= insptr[1];
1736 insptr += 2;
1737 dispatch();
1738 vInstruction(CON_SHIFTVARR_PLAYER):
1739 insptr++;
1740 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] >>= insptr[1];
1741 insptr += 2;
1742 dispatch();
1743
1744 vInstruction(CON_WHILEVARN_GLOBAL):
1745 {
1746 auto const savedinsptr = &insptr[2];
1747 do
1748 {
1749 insptr = savedinsptr;
1750 tw = (aGameVars[insptr[-1]].global != *insptr);
1751 branch(tw);
1752 } while (tw);
1753 dispatch();
1754 }
1755
1756 vInstruction(CON_WHILEVARL_GLOBAL):
1757 {
1758 auto const savedinsptr = &insptr[2];
1759 do
1760 {
1761 insptr = savedinsptr;
1762 tw = (aGameVars[insptr[-1]].global < *insptr);
1763 branch(tw);
1764 } while (tw);
1765 dispatch();
1766 }
1767
1768 vInstruction(CON_WHILEVARN_ACTOR):
1769 {
1770 auto const savedinsptr = &insptr[2];
1771 auto &v = aGameVars[savedinsptr[-1]].pValues[vm.spriteNum & (MAXSPRITES-1)];
1772 do
1773 {
1774 insptr = savedinsptr;
1775 tw = (v != *insptr);
1776 branch(tw);
1777 } while (tw);
1778
1779 dispatch();
1780 }
1781
1782 vInstruction(CON_WHILEVARL_ACTOR):
1783 {
1784 auto const savedinsptr = &insptr[2];
1785 auto &v = aGameVars[savedinsptr[-1]].pValues[vm.spriteNum & (MAXSPRITES-1)];
1786 do
1787 {
1788 insptr = savedinsptr;
1789 tw = (v < *insptr);
1790 branch(tw);
1791 } while (tw);
1792
1793 dispatch();
1794 }
1795
1796 vInstruction(CON_WHILEVARN_PLAYER):
1797 {
1798 auto const savedinsptr = &insptr[2];
1799 auto &v = aGameVars[savedinsptr[-1]].pValues[vm.playerNum & (MAXPLAYERS-1)];
1800 do
1801 {
1802 insptr = savedinsptr;
1803 tw = (v != *insptr);
1804 branch(tw);
1805 } while (tw);
1806
1807 dispatch();
1808 }
1809
1810 vInstruction(CON_WHILEVARL_PLAYER):
1811 {
1812 auto const savedinsptr = &insptr[2];
1813 auto &v = aGameVars[savedinsptr[-1]].pValues[vm.playerNum & (MAXPLAYERS-1)];
1814 do
1815 {
1816 insptr = savedinsptr;
1817 tw = (v < *insptr);
1818 branch(tw);
1819 } while (tw);
1820
1821 dispatch();
1822 }
1823
1824 vInstruction(CON_MODVAR_GLOBAL):
1825 insptr++;
1826 aGameVars[*insptr].global %= insptr[1];
1827 insptr += 2;
1828 dispatch();
1829 vInstruction(CON_MODVAR_ACTOR):
1830 insptr++;
1831 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] %= insptr[1];
1832 insptr += 2;
1833 dispatch();
1834 vInstruction(CON_MODVAR_PLAYER):
1835 insptr++;
1836 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] %= insptr[1];
1837 insptr += 2;
1838 dispatch();
1839 #endif
1840
1841 vInstruction(CON_IFVARAND):
1842 insptr++;
1843 tw = Gv_GetVar(*insptr++);
1844 branch(tw & *insptr);
1845 dispatch();
1846
1847 vInstruction(CON_IFVAROR):
1848 insptr++;
1849 tw = Gv_GetVar(*insptr++);
1850 branch(tw | *insptr);
1851 dispatch();
1852
1853 vInstruction(CON_IFVARXOR):
1854 insptr++;
1855 tw = Gv_GetVar(*insptr++);
1856 branch(tw ^ *insptr);
1857 dispatch();
1858
1859 vInstruction(CON_IFVAREITHER):
1860 insptr++;
1861 tw = Gv_GetVar(*insptr++);
1862 branch(tw || *insptr);
1863 dispatch();
1864
1865 vInstruction(CON_IFVARBOTH):
1866 insptr++;
1867 tw = Gv_GetVar(*insptr++);
1868 branch(tw && *insptr);
1869 dispatch();
1870
1871 vInstruction(CON_IFRND):
1872 branch(rnd(*(++insptr)));
1873 dispatch();
1874
1875 vInstruction(CON_IFVARG):
1876 insptr++;
1877 tw = Gv_GetVar(*insptr++);
1878 branch(tw > *insptr);
1879 dispatch();
1880
1881 vInstruction(CON_IFVARGE):
1882 insptr++;
1883 tw = Gv_GetVar(*insptr++);
1884 branch(tw >= *insptr);
1885 dispatch();
1886
1887 vInstruction(CON_IFVARL):
1888 insptr++;
1889 tw = Gv_GetVar(*insptr++);
1890 branch(tw < *insptr);
1891 dispatch();
1892
1893 vInstruction(CON_IFVARLE):
1894 insptr++;
1895 tw = Gv_GetVar(*insptr++);
1896 branch(tw <= *insptr);
1897 dispatch();
1898
1899 vInstruction(CON_IFVARA):
1900 insptr++;
1901 tw = Gv_GetVar(*insptr++);
1902 branch((uint32_t)tw > (uint32_t)*insptr);
1903 dispatch();
1904
1905 vInstruction(CON_IFVARAE):
1906 insptr++;
1907 tw = Gv_GetVar(*insptr++);
1908 branch((uint32_t)tw >= (uint32_t)*insptr);
1909 dispatch();
1910
1911 vInstruction(CON_IFVARB):
1912 insptr++;
1913 tw = Gv_GetVar(*insptr++);
1914 branch((uint32_t)tw < (uint32_t)*insptr);
1915 dispatch();
1916
1917 vInstruction(CON_IFVARBE):
1918 insptr++;
1919 tw = Gv_GetVar(*insptr++);
1920 branch((uint32_t)tw <= (uint32_t)*insptr);
1921 dispatch();
1922
1923 vInstruction(CON_SETVARVAR):
1924 insptr++;
1925 {
1926 tw = *insptr++;
1927 int const nValue = Gv_GetVar(*insptr++);
1928
1929 if ((aGameVars[tw].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) == 0)
1930 aGameVars[tw].global = nValue;
1931 else
1932 Gv_SetVar(tw, nValue);
1933 }
1934 dispatch();
1935
1936 vInstruction(CON_ADDVARVAR):
1937 insptr++;
1938 tw = *insptr++;
1939 Gv_AddVar(tw, Gv_GetVar(*insptr++));
1940 dispatch();
1941
1942 vInstruction(CON_SUBVARVAR):
1943 insptr++;
1944 tw = *insptr++;
1945 Gv_SubVar(tw, Gv_GetVar(*insptr++));
1946 dispatch();
1947
1948 vInstruction(CON_ANDVARVAR):
1949 insptr++;
1950 tw = *insptr++;
1951 Gv_AndVar(tw, Gv_GetVar(*insptr++));
1952 dispatch();
1953
1954 vInstruction(CON_XORVARVAR):
1955 insptr++;
1956 tw = *insptr++;
1957 Gv_XorVar(tw, Gv_GetVar(*insptr++));
1958 dispatch();
1959
1960 vInstruction(CON_ORVARVAR):
1961 insptr++;
1962 tw = *insptr++;
1963 Gv_OrVar(tw, Gv_GetVar(*insptr++));
1964 dispatch();
1965
1966 vInstruction(CON_SHIFTVARVARL):
1967 insptr++;
1968 tw = *insptr++;
1969 Gv_ShiftVarL(tw, Gv_GetVar(*insptr++));
1970 dispatch();
1971
1972 vInstruction(CON_SHIFTVARVARR):
1973 insptr++;
1974 tw = *insptr++;
1975 Gv_ShiftVarR(tw, Gv_GetVar(*insptr++));
1976 dispatch();
1977
1978 vInstruction(CON_MULVARVAR):
1979 insptr++;
1980 tw = *insptr++;
1981 Gv_MulVar(tw, Gv_GetVar(*insptr++));
1982 dispatch();
1983
1984 #ifdef CON_DISCRETE_VAR_ACCESS
1985 vInstruction(CON_DIVVAR_GLOBAL):
1986 insptr++;
1987 aGameVars[*insptr].global = tabledivide32(aGameVars[*insptr].global, insptr[1]);
1988 insptr += 2;
1989 dispatch();
1990
1991 vInstruction(CON_DIVVAR_PLAYER):
1992 {
1993 insptr++;
1994 auto &v = aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS - 1)];
1995
1996 v = tabledivide32(v, insptr[1]);
1997 insptr += 2;
1998 dispatch();
1999 }
2000
2001 vInstruction(CON_DIVVAR_ACTOR):
2002 {
2003 insptr++;
2004 auto &v = aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES - 1)];
2005
2006 v = tabledivide32(v, insptr[1]);
2007 insptr += 2;
2008 dispatch();
2009 }
2010 #endif
2011
2012 vInstruction(CON_DIVVARVAR):
2013 insptr++;
2014 {
2015 tw = *insptr++;
2016
2017 int const nValue = Gv_GetVar(*insptr++);
2018
2019 VM_ASSERT(nValue, "divide by zero!\n");
2020
2021 Gv_DivVar(tw, nValue);
2022 dispatch();
2023 }
2024
2025 vInstruction(CON_IFVARE):
2026 insptr++;
2027 tw = Gv_GetVar(*insptr++);
2028 branch(tw == *insptr);
2029 dispatch();
2030
2031 vInstruction(CON_IFVARN):
2032 insptr++;
2033 tw = Gv_GetVar(*insptr++);
2034 branch(tw != *insptr);
2035 dispatch();
2036
2037 vInstruction(CON_IFVARVARE):
2038 insptr++;
2039 tw = Gv_GetVar(*insptr++);
2040 tw = (tw == Gv_GetVar(*insptr++));
2041 insptr--;
2042 branch(tw);
2043 dispatch();
2044
2045 vInstruction(CON_IFVARVARN):
2046 insptr++;
2047 tw = Gv_GetVar(*insptr++);
2048 tw = (tw != Gv_GetVar(*insptr++));
2049 insptr--;
2050 branch(tw);
2051 dispatch();
2052
2053 vInstruction(CON_IFVARVARG):
2054 insptr++;
2055 tw = Gv_GetVar(*insptr++);
2056 tw = (tw > Gv_GetVar(*insptr++));
2057 insptr--;
2058 branch(tw);
2059 dispatch();
2060
2061 vInstruction(CON_IFVARVARGE):
2062 insptr++;
2063 tw = Gv_GetVar(*insptr++);
2064 tw = (tw >= Gv_GetVar(*insptr++));
2065 insptr--;
2066 branch(tw);
2067 dispatch();
2068
2069 vInstruction(CON_IFVARVARL):
2070 insptr++;
2071 tw = Gv_GetVar(*insptr++);
2072 tw = (tw < Gv_GetVar(*insptr++));
2073 insptr--;
2074 branch(tw);
2075 dispatch();
2076
2077 vInstruction(CON_IFVARVARLE):
2078 insptr++;
2079 tw = Gv_GetVar(*insptr++);
2080 tw = (tw <= Gv_GetVar(*insptr++));
2081 insptr--;
2082 branch(tw);
2083 dispatch();
2084
2085 vInstruction(CON_IFVARVARA):
2086 insptr++;
2087 tw = Gv_GetVar(*insptr++);
2088 tw = ((uint32_t)tw > (uint32_t)Gv_GetVar(*insptr++));
2089 insptr--;
2090 branch(tw);
2091 dispatch();
2092
2093 vInstruction(CON_IFVARVARAE):
2094 insptr++;
2095 tw = Gv_GetVar(*insptr++);
2096 tw = ((uint32_t)tw >= (uint32_t)Gv_GetVar(*insptr++));
2097 insptr--;
2098 branch(tw);
2099 dispatch();
2100
2101 vInstruction(CON_IFVARVARB):
2102 insptr++;
2103 tw = Gv_GetVar(*insptr++);
2104 tw = ((uint32_t)tw < (uint32_t)Gv_GetVar(*insptr++));
2105 insptr--;
2106 branch(tw);
2107 dispatch();
2108
2109 vInstruction(CON_IFVARVARBE):
2110 insptr++;
2111 tw = Gv_GetVar(*insptr++);
2112 tw = ((uint32_t)tw <= (uint32_t)Gv_GetVar(*insptr++));
2113 insptr--;
2114 branch(tw);
2115 dispatch();
2116
2117 vInstruction(CON_IFVARVARAND):
2118 insptr++;
2119 tw = Gv_GetVar(*insptr++);
2120 tw &= Gv_GetVar(*insptr++);
2121 insptr--;
2122 branch(tw);
2123 dispatch();
2124
2125 vInstruction(CON_IFVARVAROR):
2126 insptr++;
2127 tw = Gv_GetVar(*insptr++);
2128 tw |= Gv_GetVar(*insptr++);
2129 insptr--;
2130 branch(tw);
2131 dispatch();
2132
2133 vInstruction(CON_IFVARVARXOR):
2134 insptr++;
2135 tw = Gv_GetVar(*insptr++);
2136 tw ^= Gv_GetVar(*insptr++);
2137 insptr--;
2138 branch(tw);
2139 dispatch();
2140
2141 vInstruction(CON_IFVARVAREITHER):
2142 insptr++;
2143 tw = Gv_GetVar(*insptr++);
2144 tw = (Gv_GetVar(*insptr++) || tw);
2145 insptr--;
2146 branch(tw);
2147 dispatch();
2148
2149 vInstruction(CON_IFVARVARBOTH):
2150 insptr++;
2151 tw = Gv_GetVar(*insptr++);
2152 tw = (Gv_GetVar(*insptr++) && tw);
2153 insptr--;
2154 branch(tw);
2155 dispatch();
2156
2157 vInstruction(CON_WHILEVARN):
2158 {
2159 auto const savedinsptr = &insptr[2];
2160 do
2161 {
2162 insptr = savedinsptr;
2163 branch((tw = (Gv_GetVar(insptr[-1]) != *insptr)));
2164 } while (tw);
2165 dispatch();
2166 }
2167
2168 vInstruction(CON_WHILEVARVARN):
2169 {
2170 auto const savedinsptr = &insptr[2];
2171 do
2172 {
2173 insptr = savedinsptr;
2174 tw = Gv_GetVar(insptr[-1]);
2175 tw = (tw != Gv_GetVar(*insptr++));
2176 insptr--;
2177 branch(tw);
2178 } while (tw);
2179 dispatch();
2180 }
2181
2182 vInstruction(CON_WHILEVARL):
2183 {
2184 auto const savedinsptr = &insptr[2];
2185 do
2186 {
2187 insptr = savedinsptr;
2188 branch((tw = (Gv_GetVar(insptr[-1]) < *insptr)));
2189 } while (tw);
2190 dispatch();
2191 }
2192
2193 vInstruction(CON_WHILEVARVARL):
2194 {
2195 auto const savedinsptr = &insptr[2];
2196 do
2197 {
2198 insptr = savedinsptr;
2199 tw = Gv_GetVar(insptr[-1]);
2200 tw = (tw < Gv_GetVar(*insptr++));
2201 insptr--;
2202 branch(tw);
2203 } while (tw);
2204 dispatch();
2205 }
2206
2207 vInstruction(CON_SETVAR):
2208 Gv_SetVar(insptr[1], insptr[2]);
2209 insptr += 3;
2210 dispatch();
2211
2212 vInstruction(CON_ADDVAR):
2213 Gv_AddVar(insptr[1], insptr[2]);
2214 insptr += 3;
2215 dispatch();
2216
2217 vInstruction(CON_SUBVAR):
2218 Gv_SubVar(insptr[1], insptr[2]);
2219 insptr += 3;
2220 dispatch();
2221
2222 vInstruction(CON_MULVAR):
2223 Gv_MulVar(insptr[1], insptr[2]);
2224 insptr += 3;
2225 dispatch();
2226
2227 vInstruction(CON_DIVVAR):
2228 Gv_DivVar(insptr[1], insptr[2]);
2229 insptr += 3;
2230 dispatch();
2231
2232 vInstruction(CON_ANDVAR):
2233 Gv_AndVar(insptr[1], insptr[2]);
2234 insptr += 3;
2235 dispatch();
2236
2237 vInstruction(CON_XORVAR):
2238 Gv_XorVar(insptr[1], insptr[2]);
2239 insptr += 3;
2240 dispatch();
2241
2242 vInstruction(CON_ORVAR):
2243 Gv_OrVar(insptr[1], insptr[2]);
2244 insptr += 3;
2245 dispatch();
2246
2247 vInstruction(CON_SHIFTVARL):
2248 Gv_ShiftVarL(insptr[1], insptr[2]);
2249 insptr += 3;
2250 dispatch();
2251
2252 vInstruction(CON_SHIFTVARR):
2253 Gv_ShiftVarR(insptr[1], insptr[2]);
2254 insptr += 3;
2255 dispatch();
2256
2257 vInstruction(CON_MODVAR):
2258 Gv_ModVar(insptr[1], insptr[2]);
2259 insptr += 3;
2260 dispatch();
2261
2262 vInstruction(CON_MODVARVAR):
2263 insptr++;
2264 {
2265 tw = *insptr++;
2266
2267 int const nValue = Gv_GetVar(*insptr++);
2268
2269 VM_ASSERT(nValue, "mod by zero!\n");
2270
2271 Gv_ModVar(tw, nValue);
2272 dispatch();
2273 }
2274
2275 vInstruction(CON_RANDVAR):
2276 insptr++;
2277 Gv_SetVar(*insptr, mulscale16(krand(), insptr[1] + 1));
2278 insptr += 2;
2279 dispatch();
2280
2281 #ifdef CON_DISCRETE_VAR_ACCESS
2282 vInstruction(CON_RANDVAR_GLOBAL):
2283 insptr++;
2284 aGameVars[*insptr].global = mulscale16(krand(), insptr[1] + 1);
2285 insptr += 2;
2286 dispatch();
2287
2288 vInstruction(CON_RANDVAR_PLAYER):
2289 insptr++;
2290 aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] = mulscale16(krand(), insptr[1] + 1);
2291 insptr += 2;
2292 dispatch();
2293
2294 vInstruction(CON_RANDVAR_ACTOR):
2295 insptr++;
2296 aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] = mulscale16(krand(), insptr[1] + 1);
2297 insptr += 2;
2298 dispatch();
2299 #endif
2300
2301 vInstruction(CON_RANDVARVAR):
2302 insptr++;
2303 tw = *insptr++;
2304 Gv_SetVar(tw, mulscale16(krand(), Gv_GetVar(*insptr++) + 1));
2305 dispatch();
2306
2307 vInstruction(CON_SETPLAYER):
2308 insptr++;
2309 {
2310 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
2311 tw = *insptr++;
2312 auto const &playerLabel = PlayerLabels[tw];
2313 int const lParm2 = (playerLabel.flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0;
2314
2315 VM_ASSERT((unsigned)playerNum < MAXSPRITES && ((playerLabel.flags & LABEL_HASPARM2) == 0 || (unsigned)lParm2 < (unsigned)playerLabel.maxParm2),
2316 "%s[%d] invalid for player %d\n", playerLabel.name, lParm2, playerNum);
2317
2318 VM_SetPlayer(playerNum, tw, lParm2, Gv_GetVar(*insptr++));
2319 dispatch();
2320 }
2321
2322 vInstruction(CON_GETPLAYER):
2323 insptr++;
2324 {
2325 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
2326 tw = *insptr++;
2327 auto const &playerLabel = PlayerLabels[tw];
2328 int const lParm2 = (playerLabel.flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0;
2329
2330 VM_ASSERT((unsigned)playerNum < MAXSPRITES && ((playerLabel.flags & LABEL_HASPARM2) == 0 || (unsigned)lParm2 < (unsigned)playerLabel.maxParm2),
2331 "%s[%d] invalid for player %d\n", playerLabel.name, lParm2, playerNum);
2332
2333 Gv_SetVar(*insptr++, VM_GetPlayer(playerNum, tw, lParm2));
2334 dispatch();
2335 }
2336
2337 vInstruction(CON_SETPLAYERSTRUCT):
2338 insptr++;
2339 {
2340 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
2341 VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum);
2342 auto const &playerLabel = PlayerLabels[*insptr++];
2343
2344 VM_SetStruct(playerLabel.flags, (intptr_t *)((char *)&g_player[playerNum].ps[0] + playerLabel.offset), Gv_GetVar(*insptr++));
2345 dispatch();
2346 }
2347
2348 vInstruction(CON_GETPLAYERSTRUCT):
2349 insptr++;
2350 {
2351 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
2352 VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum);
2353 auto const &playerLabel = PlayerLabels[*insptr++];
2354
2355 Gv_SetVar(*insptr++, VM_GetStruct(playerLabel.flags, (intptr_t *)((char *)&g_player[playerNum].ps[0] + playerLabel.offset)));
2356 dispatch();
2357 }
2358
2359 vInstruction(CON_SETWALL):
2360 insptr++;
2361 {
2362 int const wallNum = Gv_GetVar(*insptr++);
2363 VM_ASSERT((unsigned)wallNum < MAXWALLS, "invalid wall %d\n", wallNum);
2364
2365 tw = *insptr++;
2366
2367 VM_SetWall(wallNum, tw, Gv_GetVar(*insptr++));
2368 dispatch();
2369 }
2370
2371 vInstruction(CON_GETWALL):
2372 insptr++;
2373 {
2374 int const wallNum = Gv_GetVar(*insptr++);
2375 VM_ASSERT((unsigned)wallNum < MAXWALLS, "invalid wall %d\n", wallNum);
2376
2377 tw = *insptr++;
2378 Gv_SetVar(*insptr++, VM_GetWall(wallNum, tw));
2379 dispatch();
2380 }
2381
2382 vInstruction(CON_SETWALLSTRUCT):
2383 insptr++;
2384 {
2385 int const wallNum = Gv_GetVar(*insptr++);
2386 VM_ASSERT((unsigned)wallNum < MAXWALLS, "invalid wall %d\n", wallNum);
2387
2388 auto const &wallLabel = WallLabels[*insptr++];
2389
2390 VM_SetStruct(wallLabel.flags, (intptr_t *)((char *)&wall[wallNum] + wallLabel.offset), Gv_GetVar(*insptr++));
2391 dispatch();
2392 }
2393
2394 vInstruction(CON_GETWALLSTRUCT):
2395 insptr++;
2396 {
2397 int const wallNum = Gv_GetVar(*insptr++);
2398 VM_ASSERT((unsigned)wallNum < MAXWALLS, "invalid wall %d\n", wallNum);
2399 auto const &wallLabel = WallLabels[*insptr++];
2400
2401 Gv_SetVar(*insptr++, VM_GetStruct(wallLabel.flags, (intptr_t *)((char *)&wall[wallNum] + wallLabel.offset)));
2402 dispatch();
2403 }
2404
2405 vInstruction(CON_SETACTORVAR):
2406 insptr++;
2407 {
2408 int const lSprite = Gv_GetVar(*insptr++);
2409 VM_ASSERT((unsigned)lSprite < MAXSPRITES, "invalid sprite %d\n", lSprite);
2410
2411 tw = *insptr++;
2412
2413 Gv_SetVar(tw, Gv_GetVar(*insptr++), lSprite, vm.playerNum);
2414
2415 dispatch();
2416 }
2417
2418 vInstruction(CON_GETACTORVAR):
2419 insptr++;
2420 {
2421 int const lSprite = Gv_GetVar(*insptr++);
2422 VM_ASSERT((unsigned)lSprite < MAXSPRITES, "invalid sprite %d\n", lSprite);
2423
2424 tw = Gv_GetVar(*insptr++, lSprite, vm.playerNum);
2425
2426 Gv_SetVar(*insptr++, tw);
2427
2428 dispatch();
2429 }
2430
2431 vInstruction(CON_SETPLAYERVAR):
2432 insptr++;
2433 {
2434 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
2435 VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum);
2436
2437 tw = *insptr++;
2438
2439 Gv_SetVar(tw, Gv_GetVar(*insptr++), vm.spriteNum, playerNum);
2440
2441 dispatch();
2442 }
2443
2444 vInstruction(CON_GETPLAYERVAR):
2445 insptr++;
2446 {
2447 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
2448 VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum);
2449
2450 tw = Gv_GetVar(*insptr++, vm.spriteNum, playerNum);
2451
2452 Gv_SetVar(*insptr++, tw);
2453
2454 dispatch();
2455 }
2456
2457 vInstruction(CON_SETACTOR):
2458 insptr++;
2459 {
2460 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2461 tw = *insptr++;
2462 auto const &actorLabel = ActorLabels[tw];
2463 int const lParm2 = (actorLabel.flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0;
2464
2465 VM_ASSERT((unsigned)spriteNum < MAXSPRITES && ((actorLabel.flags & LABEL_HASPARM2) == 0 || (unsigned)lParm2 < (unsigned)actorLabel.maxParm2),
2466 "%s[%d] invalid for sprite %d\n", actorLabel.name, lParm2, spriteNum);
2467
2468 VM_SetSprite(spriteNum, tw, lParm2, Gv_GetVar(*insptr++));
2469 dispatch();
2470 }
2471
2472 vInstruction(CON_GETACTOR):
2473 insptr++;
2474 {
2475 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2476 tw = *insptr++;
2477 auto const &actorLabel = ActorLabels[tw];
2478 int const lParm2 = (actorLabel.flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0;
2479
2480 VM_ASSERT((unsigned)spriteNum < MAXSPRITES && ((actorLabel.flags & LABEL_HASPARM2) == 0 || (unsigned)lParm2 < (unsigned)actorLabel.maxParm2),
2481 "%s[%d] invalid for sprite %d\n", actorLabel.name, lParm2, spriteNum);
2482
2483 Gv_SetVar(*insptr++, VM_GetSprite(spriteNum, tw, lParm2));
2484 dispatch();
2485 }
2486
2487 vInstruction(CON_SETACTORSTRUCT):
2488 insptr++;
2489 {
2490 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2491 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2492
2493 auto const &actorLabel = ActorLabels[*insptr++];
2494
2495 VM_SetStruct(actorLabel.flags, (intptr_t *)((char *)&actor[spriteNum] + actorLabel.offset), Gv_GetVar(*insptr++));
2496 dispatch();
2497 }
2498
2499 vInstruction(CON_GETACTORSTRUCT):
2500 insptr++;
2501 {
2502 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2503 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2504
2505 auto const &actorLabel = ActorLabels[*insptr++];
2506
2507 Gv_SetVar(*insptr++, VM_GetStruct(actorLabel.flags, (intptr_t *)((char *)&actor[spriteNum] + actorLabel.offset)));
2508 dispatch();
2509 }
2510
2511 vInstruction(CON_SETSPRITESTRUCT):
2512 insptr++;
2513 {
2514 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2515 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2516 auto const &spriteLabel = ActorLabels[*insptr++];
2517
2518 VM_SetStruct(spriteLabel.flags, (intptr_t *)((char *)&sprite[spriteNum] + spriteLabel.offset), Gv_GetVar(*insptr++));
2519 dispatch();
2520 }
2521
2522 vInstruction(CON_GETSPRITESTRUCT):
2523 insptr++;
2524 {
2525 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2526 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2527
2528 auto const &spriteLabel = ActorLabels[*insptr++];
2529
2530 Gv_SetVar(*insptr++, VM_GetStruct(spriteLabel.flags, (intptr_t *)((char *)&sprite[spriteNum] + spriteLabel.offset)));
2531 dispatch();
2532 }
2533 vInstruction(CON_SETSPRITEEXT):
2534 insptr++;
2535 {
2536 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2537 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2538
2539 auto const &spriteExtLabel = ActorLabels[*insptr++];
2540
2541 VM_SetStruct(spriteExtLabel.flags, (intptr_t *)((char *)&spriteext[spriteNum] + spriteExtLabel.offset), Gv_GetVar(*insptr++));
2542 dispatch();
2543 }
2544
2545 vInstruction(CON_GETSPRITEEXT):
2546 insptr++;
2547 {
2548 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2549 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2550
2551 auto const &spriteExtLabel = ActorLabels[*insptr++];
2552
2553 Gv_SetVar(*insptr++, VM_GetStruct(spriteExtLabel.flags, (intptr_t *)((char *)&spriteext[spriteNum] + spriteExtLabel.offset)));
2554 dispatch();
2555 }
2556
2557 vInstruction(CON_SETTSPR):
2558 insptr++;
2559 {
2560 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2561 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2562
2563 auto const &tsprLabel = TsprLabels[*insptr++];
2564
2565 VM_SetStruct(tsprLabel.flags, (intptr_t *)((char *)spriteext[spriteNum].tspr + tsprLabel.offset), Gv_GetVar(*insptr++));
2566 dispatch();
2567 }
2568
2569 vInstruction(CON_GETTSPR):
2570 insptr++;
2571 {
2572 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
2573 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
2574
2575 auto const &tsprLabel = TsprLabels[*insptr++];
2576
2577 Gv_SetVar(*insptr++, VM_GetStruct(tsprLabel.flags, (intptr_t *)((char *)spriteext[spriteNum].tspr + tsprLabel.offset)));
2578 dispatch();
2579 }
2580
2581 vInstruction(CON_SETSECTOR):
2582 insptr++;
2583 {
2584 int const sectNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->sectnum;
2585 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
2586
2587 tw = *insptr++;
2588
2589 VM_SetSector(sectNum, tw, Gv_GetVar(*insptr++));
2590 dispatch();
2591 }
2592
2593 vInstruction(CON_GETSECTOR):
2594 insptr++;
2595 {
2596 int const sectNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->sectnum;
2597 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
2598
2599 tw = *insptr++;
2600
2601 Gv_SetVar(*insptr++, VM_GetSector(sectNum, tw));
2602 dispatch();
2603 }
2604
2605 vInstruction(CON_SETSECTORSTRUCT):
2606 insptr++;
2607 {
2608 int const sectNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->sectnum;
2609 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
2610
2611 auto const §Label = SectorLabels[*insptr++];
2612
2613 VM_SetStruct(sectLabel.flags, (intptr_t *)((char *)§or[sectNum] + sectLabel.offset), Gv_GetVar(*insptr++));
2614 dispatch();
2615 }
2616
2617 vInstruction(CON_GETSECTORSTRUCT):
2618 insptr++;
2619 {
2620 int const sectNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->sectnum;
2621 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
2622
2623 auto const §Label = SectorLabels[*insptr++];
2624
2625 Gv_SetVar(*insptr++, VM_GetStruct(sectLabel.flags, (intptr_t *)((char *)§or[sectNum] + sectLabel.offset)));
2626 dispatch();
2627 }
2628
2629 vInstruction(CON_RETURN):
2630 vm.flags |= VM_RETURN;
2631 #if !defined CON_USE_COMPUTED_GOTO
2632 fallthrough__;
2633 #endif
2634 vInstruction(CON_ENDSWITCH):
2635 vInstruction(CON_ENDA):
2636 vInstruction(CON_BREAK):
2637 vInstruction(CON_ENDS):
2638 vInstruction(CON_ENDEVENT): return;
2639
2640 vInstruction(CON_JUMP): // this is used for event chaining
2641 insptr++;
2642 tw = Gv_GetVar(*insptr++);
2643 insptr = (intptr_t *)(tw + apScript);
2644 dispatch();
2645
2646 vInstruction(CON_SWITCH):
2647 insptr++;
2648 {
2649 // command format:
2650 // variable ID to check
2651 // script offset to 'end'
2652 // count of case statements
2653 // script offset to default case (null if none)
2654 // For each case: value, ptr to code
2655 int const lValue = Gv_GetVar(*insptr++);
2656 int const endOffset = *insptr++;
2657 int const numCases = *insptr++;
2658
2659 auto lpDefault = insptr++;
2660 auto lpCases = insptr;
2661
2662 int left = 0;
2663 int right = numCases - 1;
2664
2665 insptr += numCases << 1;
2666
2667 do
2668 {
2669 int const lCheckCase = (left + right) >> 1;
2670
2671 if (lpCases[lCheckCase << 1] > lValue)
2672 right = lCheckCase - 1;
2673 else if (lpCases[lCheckCase << 1] < lValue)
2674 left = lCheckCase + 1;
2675 else if (lpCases[lCheckCase << 1] == lValue)
2676 {
2677 // fake a 2-d Array
2678 insptr = (intptr_t *)(lpCases[(lCheckCase << 1) + 1] + &apScript[0]);
2679 VM_Execute(true);
2680 goto matched;
2681 }
2682
2683 if (right - left < 0)
2684 break;
2685 } while (1);
2686
2687 if (*lpDefault)
2688 {
2689 insptr = (intptr_t *)(*lpDefault + &apScript[0]);
2690 VM_Execute(true);
2691 }
2692
2693 matched:
2694 insptr = (intptr_t *)(endOffset + (intptr_t)&apScript[0]);
2695
2696 dispatch();
2697 }
2698
2699 vInstruction(CON_FOR): // special-purpose iteration
2700 insptr++;
2701 {
2702 int const returnVar = *insptr++;
2703 int const iterType = *insptr++;
2704 int const nIndex = iterType <= ITER_DRAWNSPRITES ? 0 : Gv_GetVar(*insptr++);
2705
2706 auto const pEnd = insptr + *insptr;
2707 auto const pNext = ++insptr;
2708
2709 auto execute = [&](int const index) {
2710 Gv_SetVar(returnVar, index);
2711 insptr = pNext;
2712 VM_Execute();
2713 return !!(vm.flags & VM_RETURN);
2714 };
2715
2716 auto spriteexecute = [&](int const index) {
2717 {
2718 MICROPROFILE_SCOPE_TOKEN(g_statnumTokens[sprite[index].statnum]);
2719 execute(index);
2720 }
2721 return !!(vm.flags & VM_RETURN);
2722 };
2723
2724 switch (iterType)
2725 {
2726 case ITER_ALLSPRITES:
2727 for (native_t jj = 0; jj < MAXSPRITES; ++jj)
2728 {
2729 if (sprite[jj].statnum == MAXSTATUS)
2730 continue;
2731
2732 if (spriteexecute(jj))
2733 return;
2734 }
2735 break;
2736
2737 case ITER_ALLSPRITESBYSTAT:
2738 for (native_t statNum = 0; statNum < MAXSTATUS; ++statNum)
2739 {
2740 for (native_t kk, SPRITES_OF_STAT_SAFE(statNum, jj, kk))
2741 {
2742 if (spriteexecute(jj))
2743 return;
2744 }
2745 }
2746 break;
2747
2748 case ITER_ALLSPRITESBYSECT:
2749 for (native_t sectNum = 0; sectNum < numsectors; ++sectNum)
2750 {
2751 for (native_t kk, SPRITES_OF_SECT_SAFE(sectNum, jj, kk))
2752 {
2753 if (spriteexecute(jj))
2754 return;
2755 }
2756 }
2757 break;
2758
2759 case ITER_ALLSECTORS:
2760 for (native_t jj = 0; jj < numsectors; ++jj)
2761 if (execute(jj))
2762 return;
2763 break;
2764
2765 case ITER_ALLWALLS:
2766 for (native_t jj = 0; jj < numwalls; ++jj)
2767 if (execute(jj))
2768 return;
2769 break;
2770
2771 case ITER_ACTIVELIGHTS:
2772 #ifdef POLYMER
2773 for (native_t jj = 0; jj < PR_MAXLIGHTS; ++jj)
2774 {
2775 if (!prlights[jj].flags.active)
2776 continue;
2777
2778 if (execute(jj))
2779 return;
2780 }
2781 #endif
2782 break;
2783
2784 case ITER_DRAWNSPRITES:
2785 for (native_t jj = 0; jj < spritesortcnt; jj++)
2786 if (execute(jj))
2787 return;
2788 break;
2789
2790 case ITER_SPRITESOFSECTOR:
2791 if ((unsigned)nIndex >= MAXSECTORS)
2792 goto badindex;
2793
2794 for (native_t kk, SPRITES_OF_SECT_SAFE(nIndex, jj, kk))
2795 {
2796 if (spriteexecute(jj))
2797 return;
2798 }
2799 break;
2800
2801 case ITER_SPRITESOFSTATUS:
2802 if ((unsigned)nIndex >= MAXSTATUS)
2803 goto badindex;
2804
2805 for (native_t kk, SPRITES_OF_STAT_SAFE(nIndex, jj, kk))
2806 {
2807 if (spriteexecute(jj))
2808 return;
2809 }
2810 break;
2811
2812 case ITER_WALLSOFSECTOR:
2813 if ((unsigned)nIndex >= MAXSECTORS)
2814 goto badindex;
2815
2816 for (native_t jj = sector[nIndex].wallptr, endwall = jj + sector[nIndex].wallnum - 1; jj <= endwall; jj++)
2817 if (execute(jj))
2818 return;
2819 break;
2820
2821 case ITER_LOOPOFWALL:
2822 if ((unsigned)nIndex >= (unsigned)numwalls)
2823 goto badindex;
2824 {
2825 int jj = nIndex;
2826 do
2827 {
2828 if (execute(jj))
2829 return;
2830
2831 jj = wall[jj].point2;
2832 } while (jj != nIndex);
2833 }
2834 break;
2835
2836 case ITER_RANGE:
2837 for (native_t jj = 0; jj < nIndex; jj++)
2838 if (execute(jj))
2839 return;
2840 break;
2841 badindex:
2842 OSD_Printf(OSD_ERROR "Line %d, for %s: index %d out of range!\n", VM_DECODE_LINE_NUMBER(g_tw), iter_tokens[iterType].token, nIndex);
2843 vm.flags |= VM_RETURN;
2844 dispatch();
2845 }
2846 insptr = pEnd;
2847 }
2848 dispatch();
2849
2850 vInstruction(CON_REDEFINEQUOTE):
2851 insptr++;
2852 {
2853 int const strIndex = *insptr++;
2854 int const XstrIndex = *insptr++;
2855
2856 Bstrcpy(apStrings[strIndex], apXStrings[XstrIndex]);
2857 dispatch();
2858 }
2859
2860 vInstruction(CON_GETTHISPROJECTILE):
2861 insptr++;
2862 {
2863 tw = *insptr++;
2864 int const spriteNum = (tw != g_thisActorVarID) ? Gv_GetVar(tw) : vm.spriteNum;
2865 tw = *insptr++;
2866
2867 Gv_SetVar(*insptr++, VM_GetActiveProjectile(spriteNum, tw));
2868 dispatch();
2869 }
2870
2871 vInstruction(CON_SETTHISPROJECTILE):
2872 insptr++;
2873 {
2874 tw = *insptr++;
2875 int const spriteNum = (tw != g_thisActorVarID) ? Gv_GetVar(tw) : vm.spriteNum;
2876 tw = *insptr++;
2877
2878 VM_SetActiveProjectile(spriteNum, tw, Gv_GetVar(*insptr++));
2879 dispatch();
2880 }
2881
2882
2883 vInstruction(CON_IFCANSHOOTTARGET):
2884 {
2885 #define CHECK_PICNUM(x) \
2886 if ((unsigned)x < MAXSPRITES && sprite[x].picnum == vm.pSprite->picnum) \
2887 { \
2888 branch(false); \
2889 dispatch(); \
2890 }
2891
2892 if (vm.playerDist > 1024)
2893 {
2894 int16_t temphit;
2895 auto checkHitSprite = [&](int x) {
2896 vm.pSprite->ang += x;
2897 tw = A_CheckHitSprite(vm.spriteNum, &temphit);
2898 vm.pSprite->ang -= x;
2899 };
2900
2901 if ((tw = A_CheckHitSprite(vm.spriteNum, &temphit)) == (1 << 30))
2902 {
2903 branch(true);
2904 dispatch();
2905 }
2906
2907 int dist = 768;
2908 int angDiff = 16;
2909
2910 if (A_CheckEnemySprite(vm.pSprite) && vm.pSprite->xrepeat > 56)
2911 {
2912 dist = 3084;
2913 angDiff = 48;
2914 }
2915
2916 if (tw > dist)
2917 {
2918 CHECK_PICNUM(temphit);
2919 checkHitSprite(angDiff);
2920
2921 if (tw > dist)
2922 {
2923 CHECK_PICNUM(temphit);
2924 checkHitSprite(-angDiff);
2925
2926 if (tw > 768)
2927 {
2928 CHECK_PICNUM(temphit);
2929 branch(true);
2930 dispatch();
2931 }
2932 }
2933 }
2934 branch(false);
2935 dispatch();
2936 }
2937 branch(true);
2938 #undef CHECK_PICNUM
2939 }
2940 dispatch();
2941
2942 vInstruction(CON_IFCANSEETARGET):
2943 tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - ((krand() & 41) << 8), vm.pSprite->sectnum, vm.pPlayer->pos.x, vm.pPlayer->pos.y,
2944 vm.pPlayer->pos.z /*-((krand()&41)<<8)*/, sprite[vm.pPlayer->i].sectnum);
2945 branch(tw);
2946 if (tw)
2947 vm.pActor->timetosleep = SLEEPTIME;
2948 dispatch();
2949
2950 vInstruction(CON_IFACTION):
2951 branch(AC_ACTION_ID(vm.pData) == *(++insptr));
2952 dispatch();
2953
2954 vInstruction(CON_IFACTIONCOUNT):
2955 branch(AC_ACTION_COUNT(vm.pData) >= *(++insptr));
2956 dispatch();
2957
2958 vInstruction(CON_IFACTOR):
2959 branch(vm.pSprite->picnum == *(++insptr));
2960 dispatch();
2961
2962 vInstruction(CON_IFACTORNOTSTAYPUT):
2963 branch(vm.pActor->stayput == -1);
2964 dispatch();
2965
2966 vInstruction(CON_IFAI):
2967 branch(AC_AI_ID(vm.pData) == *(++insptr));
2968 dispatch();
2969
2970 vInstruction(CON_IFBULLETNEAR):
2971 branch(A_Dodge(vm.pSprite) == 1);
2972 dispatch();
2973
2974 vInstruction(CON_IFCEILINGDISTL):
2975 branch((vm.pSprite->z - vm.pActor->ceilingz) <= (*(++insptr) << 8));
2976 dispatch();
2977
2978 vInstruction(CON_IFCLIENT):
2979 branch(g_netClient != NULL);
2980 dispatch();
2981
2982 vInstruction(CON_IFCOUNT):
2983 branch(AC_COUNT(vm.pData) >= *(++insptr));
2984 dispatch();
2985
2986 vInstruction(CON_IFDEAD):
2987 branch(vm.pSprite->extra <= 0);
2988 dispatch();
2989
2990 vInstruction(CON_IFFLOORDISTL):
2991 branch((vm.pActor->floorz - vm.pSprite->z) <= (*(++insptr) << 8));
2992 dispatch();
2993
2994 vInstruction(CON_IFGAPZL):
2995 branch(((vm.pActor->floorz - vm.pActor->ceilingz) >> 8) < *(++insptr));
2996 dispatch();
2997
2998 vInstruction(CON_IFHITSPACE):
2999 branch(TEST_SYNC_KEY(g_player[vm.playerNum].input.bits, SK_OPEN));
3000 dispatch();
3001
3002 vInstruction(CON_IFHITWEAPON):
3003 branch(A_IncurDamage(vm.spriteNum) >= 0);
3004 dispatch();
3005
3006 vInstruction(CON_IFINSPACE):
3007 branch(G_CheckForSpaceCeiling(vm.pSprite->sectnum));
3008 dispatch();
3009
3010 vInstruction(CON_IFINWATER):
3011 branch(sector[vm.pSprite->sectnum].lotag == ST_2_UNDERWATER);
3012 dispatch();
3013
3014 vInstruction(CON_IFONWATER):
3015 branch(sector[vm.pSprite->sectnum].lotag == ST_1_ABOVE_WATER
3016 && klabs(vm.pSprite->z - sector[vm.pSprite->sectnum].floorz) < ZOFFSET5);
3017 dispatch();
3018
3019 vInstruction(CON_IFMOVE):
3020 branch(AC_MOVE_ID(vm.pData) == *(++insptr));
3021 dispatch();
3022
3023 vInstruction(CON_IFMULTIPLAYER):
3024 branch((g_netServer || g_netClient || ud.multimode > 1));
3025 dispatch();
3026
3027 vInstruction(CON_IFOUTSIDE):
3028 branch(sector[vm.pSprite->sectnum].ceilingstat & 1);
3029 dispatch();
3030
3031 vInstruction(CON_IFPLAYBACKON):
3032 branch(false);
3033 dispatch();
3034
3035 vInstruction(CON_IFPLAYERSL):
3036 branch(numplayers < *(++insptr));
3037 dispatch();
3038
3039 vInstruction(CON_IFSERVER):
3040 branch(g_netServer != NULL);
3041 dispatch();
3042
3043 vInstruction(CON_IFSQUISHED):
3044 branch(VM_CheckSquished());
3045 dispatch();
3046
3047 vInstruction(CON_IFSTRENGTH):
3048 branch(vm.pSprite->extra <= *(++insptr));
3049 dispatch();
3050
3051 vInstruction(CON_IFSPAWNEDBY):
3052 vInstruction(CON_IFWASWEAPON):
3053 branch(vm.pActor->picnum == *(++insptr));
3054 dispatch();
3055
3056 vInstruction(CON_IFPDISTL):
3057 branch(vm.playerDist < *(++insptr));
3058 if (vm.playerDist > MAXSLEEPDIST && vm.pActor->timetosleep == 0)
3059 vm.pActor->timetosleep = SLEEPTIME;
3060 dispatch();
3061
3062 vInstruction(CON_IFPDISTG):
3063 branch(vm.playerDist > *(++insptr));
3064 if (vm.playerDist > MAXSLEEPDIST && vm.pActor->timetosleep == 0)
3065 vm.pActor->timetosleep = SLEEPTIME;
3066 dispatch();
3067
3068 vInstruction(CON_IFRESPAWN):
3069 if (A_CheckEnemySprite(vm.pSprite))
3070 branch(ud.respawn_monsters);
3071 else if (A_CheckInventorySprite(vm.pSprite))
3072 branch(ud.respawn_inventory);
3073 else
3074 branch(ud.respawn_items);
3075 dispatch();
3076
3077 vInstruction(CON_IFINOUTERSPACE):
3078 branch(G_CheckForSpaceFloor(vm.pSprite->sectnum));
3079 dispatch();
3080
3081 vInstruction(CON_IFNOTMOVING):
3082 branch((vm.pActor->movflag & 49152) > 16384);
3083 dispatch();
3084
3085 vInstruction(CON_IFCANSEE):
3086 {
3087 auto pSprite = (uspriteptr_t)&sprite[vm.pPlayer->i];
3088
3089 // select sprite for monster to target
3090 // if holoduke is on, let them target holoduke first.
3091 //
3092 #ifndef EDUKE32_STANDALONE
3093 if (!FURY && vm.pPlayer->holoduke_on >= 0)
3094 {
3095 pSprite = (uspriteptr_t)&sprite[vm.pPlayer->holoduke_on];
3096 tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - (krand() & (ZOFFSET5 - 1)), vm.pSprite->sectnum, pSprite->x, pSprite->y,
3097 pSprite->z, pSprite->sectnum);
3098
3099 if (tw == 0)
3100 {
3101 // they can't see player's holoduke
3102 // check for player...
3103 pSprite = (uspriteptr_t)&sprite[vm.pPlayer->i];
3104 }
3105 }
3106 #endif
3107 // can they see player, (or player's holoduke)
3108 tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - (krand() & ((47 << 8))), vm.pSprite->sectnum, pSprite->x, pSprite->y,
3109 pSprite->z - (24 << 8), pSprite->sectnum);
3110
3111 if (tw == 0)
3112 {
3113 // search around for target player
3114 // also modifies 'target' x&y if found..
3115
3116 tw = (A_FurthestVisiblePoint(vm.spriteNum, pSprite, &vm.pActor->lastv) != -1);
3117 }
3118 else
3119 {
3120 // else, they did see it.
3121 // save where we were looking...
3122 vm.pActor->lastv = pSprite->pos.vec2;
3123 }
3124
3125 if (tw && (vm.pSprite->statnum == STAT_ACTOR || vm.pSprite->statnum == STAT_STANDABLE))
3126 vm.pActor->timetosleep = SLEEPTIME;
3127
3128 branch(tw);
3129 dispatch();
3130 }
3131
3132 vInstruction(CON_AI):
3133 insptr++;
3134 // Following changed to use pointersizes
3135 AC_AI_ID(vm.pData) = *insptr++; // Ai
3136 AC_ACTION_ID(vm.pData) = *(apScript + AC_AI_ID(vm.pData)); // Action
3137
3138 // NOTE: "if" check added in r1155. It used to be a pointer though.
3139 if (AC_AI_ID(vm.pData))
3140 AC_MOVE_ID(vm.pData) = *(apScript + AC_AI_ID(vm.pData) + 1); // move
3141
3142 vm.pSprite->hitag = *(apScript + AC_AI_ID(vm.pData) + 2); // move flags
3143
3144 AC_COUNT(vm.pData) = 0;
3145 AC_ACTION_COUNT(vm.pData) = 0;
3146 AC_CURFRAME(vm.pData) = 0;
3147
3148 if (!A_CheckEnemySprite(vm.pSprite) || vm.pSprite->extra > 0) // hack
3149 if (vm.pSprite->hitag & random_angle)
3150 vm.pSprite->ang = krand() & 2047;
3151 dispatch();
3152
3153 vInstruction(CON_ACTION):
3154 insptr++;
3155 AC_ACTION_COUNT(vm.pData) = 0;
3156 AC_CURFRAME(vm.pData) = 0;
3157 AC_ACTION_ID(vm.pData) = *insptr++;
3158 dispatch();
3159
3160 vInstruction(CON_ADDSTRENGTH):
3161 insptr++;
3162 vm.pSprite->extra += *insptr++;
3163 dispatch();
3164
3165 vInstruction(CON_STRENGTH):
3166 insptr++;
3167 vm.pSprite->extra = *insptr++;
3168 dispatch();
3169
3170 vInstruction(CON_IFGOTWEAPONCE):
3171 insptr++;
3172
3173 if ((g_gametypeFlags[ud.coop] & GAMETYPE_WEAPSTAY) && (g_netServer || ud.multimode > 1))
3174 {
3175 if (*insptr == 0)
3176 {
3177 int j = 0;
3178 for (; j < vm.pPlayer->weapreccnt; ++j)
3179 if (vm.pPlayer->weaprecs[j] == vm.pSprite->picnum)
3180 break;
3181
3182 branch(j < vm.pPlayer->weapreccnt && vm.pSprite->owner == vm.spriteNum);
3183 dispatch();
3184 }
3185 else if (vm.pPlayer->weapreccnt < MAX_WEAPONS)
3186 {
3187 vm.pPlayer->weaprecs[vm.pPlayer->weapreccnt++] = vm.pSprite->picnum;
3188 branch(vm.pSprite->owner == vm.spriteNum);
3189 dispatch();
3190 }
3191 }
3192 branch(false);
3193 dispatch();
3194
3195 vInstruction(CON_GETLASTPAL):
3196 insptr++;
3197 if (vm.pSprite->picnum == APLAYER)
3198 vm.pSprite->pal = g_player[P_GetP(vm.pSprite)].ps->palookup;
3199 else
3200 {
3201 if (vm.pSprite->pal == 1 && vm.pSprite->extra == 0) // hack for frozen
3202 vm.pSprite->extra++;
3203 vm.pSprite->pal = vm.pActor->tempang;
3204 }
3205 vm.pActor->tempang = 0;
3206 dispatch();
3207
3208 vInstruction(CON_TOSSWEAPON):
3209 insptr++;
3210 // NOTE: assumes that current actor is APLAYER
3211 P_DropWeapon(P_GetP(vm.pSprite));
3212 dispatch();
3213
3214 vInstruction(CON_MIKESND):
3215 insptr++;
3216 VM_ASSERT((unsigned)vm.pSprite->yvel < MAXSOUNDS, "invalid sound %d\n", vm.pUSprite->yvel);
3217 if (!S_CheckSoundPlaying(vm.pSprite->yvel))
3218 A_PlaySound(vm.pSprite->yvel, vm.spriteNum);
3219 dispatch();
3220
3221 vInstruction(CON_PKICK):
3222 insptr++;
3223
3224 if ((g_netServer || ud.multimode > 1) && vm.pSprite->picnum == APLAYER)
3225 {
3226 if (g_player[otherp].ps->quick_kick == 0)
3227 g_player[otherp].ps->quick_kick = 14;
3228 }
3229 else if (vm.pSprite->picnum != APLAYER && vm.pPlayer->quick_kick == 0)
3230 vm.pPlayer->quick_kick = 14;
3231 dispatch();
3232
3233 vInstruction(CON_SIZETO):
3234 insptr++;
3235
3236 tw = (*insptr++ - vm.pSprite->xrepeat) << 1;
3237 vm.pSprite->xrepeat += ksgn(tw);
3238
3239 if ((vm.pSprite->picnum == APLAYER && vm.pSprite->yrepeat < 36) || *insptr < vm.pSprite->yrepeat
3240 || ((vm.pSprite->yrepeat * (tilesiz[vm.pSprite->picnum].y + 8)) << 2) < (vm.pActor->floorz - vm.pActor->ceilingz))
3241 {
3242 tw = ((*insptr) - vm.pSprite->yrepeat) << 1;
3243 if (klabs(tw))
3244 vm.pSprite->yrepeat += ksgn(tw);
3245 }
3246
3247 insptr++;
3248
3249 dispatch();
3250
3251 vInstruction(CON_SIZEAT):
3252 insptr++;
3253 vm.pSprite->xrepeat = (uint8_t)*insptr++;
3254 vm.pSprite->yrepeat = (uint8_t)*insptr++;
3255 dispatch();
3256
3257 vInstruction(CON_IFACTORSOUND):
3258 insptr++;
3259 {
3260 int const spriteNum = Gv_GetVar(*insptr++);
3261 int const soundNum = Gv_GetVar(*insptr++);
3262
3263 VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum);
3264
3265 insptr--;
3266 branch(A_CheckSoundPlaying(spriteNum, soundNum));
3267 }
3268 dispatch();
3269
3270 vInstruction(CON_IFSOUND):
3271 insptr++;
3272 VM_ASSERT((unsigned)*insptr < MAXSOUNDS, "invalid sound %d\n", (int32_t)*insptr);
3273 branch(S_CheckSoundPlaying(*insptr));
3274 // VM_DoConditional(SoundOwner[*insptr][0].ow == vm.spriteNum);
3275 dispatch();
3276
3277 vInstruction(CON_STOPACTORSOUND):
3278 insptr++;
3279 {
3280 int const spriteNum = Gv_GetVar(*insptr++);
3281 int const soundNum = Gv_GetVar(*insptr++);
3282
3283 VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum);
3284
3285 if (A_CheckSoundPlaying(spriteNum, soundNum))
3286 S_StopEnvSound(soundNum, spriteNum);
3287
3288 dispatch();
3289 }
3290
3291 vInstruction(CON_ACTORSOUND):
3292 insptr++;
3293 {
3294 int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum;
3295 int const soundNum = Gv_GetVar(*insptr++);
3296
3297 VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum);
3298
3299 A_PlaySound(soundNum, spriteNum);
3300
3301 dispatch();
3302 }
3303
3304 vInstruction(CON_SETACTORSOUNDPITCH):
3305 insptr++;
3306 {
3307 int const spriteNum = Gv_GetVar(*insptr++);
3308 int const soundNum = Gv_GetVar(*insptr++);
3309 int const newPitch = Gv_GetVar(*insptr++);
3310
3311 VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum);
3312
3313 S_ChangeSoundPitch(soundNum, spriteNum, newPitch);
3314
3315 dispatch();
3316 }
3317
3318 vInstruction(CON_TIP):
3319 insptr++;
3320 vm.pPlayer->tipincs = GAMETICSPERSEC;
3321 dispatch();
3322
3323 vInstruction(CON_FALL):
3324 insptr++;
3325 VM_Fall(vm.spriteNum, vm.pSprite);
3326 dispatch();
3327
3328 vInstruction(CON_NULLOP): insptr++; dispatch();
3329
3330 vInstruction(CON_ADDAMMO):
3331 insptr++;
3332 {
3333 int const weaponNum = *insptr++;
3334 int const addAmount = *insptr++;
3335
3336 VM_AddAmmo(vm.pPlayer, weaponNum, addAmount);
3337
3338 dispatch();
3339 }
3340
3341 vInstruction(CON_MONEY):
3342 insptr++;
3343 A_SpawnMultiple(vm.spriteNum, MONEY, *insptr++);
3344 dispatch();
3345
3346 vInstruction(CON_MAIL):
3347 insptr++;
3348 A_SpawnMultiple(vm.spriteNum, MAIL, *insptr++);
3349 dispatch();
3350
3351 vInstruction(CON_SLEEPTIME):
3352 insptr++;
3353 vm.pActor->timetosleep = (int16_t)*insptr++;
3354 dispatch();
3355
3356 vInstruction(CON_PAPER):
3357 insptr++;
3358 A_SpawnMultiple(vm.spriteNum, PAPER, *insptr++);
3359 dispatch();
3360
3361 vInstruction(CON_ADDKILLS):
3362 insptr++;
3363 P_AddKills(vm.pPlayer, *insptr++);
3364 vm.pActor->stayput = -1;
3365 dispatch();
3366
3367 vInstruction(CON_LOTSOFGLASS):
3368 insptr++;
3369 #ifndef EDUKE32_STANDALONE
3370 if (!FURY)
3371 A_SpawnGlass(vm.spriteNum, *insptr++);
3372 #else
3373 insptr++;
3374 #endif
3375 dispatch();
3376
3377 vInstruction(CON_SPAWNWALLGLASS):
3378 insptr++;
3379 {
3380 #ifndef EDUKE32_STANDALONE
3381 if (!FURY)
3382 {
3383 int const wallNum = Gv_GetVar(*insptr++);
3384 int const numShards = Gv_GetVar(*insptr++);
3385 A_SpawnWallGlass(vm.spriteNum, wallNum, numShards);
3386 }
3387 #else
3388 Gv_GetVar(*insptr++);
3389 Gv_GetVar(*insptr++);
3390 #endif
3391 }
3392 dispatch();
3393
3394 vInstruction(CON_SPAWNWALLSTAINEDGLASS):
3395 insptr++;
3396 {
3397 #ifndef EDUKE32_STANDALONE
3398 if (!FURY)
3399 {
3400 int const wallNum = Gv_GetVar(*insptr++);
3401 int const numShards = Gv_GetVar(*insptr++);
3402 A_SpawnRandomGlass(vm.spriteNum, wallNum, numShards);
3403 }
3404 #else
3405 Gv_GetVar(*insptr++);
3406 Gv_GetVar(*insptr++);
3407 #endif
3408 }
3409 dispatch();
3410
3411 vInstruction(CON_SPAWNCEILINGGLASS):
3412 insptr++;
3413 {
3414 #ifndef EDUKE32_STANDALONE
3415 if (!FURY)
3416 {
3417 int const sectNum = Gv_GetVar(*insptr++);
3418 int const numShards = Gv_GetVar(*insptr++);
3419 A_SpawnCeilingGlass(vm.spriteNum, sectNum, numShards);
3420 }
3421 #else
3422 Gv_GetVar(*insptr++);
3423 Gv_GetVar(*insptr++);
3424 #endif
3425 }
3426 dispatch();
3427
3428 vInstruction(CON_KILLIT):
3429 insptr++;
3430 vm.flags |= VM_KILL;
3431 return;
3432
3433 vInstruction(CON_DEBUG):
3434 insptr++;
3435 buildprint(*insptr++, "\n");
3436 dispatch();
3437
3438 vInstruction(CON_ENDOFGAME):
3439 vInstruction(CON_ENDOFLEVEL):
3440 insptr++;
3441 vm.pPlayer->timebeforeexit = *insptr++;
3442 vm.pPlayer->customexitsound = -1;
3443 ud.eog = 1;
3444 dispatch();
3445
3446 vInstruction(CON_ADDPHEALTH):
3447 insptr++;
3448
3449 {
3450 if (vm.pPlayer->newowner >= 0)
3451 G_ClearCameraView(vm.pPlayer);
3452
3453 int newHealth = sprite[vm.pPlayer->i].extra;
3454
3455 #ifndef EDUKE32_STANDALONE
3456 if (!FURY && vm.pSprite->picnum == ATOMICHEALTH)
3457 {
3458 if (newHealth > 0)
3459 newHealth += *insptr;
3460 if (newHealth > (vm.pPlayer->max_player_health << 1))
3461 newHealth = (vm.pPlayer->max_player_health << 1);
3462 }
3463 else
3464 #endif
3465 {
3466 if (newHealth > vm.pPlayer->max_player_health && *insptr > 0)
3467 {
3468 insptr++;
3469 dispatch();
3470 }
3471 else
3472 {
3473 if (newHealth > 0)
3474 newHealth += *insptr;
3475 if (newHealth > vm.pPlayer->max_player_health && *insptr > 0)
3476 newHealth = vm.pPlayer->max_player_health;
3477 }
3478 }
3479
3480 if (newHealth < 0)
3481 newHealth = 0;
3482
3483 if (ud.god == 0)
3484 {
3485 if (*insptr > 0)
3486 {
3487 #ifndef EDUKE32_STANDALONE
3488 if (!FURY && (newHealth - *insptr) < (vm.pPlayer->max_player_health >> 2) && newHealth >= (vm.pPlayer->max_player_health >> 2))
3489 A_PlaySound(DUKE_GOTHEALTHATLOW, vm.pPlayer->i);
3490 #endif
3491 vm.pPlayer->last_extra = newHealth;
3492 }
3493
3494 sprite[vm.pPlayer->i].extra = newHealth;
3495 }
3496 }
3497
3498 insptr++;
3499 dispatch();
3500
3501 vInstruction(CON_MOVE):
3502 insptr++;
3503 AC_COUNT(vm.pData) = 0;
3504 AC_MOVE_ID(vm.pData) = *insptr++;
3505 vm.pSprite->hitag = *insptr++;
3506
3507 if (!A_CheckEnemySprite(vm.pSprite) || vm.pSprite->extra > 0) // hack
3508 if (vm.pSprite->hitag & random_angle)
3509 vm.pSprite->ang = krand() & 2047;
3510 dispatch();
3511
3512 vInstruction(CON_ADDWEAPON):
3513 insptr++;
3514 {
3515 int const weaponNum = Gv_GetVar(*insptr++);
3516 VM_AddWeapon(vm.pPlayer, weaponNum, Gv_GetVar(*insptr++));
3517 dispatch();
3518 }
3519
3520 vInstruction(CON_SETASPECT):
3521 insptr++;
3522 {
3523 int const xRange = Gv_GetVar(*insptr++);
3524 renderSetAspect(xRange, Gv_GetVar(*insptr++));
3525 dispatch();
3526 }
3527
3528 vInstruction(CON_SSP):
3529 insptr++;
3530 {
3531 int const spriteNum = Gv_GetVar(*insptr++);
3532 int const clipType = Gv_GetVar(*insptr++);
3533
3534 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
3535
3536 A_SetSprite(spriteNum, clipType);
3537 dispatch();
3538 }
3539
3540 vInstruction(CON_ACTIVATEBYSECTOR):
3541 insptr++;
3542 {
3543 int const sectNum = Gv_GetVar(*insptr++);
3544 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
3545
3546 int const spriteNum = Gv_GetVar(*insptr++);
3547
3548 G_ActivateBySector(sectNum, spriteNum);
3549 dispatch();
3550 }
3551
3552 vInstruction(CON_OPERATESECTORS):
3553 insptr++;
3554 {
3555 int const sectNum = Gv_GetVar(*insptr++);
3556 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
3557
3558 int const spriteNum = Gv_GetVar(*insptr++);
3559
3560 G_OperateSectors(sectNum, spriteNum);
3561 dispatch();
3562 }
3563
3564 vInstruction(CON_OPERATEACTIVATORS):
3565 insptr++;
3566 {
3567 int const nTag = Gv_GetVar(*insptr++);
3568 int const playerNum = (*insptr++ == g_thisActorVarID) ? vm.playerNum : Gv_GetVar(insptr[-1]);
3569
3570 VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum);
3571
3572 G_OperateActivators(nTag, playerNum);
3573 dispatch();
3574 }
3575
3576
3577 vInstruction(CON_CANSEESPR):
3578 insptr++;
3579 {
3580 int const nSprite1 = Gv_GetVar(*insptr++);
3581 int const nSprite2 = Gv_GetVar(*insptr++);
3582
3583 VM_ASSERT((unsigned)nSprite1 < MAXSPRITES && (unsigned)nSprite2 < MAXSPRITES, "invalid sprite %d\n",
3584 (unsigned)nSprite1 >= MAXSPRITES ? nSprite1 : nSprite2);
3585
3586 int const nResult = cansee(sprite[nSprite1].x, sprite[nSprite1].y, sprite[nSprite1].z, sprite[nSprite1].sectnum,
3587 sprite[nSprite2].x, sprite[nSprite2].y, sprite[nSprite2].z, sprite[nSprite2].sectnum);
3588
3589 Gv_SetVar(*insptr++, nResult);
3590 dispatch();
3591 }
3592
3593 vInstruction(CON_OPERATERESPAWNS):
3594 insptr++;
3595 G_OperateRespawns(Gv_GetVar(*insptr++));
3596 dispatch();
3597
3598 vInstruction(CON_OPERATEMASTERSWITCHES):
3599 insptr++;
3600 G_OperateMasterSwitches(Gv_GetVar(*insptr++));
3601 dispatch();
3602
3603 vInstruction(CON_CHECKACTIVATORMOTION):
3604 insptr++;
3605 aGameVars[g_returnVarID].global = G_CheckActivatorMotion(Gv_GetVar(*insptr++));
3606 dispatch();
3607
3608 vInstruction(CON_INSERTSPRITEQ):
3609 insptr++;
3610 A_AddToDeleteQueue(vm.spriteNum);
3611 dispatch();
3612
3613 vInstruction(CON_QSTRLEN):
3614 insptr++;
3615 {
3616 int const gameVar = *insptr++;
3617 int const quoteNum = Gv_GetVar(*insptr++);
3618
3619 VM_ASSERT((unsigned)quoteNum < MAXQUOTES && apStrings[quoteNum], "invalid quote %d\n", quoteNum);
3620
3621 Gv_SetVar(gameVar, Bstrlen(apStrings[quoteNum]));
3622 dispatch();
3623 }
3624
3625 vInstruction(CON_QSTRDIM):
3626 insptr++;
3627 {
3628 int const widthVar = *insptr++;
3629 int const heightVar = *insptr++;
3630
3631 struct
3632 {
3633 int32_t tileNum;
3634 vec3_t vect;
3635 int32_t blockAngle, quoteNum, orientation;
3636 vec2_t offset, between;
3637 int32_t f;
3638 vec2_t bound[2];
3639 } v;
3640 Gv_FillWithVars(v);
3641
3642 if (EDUKE32_PREDICT_FALSE(v.tileNum < 0 || v.tileNum + 127 >= MAXTILES))
3643 CON_ERRPRINTF("invalid base tilenum %d\n", v.tileNum);
3644 else if (EDUKE32_PREDICT_FALSE((unsigned)v.quoteNum >= MAXQUOTES || apStrings[v.quoteNum] == NULL))
3645 CON_ERRPRINTF("invalid quote %d\n", v.quoteNum);
3646 else
3647 {
3648 if (!(v.orientation & ROTATESPRITE_FULL16))
3649 {
3650 v.vect.x <<= 16;
3651 v.vect.y <<= 16;
3652 v.offset.x <<= 16;
3653 v.offset.y <<= 16;
3654 v.between.x <<= 16;
3655 v.between.y <<= 16;
3656 }
3657
3658 G_SetScreenTextEmpty(v.offset, v.tileNum, v.f);
3659
3660 vec2_t dim = G_ScreenTextSize(v.tileNum, v.vect.x, v.vect.y, v.vect.z, v.blockAngle, apStrings[v.quoteNum], 2 | v.orientation,
3661 v.offset.x, v.offset.y, v.between.x, v.between.y, v.f, v.bound[0].x, v.bound[0].y, v.bound[1].x,
3662 v.bound[1].y);
3663
3664 if (!(v.orientation & ROTATESPRITE_FULL16))
3665 {
3666 dim.x >>= 16;
3667 dim.y >>= 16;
3668 }
3669
3670 Gv_SetVar(widthVar, dim.x);
3671 Gv_SetVar(heightVar, dim.y);
3672 }
3673 dispatch();
3674 }
3675
3676 vInstruction(CON_HEADSPRITESTAT):
3677 insptr++;
3678 {
3679 int const gameVar = *insptr++;
3680 int const statNum = Gv_GetVar(*insptr++);
3681
3682 VM_ASSERT((unsigned)statNum < MAXSTATUS, "invalid status list %d\n", statNum);
3683
3684 Gv_SetVar(gameVar, headspritestat[statNum]);
3685 dispatch();
3686 }
3687
3688 vInstruction(CON_PREVSPRITESTAT):
3689 insptr++;
3690 {
3691 int const gameVar = *insptr++;
3692 int const spriteNum = Gv_GetVar(*insptr++);
3693
3694 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
3695
3696 Gv_SetVar(gameVar, prevspritestat[spriteNum]);
3697 dispatch();
3698 }
3699
3700 vInstruction(CON_NEXTSPRITESTAT):
3701 insptr++;
3702 {
3703 int const gameVar = *insptr++;
3704 int const spriteNum = Gv_GetVar(*insptr++);
3705
3706 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
3707
3708 Gv_SetVar(gameVar, nextspritestat[spriteNum]);
3709 dispatch();
3710 }
3711
3712 vInstruction(CON_HEADSPRITESECT):
3713 insptr++;
3714 {
3715 int const gameVar = *insptr++;
3716 int const sectNum = Gv_GetVar(*insptr++);
3717
3718 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
3719
3720 Gv_SetVar(gameVar, headspritesect[sectNum]);
3721 dispatch();
3722 }
3723
3724 vInstruction(CON_PREVSPRITESECT):
3725 insptr++;
3726 {
3727 int const gameVar = *insptr++;
3728 int const spriteNum = Gv_GetVar(*insptr++);
3729
3730 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
3731
3732 Gv_SetVar(gameVar, prevspritesect[spriteNum]);
3733 dispatch();
3734 }
3735
3736 vInstruction(CON_NEXTSPRITESECT):
3737 insptr++;
3738 {
3739 int const gameVar = *insptr++;
3740 int const spriteNum = Gv_GetVar(*insptr++);
3741
3742 VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum);
3743
3744 Gv_SetVar(gameVar, nextspritesect[spriteNum]);
3745 dispatch();
3746 }
3747
3748 vInstruction(CON_GETKEYNAME):
3749 insptr++;
3750 {
3751 int const quoteIndex = Gv_GetVar(*insptr++);
3752 int const gameFunc = Gv_GetVar(*insptr++);
3753 int const funcPos = Gv_GetVar(*insptr++);
3754
3755 VM_ASSERT((unsigned)quoteIndex < MAXQUOTES && apStrings[quoteIndex], "invalid quote %d\n", quoteIndex);
3756 VM_ASSERT((unsigned)gameFunc < NUMGAMEFUNCTIONS, "invalid function %d\n", gameFunc);
3757
3758 if (funcPos < 2)
3759 Bstrcpy(tempbuf, KB_ScanCodeToString(ud.config.KeyboardKeys[gameFunc][funcPos]));
3760 else
3761 {
3762 Bstrcpy(tempbuf, KB_ScanCodeToString(ud.config.KeyboardKeys[gameFunc][0]));
3763
3764 if (!*tempbuf)
3765 Bstrcpy(tempbuf, KB_ScanCodeToString(ud.config.KeyboardKeys[gameFunc][1]));
3766 }
3767
3768 if (*tempbuf)
3769 Bstrcpy(apStrings[quoteIndex], tempbuf);
3770
3771 dispatch();
3772 }
3773
3774 vInstruction(CON_GETGAMEFUNCBIND):
3775 insptr++;
3776 {
3777 int const quoteIndex = Gv_GetVar(*insptr++);
3778 VM_ASSERT((unsigned)quoteIndex < MAXQUOTES && apStrings[quoteIndex], "invalid quote %d\n", quoteIndex);
3779
3780 int const gameFunc = Gv_GetVar(*insptr++);
3781 VM_ASSERT((unsigned)gameFunc < NUMGAMEFUNCTIONS, "invalid function %d\n", gameFunc);
3782
3783 static char const s_KeyboardFormat[] = "[%s]";
3784 static char const s_JoystickFormat[] = "(%s)";
3785 static char const s_Unbound[] = "UNBOUND";
3786
3787 if (CONTROL_LastSeenInput == LastSeenInput::Joystick)
3788 {
3789 char const * joyname = CONFIG_GetGameFuncOnJoystick(gameFunc);
3790 if (joyname != nullptr && joyname[0] != '\0')
3791 {
3792 snprintf(apStrings[quoteIndex], MAXQUOTELEN, s_JoystickFormat, joyname);
3793 dispatch();
3794 }
3795
3796 char const * keyname = CONFIG_GetGameFuncOnKeyboard(gameFunc);
3797 if (keyname != nullptr && keyname[0] != '\0')
3798 {
3799 snprintf(apStrings[quoteIndex], MAXQUOTELEN, s_KeyboardFormat, keyname);
3800 dispatch();
3801 }
3802
3803 snprintf(apStrings[quoteIndex], MAXQUOTELEN, s_JoystickFormat, s_Unbound);
3804 }
3805 else
3806 {
3807 char const * keyname = CONFIG_GetGameFuncOnKeyboard(gameFunc);
3808 if (keyname != nullptr && keyname[0] != '\0')
3809 {
3810 snprintf(apStrings[quoteIndex], MAXQUOTELEN, s_KeyboardFormat, keyname);
3811 dispatch();
3812 }
3813
3814 char const * joyname = CONFIG_GetGameFuncOnJoystick(gameFunc);
3815 if (joyname != nullptr && joyname[0] != '\0')
3816 {
3817 snprintf(apStrings[quoteIndex], MAXQUOTELEN, s_JoystickFormat, joyname);
3818 dispatch();
3819 }
3820
3821 snprintf(apStrings[quoteIndex], MAXQUOTELEN, s_KeyboardFormat, s_Unbound);
3822 }
3823
3824 dispatch();
3825 }
3826
3827 vInstruction(CON_QSUBSTR):
3828 insptr++;
3829 {
3830 struct
3831 {
3832 int32_t outputQuote, inputQuote, quotePos, quoteLength;
3833 } v;
3834 Gv_FillWithVars(v);
3835
3836 VM_ASSERT((unsigned)v.outputQuote < MAXQUOTES && apStrings[v.outputQuote] != NULL &&
3837 (unsigned)v.inputQuote < MAXQUOTES && apStrings[v.inputQuote] != NULL,
3838 "invalid quote %d\n", (unsigned)v.outputQuote < MAXQUOTES && apStrings[v.outputQuote] ? v.inputQuote : v.outputQuote);
3839 VM_ASSERT((unsigned)v.quotePos < MAXQUOTELEN, "invalid position %d\n", v.quotePos);
3840 VM_ASSERT(v.quoteLength >= 0, "invalid length %d\n", v.quoteLength);
3841
3842 char * pOutput = apStrings[v.outputQuote];
3843 char const *pInput = apStrings[v.inputQuote];
3844
3845 while (*pInput && v.quotePos--)
3846 pInput++;
3847 while ((*pOutput = *pInput) && v.quoteLength--)
3848 {
3849 pOutput++;
3850 pInput++;
3851 }
3852 *pOutput = '\0';
3853
3854 dispatch();
3855 }
3856
3857 vInstruction(CON_QSTRCMP):
3858 insptr++;
3859 {
3860 int const quote1 = Gv_GetVar(*insptr++);
3861 int const quote2 = Gv_GetVar(*insptr++);
3862 int const gameVar = *insptr++;
3863
3864 VM_ASSERT((unsigned)quote1 < MAXQUOTES && apStrings[quote1] != NULL &&
3865 (unsigned)quote2 < MAXQUOTES && apStrings[quote2] != NULL,
3866 "invalid quote %d\n", (unsigned)quote1 < MAXQUOTES && apStrings[quote1] != NULL ? quote2 : quote1);
3867
3868 Gv_SetVar(gameVar, strcmp(apStrings[quote1], apStrings[quote2]));
3869 dispatch();
3870 }
3871
3872 vInstruction(CON_GETPNAME):
3873 vInstruction(CON_QSTRNCAT):
3874 vInstruction(CON_QSTRCAT):
3875 vInstruction(CON_QSTRCPY):
3876 vInstruction(CON_QGETSYSSTR):
3877 insptr++;
3878 {
3879 int const q = Gv_GetVar(*insptr++);
3880 int j;
3881 if (VM_DECODE_INST(tw) == CON_GETPNAME && *insptr == g_thisActorVarID)
3882 {
3883 j = vm.playerNum;
3884 insptr++;
3885 }
3886 else
3887 j = Gv_GetVar(*insptr++);
3888
3889 switch (VM_DECODE_INST(tw))
3890 {
3891 case CON_GETPNAME:
3892 VM_ASSERT((unsigned)q < MAXQUOTES && apStrings[q], "invalid quote %d\n", q);
3893 if (g_player[j].user_name[0])
3894 Bstrcpy(apStrings[q], g_player[j].user_name);
3895 else
3896 Bsprintf(apStrings[q], "%d", j);
3897 break;
3898 case CON_QGETSYSSTR:
3899 VM_ASSERT((unsigned)q < MAXQUOTES && apStrings[q], "invalid quote %d\n", q);
3900 switch (j)
3901 {
3902 case STR_MAPNAME:
3903 case STR_MAPFILENAME:
3904 {
3905 if (G_HaveUserMap())
3906 {
3907 snprintf(apStrings[q], MAXQUOTELEN, "%s", boardfilename);
3908 break;
3909 }
3910
3911 int const levelNum = ud.volume_number * MAXLEVELS + ud.level_number;
3912 const char *pName;
3913
3914 VM_ASSERT((unsigned)levelNum < ARRAY_SIZE(g_mapInfo), "out of bounds map number (vol=%d, lev=%d)\n",
3915 ud.volume_number, ud.level_number);
3916
3917 pName = j == STR_MAPNAME ? g_mapInfo[levelNum].name : g_mapInfo[levelNum].filename;
3918
3919 VM_ASSERT(pName != nullptr, "attempted access to %s of non-existent map (vol=%d, lev=%d)",
3920 j == STR_MAPNAME ? "name" : "file name", ud.volume_number, ud.level_number);
3921
3922 Bstrcpy(apStrings[q], j == STR_MAPNAME ? g_mapInfo[levelNum].name : g_mapInfo[levelNum].filename);
3923 break;
3924 }
3925 case STR_PLAYERNAME:
3926 VM_ASSERT((unsigned)vm.playerNum < MAXPLAYERS, "invalid player %d\n", vm.playerNum);
3927 Bstrcpy(apStrings[q], g_player[vm.playerNum].user_name);
3928 break;
3929 case STR_VERSION:
3930 Bsprintf(tempbuf, HEAD2 " %s", s_buildRev);
3931 Bstrcpy(apStrings[q], tempbuf);
3932 break;
3933 case STR_GAMETYPE: Bstrcpy(apStrings[q], g_gametypeNames[ud.coop]); break;
3934 case STR_VOLUMENAME:
3935 if (G_HaveUserMap())
3936 {
3937 apStrings[q][0] = '\0';
3938 break;
3939 }
3940
3941 VM_ASSERT((unsigned)ud.volume_number < MAXVOLUMES, "invalid volume %d\n", ud.volume_number);
3942 Bstrcpy(apStrings[q], g_volumeNames[ud.volume_number]);
3943 break;
3944 case STR_REVISION: Bstrcpy(apStrings[q], &s_buildRev[1]); break;
3945 case STR_YOURTIME: Bstrcpy(apStrings[q], G_PrintYourTime()); break;
3946 case STR_PARTIME: Bstrcpy(apStrings[q], G_PrintParTime()); break;
3947 case STR_DESIGNERTIME: Bstrcpy(apStrings[q], G_PrintDesignerTime()); break;
3948 case STR_BESTTIME: Bstrcpy(apStrings[q], G_PrintBestTime()); break;
3949 case STR_USERMAPFILENAME: snprintf(apStrings[q], MAXQUOTELEN, "%s", boardfilename); break;
3950 default: CON_ERRPRINTF("invalid string index %d or %d\n", q, j); abort_after_error();
3951 }
3952 break;
3953 case CON_QSTRCAT:
3954 if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL || apStrings[j] == NULL))
3955 goto nullquote;
3956 Bstrncat(apStrings[q], apStrings[j], (MAXQUOTELEN - 1) - Bstrlen(apStrings[q]));
3957 break;
3958 case CON_QSTRNCAT:
3959 if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL || apStrings[j] == NULL))
3960 goto nullquote;
3961 Bstrncat(apStrings[q], apStrings[j], Gv_GetVar(*insptr++));
3962 break;
3963 case CON_QSTRCPY:
3964 if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL || apStrings[j] == NULL))
3965 goto nullquote;
3966 if (q != j)
3967 Bstrcpy(apStrings[q], apStrings[j]);
3968 break;
3969 default:
3970 nullquote:
3971 CON_ERRPRINTF("invalid quote %d\n", apStrings[q] ? j : q);
3972 abort_after_error();
3973 }
3974 dispatch();
3975 }
3976
3977 vInstruction(CON_CHANGESPRITESECT):
3978 insptr++;
3979 {
3980 int const spriteNum = Gv_GetVar(*insptr++);
3981 int const sectNum = Gv_GetVar(*insptr++);
3982
3983 VM_ASSERT((unsigned)spriteNum < MAXSPRITES && (unsigned)sectNum < MAXSECTORS, "invalid parameters: %d, %d\n", spriteNum, sectNum);
3984
3985 if (sprite[spriteNum].sectnum == sectNum)
3986 dispatch();
3987
3988 changespritesect(spriteNum, sectNum);
3989 dispatch();
3990 }
3991
3992 vInstruction(CON_CHANGESPRITESTAT):
3993 insptr++;
3994 {
3995 int const spriteNum = Gv_GetVar(*insptr++);
3996 int const statNum = Gv_GetVar(*insptr++);
3997
3998 VM_ASSERT((unsigned)spriteNum < MAXSPRITES && (unsigned)statNum < MAXSTATUS, "invalid parameters: %d, %d\n", spriteNum, statNum);
3999
4000 if (sprite[spriteNum].statnum == statNum)
4001 dispatch();
4002
4003 /* initialize actor data when changing to an actor statnum because there's usually
4004 garbage left over from being handled as a hard coded object */
4005
4006 if (sprite[spriteNum].statnum > STAT_ZOMBIEACTOR && (statNum == STAT_ACTOR || statNum == STAT_ZOMBIEACTOR))
4007 {
4008 auto pActor = &actor[spriteNum];
4009 auto pSprite = &sprite[spriteNum];
4010
4011
4012 Bmemset(&pActor->t_data, 0, sizeof pActor->t_data);
4013
4014 pActor->lastv = { 0, 0 };
4015 pActor->timetosleep = 0;
4016 pActor->cgg = 0;
4017 pActor->movflag = 0;
4018 pActor->tempang = 0;
4019 pActor->dispicnum = 0;
4020 pActor->flags = 0;
4021 pSprite->hitag = 0;
4022
4023 if (G_TileHasActor(pSprite->picnum))
4024 {
4025 auto actorptr = g_tile[pSprite->picnum].execPtr;
4026 // offsets
4027 AC_ACTION_ID(pActor->t_data) = actorptr[1];
4028 AC_MOVE_ID(pActor->t_data) = actorptr[2];
4029 AC_MOVFLAGS(pSprite, pActor) = actorptr[3]; // ai bits (movflags)
4030 }
4031 }
4032
4033 changespritestat(spriteNum, statNum);
4034 dispatch();
4035 }
4036
4037 vInstruction(CON_STARTLEVEL):
4038 insptr++; // skip command
4039 {
4040 // from 'level' cheat in game.c (about line 6250)
4041 int const volumeNum = Gv_GetVar(*insptr++);
4042 int const levelNum = Gv_GetVar(*insptr++);
4043
4044 VM_ASSERT((unsigned)volumeNum < MAXVOLUMES && (unsigned)levelNum < MAXLEVELS, "invalid parameters: %d, %d\n", volumeNum, levelNum);
4045
4046 ud.m_volume_number = ud.volume_number = volumeNum;
4047 ud.m_level_number = ud.level_number = levelNum;
4048 // if (numplayers > 1 && g_netServer)
4049 // Net_NewGame(volnume,levnume);
4050 //else
4051 {
4052 g_player[myconnectindex].ps->gm |= MODE_EOL;
4053 ud.display_bonus_screen = 0;
4054 } // MODE_RESTART;
4055
4056 dispatch();
4057 }
4058
4059 vInstruction(CON_MYOSX):
4060 vInstruction(CON_MYOSPALX):
4061 vInstruction(CON_MYOS):
4062 vInstruction(CON_MYOSPAL):
4063 insptr++;
4064 {
4065 struct
4066 {
4067 vec2_t pos;
4068 int32_t tilenum, shade, orientation;
4069 } v;
4070 Gv_FillWithVars(v);
4071
4072 switch (VM_DECODE_INST(tw))
4073 {
4074 case CON_MYOS: VM_DrawTile(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation); break;
4075 case CON_MYOSPAL: VM_DrawTilePal(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation, Gv_GetVar(*insptr++)); break;
4076 case CON_MYOSX: VM_DrawTileSmall(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation); break;
4077 case CON_MYOSPALX: VM_DrawTilePalSmall(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation, Gv_GetVar(*insptr++)); break;
4078 }
4079 dispatch();
4080 }
4081
4082 vInstruction(CON_DISPLAYRAND):
4083 insptr++;
4084 Gv_SetVar(*insptr++, system_15bit_rand());
4085 dispatch();
4086
4087 vInstruction(CON_DRAGPOINT):
4088 insptr++;
4089 {
4090 int const wallNum = Gv_GetVar(*insptr++);
4091 VM_ASSERT((unsigned)wallNum < (unsigned)numwalls, "invalid wall %d\n", wallNum);
4092
4093 vec2_t n;
4094 Gv_FillWithVars(n);
4095
4096 dragpoint(wallNum, n.x, n.y, 0);
4097 dispatch();
4098 }
4099
4100 vInstruction(CON_LDIST):
4101 vInstruction(CON_DIST):
4102 insptr++;
4103 {
4104 int const out = *insptr++;
4105 vec2_t in;
4106 Gv_FillWithVars(in);
4107
4108 VM_ASSERT((unsigned)in.x < MAXSPRITES && (unsigned)in.y < MAXSPRITES, "invalid sprite %d, %d\n", in.x, in.y);
4109
4110 Gv_SetVar(out, (VM_DECODE_INST(tw) == CON_LDIST ? ldist : dist)(&sprite[in.x], &sprite[in.y]));
4111 dispatch();
4112 }
4113
4114 vInstruction(CON_GETANGLE):
4115 vInstruction(CON_GETINCANGLE):
4116 insptr++;
4117 {
4118 int const out = *insptr++;
4119 vec2_t in;
4120 Gv_FillWithVars(in);
4121 Gv_SetVar(out, (VM_DECODE_INST(tw) == CON_GETANGLE ? getangle : G_GetAngleDelta)(in.x, in.y));
4122 dispatch();
4123 }
4124
4125 vInstruction(CON_MULSCALE):
4126 vInstruction(CON_DIVSCALE):
4127 insptr++;
4128 {
4129 int const out = *insptr++;
4130 vec3_t in;
4131 Gv_FillWithVars(in);
4132
4133 if (VM_DECODE_INST(tw) == CON_MULSCALE)
4134 Gv_SetVar(out, mulscale(in.x, in.y, in.z));
4135 else
4136 Gv_SetVar(out, divscale(in.x, in.y, in.z));
4137
4138 dispatch();
4139 }
4140
4141 vInstruction(CON_SCALEVAR):
4142 insptr++;
4143 {
4144 int const out = *insptr++;
4145 vec3_t in;
4146 Gv_FillWithVars(in);
4147 Gv_SetVar(out, scale(in.x, in.y, in.z));
4148 dispatch();
4149 }
4150
4151 vInstruction(CON_INITTIMER):
4152 insptr++;
4153 G_InitTimer(Gv_GetVar(*insptr++));
4154 dispatch();
4155
4156 vInstruction(CON_NEXTSECTORNEIGHBORZ):
4157 insptr++;
4158 {
4159 int32_t params[4];
4160 Gv_FillWithVars(params);
4161 aGameVars[g_returnVarID].global = nextsectorneighborz(params[0], params[1], params[2], params[3]);
4162 }
4163 dispatch();
4164
4165 vInstruction(CON_MOVESECTOR):
4166 insptr++;
4167 A_MoveSector(Gv_GetVar(*insptr++));
4168 dispatch();
4169
4170 vInstruction(CON_TIME): insptr += 2; dispatch();
4171
4172 vInstruction(CON_ESPAWN):
4173 vInstruction(CON_EQSPAWN):
4174 vInstruction(CON_QSPAWN):
4175 insptr++;
4176 {
4177 VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum);
4178
4179 int const tileNum = Gv_GetVar(*insptr++);
4180 int const spriteNum = A_Spawn(vm.spriteNum, tileNum);
4181
4182 switch (VM_DECODE_INST(tw))
4183 {
4184 case CON_EQSPAWN:
4185 if (spriteNum != -1)
4186 A_AddToDeleteQueue(spriteNum);
4187 fallthrough__;
4188 case CON_ESPAWN: aGameVars[g_returnVarID].global = spriteNum; break;
4189 case CON_QSPAWN:
4190 if (spriteNum != -1)
4191 A_AddToDeleteQueue(spriteNum);
4192 break;
4193 }
4194 dispatch();
4195 }
4196
4197 vInstruction(CON_SHOOT):
4198 vInstruction(CON_ESHOOT):
4199 insptr++;
4200 {
4201 VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum);
4202
4203 int j = Gv_GetVar(*insptr++);
4204 j = A_Shoot(vm.spriteNum, j);
4205
4206 if (VM_DECODE_INST(tw) == CON_ESHOOT)
4207 aGameVars[g_returnVarID].global = j;
4208
4209 dispatch();
4210 }
4211
4212 vInstruction(CON_EZSHOOT):
4213 vInstruction(CON_ZSHOOT):
4214 insptr++;
4215 {
4216 VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum);
4217
4218 int const zvel = (int16_t)Gv_GetVar(*insptr++);
4219 int j = A_ShootWithZvel(vm.spriteNum, Gv_GetVar(*insptr++), zvel);
4220
4221 if (VM_DECODE_INST(tw) == CON_EZSHOOT)
4222 aGameVars[g_returnVarID].global = j;
4223
4224 dispatch();
4225 }
4226
4227 vInstruction(CON_CMENU):
4228 insptr++;
4229 Menu_Change(Gv_GetVar(*insptr++));
4230 dispatch();
4231
4232 vInstruction(CON_SOUND):
4233 vInstruction(CON_STOPSOUND):
4234 vInstruction(CON_SOUNDONCE):
4235 vInstruction(CON_GLOBALSOUND):
4236 vInstruction(CON_SCREENSOUND):
4237 insptr++;
4238 {
4239 int const soundNum = Gv_GetVar(*insptr++);
4240
4241 VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum);
4242
4243 switch (VM_DECODE_INST(tw))
4244 {
4245 case CON_SOUNDONCE:
4246 if (!S_CheckSoundPlaying(soundNum))
4247 {
4248 fallthrough__;
4249 case CON_SOUND: A_PlaySound((int16_t)soundNum, vm.spriteNum);
4250 }
4251 dispatch();
4252 case CON_GLOBALSOUND: A_PlaySound((int16_t)soundNum, g_player[screenpeek].ps->i); dispatch();
4253 case CON_STOPSOUND:
4254 if (S_CheckSoundPlaying(soundNum))
4255 S_StopSound((int16_t)soundNum);
4256 dispatch();
4257 case CON_SCREENSOUND: S_PlaySound(soundNum); dispatch();
4258 }
4259 }
4260 dispatch();
4261
4262 vInstruction(CON_STARTCUTSCENE):
4263 vInstruction(CON_IFCUTSCENE):
4264 insptr++;
4265 {
4266 int const nQuote = Gv_GetVar(*insptr++);
4267
4268 VM_ASSERT((unsigned)nQuote < MAXQUOTES && apStrings[nQuote], "invalid quote %d\n", nQuote);
4269
4270 if (VM_DECODE_INST(tw) == CON_IFCUTSCENE)
4271 {
4272 insptr--;
4273 branch(g_animPtr == Anim_Find(apStrings[nQuote]));
4274 dispatch();
4275 }
4276
4277 tw = vm.pPlayer->palette;
4278 I_ClearAllInput();
4279 Anim_Play(apStrings[nQuote]);
4280 P_SetGamePalette(vm.pPlayer, tw, 2 + 16);
4281 dispatch();
4282 }
4283
4284 vInstruction(CON_STARTSCREEN):
4285 insptr++;
4286 I_ClearAllInput();
4287 Screen_Play();
4288 dispatch();
4289
4290 vInstruction(CON_GUNIQHUDID):
4291 insptr++;
4292 {
4293 tw = Gv_GetVar(*insptr++);
4294 if (EDUKE32_PREDICT_FALSE((unsigned)tw >= MAXUNIQHUDID - 1))
4295 CON_ERRPRINTF("invalid value %d\n", (int)tw);
4296 else
4297 guniqhudid = tw;
4298
4299 dispatch();
4300 }
4301
4302 vInstruction(CON_SAVEGAMEVAR):
4303 vInstruction(CON_READGAMEVAR):
4304 {
4305 int32_t nValue = 0;
4306 insptr++;
4307 if (ud.config.scripthandle < 0)
4308 {
4309 insptr++;
4310 dispatch();
4311 }
4312 switch (VM_DECODE_INST(tw))
4313 {
4314 case CON_SAVEGAMEVAR:
4315 nValue = Gv_GetVar(*insptr);
4316 SCRIPT_PutNumber(ud.config.scripthandle, "Gamevars", aGameVars[*insptr++].szLabel, nValue, FALSE, FALSE);
4317 break;
4318 case CON_READGAMEVAR:
4319 SCRIPT_GetNumber(ud.config.scripthandle, "Gamevars", aGameVars[*insptr].szLabel, &nValue);
4320 Gv_SetVar(*insptr++, nValue);
4321 break;
4322 }
4323 dispatch();
4324 }
4325
4326 vInstruction(CON_SHOWVIEW):
4327 vInstruction(CON_SHOWVIEWUNBIASED):
4328 vInstruction(CON_SHOWVIEWQ16):
4329 vInstruction(CON_SHOWVIEWQ16UNBIASED):
4330 insptr++;
4331 {
4332 struct
4333 {
4334 vec3_t vec;
4335 int32_t params[3];
4336 vec2_t scrn[2];
4337 } v;
4338 Gv_FillWithVars(v);
4339
4340 VM_ASSERT(v.scrn[0].x >= 0 && v.scrn[0].y >= 0 && v.scrn[1].x < 320 && v.scrn[1].y < 200, "invalid coordinates\n");
4341 VM_ASSERT((unsigned)v.params[2] < MAXSECTORS, "invalid sector %d\n", v.params[2]);
4342
4343 if (VM_DECODE_INST(tw) != CON_SHOWVIEWQ16 && VM_DECODE_INST(tw) != CON_SHOWVIEWQ16UNBIASED)
4344 {
4345 v.params[0] <<= 16;
4346 v.params[1] <<= 16;
4347 }
4348
4349 G_ShowView(v.vec, v.params[0], v.params[1], v.params[2], v.scrn[0].x, v.scrn[0].y, v.scrn[1].x, v.scrn[1].y,
4350 (VM_DECODE_INST(tw) != CON_SHOWVIEW && VM_DECODE_INST(tw) != CON_SHOWVIEWQ16));
4351
4352 dispatch();
4353 }
4354
4355 vInstruction(CON_ROTATESPRITEA):
4356 vInstruction(CON_ROTATESPRITE16):
4357 vInstruction(CON_ROTATESPRITE):
4358 insptr++;
4359 {
4360 struct
4361 {
4362 vec3_t pos;
4363 int32_t ang, tilenum, shade, pal, orientation;
4364 } v;
4365 Gv_FillWithVars(v);
4366
4367 VM_ASSERT((unsigned)v.tilenum < MAXTILES, "invalid tilenum %d\n", v.tilenum);
4368
4369 int32_t alpha = (VM_DECODE_INST(tw) == CON_ROTATESPRITEA) ? Gv_GetVar(*insptr++) : 0;
4370 vec2_t bound[2];
4371 Gv_FillWithVars(bound);
4372
4373 if (VM_DECODE_INST(tw) != CON_ROTATESPRITE16 && !(v.orientation & ROTATESPRITE_FULL16))
4374 {
4375 v.pos.x <<= 16;
4376 v.pos.y <<= 16;
4377 }
4378
4379 int32_t blendidx = 0;
4380
4381 NEG_ALPHA_TO_BLEND(alpha, blendidx, v.orientation);
4382
4383 rotatesprite_(v.pos.x, v.pos.y, v.pos.z, v.ang, v.tilenum, v.shade, v.pal, 2 | (v.orientation & (ROTATESPRITE_MAX - 1)), alpha,
4384 blendidx, bound[0].x, bound[0].y, bound[1].x, bound[1].y);
4385 dispatch();
4386 }
4387
4388 vInstruction(CON_GAMETEXT):
4389 vInstruction(CON_GAMETEXTZ):
4390 insptr++;
4391 {
4392 struct
4393 {
4394 int32_t tilenum;
4395 vec2_t pos;
4396 int32_t nQuote, shade, pal, orientation;
4397 vec2_t bound[2];
4398 } v;
4399 Gv_FillWithVars(v);
4400
4401 VM_ASSERT(v.tilenum >= 0 && v.tilenum + 127 < MAXTILES, "invalid base tilenum %d\n", v.tilenum);
4402 VM_ASSERT((unsigned)v.nQuote < MAXQUOTES && apStrings[v.nQuote], "invalid quote %d\n", v.nQuote);
4403
4404 int32_t const z = (VM_DECODE_INST(tw) == CON_GAMETEXTZ) ? Gv_GetVar(*insptr++) : 65536;
4405
4406 G_PrintGameText(v.tilenum, v.pos.x >> 1, v.pos.y, apStrings[v.nQuote], v.shade, v.pal, v.orientation & (ROTATESPRITE_MAX - 1),
4407 v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y, z, 0);
4408 dispatch();
4409 }
4410
4411 vInstruction(CON_DIGITALNUMBER):
4412 vInstruction(CON_DIGITALNUMBERZ):
4413 insptr++;
4414 {
4415 struct
4416 {
4417 int32_t tilenum;
4418 vec2_t pos;
4419 int32_t nQuote, shade, pal, orientation;
4420 vec2_t bound[2];
4421 } v;
4422 Gv_FillWithVars(v);
4423
4424 VM_ASSERT(v.tilenum >= 0 && v.tilenum + 9 < MAXTILES, "invalid base tilenum %d\n", v.tilenum);
4425
4426 int32_t const nZoom = (VM_DECODE_INST(tw) == CON_DIGITALNUMBERZ) ? Gv_GetVar(*insptr++) : 65536;
4427
4428 // NOTE: '-' not taken into account, but we have rotatesprite() bound check now anyway
4429
4430 G_DrawTXDigiNumZ(v.tilenum, v.pos.x, v.pos.y, v.nQuote, v.shade, v.pal, v.orientation & (ROTATESPRITE_MAX - 1), v.bound[0].x,
4431 v.bound[0].y, v.bound[1].x, v.bound[1].y, nZoom);
4432 dispatch();
4433 }
4434
4435 vInstruction(CON_MINITEXT):
4436 insptr++;
4437 {
4438 struct
4439 {
4440 vec2_t pos;
4441 int32_t nQuote, shade, pal;
4442 } v;
4443 Gv_FillWithVars(v);
4444
4445 VM_ASSERT((unsigned)v.nQuote < MAXQUOTES && apStrings[v.nQuote], "invalid quote %d\n", v.nQuote);
4446
4447 minitextshade(v.pos.x, v.pos.y, apStrings[v.nQuote], v.shade, v.pal, 2 + 8 + 16);
4448 dispatch();
4449 }
4450
4451 vInstruction(CON_SCREENTEXT):
4452 insptr++;
4453 {
4454 struct
4455 {
4456 int32_t tilenum;
4457 vec3_t v;
4458 int32_t blockangle, charangle, nQuote, shade, pal, orientation, alpha;
4459 vec2_t spacing, between;
4460 int32_t nFlags;
4461 vec2_t bound[2];
4462 } v;
4463 Gv_FillWithVars(v);
4464
4465 VM_ASSERT(v.tilenum >= 0 && v.tilenum + 127 < MAXTILES, "invalid base tilenum %d\n", v.tilenum);
4466 VM_ASSERT((unsigned)v.nQuote < MAXQUOTES && apStrings[v.nQuote], "invalid quote %d\n", v.nQuote);
4467
4468 if (!(v.orientation & ROTATESPRITE_FULL16))
4469 {
4470 v.v.x <<= 16;
4471 v.v.y <<= 16;
4472 v.spacing.x <<= 16;
4473 v.spacing.y <<= 16;
4474 v.between.x <<= 16;
4475 v.between.y <<= 16;
4476 }
4477
4478 G_SetScreenTextEmpty(v.spacing, v.tilenum, v.nFlags);
4479
4480 G_ScreenText(v.tilenum, v.v.x, v.v.y, v.v.z, v.blockangle, v.charangle, apStrings[v.nQuote], v.shade, v.pal,
4481 2 | (v.orientation & (ROTATESPRITE_MAX - 1)), v.alpha, v.spacing.x, v.spacing.y, v.between.x, v.between.y, v.nFlags,
4482 v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y);
4483 dispatch();
4484 }
4485
4486 vInstruction(CON_GETZRANGE):
4487 insptr++;
4488 {
4489 struct
4490 {
4491 vec3_t vect;
4492 int32_t sectNum;
4493 } v;
4494 Gv_FillWithVars(v);
4495
4496 VM_ASSERT((unsigned)v.sectNum < MAXSECTORS, "invalid sector %d\n", v.sectNum);
4497
4498 int const ceilzvar = *insptr++;
4499 int const ceilhitvar = *insptr++;
4500 int const florzvar = *insptr++;
4501 int const florhitvar = *insptr++;
4502
4503 struct
4504 {
4505 int32_t walldist, clipmask;
4506 } v2;
4507 Gv_FillWithVars(v2);
4508
4509 int32_t ceilz, ceilhit, florz, florhit;
4510
4511 getzrange(&v.vect, v.sectNum, &ceilz, &ceilhit, &florz, &florhit, v2.walldist, v2.clipmask);
4512 Gv_SetVar(ceilzvar, ceilz);
4513 Gv_SetVar(ceilhitvar, ceilhit);
4514 Gv_SetVar(florzvar, florz);
4515 Gv_SetVar(florhitvar, florhit);
4516
4517 dispatch();
4518 }
4519
4520 vInstruction(CON_SECTSETINTERPOLATION):
4521 vInstruction(CON_SECTCLEARINTERPOLATION):
4522 insptr++;
4523 {
4524 int const sectnum = Gv_GetVar(*insptr++);
4525
4526 VM_ASSERT((unsigned)sectnum < MAXSECTORS, "invalid sector %d\n", sectnum);
4527
4528 if (VM_DECODE_INST(tw) == CON_SECTSETINTERPOLATION)
4529 Sect_SetInterpolation(sectnum);
4530 else
4531 Sect_ClearInterpolation(sectnum);
4532
4533 dispatch();
4534 }
4535
4536 vInstruction(CON_CALCHYPOTENUSE):
4537 insptr++;
4538 {
4539 int32_t returnVar = *insptr++;
4540 vec2_t da;
4541 Gv_FillWithVars(da);
4542 int64_t const hypsq = (int64_t)da.x * da.x + (int64_t)da.y * da.y;
4543
4544 Gv_SetVar(returnVar, (hypsq > (int64_t)INT32_MAX) ? (int32_t)sqrt((double)hypsq) : ksqrt((uint32_t)hypsq));
4545 dispatch();
4546 }
4547
4548 vInstruction(CON_LINEINTERSECT):
4549 vInstruction(CON_RAYINTERSECT):
4550 insptr++;
4551 {
4552 struct
4553 {
4554 vec3_t vec[2];
4555 vec2_t vec2[2];
4556 } v;
4557 Gv_FillWithVars(v);
4558
4559 int const intxvar = *insptr++;
4560 int const intyvar = *insptr++;
4561 int const intzvar = *insptr++;
4562 int const retvar = *insptr++;
4563 vec3_t in;
4564
4565 int ret = ((VM_DECODE_INST(tw) == CON_LINEINTERSECT) ? lintersect : rayintersect)(v.vec[0].x, v.vec[0].y, v.vec[0].z, v.vec[1].x, v.vec[1].y,
4566 v.vec[1].z, v.vec2[0].x, v.vec2[0].y, v.vec2[1].x, v.vec2[1].y,
4567 &in.x, &in.y, &in.z);
4568
4569 Gv_SetVar(retvar, ret);
4570
4571 if (ret)
4572 {
4573 Gv_SetVar(intxvar, in.x);
4574 Gv_SetVar(intyvar, in.y);
4575 Gv_SetVar(intzvar, in.z);
4576 }
4577
4578 dispatch();
4579 }
4580
4581 vInstruction(CON_CLIPMOVE):
4582 vInstruction(CON_CLIPMOVENOSLIDE):
4583 insptr++;
4584 {
4585 typedef struct
4586 {
4587 int32_t w, f, c;
4588 } vec3dist_t;
4589
4590 int const returnVar = *insptr++;
4591 int const xReturn = *insptr++;
4592 int const yReturn = *insptr++;
4593
4594 insptr -= 2;
4595
4596 typedef struct
4597 {
4598 vec3_t vec3;
4599 int32_t sectNum32;
4600 vec2_t vec2;
4601 vec3dist_t dist;
4602 int32_t clipMask;
4603 } clipmoveparams_t;
4604
4605 int32_t const sectReturn = insptr[offsetof(clipmoveparams_t, sectNum32) / sizeof(int32_t)];
4606
4607 clipmoveparams_t v;
4608 Gv_FillWithVars(v);
4609
4610 int16_t sectNum = v.sectNum32;
4611 VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum);
4612
4613 Gv_SetVar(
4614 returnVar,
4615 clipmovex(&v.vec3, §Num, v.vec2.x, v.vec2.y, v.dist.w, v.dist.f, v.dist.c, v.clipMask, (VM_DECODE_INST(tw) == CON_CLIPMOVENOSLIDE)));
4616 Gv_SetVar(sectReturn, v.sectNum32);
4617 Gv_SetVar(xReturn, v.vec3.x);
4618 Gv_SetVar(yReturn, v.vec3.y);
4619
4620 dispatch();
4621 }
4622
4623 vInstruction(CON_HITSCAN):
4624 insptr++;
4625 {
4626 struct
4627 {
4628 vec3_t origin;
4629 int32_t sectnum;
4630 vec3_t vect;
4631 } v;
4632 Gv_FillWithVars(v);
4633
4634 VM_ASSERT((unsigned)v.sectnum < MAXSECTORS, "invalid sector %d\n", v.sectnum);
4635
4636 int const sectReturn = *insptr++;
4637 int const wallReturn = *insptr++;
4638 int const spriteReturn = *insptr++;
4639 int const xReturn = *insptr++;
4640 int const yReturn = *insptr++;
4641 int const zReturn = *insptr++;
4642 int const clipType = Gv_GetVar(*insptr++);
4643
4644 hitdata_t hit;
4645 hitscan(&v.origin, v.sectnum, v.vect.x, v.vect.y, v.vect.z, &hit, clipType);
4646
4647 Gv_SetVar(sectReturn, hit.sect);
4648 Gv_SetVar(wallReturn, hit.wall);
4649 Gv_SetVar(spriteReturn, hit.sprite);
4650 Gv_SetVar(xReturn, hit.pos.x);
4651 Gv_SetVar(yReturn, hit.pos.y);
4652 Gv_SetVar(zReturn, hit.pos.z);
4653 dispatch();
4654 }
4655
4656 vInstruction(CON_CANSEE):
4657 insptr++;
4658 {
4659 struct
4660 {
4661 vec3_t vec1;
4662 int32_t firstSector;
4663 vec3_t vec2;
4664 int32_t secondSector;
4665 } v;
4666 Gv_FillWithVars(v);
4667
4668 VM_ASSERT((unsigned)v.firstSector < (unsigned)numsectors && (unsigned)v.secondSector < (unsigned)numsectors, "invalid sector %d\n",
4669 (unsigned)v.firstSector >= (unsigned)numsectors ? v.firstSector : v.secondSector);
4670
4671 Gv_SetVar(*insptr++, cansee(v.vec1.x, v.vec1.y, v.vec1.z, v.firstSector, v.vec2.x, v.vec2.y, v.vec2.z, v.secondSector));
4672 dispatch();
4673 }
4674
4675 vInstruction(CON_ROTATEPOINT):
4676 insptr++;
4677 {
4678 struct
4679 {
4680 vec2_t point[2];
4681 int32_t angle;
4682 } v;
4683 Gv_FillWithVars(v);
4684
4685 int const xReturn = *insptr++;
4686 int const yReturn = *insptr++;
4687 vec2_t result;
4688
4689 rotatepoint(v.point[0], v.point[1], v.angle, &result);
4690
4691 Gv_SetVar(xReturn, result.x);
4692 Gv_SetVar(yReturn, result.y);
4693 dispatch();
4694 }
4695
4696 vInstruction(CON_NEARTAG):
4697 insptr++;
4698 {
4699 // neartag(int32_t x, int32_t y, int32_t z, short sectnum, short ang, //Starting position & angle
4700 // short *neartagsector, //Returns near sector if sector[].tag != 0
4701 // short *neartagwall, //Returns near wall if wall[].tag != 0
4702 // short *neartagsprite, //Returns near sprite if sprite[].tag != 0
4703 // int32_t *neartaghitdist, //Returns actual distance to object (scale: 1024=largest grid size)
4704 // int32_t neartagrange, //Choose maximum distance to scan (scale: 1024=largest grid size)
4705 // char tagsearch) //1-lotag only, 2-hitag only, 3-lotag&hitag
4706
4707 struct
4708 {
4709 vec3_t point;
4710 int32_t sectNum, nAngle;
4711 } v;
4712 Gv_FillWithVars(v);
4713
4714 VM_ASSERT((unsigned)v.sectNum < MAXSECTORS, "invalid sector %d\n", v.sectNum);
4715
4716 int const sectReturn = *insptr++;
4717 int const wallReturn = *insptr++;
4718 int const spriteReturn = *insptr++;
4719 int const distReturn = *insptr++;
4720
4721 struct
4722 {
4723 int32_t tagRange, tagSearch;
4724 } v2;
4725 Gv_FillWithVars(v2);
4726
4727
4728 int16_t neartagsector, neartagwall, neartagsprite;
4729 int32_t neartaghitdist;
4730
4731 neartag(v.point.x, v.point.y, v.point.z, v.sectNum, v.nAngle, &neartagsector, &neartagwall, &neartagsprite, &neartaghitdist,
4732 v2.tagRange, v2.tagSearch, NULL);
4733
4734 Gv_SetVar(sectReturn, neartagsector);
4735 Gv_SetVar(wallReturn, neartagwall);
4736 Gv_SetVar(spriteReturn, neartagsprite);
4737 Gv_SetVar(distReturn, neartaghitdist);
4738 dispatch();
4739 }
4740
4741 vInstruction(CON_GETTIMEDATE):
4742 insptr++;
4743 {
4744 int32_t values[8];
4745 G_GetTimeDate(values);
4746
4747 for (int value : values)
4748 Gv_SetVar(*insptr++, value);
4749
4750 dispatch();
4751 }
4752
4753 vInstruction(CON_MOVESPRITE):
4754 insptr++;
4755 {
4756 struct
4757 {
4758 int32_t spriteNum;
4759 vec3_t vect;
4760 int32_t clipType;
4761 } v;
4762 Gv_FillWithVars(v);
4763
4764 VM_ASSERT((unsigned)v.spriteNum < MAXSPRITES, "invalid sprite %d\n", v.spriteNum);
4765
4766 Gv_SetVar(*insptr++, A_MoveSprite(v.spriteNum, &v.vect, v.clipType));
4767 dispatch();
4768 }
4769
4770 vInstruction(CON_SETSPRITE):
4771 insptr++;
4772 {
4773 struct
4774 {
4775 int32_t spriteNum;
4776 vec3_t vect;
4777 } v;
4778 Gv_FillWithVars(v);
4779
4780 VM_ASSERT((unsigned)v.spriteNum < MAXSPRITES, "invalid sprite %d\n", v.spriteNum);
4781
4782 setsprite(v.spriteNum, &v.vect);
4783 dispatch();
4784 }
4785
4786 vInstruction(CON_GETFLORZOFSLOPE):
4787 vInstruction(CON_GETCEILZOFSLOPE):
4788 insptr++;
4789 {
4790 struct
4791 {
4792 int32_t sectNum;
4793 vec2_t vect;
4794 } v;
4795 Gv_FillWithVars(v);
4796
4797 VM_ASSERT((unsigned)v.sectNum < MAXSECTORS, "invalid sector %d\n", v.sectNum);
4798
4799 Gv_SetVar(*insptr++, (VM_DECODE_INST(tw) == CON_GETFLORZOFSLOPE ? yax_getflorzofslope : yax_getceilzofslope)(v.sectNum, v.vect));
4800 dispatch();
4801 }
4802
4803 vInstruction(CON_UPDATESECTOR):
4804 insptr++;
4805 {
4806 vec2_t vect = { 0, 0 };
4807 Gv_FillWithVars(vect);
4808
4809 int const returnVar = *insptr++;
4810 int16_t sectNum = Gv_GetVar(returnVar);
4811
4812 if ((unsigned)sectNum >= MAXSECTORS)
4813 sectNum = vm.pSprite->sectnum;
4814
4815 updatesector(vect.x, vect.y, §Num);
4816 Gv_SetVar(returnVar, sectNum);
4817 dispatch();
4818 }
4819
4820 vInstruction(CON_UPDATESECTORZ):
4821 insptr++;
4822 {
4823 vec3_t vect = { 0, 0, 0 };
4824 Gv_FillWithVars(vect);
4825
4826 int const returnVar = *insptr++;
4827 int16_t sectNum = Gv_GetVar(returnVar);
4828
4829 if ((unsigned)sectNum >= MAXSECTORS)
4830 sectNum = vm.pSprite->sectnum;
4831
4832 updatesectorz(vect.x, vect.y, vect.z, §Num);
4833 Gv_SetVar(returnVar, sectNum);
4834 dispatch();
4835 }
4836
4837 vInstruction(CON_UPDATESECTORNEIGHBOR):
4838 insptr++;
4839 {
4840 vec2_t vect = { 0, 0 };
4841 Gv_FillWithVars(vect);
4842
4843 int const returnVar = *insptr++;
4844 int16_t sectNum = Gv_GetVar(returnVar);
4845
4846 if ((unsigned)sectNum >= MAXSECTORS)
4847 sectNum = vm.pSprite->sectnum;
4848
4849 updatesectorneighbor(vect.x, vect.y, §Num, getsectordist(vect, sectNum));
4850 Gv_SetVar(returnVar, sectNum);
4851 dispatch();
4852 }
4853
4854 vInstruction(CON_UPDATESECTORNEIGHBORZ):
4855 insptr++;
4856 {
4857 vec3_t vect = { 0, 0, 0 };
4858 Gv_FillWithVars(vect);
4859
4860 int const returnVar = *insptr++;
4861 int16_t sectNum = Gv_GetVar(returnVar);
4862
4863 if ((unsigned)sectNum >= MAXSECTORS)
4864 sectNum = vm.pSprite->sectnum;
4865
4866 updatesectorneighborz(vect.x, vect.y, vect.z, §Num, getsectordist({vect.x, vect.y}, sectNum));
4867 Gv_SetVar(returnVar, sectNum);
4868 dispatch();
4869 }
4870
4871 vInstruction(CON_CAPIA):
4872 {
4873 insptr++;
4874 int const nQuote = Gv_GetVar(*insptr++);
4875
4876 VM_ASSERT((unsigned)nQuote < MAXQUOTES && apStrings[nQuote], "invalid quote %d\n", nQuote);
4877
4878 communityapiUnlockAchievement(apStrings[nQuote]);
4879 dispatch();
4880 }
4881
4882 vInstruction(CON_CAPIS):
4883 {
4884 insptr++;
4885 int const nQuote = Gv_GetVar(*insptr++);
4886 int const value = Gv_GetVar(*insptr++);
4887
4888 VM_ASSERT((unsigned)nQuote < MAXQUOTES && apStrings[nQuote], "invalid quote %d\n", nQuote);
4889
4890 communityapiSetStat(apStrings[nQuote], value);
4891 dispatch();
4892 }
4893
4894 vInstruction(CON_SPAWN):
4895 insptr++;
4896
4897 VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum);
4898
4899 A_Spawn(vm.spriteNum, *insptr++);
4900 dispatch();
4901
4902 vInstruction(CON_RESETACTIONCOUNT):
4903 insptr++;
4904 AC_ACTION_COUNT(vm.pData) = 0;
4905 dispatch();
4906
4907 vInstruction(CON_DEBRIS):
4908 insptr++;
4909 {
4910 #ifndef EDUKE32_STANDALONE
4911 if (!FURY)
4912 {
4913 int debrisTile = *insptr++;
4914
4915 if ((unsigned)vm.pSprite->sectnum < MAXSECTORS)
4916 for (native_t cnt = (*insptr) - 1; cnt >= 0; cnt--)
4917 {
4918 int const tileOffset = (vm.pSprite->picnum == BLIMP && debrisTile == SCRAP1) ? 0 : (krand() % 3);
4919
4920 int const spriteNum = A_InsertSprite(
4921 vm.pSprite->sectnum, vm.pSprite->x + (krand() & 255) - 128, vm.pSprite->y + (krand() & 255) - 128,
4922 vm.pSprite->z - (8 << 8) - (krand() & 8191), debrisTile + tileOffset, vm.pSprite->shade, 32 + (krand() & 15),
4923 32 + (krand() & 15), krand() & 2047, (krand() & 127) + 32, -(krand() & 2047), vm.spriteNum, 5);
4924
4925 sprite[spriteNum].yvel = (vm.pSprite->picnum == BLIMP && debrisTile == SCRAP1) ? g_blimpSpawnItems[cnt % 14] : -1;
4926 sprite[spriteNum].pal = vm.pSprite->pal;
4927 }
4928 }
4929 #else
4930 insptr++;
4931 #endif
4932 insptr++;
4933 }
4934 dispatch();
4935
4936 vInstruction(CON_COUNT):
4937 insptr++;
4938 AC_COUNT(vm.pData) = (int16_t)*insptr++;
4939 dispatch();
4940
4941 vInstruction(CON_CSTATOR):
4942 insptr++;
4943 vm.pSprite->cstat |= (int16_t)*insptr++;
4944 dispatch();
4945
4946 vInstruction(CON_CLIPDIST):
4947 insptr++;
4948 vm.pSprite->clipdist = (int16_t)*insptr++;
4949 dispatch();
4950
4951 vInstruction(CON_CSTAT):
4952 insptr++;
4953 vm.pSprite->cstat = (int16_t)*insptr++;
4954 dispatch();
4955
4956 vInstruction(CON_SAVENN):
4957 vInstruction(CON_SAVE):
4958 insptr++;
4959 {
4960 int32_t const requestedSlot = *insptr++;
4961
4962 if ((unsigned)requestedSlot >= 10)
4963 dispatch();
4964
4965 // check if we need to make a new file
4966 if (strcmp(g_lastautosave.path, g_lastusersave.path) == 0 || requestedSlot != g_lastAutoSaveArbitraryID)
4967 {
4968 g_lastautosave.reset();
4969 }
4970
4971 g_lastAutoSaveArbitraryID = requestedSlot;
4972
4973 if (VM_DECODE_INST(tw) == CON_SAVE || g_lastautosave.name[0] == 0)
4974 {
4975 time_t timeStruct = time(NULL);
4976 struct tm *pTime = localtime(&timeStruct);
4977
4978 strftime(g_lastautosave.name, sizeof(g_lastautosave.name), "%d %b %Y %I:%M%p", pTime);
4979 }
4980
4981 g_saveRequested = true;
4982
4983 dispatch();
4984 }
4985
4986 vInstruction(CON_QUAKE):
4987 insptr++;
4988 g_earthquakeTime = Gv_GetVar(*insptr++);
4989 #ifndef EDUKE32_STANDALONE
4990 if (!FURY)
4991 A_PlaySound(EARTHQUAKE, g_player[screenpeek].ps->i);
4992 #endif
4993 dispatch();
4994
4995 vInstruction(CON_RESETPLAYER):
4996 insptr++;
4997 vm.flags = VM_ResetPlayer(vm.playerNum, vm.flags, 0);
4998 dispatch();
4999
5000 vInstruction(CON_RESETPLAYERFLAGS):
5001 insptr++;
5002 vm.flags = VM_ResetPlayer(vm.playerNum, vm.flags, Gv_GetVar(*insptr++));
5003 dispatch();
5004
5005 vInstruction(CON_RESETCOUNT):
5006 insptr++;
5007 AC_COUNT(vm.pData) = 0;
5008 dispatch();
5009
5010 vInstruction(CON_ADDINVENTORY):
5011 insptr += 2;
5012
5013 VM_AddInventory(vm.pPlayer, insptr[-1], *insptr);
5014
5015 insptr++;
5016 dispatch();
5017
5018 vInstruction(CON_HITRADIUS):
5019 insptr++;
5020 {
5021 int32_t params[5];
5022 Gv_FillWithVars(params);
5023 A_RadiusDamage(vm.spriteNum, params[0], params[1], params[2], params[3], params[4]);
5024 }
5025 dispatch();
5026
5027 vInstruction(CON_IFP):
5028 {
5029 int const moveFlags = *(++insptr);
5030 int nResult = 0;
5031 int const playerXVel = sprite[vm.pPlayer->i].xvel;
5032 int const syncBits = g_player[vm.playerNum].input.bits;
5033
5034 if (((moveFlags & pducking) && vm.pPlayer->on_ground && TEST_SYNC_KEY(syncBits, SK_CROUCH))
5035 || ((moveFlags & pfalling) && vm.pPlayer->jumping_counter == 0 && !vm.pPlayer->on_ground && vm.pPlayer->vel.z > 2048)
5036 || ((moveFlags & pjumping) && vm.pPlayer->jumping_counter > 348)
5037 || ((moveFlags & pstanding) && playerXVel >= 0 && playerXVel < 8)
5038 || ((moveFlags & pwalking) && playerXVel >= 8 && !TEST_SYNC_KEY(syncBits, SK_RUN))
5039 || ((moveFlags & prunning) && playerXVel >= 8 && TEST_SYNC_KEY(syncBits, SK_RUN))
5040 || ((moveFlags & phigher) && vm.pPlayer->pos.z < (vm.pSprite->z - (48 << 8)))
5041 || ((moveFlags & pwalkingback) && playerXVel <= -8 && !TEST_SYNC_KEY(syncBits, SK_RUN))
5042 || ((moveFlags & prunningback) && playerXVel <= -8 && TEST_SYNC_KEY(syncBits, SK_RUN))
5043 || ((moveFlags & pkicking)
5044 && (vm.pPlayer->quick_kick > 0
5045 || (PWEAPON(vm.playerNum, vm.pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON && vm.pPlayer->kickback_pic > 0)))
5046 || ((moveFlags & pshrunk) && sprite[vm.pPlayer->i].xrepeat < 32)
5047 || ((moveFlags & pjetpack) && vm.pPlayer->jetpack_on)
5048 || ((moveFlags & ponsteroids) && vm.pPlayer->inv_amount[GET_STEROIDS] > 0 && vm.pPlayer->inv_amount[GET_STEROIDS] < 400)
5049 || ((moveFlags & ponground) && vm.pPlayer->on_ground)
5050 || ((moveFlags & palive) && sprite[vm.pPlayer->i].xrepeat > 32 && sprite[vm.pPlayer->i].extra > 0 && vm.pPlayer->timebeforeexit == 0)
5051 || ((moveFlags & pdead) && sprite[vm.pPlayer->i].extra <= 0))
5052 nResult = 1;
5053 else if ((moveFlags & pfacing))
5054 {
5055 nResult
5056 = (vm.pSprite->picnum == APLAYER && (g_netServer || ud.multimode > 1))
5057 ? G_GetAngleDelta(fix16_to_int(g_player[otherp].ps->q16ang),
5058 getangle(vm.pPlayer->pos.x - g_player[otherp].ps->pos.x, vm.pPlayer->pos.y - g_player[otherp].ps->pos.y))
5059 : G_GetAngleDelta(fix16_to_int(vm.pPlayer->q16ang), getangle(vm.pSprite->x - vm.pPlayer->pos.x, vm.pSprite->y - vm.pPlayer->pos.y));
5060
5061 nResult = (nResult > -128 && nResult < 128);
5062 }
5063 branch(nResult);
5064 }
5065 dispatch();
5066
5067 vInstruction(CON_GUTS):
5068 #ifndef EDUKE32_STANDALONE
5069 if (!FURY)
5070 A_DoGuts(vm.spriteNum, insptr[1], insptr[2]);
5071 #endif
5072 insptr += 3;
5073 dispatch();
5074
5075 vInstruction(CON_WACKPLAYER):
5076 insptr++;
5077 P_ForceAngle(vm.pPlayer);
5078 dispatch();
5079
5080 vInstruction(CON_FLASH):
5081 insptr++;
5082 vm.pSprite->shade = -127;
5083 vm.pPlayer->visibility = -127;
5084 dispatch();
5085
5086 vInstruction(CON_SAVEMAPSTATE):
5087 G_SaveMapState();
5088 insptr++;
5089 dispatch();
5090
5091 vInstruction(CON_LOADMAPSTATE):
5092 G_RestoreMapState();
5093 insptr++;
5094 dispatch();
5095
5096 vInstruction(CON_CLEARMAPSTATE):
5097 insptr++;
5098 {
5099 int const levelNum = Gv_GetVar(*insptr++);
5100 VM_ASSERT((unsigned)levelNum < MAXVOLUMES * MAXLEVELS, "invalid map number %d\n", levelNum);
5101 G_FreeMapState(levelNum);
5102 }
5103 dispatch();
5104
5105 vInstruction(CON_STOPALLSOUNDS):
5106 insptr++;
5107 if (screenpeek == vm.playerNum)
5108 FX_StopAllSounds();
5109 dispatch();
5110
5111 vInstruction(CON_STOPALLMUSIC):
5112 insptr++;
5113 S_StopMusic();
5114 dispatch();
5115
5116 vInstruction(CON_OPERATE):
5117 insptr++;
5118 if (sector[vm.pSprite->sectnum].lotag == 0)
5119 {
5120 int16_t foundSect, foundWall, foundSprite;
5121 int32_t foundDist;
5122
5123 neartag(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - ZOFFSET5, vm.pSprite->sectnum, vm.pSprite->ang, &foundSect, &foundWall,
5124 &foundSprite, &foundDist, 768, 4 + 1, NULL);
5125
5126 if (foundSect >= 0 && isanearoperator(sector[foundSect].lotag))
5127 if ((sector[foundSect].lotag & 0xff) == ST_23_SWINGING_DOOR || sector[foundSect].floorz == sector[foundSect].ceilingz)
5128 if ((sector[foundSect].lotag & (16384u | 32768u)) == 0)
5129 {
5130 int32_t j;
5131
5132 for (SPRITES_OF_SECT(foundSect, j))
5133 if (sprite[j].picnum == ACTIVATOR)
5134 break;
5135
5136 if (j == -1)
5137 G_OperateSectors(foundSect, vm.spriteNum);
5138 }
5139 }
5140 dispatch();
5141
5142
5143 vInstruction(CON_SPRITEPAL):
5144 insptr++;
5145 if (vm.pSprite->picnum != APLAYER)
5146 vm.pActor->tempang = vm.pSprite->pal;
5147 vm.pSprite->pal = *insptr++;
5148 dispatch();
5149
5150 vInstruction(CON_CACTOR):
5151 insptr++;
5152 vm.pSprite->picnum = *insptr++;
5153 dispatch();
5154
5155 vInstruction(CON_PALFROM):
5156 insptr++;
5157 VM_ASSERT((unsigned)vm.playerNum < MAXPLAYERS, "invalid player %d\n", vm.playerNum);
5158 {
5159 palette_t const pal = { uint8_t(insptr[1]), uint8_t(insptr[2]), uint8_t(insptr[3]), uint8_t(insptr[0]) };
5160 insptr += 4;
5161 P_PalFrom(vm.pPlayer, pal.f, pal.r, pal.g, pal.b);
5162 }
5163 dispatch();
5164
5165 vInstruction(CON_SCREENPAL):
5166 insptr++;
5167 {
5168 int32_t params[4];
5169 Gv_FillWithVars(params);
5170 videoFadePalette(params[0], params[1], params[2], params[3]);
5171 }
5172 dispatch();
5173
5174 vInstruction(CON_SECTOROFWALL):
5175 insptr++;
5176 tw = *insptr++;
5177 Gv_SetVar(tw, sectorofwall(Gv_GetVar(*insptr++)));
5178 dispatch();
5179
5180 vInstruction(CON_QSPRINTF):
5181 insptr++;
5182 {
5183 int const outputQuote = Gv_GetVar(*insptr++);
5184 int const inputQuote = Gv_GetVar(*insptr++);
5185
5186 VM_ASSERT(apStrings[inputQuote] != NULL && apStrings[outputQuote] != NULL, "null quote %d\n", apStrings[inputQuote] ? outputQuote : inputQuote);
5187
5188 auto &inBuf = apStrings[inputQuote];
5189
5190 int32_t arg[32];
5191 char outBuf[MAXQUOTELEN];
5192
5193 int const quoteLen = Bstrlen(inBuf);
5194
5195 int inputPos = 0;
5196 int outputPos = 0;
5197 int argIdx = 0;
5198
5199 while (VM_DECODE_INST(*insptr) != CON_NULLOP && argIdx < 32)
5200 arg[argIdx++] = Gv_GetVar(*insptr++);
5201
5202 int numArgs = argIdx;
5203
5204 insptr++; // skip the NOP
5205
5206 argIdx = 0;
5207
5208 do
5209 {
5210 while (inputPos < quoteLen && outputPos < MAXQUOTELEN && inBuf[inputPos] != '%')
5211 outBuf[outputPos++] = inBuf[inputPos++];
5212
5213 if (inBuf[inputPos] == '%')
5214 {
5215 inputPos++;
5216 switch (inBuf[inputPos])
5217 {
5218 case 'l':
5219 if (inBuf[inputPos + 1] != 'd')
5220 {
5221 // write the % and l
5222 outBuf[outputPos++] = inBuf[inputPos - 1];
5223 outBuf[outputPos++] = inBuf[inputPos++];
5224 break;
5225 }
5226 inputPos++;
5227 fallthrough__;
5228 case 'd':
5229 {
5230 if (argIdx >= numArgs)
5231 goto finish_qsprintf;
5232
5233 char buf[16];
5234 Bsprintf(buf, "%d", arg[argIdx++]);
5235
5236 int const bufLen = Bstrlen(buf);
5237 Bmemcpy(&outBuf[outputPos], buf, bufLen);
5238 outputPos += bufLen;
5239 inputPos++;
5240 }
5241 break;
5242
5243 case 's':
5244 {
5245 if (argIdx >= numArgs)
5246 goto finish_qsprintf;
5247
5248 int const argLen = Bstrlen(apStrings[arg[argIdx]]);
5249
5250 Bmemcpy(&outBuf[outputPos], apStrings[arg[argIdx]], argLen);
5251 outputPos += argLen;
5252 argIdx++;
5253 inputPos++;
5254 }
5255 break;
5256
5257 default: outBuf[outputPos++] = inBuf[inputPos - 1]; break;
5258 }
5259 }
5260 } while (inputPos < quoteLen && outputPos < MAXQUOTELEN);
5261 finish_qsprintf:
5262 outBuf[outputPos] = '\0';
5263 Bstrncpyz(apStrings[outputQuote], outBuf, MAXQUOTELEN);
5264 dispatch();
5265 }
5266
5267 vInstruction(CON_ADDLOGVAR):
5268 insptr++;
5269 {
5270 int32_t m = 1;
5271 char szBuf[256];
5272 int32_t lVarID = *insptr;
5273
5274 if ((lVarID >= g_gameVarCount) || lVarID < 0)
5275 {
5276 if (*insptr == MAXGAMEVARS) // addlogvar for a constant? Har.
5277 insptr++;
5278 // else if (*insptr > g_gameVarCount && (*insptr < (MAXGAMEVARS<<1)+MAXGAMEVARS+1+MAXGAMEARRAYS))
5279 else if (*insptr & (MAXGAMEVARS << 2))
5280 {
5281 int32_t index;
5282
5283 lVarID ^= (MAXGAMEVARS << 2);
5284
5285 if (lVarID & GV_FLAG_NEGATIVE)
5286 {
5287 m = -m;
5288 lVarID ^= GV_FLAG_NEGATIVE;
5289 }
5290
5291 insptr++;
5292
5293 index = Gv_GetVar(*insptr++);
5294 if (EDUKE32_PREDICT_TRUE((unsigned)index < (unsigned)aGameArrays[lVarID].size))
5295 {
5296 initprintf(OSDTEXT_GREEN "CONLOGVAR: L=%d %s[%d] =%d\n", VM_DECODE_LINE_NUMBER(g_tw), aGameArrays[lVarID].szLabel, index,
5297 (int32_t)(m * Gv_GetArrayValue(lVarID, index)));
5298 dispatch();
5299 }
5300 else
5301 {
5302 CON_ERRPRINTF("invalid array index\n");
5303 abort_after_error();
5304 }
5305 }
5306 else if (*insptr & (MAXGAMEVARS << 3))
5307 {
5308 // FIXME FIXME FIXME
5309 if ((lVarID & (MAXGAMEVARS - 1)) == g_structVarIDs + STRUCT_ACTORVAR)
5310 {
5311 auto const oinsptr = insptr++;
5312 int32_t index = Gv_GetVar(*insptr++);
5313 insptr = oinsptr;
5314
5315 if (EDUKE32_PREDICT_FALSE((unsigned)index >= MAXSPRITES - 1))
5316 {
5317 CON_ERRPRINTF("invalid array index\n");
5318 abort_after_error();
5319 }
5320 initprintf(OSDTEXT_GREEN "CONLOGVAR: L=%d %d %d\n", VM_DECODE_LINE_NUMBER(g_tw), index, Gv_GetVar(*insptr++, index, vm.playerNum));
5321 dispatch();
5322 }
5323 }
5324 else if (EDUKE32_PREDICT_TRUE(*insptr & GV_FLAG_NEGATIVE))
5325 {
5326 m = -m;
5327 lVarID ^= GV_FLAG_NEGATIVE;
5328 }
5329 else
5330 {
5331 // invalid varID
5332 CON_ERRPRINTF("invalid variable\n");
5333 abort_after_error();
5334 }
5335 }
5336 Bsprintf(tempbuf, "CONLOGVAR: L=%d %s ", VM_DECODE_LINE_NUMBER(g_tw), aGameVars[lVarID].szLabel);
5337
5338 if (aGameVars[lVarID].flags & GAMEVAR_READONLY)
5339 {
5340 Bsprintf(szBuf, " (read-only)");
5341 Bstrcat(tempbuf, szBuf);
5342 }
5343 if (aGameVars[lVarID].flags & GAMEVAR_PERPLAYER)
5344 {
5345 Bsprintf(szBuf, " (Per Player. Player=%d)", vm.playerNum);
5346 }
5347 else if (aGameVars[lVarID].flags & GAMEVAR_PERACTOR)
5348 {
5349 Bsprintf(szBuf, " (Per Actor. Actor=%d)", vm.spriteNum);
5350 }
5351 else
5352 {
5353 Bsprintf(szBuf, " (Global)");
5354 }
5355 Bstrcat(tempbuf, szBuf);
5356 Bsprintf(szBuf, " =%d\n", Gv_GetVar(lVarID) * m);
5357 Bstrcat(tempbuf, szBuf);
5358 initprintf(OSDTEXT_GREEN "%s", tempbuf);
5359 insptr++;
5360 dispatch();
5361 }
5362
5363 vInstruction(CON_SQRT):
5364 insptr++;
5365 {
5366 // syntax sqrt <invar> <outvar>
5367 int const sqrtval = ksqrt((uint32_t)Gv_GetVar(*insptr++));
5368 Gv_SetVar(*insptr++, sqrtval);
5369 dispatch();
5370 }
5371
5372 vInstruction(CON_FINDNEARACTOR):
5373 vInstruction(CON_FINDNEARSPRITE):
5374 vInstruction(CON_FINDNEARACTOR3D):
5375 vInstruction(CON_FINDNEARSPRITE3D):
5376 insptr++;
5377 {
5378 // syntax findnearactorvar <type> <maxdistvar> <getvar>
5379 // gets the sprite ID of the nearest actor within max dist
5380 // that is of <type> into <getvar>
5381 // -1 for none found
5382 // <type> <maxdistvarid> <varid>
5383 int const decodedInst = VM_DECODE_INST(tw);
5384 int const actorsOnly = (decodedInst == CON_FINDNEARACTOR || decodedInst == CON_FINDNEARACTOR3D);
5385 auto const dist_funcptr = (decodedInst == CON_FINDNEARACTOR || decodedInst == CON_FINDNEARSPRITE) ? &ldist : &dist;
5386
5387 int const findTile = *insptr++;
5388 int maxDist = Gv_GetVar(*insptr++);
5389 int const returnVar = *insptr++;
5390
5391 int findStatnum = actorsOnly ? STAT_ACTOR : MAXSTATUS - 1;
5392 int foundSprite = -1;
5393
5394 do
5395 {
5396 int spriteNum = headspritestat[findStatnum]; // all sprites
5397
5398 while ((unsigned)spriteNum < MAXSPRITES)
5399 {
5400 if (sprite[spriteNum].picnum == findTile && spriteNum != vm.spriteNum)
5401 {
5402 int const foundDist = dist_funcptr(vm.pSprite, &sprite[spriteNum]);
5403
5404 if (foundDist < maxDist)
5405 {
5406 maxDist = foundDist;
5407 foundSprite = spriteNum;
5408 }
5409 }
5410
5411 spriteNum = nextspritestat[spriteNum];
5412 }
5413
5414 if (actorsOnly)
5415 break;
5416 }
5417 while (findStatnum--);
5418
5419 Gv_SetVar(returnVar, foundSprite);
5420 dispatch();
5421 }
5422
5423 vInstruction(CON_FINDNEARACTORZ):
5424 vInstruction(CON_FINDNEARSPRITEZ):
5425 insptr++;
5426 {
5427 // syntax findnearactorvar <type> <maxdistvar> <getvar>
5428 // gets the sprite ID of the nearest actor within max dist
5429 // that is of <type> into <getvar>
5430 // -1 for none found
5431 // <type> <maxdistvarid> <varid>
5432 int const actorsOnly = (VM_DECODE_INST(tw) == CON_FINDNEARACTORZ);
5433
5434 int const findTile = *insptr++;
5435 int maxDist = Gv_GetVar(*insptr++);
5436 int const maxZDist = Gv_GetVar(*insptr++);
5437 int const returnVar = *insptr++;
5438
5439 int findStatnum = actorsOnly ? STAT_ACTOR : MAXSTATUS - 1;
5440 int foundSprite = -1;
5441
5442 do
5443 {
5444 int spriteNum = headspritestat[findStatnum]; // all sprites
5445
5446 while ((unsigned)spriteNum < MAXSPRITES)
5447 {
5448 if (sprite[spriteNum].picnum == findTile && spriteNum != vm.spriteNum)
5449 {
5450 int const foundDist = ldist(vm.pSprite, &sprite[spriteNum]);
5451
5452 if (foundDist < maxDist && klabs(vm.pSprite->z - sprite[spriteNum].z) < maxZDist)
5453 {
5454 maxDist = foundDist;
5455 foundSprite = spriteNum;
5456 }
5457 }
5458
5459 spriteNum = nextspritestat[spriteNum];
5460 }
5461
5462 if (actorsOnly)
5463 break;
5464 }
5465 while (findStatnum--);
5466
5467 Gv_SetVar(returnVar, foundSprite);
5468 dispatch();
5469 }
5470
5471 vInstruction(CON_FINDPLAYER):
5472 {
5473 int32_t tw;
5474 insptr++;
5475 aGameVars[g_returnVarID].global = A_FindPlayer(vm.pSprite, &tw);
5476 Gv_SetVar(*insptr++, tw);
5477 dispatch();
5478 }
5479
5480 vInstruction(CON_FINDOTHERPLAYER):
5481 {
5482 int32_t tw;
5483 insptr++;
5484 aGameVars[g_returnVarID].global = P_FindOtherPlayer(vm.playerNum, &tw);
5485 Gv_SetVar(*insptr++, tw);
5486 dispatch();
5487 }
5488
5489
5490 vInstruction(CON_GETINPUT):
5491 insptr++;
5492 {
5493 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
5494 tw = *insptr++;
5495
5496 Gv_SetVar(*insptr++, VM_GetPlayerInput(playerNum, tw));
5497 dispatch();
5498 }
5499
5500 vInstruction(CON_SETINPUT):
5501 insptr++;
5502 {
5503 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum;
5504 tw = *insptr++;
5505
5506 VM_SetPlayerInput(playerNum, tw, Gv_GetVar(*insptr++));
5507 dispatch();
5508 }
5509
5510 vInstruction(CON_GETUSERDEF):
5511 insptr++;
5512 {
5513 tw = *insptr++;
5514 int const lParm2 = (UserdefsLabels[tw].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0;
5515
5516 Gv_SetVar(*insptr++, VM_GetUserdef(tw, lParm2));
5517 dispatch();
5518 }
5519
5520 vInstruction(CON_GETTILEDATA):
5521 insptr++;
5522 {
5523 int const tileNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->picnum;
5524 tw = *insptr++;
5525
5526 Gv_SetVar(*insptr++, VM_GetTileData(tileNum, tw));
5527 dispatch();
5528 }
5529
5530 vInstruction(CON_SETTILEDATA):
5531 insptr++;
5532 {
5533 int const tileNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->picnum;
5534 tw = *insptr++;
5535
5536 VM_SetTileData(tileNum, tw, Gv_GetVar(*insptr++));
5537 dispatch();
5538 }
5539
5540 vInstruction(CON_SETUSERDEF):
5541 insptr++;
5542 {
5543 tw = *insptr++;
5544 int const lParm2 = (UserdefsLabels[tw].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0;
5545
5546 VM_SetUserdef(tw, lParm2, Gv_GetVar(*insptr++));
5547 dispatch();
5548 }
5549
5550 vInstruction(CON_GETPROJECTILE):
5551 insptr++;
5552 {
5553 tw = Gv_GetVar(*insptr++);
5554 int const labelNum = *insptr++;
5555 Gv_SetVar(*insptr++, VM_GetProjectile(tw, labelNum));
5556 dispatch();
5557 }
5558
5559 vInstruction(CON_SETPROJECTILE):
5560 insptr++;
5561 {
5562 tw = Gv_GetVar(*insptr++);
5563 int const labelNum = *insptr++;
5564 VM_SetProjectile(tw, labelNum, Gv_GetVar(*insptr++));
5565 dispatch();
5566 }
5567
5568 vInstruction(CON_GETANGLETOTARGET):
5569 insptr++;
5570 // vm.pActor->lastvx and lastvy are last known location of target.
5571 Gv_SetVar(*insptr++, getangle(vm.pActor->lastv.x - vm.pSprite->x, vm.pActor->lastv.y - vm.pSprite->y));
5572 dispatch();
5573
5574 vInstruction(CON_ANGOFF):
5575 insptr++;
5576 spriteext[vm.spriteNum].mdangoff = Gv_GetVar(*insptr++);
5577 dispatch();
5578
5579 vInstruction(CON_LOCKPLAYER):
5580 insptr++;
5581 vm.pPlayer->transporter_hold = Gv_GetVar(*insptr++);
5582 dispatch();
5583
5584 vInstruction(CON_CHECKAVAILWEAPON):
5585 vInstruction(CON_CHECKAVAILINVEN):
5586 {
5587 insptr++;
5588 int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(*insptr) : vm.playerNum;
5589
5590 VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum);
5591
5592 if (VM_DECODE_INST(tw) == CON_CHECKAVAILWEAPON)
5593 P_CheckWeapon(g_player[playerNum].ps);
5594 else
5595 P_SelectNextInvItem(g_player[playerNum].ps);
5596
5597 dispatch();
5598 }
5599
5600 vInstruction(CON_GETPLAYERANGLE):
5601 insptr++;
5602 Gv_SetVar(*insptr++, fix16_to_int(vm.pPlayer->q16ang));
5603 dispatch();
5604
5605 vInstruction(CON_GETACTORANGLE):
5606 insptr++;
5607 Gv_SetVar(*insptr++, vm.pSprite->ang);
5608 dispatch();
5609
5610 vInstruction(CON_SETPLAYERANGLE):
5611 insptr++;
5612 vm.pPlayer->q16ang = fix16_from_int(Gv_GetVar(*insptr++) & 2047);
5613 dispatch();
5614
5615 vInstruction(CON_SETACTORANGLE):
5616 insptr++;
5617 vm.pSprite->ang = Gv_GetVar(*insptr++) & 2047;
5618 dispatch();
5619
5620 vInstruction(CON_KLABS):
5621 if ((aGameVars[insptr[1]].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) == 0)
5622 aGameVars[insptr[1]].global = klabs(aGameVars[insptr[1]].global);
5623 else
5624 Gv_SetVar(insptr[1], klabs(Gv_GetVar(insptr[1])));
5625 insptr += 2;
5626 dispatch();
5627
5628 vInstruction(CON_SETARRAY):
5629 insptr++;
5630 {
5631 tw = *insptr++;
5632 int const arrayIndex = Gv_GetVar(*insptr++);
5633 VM_ASSERT((unsigned)tw < (unsigned)g_gameArrayCount && (unsigned)arrayIndex < (unsigned)aGameArrays[tw].size,
5634 "invalid array %d or index %d\n", tw, arrayIndex);
5635
5636 SetArray(tw, arrayIndex, Gv_GetVar(*insptr++));
5637
5638 dispatch();
5639 }
5640
5641 vInstruction(CON_GETARRAYSEQUENCE):
5642 {
5643 insptr++;
5644 int32_t const arrayNum = *insptr++;
5645 int32_t const arraySize = (aGameArrays[arrayNum].flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(aGameArrays[arrayNum].size) : aGameArrays[arrayNum].size;
5646 int32_t const sequenceSize = *insptr++;
5647 int32_t const copySize = min(sequenceSize, arraySize); // warning?
5648 auto const insptrbak = insptr;
5649
5650 for (int arrayIndex = 0; arrayIndex < copySize; ++arrayIndex)
5651 Gv_SetVar(*insptr++, Gv_GetArrayValue(arrayNum, arrayIndex));
5652
5653 insptr = insptrbak + sequenceSize;
5654 dispatch();
5655 }
5656
5657 vInstruction(CON_SETARRAYSEQUENCE):
5658 {
5659 insptr++;
5660 int32_t const arrayNum = *insptr++;
5661 int32_t const sequenceSize = *insptr++;
5662
5663 VM_ASSERT((unsigned)arrayNum < (unsigned)g_gameArrayCount, "invalid array %d\n", arrayNum);
5664
5665 ResizeArray(arrayNum, sequenceSize);
5666
5667 for (int arrayIndex = 0; arrayIndex < sequenceSize; ++arrayIndex)
5668 SetArray(arrayNum, arrayIndex, Gv_GetVar(*insptr++));
5669
5670 dispatch();
5671 }
5672
5673 vInstruction(CON_READARRAYFROMFILE):
5674 insptr++;
5675 {
5676 int const arrayNum = *insptr++;
5677 int const quoteFilename = *insptr++;
5678
5679 VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename);
5680
5681 buildvfs_kfd kFile = kopen4loadfrommod(apStrings[quoteFilename], 0);
5682
5683 if (kFile == buildvfs_kfd_invalid)
5684 dispatch();
5685
5686 size_t const filelength = kfilelength(kFile);
5687 size_t const numElements = Gv_GetArrayCountForAllocSize(arrayNum, filelength);
5688
5689 if (numElements > 0)
5690 {
5691 size_t const newBytes = Gv_GetArrayAllocSizeForCount(arrayNum, numElements);
5692 size_t const readBytes = min(newBytes, filelength);
5693 size_t const oldBytes = Gv_GetArrayAllocSize(arrayNum);
5694
5695 intptr_t *&pValues = aGameArrays[arrayNum].pValues;
5696
5697 if (newBytes != oldBytes)
5698 {
5699 Xaligned_free(pValues);
5700 pValues = (intptr_t *)Xaligned_alloc(ARRAY_ALIGNMENT, newBytes);
5701 }
5702
5703 aGameArrays[arrayNum].size = numElements;
5704
5705 uintptr_t const flags = aGameArrays[arrayNum].flags;
5706
5707 switch (flags & GAMEARRAY_SIZE_MASK)
5708 {
5709 case 0:
5710 #ifdef BITNESS64
5711 {
5712 void *const pArray = Xcalloc(1, newBytes);
5713
5714 kread(kFile, pArray, readBytes);
5715
5716 if (flags & GAMEARRAY_UNSIGNED)
5717 {
5718 for (unative_t i = 0; i < numElements; ++i)
5719 pValues[i] = ((uint32_t *)pArray)[i];
5720 }
5721 else
5722 {
5723 for (unative_t i = 0; i < numElements; ++i)
5724 pValues[i] = ((int32_t *)pArray)[i];
5725 }
5726
5727 Xfree(pArray);
5728 break;
5729 }
5730 #endif
5731 default:
5732 memset((char *)pValues + readBytes, 0, newBytes - readBytes);
5733 kread(kFile, pValues, readBytes);
5734 break;
5735 }
5736 }
5737
5738 kclose(kFile);
5739 dispatch();
5740 }
5741
5742 vInstruction(CON_WRITEARRAYTOFILE):
5743 insptr++;
5744 {
5745 int const arrayNum = *insptr++;
5746 int const quoteFilename = *insptr++;
5747
5748 VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename);
5749
5750 char temp[BMAX_PATH];
5751
5752 if (EDUKE32_PREDICT_FALSE(G_ModDirSnprintf(temp, sizeof(temp), "%s", apStrings[quoteFilename])))
5753 {
5754 CON_ERRPRINTF("file name too long\n");
5755 abort_after_error();
5756 }
5757
5758 buildvfs_FILE const fil = buildvfs_fopen_write(temp);
5759
5760 if (EDUKE32_PREDICT_FALSE(fil == NULL))
5761 {
5762 CON_ERRPRINTF("couldn't open file \"%s\"\n", temp);
5763 abort_after_error();
5764 }
5765
5766 switch (aGameArrays[arrayNum].flags & GAMEARRAY_SIZE_MASK)
5767 {
5768 case 0:
5769 #ifdef BITNESS64
5770 {
5771 size_t const numElements = aGameArrays[arrayNum].size;
5772 size_t const numDiskBytes = numElements * sizeof(int32_t);
5773 int32_t *const pArray = (int32_t *)Xmalloc(numDiskBytes);
5774
5775 for (unative_t k = 0; k < numElements; ++k)
5776 pArray[k] = Gv_GetArrayValue(arrayNum, k);
5777
5778 buildvfs_fwrite(pArray, 1, numDiskBytes, fil);
5779 Xfree(pArray);
5780 break;
5781 }
5782 #endif
5783 default: buildvfs_fwrite(aGameArrays[arrayNum].pValues, 1, Gv_GetArrayAllocSize(arrayNum), fil); break;
5784 }
5785
5786 buildvfs_fclose(fil);
5787 dispatch();
5788 }
5789
5790 vInstruction(CON_GETARRAYSIZE):
5791 insptr++;
5792 tw = *insptr++;
5793 Gv_SetVar(*insptr++, (aGameArrays[tw].flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(aGameArrays[tw].size) : aGameArrays[tw].size);
5794 dispatch();
5795
5796 vInstruction(CON_RESIZEARRAY):
5797 insptr++;
5798 {
5799 tw = *insptr++;
5800 int const newSize = Gv_GetVar(*insptr++);
5801 ResizeArray(tw, newSize);
5802
5803 dispatch();
5804 }
5805
5806 vInstruction(CON_COPY):
5807 insptr++;
5808 {
5809 int const srcArray = *insptr++;
5810 int srcArrayIndex = Gv_GetVar(*insptr++); //, vm.spriteNum, vm.playerNum);
5811 int const destArray = *insptr++;
5812 int destArrayIndex = Gv_GetVar(*insptr++);
5813 int numElements = Gv_GetVar(*insptr++);
5814
5815 auto &src = aGameArrays[srcArray];
5816 auto &dest = aGameArrays[destArray];
5817
5818 int const srcArraySize = (src.flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(src.size) : src.size;
5819 int const destArraySize = (dest.flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(dest.size) : dest.size;
5820
5821 if (srcArrayIndex > srcArraySize || destArrayIndex > destArraySize)
5822 dispatch();
5823
5824 if ((srcArrayIndex + numElements) > srcArraySize)
5825 numElements = srcArraySize - srcArrayIndex;
5826
5827 if ((destArrayIndex + numElements) > destArraySize)
5828 numElements = destArraySize - destArrayIndex;
5829
5830 // Switch depending on the source array type.
5831
5832 int const srcInc = 1 << (int)!!((src.flags & GAMEARRAY_STRIDE2));
5833 int const destInc = 1 << (int)!!((dest.flags & GAMEARRAY_STRIDE2));
5834
5835 // matching array types, no BITMAPs, no STRIDE2 flag
5836 if ((src.flags & GAMEARRAY_SIZE_MASK) == (dest.flags & GAMEARRAY_SIZE_MASK)
5837 && !((src.flags | dest.flags) & GAMEARRAY_BITMAP) && (srcInc & destInc) == 1)
5838 {
5839 Bmemcpy(dest.pValues + destArrayIndex, src.pValues + srcArrayIndex,
5840 numElements * Gv_GetArrayElementSize(srcArray));
5841 dispatch();
5842 }
5843
5844 switch (dest.flags & GAMEARRAY_TYPE_MASK)
5845 {
5846 case 0:
5847 for (; numElements > 0; --numElements)
5848 {
5849 dest.pValues[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++);
5850 destArrayIndex += destInc;
5851 }
5852 break;
5853 case GAMEARRAY_INT16:
5854 for (; numElements > 0; --numElements)
5855 {
5856 ((int16_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++);
5857 destArrayIndex += destInc;
5858 }
5859 break;
5860 case GAMEARRAY_INT8:
5861 for (; numElements > 0; --numElements)
5862 {
5863 ((int8_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++);
5864 destArrayIndex += destInc;
5865 }
5866 break;
5867 case GAMEARRAY_UINT16:
5868 for (; numElements > 0; --numElements)
5869 {
5870 ((uint16_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++);
5871 destArrayIndex += destInc;
5872 }
5873 break;
5874 case GAMEARRAY_UINT8:
5875 for (; numElements > 0; --numElements)
5876 {
5877 ((uint8_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++);
5878 destArrayIndex += destInc;
5879 }
5880 break;
5881 case GAMEARRAY_BITMAP:
5882 for (; numElements > 0; --numElements)
5883 {
5884 uint32_t const newValue = Gv_GetArrayValue(srcArray, srcArrayIndex++);
5885 uint32_t const mask = 1 << (destArrayIndex & 7);
5886 uint8_t & value = ((uint8_t *)dest.pValues)[destArrayIndex >> 3];
5887 value = (value & ~mask) | (-!!newValue & mask);
5888 destArrayIndex += destInc;
5889 }
5890 break;
5891 }
5892
5893 dispatch();
5894 }
5895
5896 vInstruction(CON_SWAPARRAYS):
5897 insptr++;
5898 {
5899 auto &array1 = aGameArrays[*insptr++];
5900 auto &array2 = aGameArrays[*insptr++];
5901
5902 swap(&array1.size, &array2.size);
5903 swap(&array1.pValues, &array2.pValues);
5904
5905 dispatch();
5906 }
5907
5908 vInstruction(CON_DISPLAYRANDVAR):
5909 insptr++;
5910 Gv_SetVar(*insptr, mulscale15(system_15bit_rand(), insptr[1] + 1));
5911 insptr += 2;
5912 dispatch();
5913
5914 vInstruction(CON_CLAMP):
5915 insptr++;
5916 {
5917 tw = *insptr++;
5918 int const min = Gv_GetVar(*insptr++);
5919 Gv_SetVar(tw, clamp2(Gv_GetVar(tw), min, Gv_GetVar(*insptr++)));
5920 }
5921 dispatch();
5922
5923 vInstruction(CON_GETCLOSESTCOL):
5924 insptr++;
5925 {
5926 tw = *insptr++;
5927 int32_t const rgb = Gv_GetVar(*insptr++);
5928 Gv_SetVar(tw, paletteGetClosestColorUpToIndex(rgb & 0xFF, (rgb >> 8) & 0xFF, (rgb >> 16) & 0xFF, Gv_GetVar(*insptr++)));
5929 }
5930 dispatch();
5931
5932 vInstruction(CON_DRAWLINE256):
5933 insptr++;
5934 {
5935 struct
5936 {
5937 vec2_t pos[2];
5938 int32_t index;
5939 } v;
5940
5941 Gv_FillWithVars(v);
5942
5943 renderDrawLine(v.pos[0].x, v.pos[0].y, v.pos[1].x, v.pos[1].y, v.index);
5944 }
5945 dispatch();
5946
5947 vInstruction(CON_DRAWLINERGB):
5948 insptr++;
5949 {
5950 struct
5951 {
5952 vec2_t pos[2];
5953 int32_t index, rgb;
5954 } v;
5955
5956 Gv_FillWithVars(v);
5957
5958 palette_t const p
5959 = { (uint8_t)(v.rgb & 0xFF), (uint8_t)((v.rgb >> 8) & 0xFF), (uint8_t)((v.rgb >> 16) & 0xFF), (uint8_t)v.index };
5960
5961 drawlinergb(v.pos[0].x, v.pos[0].y, v.pos[1].x, v.pos[1].y, p);
5962 }
5963 dispatch();
5964
5965 vInstruction(CON_INV):
5966 if ((aGameVars[insptr[1]].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) == 0)
5967 aGameVars[insptr[1]].global = -aGameVars[insptr[1]].global;
5968 else
5969 Gv_SetVar(insptr[1], -Gv_GetVar(insptr[1]));
5970 insptr += 2;
5971 dispatch();
5972
5973 vInstruction(CON_DISPLAYRANDVARVAR):
5974 insptr++;
5975 tw = *insptr++;
5976 Gv_SetVar(tw, mulscale15(system_15bit_rand(), Gv_GetVar(*insptr++) + 1));
5977 dispatch();
5978
5979 vInstruction(CON_GMAXAMMO):
5980 insptr++;
5981 tw = Gv_GetVar(*insptr++);
5982 VM_ASSERT((unsigned)tw < MAX_WEAPONS, "invalid weapon %d\n", (int)tw);
5983 Gv_SetVar(*insptr++, vm.pPlayer->max_ammo_amount[tw]);
5984 dispatch();
5985
5986 vInstruction(CON_SMAXAMMO):
5987 insptr++;
5988 tw = Gv_GetVar(*insptr++);
5989 VM_ASSERT((unsigned)tw < MAX_WEAPONS, "invalid weapon %d\n", (int)tw);
5990 vm.pPlayer->max_ammo_amount[tw] = Gv_GetVar(*insptr++);
5991 dispatch();
5992
5993
5994 vInstruction(CON_DIVR): // div round to nearest
5995 insptr++;
5996 {
5997 tw = *insptr++;
5998
5999 int const dividend = Gv_GetVar(tw);
6000 int const divisor = Gv_GetVar(*insptr++);
6001
6002 if (EDUKE32_PREDICT_FALSE(!divisor))
6003 {
6004 CON_CRITICALERRPRINTF("divide by zero!\n");
6005 abort_after_error();
6006 }
6007
6008 Gv_SetVar(tw, tabledivide32((dividend + ksgn(dividend) * klabs(divisor / 2)), divisor));
6009 dispatch();
6010 }
6011
6012 vInstruction(CON_DIVRU): // div round away from zero
6013 insptr++;
6014 {
6015 tw = *insptr++;
6016
6017 int const dividend = Gv_GetVar(tw);
6018 int const divisor = Gv_GetVar(*insptr++);
6019
6020 if (EDUKE32_PREDICT_FALSE(!divisor))
6021 {
6022 CON_CRITICALERRPRINTF("divide by zero!\n");
6023 abort_after_error();
6024 }
6025
6026 Gv_SetVar(tw, tabledivide32((dividend + ksgn(dividend) * klabs(divisor) + 1), divisor));
6027 dispatch();
6028 }
6029
6030 vInstruction(CON_SIN):
6031 insptr++;
6032 tw = *insptr++;
6033 Gv_SetVar(tw, sintable[Gv_GetVar(*insptr++) & 2047]);
6034 dispatch();
6035
6036 vInstruction(CON_COS):
6037 insptr++;
6038 tw = *insptr++;
6039 Gv_SetVar(tw, sintable[(Gv_GetVar(*insptr++) + 512) & 2047]);
6040 dispatch();
6041
6042
6043 vInstruction(CON_SPGETLOTAG):
6044 insptr++;
6045 aGameVars[g_lotagVarID].global = vm.pSprite->lotag;
6046 dispatch();
6047
6048 vInstruction(CON_SPGETHITAG):
6049 insptr++;
6050 aGameVars[g_hitagVarID].global = vm.pSprite->hitag;
6051 dispatch();
6052
6053 vInstruction(CON_SECTGETLOTAG):
6054 insptr++;
6055 aGameVars[g_lotagVarID].global = sector[vm.pSprite->sectnum].lotag;
6056 dispatch();
6057
6058 vInstruction(CON_SECTGETHITAG):
6059 insptr++;
6060 aGameVars[g_hitagVarID].global = sector[vm.pSprite->sectnum].hitag;
6061 dispatch();
6062
6063 vInstruction(CON_GETTEXTUREFLOOR):
6064 insptr++;
6065 aGameVars[g_textureVarID].global = sector[vm.pSprite->sectnum].floorpicnum;
6066 dispatch();
6067
6068 vInstruction(CON_STARTTRACK):
6069 insptr++;
6070 G_StartTrackSlotWrap(ud.volume_number, Gv_GetVar(*(insptr++)));
6071 dispatch();
6072
6073 vInstruction(CON_STARTTRACKSLOT):
6074 insptr++;
6075 {
6076 int const volumeNum = Gv_GetVar(*(insptr++));
6077 int const levelNum = Gv_GetVar(*(insptr++));
6078 G_StartTrackSlotWrap(volumeNum == -1 ? MAXVOLUMES : volumeNum, levelNum);
6079 }
6080 dispatch();
6081
6082 vInstruction(CON_SWAPTRACKSLOT):
6083 insptr++;
6084 {
6085 int const volumeNum = Gv_GetVar(*(insptr++));
6086 int const levelNum = Gv_GetVar(*(insptr++));
6087
6088 if (volumeNum == ud.music_episode && levelNum == ud.music_level)
6089 dispatch();
6090
6091 // This is the best ASS can do right now. Better implementation pending.
6092 int32_t position = S_GetMusicPosition();
6093 if (!G_StartTrackSlotWrap(volumeNum == -1 ? MAXVOLUMES : volumeNum, levelNum))
6094 S_SetMusicPosition(position);
6095 }
6096 dispatch();
6097
6098 vInstruction(CON_PRELOADTRACKSLOTFORSWAP):
6099 // ASS can't even handle this command right now.
6100 insptr++;
6101 Gv_GetVar(*(insptr++));
6102 Gv_GetVar(*(insptr++));
6103 dispatch();
6104
6105 vInstruction(CON_SETMUSICPOSITION):
6106 insptr++;
6107 Gv_GetVar(*(insptr++));
6108 dispatch();
6109 vInstruction(CON_GETMUSICPOSITION): insptr += 2; dispatch();
6110
6111 vInstruction(CON_ACTIVATECHEAT):
6112 insptr++;
6113 tw = Gv_GetVar(*(insptr++));
6114 VM_ASSERT(numplayers == 1 && (g_player[myconnectindex].ps->gm & MODE_GAME), "not in a single-player game.\n");
6115 osdcmd_cheatsinfo_stat.cheatnum = tw;
6116 dispatch();
6117
6118 vInstruction(CON_SETGAMEPALETTE):
6119 insptr++;
6120 P_SetGamePalette(vm.pPlayer, Gv_GetVar(*(insptr++)), 2 + 16);
6121 dispatch();
6122
6123 vInstruction(CON_GETTEXTURECEILING):
6124 insptr++;
6125 aGameVars[g_textureVarID].global = sector[vm.pSprite->sectnum].ceilingpicnum;
6126 dispatch();
6127
6128 vInstruction(CON_IFPHEALTHL):
6129 insptr++;
6130 branch(sprite[vm.pPlayer->i].extra < *insptr);
6131 dispatch();
6132
6133 vInstruction(CON_IFPINVENTORY):
6134 insptr++;
6135
6136 switch (*insptr++)
6137 {
6138 case GET_STEROIDS:
6139 case GET_SHIELD:
6140 case GET_SCUBA:
6141 case GET_HOLODUKE:
6142 case GET_HEATS:
6143 case GET_FIRSTAID:
6144 case GET_BOOTS:
6145 case GET_JETPACK: tw = (vm.pPlayer->inv_amount[insptr[-1]] != *insptr); break;
6146
6147 case GET_ACCESS:
6148 switch (vm.pSprite->pal)
6149 {
6150 case 0: tw = (vm.pPlayer->got_access & 1); break;
6151 case 21: tw = (vm.pPlayer->got_access & 2); break;
6152 case 23: tw = (vm.pPlayer->got_access & 4); break;
6153 }
6154 break;
6155 default: tw = 0; CON_ERRPRINTF("invalid inventory item %d\n", (int32_t) * (insptr - 1));
6156 dispatch();
6157 }
6158
6159 branch(tw);
6160 dispatch();
6161
6162 vInstruction(CON_PSTOMP):
6163 insptr++;
6164 if (vm.pPlayer->knee_incs == 0 && sprite[vm.pPlayer->i].xrepeat >= 40)
6165 if (cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - ZOFFSET6, vm.pSprite->sectnum, vm.pPlayer->pos.x, vm.pPlayer->pos.y,
6166 vm.pPlayer->pos.z + ZOFFSET2, sprite[vm.pPlayer->i].sectnum))
6167 {
6168 int numPlayers = g_mostConcurrentPlayers - 1;
6169
6170 for (; numPlayers >= 0; --numPlayers)
6171 {
6172 if (g_player[numPlayers].ps->actorsqu == vm.spriteNum)
6173 break;
6174 }
6175
6176 if (numPlayers == -1)
6177 {
6178 if (vm.pPlayer->weapon_pos == 0)
6179 vm.pPlayer->weapon_pos = -1;
6180
6181 vm.pPlayer->actorsqu = vm.spriteNum;
6182 vm.pPlayer->knee_incs = 1;
6183 }
6184 }
6185 dispatch();
6186
6187 vInstruction(CON_IFAWAYFROMWALL):
6188 {
6189 int16_t otherSectnum = vm.pSprite->sectnum;
6190 tw = 0;
6191
6192 #define IFAWAYDIST 108
6193
6194 updatesector(vm.pSprite->x + IFAWAYDIST, vm.pSprite->y + IFAWAYDIST, &otherSectnum);
6195 if (otherSectnum == vm.pSprite->sectnum)
6196 {
6197 updatesector(vm.pSprite->x - IFAWAYDIST, vm.pSprite->y - IFAWAYDIST, &otherSectnum);
6198 if (otherSectnum == vm.pSprite->sectnum)
6199 {
6200 updatesector(vm.pSprite->x + IFAWAYDIST, vm.pSprite->y - IFAWAYDIST, &otherSectnum);
6201 if (otherSectnum == vm.pSprite->sectnum)
6202 {
6203 updatesector(vm.pSprite->x - IFAWAYDIST, vm.pSprite->y + IFAWAYDIST, &otherSectnum);
6204 if (otherSectnum == vm.pSprite->sectnum)
6205 tw = 1;
6206 }
6207 }
6208 }
6209
6210 branch(tw);
6211
6212 #undef IFAWAYDIST
6213 }
6214 dispatch();
6215
6216 vInstruction(CON_QUOTE):
6217 insptr++;
6218
6219 VM_ASSERT((unsigned)vm.playerNum < MAXPLAYERS, "invalid player %d\n", vm.playerNum);
6220
6221 P_DoQuote(*(insptr++) | MAXQUOTES, vm.pPlayer);
6222 dispatch();
6223
6224 vInstruction(CON_USERQUOTE):
6225 insptr++;
6226 tw = Gv_GetVar(*insptr++);
6227
6228 VM_ASSERT((unsigned)tw < MAXQUOTES && apStrings[tw], "invalid quote %d\n", (int)tw);
6229
6230 G_AddUserQuote(apStrings[tw]);
6231 dispatch();
6232
6233 vInstruction(CON_ECHO):
6234 insptr++;
6235 tw = Gv_GetVar(*insptr++);
6236
6237 VM_ASSERT((unsigned)tw < MAXQUOTES && apStrings[tw], "invalid quote %d\n", (int)tw);
6238
6239 OSD_Printf("%s\n", apStrings[tw]);
6240 dispatch();
6241
6242 vInstruction(CON_RESPAWNHITAG):
6243 insptr++;
6244 switch (DYNAMICTILEMAP(vm.pSprite->picnum))
6245 {
6246 #ifndef EDUKE32_STANDALONE
6247 case FEM1__STATIC:
6248 case FEM2__STATIC:
6249 case FEM3__STATIC:
6250 case FEM4__STATIC:
6251 case FEM5__STATIC:
6252 case FEM6__STATIC:
6253 case FEM7__STATIC:
6254 case FEM8__STATIC:
6255 case FEM9__STATIC:
6256 case FEM10__STATIC:
6257 case PODFEM1__STATIC:
6258 case NAKED1__STATIC:
6259 case STATUE__STATIC:
6260 if (!FURY)
6261 {
6262 if (vm.pSprite->yvel)
6263 G_OperateRespawns(vm.pSprite->yvel);
6264 break;
6265 }
6266 fallthrough__;
6267 #endif
6268 default:
6269 if (vm.pSprite->hitag >= 0)
6270 G_OperateRespawns(vm.pSprite->hitag);
6271 break;
6272 }
6273 dispatch();
6274
6275 vInstruction(CON_IFSPRITEPAL):
6276 insptr++;
6277 branch(vm.pSprite->pal == *insptr);
6278 dispatch();
6279
6280 vInstruction(CON_IFANGDIFFL):
6281 insptr++;
6282 tw = klabs(G_GetAngleDelta(fix16_to_int(vm.pPlayer->q16ang), vm.pSprite->ang));
6283 branch(tw <= *insptr);
6284 dispatch();
6285
6286 vInstruction(CON_IFNOSOUNDS): branch(!A_CheckAnySoundPlaying(vm.spriteNum)); dispatch();
6287
6288 vInstruction(CON_SPRITEFLAGS):
6289 insptr++;
6290 vm.pActor->flags = Gv_GetVar(*insptr++);
6291 dispatch();
6292
6293 vInstruction(CON_GETTICKS):
6294 insptr++;
6295 Gv_SetVar(*insptr++, timerGetTicks());
6296 dispatch();
6297
6298 vInstruction(CON_GETCURRADDRESS):
6299 insptr++;
6300 tw = *insptr++;
6301 Gv_SetVar(tw, (intptr_t)(insptr - apScript));
6302 dispatch();
6303
6304 vmErrorCase: // you're not supposed to be here
6305 VM_ScriptInfo(insptr, 64);
6306 debug_break();
6307 G_GameExit("An error has occurred in the " APPNAME " virtual machine.\n\n"
6308 "If you are an end user, please e-mail the file " APPBASENAME ".log\n"
6309 "along with links to any mods you're using to development@voidpoint.com.\n\n"
6310 "If you are a developer, please attach all of your script files\n"
6311 "along with instructions on how to reproduce this error.\n\n"
6312 "Thank you!");
6313 }
6314 #ifndef CON_USE_COMPUTED_GOTO
6315 }
6316 while (vm_execution_depth && (vm.flags & (VM_RETURN|VM_KILL|VM_NOEXECUTE)) == 0);
6317 #endif
6318 }
6319
6320 // NORECURSE
6321 void A_LoadActor(int const spriteNum)
6322 {
6323 vm.spriteNum = spriteNum; // Sprite ID
6324 vm.pSprite = &sprite[spriteNum]; // Pointer to sprite structure
6325 vm.pActor = &actor[spriteNum];
6326
6327 if (g_tile[vm.pSprite->picnum].loadPtr == NULL)
6328 return;
6329
6330 vm.pData = &actor[spriteNum].t_data[0]; // Sprite's 'extra' data
6331 vm.playerNum = -1; // Player ID
6332 vm.playerDist = -1; // Distance
6333 vm.pPlayer = g_player[0].ps;
6334
6335 vm.flags &= ~(VM_RETURN|VM_KILL|VM_NOEXECUTE);
6336
6337 if ((unsigned)vm.pSprite->sectnum >= MAXSECTORS)
6338 {
6339 A_DeleteSprite(vm.spriteNum);
6340 return;
6341 }
6342
6343 insptr = g_tile[vm.pSprite->picnum].loadPtr;
6344 VM_Execute(true);
6345 insptr = NULL;
6346
6347 if (vm.flags & VM_KILL)
6348 A_DeleteSprite(vm.spriteNum);
6349 }
6350
6351 void VM_UpdateAnim(int const spriteNum, int32_t * const pData)
6352 {
6353 size_t const actionofs = AC_ACTION_ID(pData);
6354 auto const actionptr = (actionofs != 0 && actionofs + (ACTION_PARAM_COUNT-1) < (unsigned) g_scriptSize) ? &apScript[actionofs] : NULL;
6355
6356 if (actionptr != NULL)
6357 {
6358 int const action_frames = actionptr[ACTION_NUMFRAMES];
6359 int const action_incval = actionptr[ACTION_INCVAL];
6360 int const action_delay = actionptr[ACTION_DELAY];
6361 auto actionticsptr = &AC_ACTIONTICS(&sprite[spriteNum], &actor[spriteNum]);
6362 *actionticsptr += TICSPERFRAME;
6363
6364 if (*actionticsptr > action_delay)
6365 {
6366 *actionticsptr = 0;
6367 AC_ACTION_COUNT(pData)++;
6368 AC_CURFRAME(pData) += action_incval;
6369 }
6370
6371 if (klabs(AC_CURFRAME(pData)) >= klabs(action_frames * action_incval))
6372 AC_CURFRAME(pData) = 0;
6373 }
6374 }
6375
6376 // NORECURSE
6377 void A_Execute(int const spriteNum, int const playerNum, int const playerDist)
6378 {
6379 // for some reason this is faster than using the C++ syntax; e.g vm = vmstate_t{ ... }
6380 vmstate_t const tempvm
6381 = { spriteNum, playerNum, playerDist, 0, &sprite[spriteNum], &actor[spriteNum].t_data[0], g_player[playerNum].ps, &actor[spriteNum] };
6382 vm = tempvm;
6383
6384 MICROPROFILE_SCOPE_TOKEN(g_actorTokens[vm.pSprite->picnum]);
6385
6386 #ifndef NETCODE_DISABLE
6387 if (g_netClient)
6388 {
6389 #if 0
6390 if (A_CheckSpriteFlags(spriteNum, SFLAG_NULL))
6391 {
6392 A_DeleteSprite(spriteNum);
6393 return;
6394 }
6395 #endif
6396 // [75] The server should not overwrite its own randomseed
6397 randomseed = ticrandomseed;
6398 }
6399 #endif
6400
6401 if (EDUKE32_PREDICT_FALSE((unsigned)vm.pSprite->sectnum >= MAXSECTORS))
6402 {
6403 if (A_CheckEnemySprite(vm.pSprite))
6404 P_AddKills(vm.pPlayer, 1);
6405
6406 A_DeleteSprite(vm.spriteNum);
6407 return;
6408 }
6409
6410 VM_UpdateAnim(vm.spriteNum, vm.pData);
6411
6412 insptr = 4 + (g_tile[vm.pSprite->picnum].execPtr);
6413 VM_Execute(true);
6414 insptr = NULL;
6415
6416 if ((vm.flags & VM_KILL) == 0)
6417 {
6418 VM_Move();
6419
6420 if (vm.pSprite->statnum == STAT_ACTOR)
6421 {
6422 if (A_CheckEnemySprite(vm.pSprite))
6423 {
6424 if (vm.pSprite->xrepeat > 60 || (ud.respawn_monsters == 1 && vm.pSprite->extra <= 0))
6425 return;
6426 }
6427 else if ((ud.respawn_items == 1 && (vm.pSprite->cstat & 32768)))
6428 return;
6429
6430 if (A_CheckSpriteFlags(vm.spriteNum, SFLAG_USEACTIVATOR) && sector[vm.pSprite->sectnum].lotag & 16384)
6431 changespritestat(vm.spriteNum, STAT_ZOMBIEACTOR);
6432 else if (vm.pActor->timetosleep > 1)
6433 vm.pActor->timetosleep--;
6434 else if (vm.pActor->timetosleep == 1)
6435 {
6436 // hack for 1.3D fire sprites
6437 #ifndef EDUKE32_STANDALONE
6438 if (!FURY && EDUKE32_PREDICT_FALSE(g_scriptVersion == 13 && (vm.pSprite->picnum == FIRE || vm.pSprite->picnum == FIRE2)))
6439 return;
6440 #endif
6441 changespritestat(vm.spriteNum, STAT_ZOMBIEACTOR);
6442 }
6443 }
6444 #ifndef EDUKE32_STANDALONE
6445 else if (!FURY && vm.pSprite->statnum == STAT_STANDABLE)
6446 {
6447 switch (DYNAMICTILEMAP(vm.pSprite->picnum))
6448 {
6449 case RUBBERCAN__STATIC:
6450 case EXPLODINGBARREL__STATIC:
6451 case WOODENHORSE__STATIC:
6452 case HORSEONSIDE__STATIC:
6453 case CANWITHSOMETHING__STATIC:
6454 case FIREBARREL__STATIC:
6455 case NUKEBARREL__STATIC:
6456 case NUKEBARRELDENTED__STATIC:
6457 case NUKEBARRELLEAKED__STATIC:
6458 case TRIPBOMB__STATIC:
6459 case EGG__STATIC:
6460 if (vm.pActor->timetosleep > 1)
6461 vm.pActor->timetosleep--;
6462 else if (vm.pActor->timetosleep == 1)
6463 changespritestat(vm.spriteNum, STAT_ZOMBIEACTOR);
6464 default:
6465 break;
6466 }
6467 }
6468 #endif
6469 }
6470 else VM_DeleteSprite(spriteNum, playerNum);
6471 }
6472
6473 void G_SaveMapState(void)
6474 {
6475 int const levelNum = ud.volume_number * MAXLEVELS + ud.level_number;
6476 map_t *const pMapInfo = &g_mapInfo[levelNum];
6477
6478 if (pMapInfo->savedstate == NULL)
6479 {
6480 pMapInfo->savedstate = (mapstate_t *) Xaligned_alloc(ACTOR_VAR_ALIGNMENT, sizeof(mapstate_t));
6481 Bmemset(pMapInfo->savedstate, 0, sizeof(mapstate_t));
6482 }
6483
6484 mapstate_t *save = pMapInfo->savedstate;
6485
6486 if (save == NULL)
6487 return;
6488
6489 save->numwalls = numwalls;
6490 Bmemcpy(&save->wall[0],&wall[0],sizeof(walltype)*MAXWALLS);
6491 save->numsectors = numsectors;
6492 Bmemcpy(&save->sector[0],§or[0],sizeof(sectortype)*MAXSECTORS);
6493 Bmemcpy(&save->sprite[0],&sprite[0],sizeof(spritetype)*MAXSPRITES);
6494
6495 // If we're in EVENT_ANIMATESPRITES, we'll be saving pointer values to disk :-/
6496 if (EDUKE32_PREDICT_FALSE(g_currentEvent == EVENT_ANIMATESPRITES))
6497 initprintf("Line %d: savemapstate called from EVENT_ANIMATESPRITES. WHY?\n", VM_DECODE_LINE_NUMBER(g_tw));
6498 Bmemcpy(&save->spriteext[0],&spriteext[0],sizeof(spriteext_t)*MAXSPRITES);
6499 #ifndef NEW_MAP_FORMAT
6500 Bmemcpy(&save->wallext[0],&wallext[0],sizeof(wallext_t)*MAXWALLS);
6501 #endif
6502
6503 save->numsprites = Numsprites;
6504 save->tailspritefree = tailspritefree;
6505 Bmemcpy(&save->headspritesect[0],&headspritesect[0],sizeof(headspritesect));
6506 Bmemcpy(&save->prevspritesect[0],&prevspritesect[0],sizeof(prevspritesect));
6507 Bmemcpy(&save->nextspritesect[0],&nextspritesect[0],sizeof(nextspritesect));
6508 Bmemcpy(&save->headspritestat[0],&headspritestat[0],sizeof(headspritestat));
6509 Bmemcpy(&save->prevspritestat[0],&prevspritestat[0],sizeof(prevspritestat));
6510 Bmemcpy(&save->nextspritestat[0],&nextspritestat[0],sizeof(nextspritestat));
6511 #ifdef YAX_ENABLE
6512 save->numyaxbunches = numyaxbunches;
6513 # if !defined NEW_MAP_FORMAT
6514 Bmemcpy(save->yax_bunchnum, yax_bunchnum, sizeof(yax_bunchnum));
6515 Bmemcpy(save->yax_nextwall, yax_nextwall, sizeof(yax_nextwall));
6516 # endif
6517 #endif
6518 Bmemcpy(&save->actor[0],&actor[0],sizeof(actor_t)*MAXSPRITES);
6519
6520 save->g_cyclerCnt = g_cyclerCnt;
6521 Bmemcpy(save->g_cyclers, g_cyclers, sizeof(g_cyclers));
6522 Bmemcpy(&save->g_playerSpawnPoints[0],&g_playerSpawnPoints[0],sizeof(g_playerSpawnPoints));
6523 save->g_animWallCnt = g_animWallCnt;
6524 Bmemcpy(&save->SpriteDeletionQueue[0],&SpriteDeletionQueue[0],sizeof(SpriteDeletionQueue));
6525 save->g_spriteDeleteQueuePos = g_spriteDeleteQueuePos;
6526 Bmemcpy(&save->animwall[0],&animwall[0],sizeof(animwall));
6527 Bmemcpy(&save->origins[0],&g_origins[0],sizeof(g_origins));
6528 Bmemcpy(&save->g_mirrorWall[0],&g_mirrorWall[0],sizeof(g_mirrorWall));
6529 Bmemcpy(&save->g_mirrorSector[0],&g_mirrorSector[0],sizeof(g_mirrorSector));
6530 save->g_mirrorCount = g_mirrorCount;
6531 Bmemcpy(&save->show2dsector[0],&show2dsector[0],sizeof(show2dsector));
6532 save->g_cloudCnt = g_cloudCnt;
6533 Bmemcpy(&save->g_cloudSect[0],&g_cloudSect[0],sizeof(g_cloudSect));
6534 save->g_cloudX = g_cloudX;
6535 save->g_cloudY = g_cloudY;
6536 save->pskyidx = g_pskyidx;
6537 Bmemcpy(&save->g_animateGoal[0],&g_animateGoal[0],sizeof(g_animateGoal));
6538 Bmemcpy(&save->g_animateVel[0],&g_animateVel[0],sizeof(g_animateVel));
6539 save->g_animateCnt = g_animateCnt;
6540 Bmemcpy(&save->g_animateSect[0],&g_animateSect[0],sizeof(g_animateSect));
6541
6542 G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_FWD);
6543 Bmemcpy(&save->g_animatePtr[0],&g_animatePtr[0],sizeof(g_animatePtr));
6544 G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK);
6545
6546 {
6547 EDUKE32_STATIC_ASSERT(sizeof(save->g_animatePtr) == sizeof(g_animatePtr));
6548 }
6549
6550 save->g_playerSpawnCnt = g_playerSpawnCnt;
6551 save->g_earthquakeTime = g_earthquakeTime;
6552 save->randomseed = randomseed;
6553 save->g_globalRandom = g_globalRandom;
6554
6555 for (native_t i=g_gameVarCount-1; i>=0; i--)
6556 {
6557 if (aGameVars[i].flags & SAVEGAMEMAPSTATEVARSKIPMASK)
6558 continue;
6559
6560 if (aGameVars[i].flags & GAMEVAR_PERPLAYER)
6561 {
6562 if (!save->vars[i])
6563 save->vars[i] = (intptr_t *)Xaligned_alloc(PLAYER_VAR_ALIGNMENT, MAXPLAYERS * sizeof(intptr_t));
6564 Bmemcpy(&save->vars[i][0], aGameVars[i].pValues, sizeof(intptr_t) * MAXPLAYERS);
6565 }
6566 else if (aGameVars[i].flags & GAMEVAR_PERACTOR)
6567 {
6568 if (!save->vars[i])
6569 save->vars[i] = (intptr_t *)Xaligned_alloc(ACTOR_VAR_ALIGNMENT, MAXSPRITES * sizeof(intptr_t));
6570 Bmemcpy(&save->vars[i][0], aGameVars[i].pValues, sizeof(intptr_t) * MAXSPRITES);
6571 }
6572 else
6573 save->vars[i] = (intptr_t *)aGameVars[i].global;
6574 }
6575
6576 for (native_t i=g_gameArrayCount-1; i>=0; i--)
6577 {
6578 auto &array = aGameArrays[i];
6579
6580 if (((array.flags & GAMEARRAY_RESTORE) != GAMEARRAY_RESTORE) || array.flags & SAVEGAMEARRAYSKIPMASK)
6581 continue;
6582
6583 save->arraysiz[i] = array.size;
6584 Xaligned_free(save->arrays[i]);
6585 save->arrays[i] = (intptr_t *)Xaligned_alloc(ARRAY_ALIGNMENT, Gv_GetArrayAllocSize(i));
6586 Bmemcpy(&save->arrays[i][0], array.pValues, Gv_GetArrayAllocSize(i));
6587 }
6588 ototalclock = totalclock;
6589 }
6590
6591 void G_RestoreMapState(void)
6592 {
6593 int const levelNum = ud.volume_number * MAXLEVELS + ud.level_number;
6594 mapstate_t *pSavedState = g_mapInfo[levelNum].savedstate;
6595
6596 if (pSavedState != NULL)
6597 {
6598 int playerHealth[MAXPLAYERS];
6599
6600 for (native_t i=0; i<g_mostConcurrentPlayers; i++)
6601 playerHealth[i] = sprite[g_player[i].ps->i].extra;
6602
6603 pub = NUMPAGES;
6604 pus = NUMPAGES;
6605 G_UpdateScreenArea();
6606
6607 numwalls = pSavedState->numwalls;
6608 Bmemcpy(&wall[0],&pSavedState->wall[0],sizeof(walltype)*MAXWALLS);
6609 #ifndef NEW_MAP_FORMAT
6610 Bmemcpy(&wallext[0],&pSavedState->wallext[0],sizeof(wallext_t)*MAXWALLS);
6611 #endif
6612 numsectors = pSavedState->numsectors;
6613 Bmemcpy(§or[0],&pSavedState->sector[0],sizeof(sectortype)*MAXSECTORS);
6614 Bmemcpy(&sprite[0],&pSavedState->sprite[0],sizeof(spritetype)*MAXSPRITES);
6615 Bmemcpy(&spriteext[0],&pSavedState->spriteext[0],sizeof(spriteext_t)*MAXSPRITES);
6616
6617 // If we're restoring from EVENT_ANIMATESPRITES, all spriteext[].tspr
6618 // will be overwritten, so NULL them.
6619 if (EDUKE32_PREDICT_FALSE(g_currentEvent == EVENT_ANIMATESPRITES))
6620 {
6621 initprintf("Line %d: loadmapstate called from EVENT_ANIMATESPRITES. WHY?\n", VM_DECODE_LINE_NUMBER(g_tw));
6622 for (native_t i=0; i<MAXSPRITES; i++)
6623 spriteext[i].tspr = NULL;
6624 }
6625 Numsprites = pSavedState->numsprites;
6626 tailspritefree = pSavedState->tailspritefree;
6627 Bmemcpy(&headspritesect[0],&pSavedState->headspritesect[0],sizeof(headspritesect));
6628 Bmemcpy(&prevspritesect[0],&pSavedState->prevspritesect[0],sizeof(prevspritesect));
6629 Bmemcpy(&nextspritesect[0],&pSavedState->nextspritesect[0],sizeof(nextspritesect));
6630 Bmemcpy(&headspritestat[0],&pSavedState->headspritestat[0],sizeof(headspritestat));
6631 Bmemcpy(&prevspritestat[0],&pSavedState->prevspritestat[0],sizeof(prevspritestat));
6632 Bmemcpy(&nextspritestat[0],&pSavedState->nextspritestat[0],sizeof(nextspritestat));
6633 #ifdef YAX_ENABLE
6634 numyaxbunches = pSavedState->numyaxbunches;
6635 # if !defined NEW_MAP_FORMAT
6636 Bmemcpy(yax_bunchnum, pSavedState->yax_bunchnum, sizeof(yax_bunchnum));
6637 Bmemcpy(yax_nextwall, pSavedState->yax_nextwall, sizeof(yax_nextwall));
6638 # endif
6639 #endif
6640 Bmemcpy(&actor[0],&pSavedState->actor[0],sizeof(actor_t)*MAXSPRITES);
6641
6642 g_cyclerCnt = pSavedState->g_cyclerCnt;
6643 Bmemcpy(g_cyclers, pSavedState->g_cyclers, sizeof(g_cyclers));
6644 Bmemcpy(&g_playerSpawnPoints[0],&pSavedState->g_playerSpawnPoints[0],sizeof(g_playerSpawnPoints));
6645 g_animWallCnt = pSavedState->g_animWallCnt;
6646 Bmemcpy(&SpriteDeletionQueue[0],&pSavedState->SpriteDeletionQueue[0],sizeof(SpriteDeletionQueue));
6647 g_spriteDeleteQueuePos = pSavedState->g_spriteDeleteQueuePos;
6648 Bmemcpy(&animwall[0],&pSavedState->animwall[0],sizeof(animwall));
6649 Bmemcpy(&g_origins[0],&pSavedState->origins[0],sizeof(g_origins));
6650 Bmemcpy(&g_mirrorWall[0],&pSavedState->g_mirrorWall[0],sizeof(g_mirrorWall));
6651 Bmemcpy(&g_mirrorSector[0],&pSavedState->g_mirrorSector[0],sizeof(g_mirrorSector));
6652 g_mirrorCount = pSavedState->g_mirrorCount;
6653 Bmemcpy(&show2dsector[0],&pSavedState->show2dsector[0],sizeof(show2dsector));
6654 g_cloudCnt = pSavedState->g_cloudCnt;
6655 Bmemcpy(&g_cloudSect[0],&pSavedState->g_cloudSect[0],sizeof(g_cloudSect));
6656 g_cloudX = pSavedState->g_cloudX;
6657 g_cloudY = pSavedState->g_cloudY;
6658 g_pskyidx = pSavedState->pskyidx;
6659 Bmemcpy(&g_animateGoal[0],&pSavedState->g_animateGoal[0],sizeof(g_animateGoal));
6660 Bmemcpy(&g_animateVel[0],&pSavedState->g_animateVel[0],sizeof(g_animateVel));
6661 g_animateCnt = pSavedState->g_animateCnt;
6662 Bmemcpy(&g_animateSect[0],&pSavedState->g_animateSect[0],sizeof(g_animateSect));
6663
6664 Bmemcpy(&g_animatePtr[0],&pSavedState->g_animatePtr[0],sizeof(g_animatePtr));
6665 G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK);
6666
6667 g_playerSpawnCnt = pSavedState->g_playerSpawnCnt;
6668 g_earthquakeTime = pSavedState->g_earthquakeTime;
6669 randomseed = pSavedState->randomseed;
6670 g_globalRandom = pSavedState->g_globalRandom;
6671
6672 for (native_t i=g_gameVarCount-1; i>=0; i--)
6673 {
6674 if (aGameVars[i].flags & SAVEGAMEMAPSTATEVARSKIPMASK)
6675 continue;
6676
6677 if (aGameVars[i].flags & GAMEVAR_PERPLAYER)
6678 {
6679 if (!pSavedState->vars[i])
6680 continue;
6681 Bmemcpy(aGameVars[i].pValues, pSavedState->vars[i], sizeof(intptr_t) * MAXPLAYERS);
6682 }
6683 else if (aGameVars[i].flags & GAMEVAR_PERACTOR)
6684 {
6685 if (!pSavedState->vars[i])
6686 continue;
6687 Bmemcpy(aGameVars[i].pValues, pSavedState->vars[i], sizeof(intptr_t) * MAXSPRITES);
6688 }
6689 else
6690 aGameVars[i].global = (intptr_t)pSavedState->vars[i];
6691 }
6692
6693 for (native_t i=g_gameArrayCount-1; i>=0; i--)
6694 {
6695 auto &array = aGameArrays[i];
6696
6697 if (((array.flags & GAMEARRAY_RESTORE) != GAMEARRAY_RESTORE) || array.flags & SAVEGAMEARRAYSKIPMASK)
6698 continue;
6699
6700 array.size = pSavedState->arraysiz[i];
6701 Xaligned_free(array.pValues);
6702 array.pValues = (intptr_t *) Xaligned_alloc(ARRAY_ALIGNMENT, Gv_GetArrayAllocSize(i));
6703
6704 Bmemcpy(array.pValues, pSavedState->arrays[i], Gv_GetArrayAllocSize(i));
6705 }
6706
6707 Gv_RefreshPointers();
6708 // Update g_player[].ps->i (sprite indices of players) to be consistent
6709 // with just loaded sprites.
6710 // Otherwise, crashes may ensue: e.g. WGR2 SVN r391, map spiderden:
6711 // - walk forward (to door leading to other level "Shadowpine Forest")
6712 // - in new level, walk backward to get back to the Spider Den
6713 // - walk backward to the door leading to Shadowpine Forest --> crash.
6714 for (native_t SPRITES_OF(STAT_PLAYER, i))
6715 {
6716 int32_t snum = P_Get(i);
6717 Bassert((unsigned)snum < MAXPLAYERS);
6718 g_player[snum].ps->i = i;
6719 }
6720
6721 for (native_t i=0; i<g_mostConcurrentPlayers; i++)
6722 sprite[g_player[i].ps->i].extra = playerHealth[i];
6723
6724 if (g_player[myconnectindex].ps->over_shoulder_on != 0)
6725 {
6726 CAMERADIST = 0;
6727 CAMERACLOCK = 0;
6728 g_player[myconnectindex].ps->over_shoulder_on = 1;
6729 }
6730
6731 screenpeek = myconnectindex;
6732
6733 #ifndef EDUKE32_STANDALONE
6734 if (ud.lockout)
6735 {
6736 for (native_t x=g_animWallCnt-1; x>=0; x--)
6737 switch (DYNAMICTILEMAP(wall[animwall[x].wallnum].picnum))
6738 {
6739 case FEMPIC1__STATIC: wall[animwall[x].wallnum].picnum = BLANKSCREEN; break;
6740 case FEMPIC2__STATIC:
6741 case FEMPIC3__STATIC: wall[animwall[x].wallnum].picnum = SCREENBREAK6; break;
6742 }
6743 }
6744 #if 0
6745 else
6746 {
6747 for (native_t x=g_numAnimWalls-1; x>=0; x--)
6748 if (wall[animwall[x].wallnum].extra >= 0)
6749 wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra;
6750 }
6751 #endif
6752 #endif
6753 #ifdef YAX_ENABLE
6754 sv_postyaxload();
6755 #endif
6756 G_ResetInterpolations();
6757
6758 Net_ResetPrediction();
6759
6760 G_ClearFIFO();
6761 G_ResetTimers(0);
6762 }
6763 }
6764
6765 // MYOS* CON commands.
6766 void VM_DrawTileGeneric(int32_t x, int32_t y, int32_t zoom, int32_t tilenum, int32_t shade, int32_t orientation, int32_t p)
6767 {
6768 orientation &= (ROTATESPRITE_MAX-1);
6769
6770 int const rotAngle = (orientation&4) ? 1024 : 0;
6771
6772 if (!(orientation&ROTATESPRITE_FULL16))
6773 {
6774 x<<=16;
6775 y<<=16;
6776 }
6777
6778 rotatesprite_win(x, y, zoom, rotAngle, tilenum, shade, p, 2|orientation);
6779 }
6780
6781 void VM_DrawTile(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation)
6782 {
6783 auto const pPlayer = g_player[screenpeek].ps;
6784 int32_t tilePal = pPlayer->cursectnum >= 0 ? sector[pPlayer->cursectnum].floorpal : 0;
6785
6786 VM_DrawTileGeneric(x, y, 65536, tilenum, shade, orientation, tilePal);
6787 }
6788
6789 void VM_DrawTileSmall(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation)
6790 {
6791 auto const pPlayer = g_player[screenpeek].ps;
6792 int32_t tilePal = pPlayer->cursectnum >= 0 ? sector[pPlayer->cursectnum].floorpal : 0;
6793
6794 VM_DrawTileGeneric(x, y, 32768, tilenum, shade, orientation, tilePal);
6795 }
6796