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)&sector[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 &sectLabel = SectorLabels[*insptr++];
2612 
2613                     VM_SetStruct(sectLabel.flags, (intptr_t *)((char *)&sector[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 &sectLabel = SectorLabels[*insptr++];
2624 
2625                     Gv_SetVar(*insptr++, VM_GetStruct(sectLabel.flags, (intptr_t *)((char *)&sector[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, &sectNum, 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, &sectNum);
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, &sectNum);
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, &sectNum, 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, &sectNum, 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],&sector[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(&sector[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