//------------------------------------------------------------------------- /* Copyright (C) 2016 EDuke32 developers and contributors This file is part of EDuke32. EDuke32 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include "ns.h" // Must come before everything else! #include "anim.h" #include "cmdline.h" #include "imagehelpers.h" #include "compat.h" #include "duke3d.h" #include "palutil.h" #include "gamestructures.h" #include "menus.h" #include "osdcmds.h" #include "savegame.h" #include "gamecvars.h" #include "gamecontrol.h" #include "gameconfigfile.h" #include "files.h" #include "base64.h" #include "version.h" #include "menu/menu.h" #include "c_dispatch.h" #include "quotemgr.h" #include "mapinfo.h" #include "version.h" #include "v_video.h" #include "colormatcher.h" #include "debugbreak.h" FString C_CON_GetBoundKeyForLastInput(int gameFunc); const char* C_CON_GetButtonFunc(int num); const char* KB_ScanCodeToString(int scancode); // convert scancode into a string BEGIN_DUKE_NS #if KRANDDEBUG # define GAMEEXEC_INLINE # define GAMEEXEC_STATIC #else # define GAMEEXEC_INLINE inline # define GAMEEXEC_STATIC static #endif vmstate_t vm; #if !defined LUNATIC int32_t g_tw; int32_t g_currentEvent = -1; intptr_t const *insptr; int32_t g_returnVarID = -1; // var ID of "RETURN" int32_t g_weaponVarID = -1; // var ID of "WEAPON" int32_t g_worksLikeVarID = -1; // var ID of "WORKSLIKE" int32_t g_zRangeVarID = -1; // var ID of "ZRANGE" int32_t g_angRangeVarID = -1; // var ID of "ANGRANGE" int32_t g_aimAngleVarID = -1; // var ID of "AUTOAIMANGLE" int32_t g_lotagVarID = -1; // var ID of "LOTAG" int32_t g_hitagVarID = -1; // var ID of "HITAG" int32_t g_textureVarID = -1; // var ID of "TEXTURE" int32_t g_thisActorVarID = -1; // var ID of "THISACTOR" int32_t g_structVarIDs = -1; // for timing events and actors uint32_t g_eventCalls[MAXEVENTS], g_actorCalls[MAXTILES]; double g_eventTotalMs[MAXEVENTS], g_actorTotalMs[MAXTILES], g_actorMinMs[MAXTILES], g_actorMaxMs[MAXTILES]; GAMEEXEC_STATIC void VM_Execute(int const loop = false); #endif #if !defined LUNATIC void VM_ScriptInfo(intptr_t const * const ptr, int const range) { if (!apScript || !ptr || g_currentEvent == -1) return; Printf("\n"); for (auto pScript = max(ptr - (range >> 1), apScript), p_end = min(ptr + (range >> 1), apScript + g_scriptSize); pScript < p_end; ++pScript) { Printf("%5d: %3d: ", (int32_t)(pScript - apScript), (int32_t)(pScript - ptr)); auto &v = *pScript; int const lineNum = VM_DECODE_LINE_NUMBER(v); int const vmInst = VM_DECODE_INST(v); if (lineNum && lineNum != VM_IFELSE_MAGIC && vmInst < CON_OPCODE_END) Printf("%5d %s (%d)\n", lineNum, VM_GetKeywordForID(vmInst), vmInst); else Printf("%d\n", (int32_t)*pScript); } Printf("\n"); if (ptr == insptr) { if (vm.pUSprite) Printf("current actor: %d (%d)\n", vm.spriteNum, vm.pUSprite->picnum); Printf("g_errorLineNum: %d, g_tw: %d\n", VM_DECODE_LINE_NUMBER(g_tw), VM_DECODE_INST(g_tw)); } } #endif static void VM_DeleteSprite(int const spriteNum, int const playerNum) { if (EDUKE32_PREDICT_FALSE((unsigned) spriteNum >= MAXSPRITES)) return; // if player was set to squish, first stop that... if (EDUKE32_PREDICT_FALSE(playerNum >= 0 && g_player[playerNum].ps->actorsqu == spriteNum)) g_player[playerNum].ps->actorsqu = -1; A_DeleteSprite(spriteNum); } intptr_t apScriptEvents[MAXEVENTS]; // May recurse, e.g. through EVENT_XXX -> ... -> EVENT_KILLIT #ifdef LUNATIC static FORCE_INLINE int32_t VM_EventInlineInternal__(int const &eventNum, int const &spriteNum, int const &playerNum, int const &playerDist, int32_t returnValue) { const double t = timerGetHiTicks(); int32_t ret = El_CallEvent(&g_ElState, eventNum, spriteNum, playerNum, playerDist, &returnValue); // NOTE: the run times are those of the called event plus any events // called by it, *not* "self" time. g_eventTotalMs[eventNum] += timerGetHiTicks()-t; g_eventCalls[eventNum]++; if (ret == 1) VM_DeleteSprite(spriteNum, playerNum); return returnValue; } #else static uspritetype dummy_sprite; static actor_t dummy_actor; static inline void VM_DummySprite(void) { vm.pUSprite = &dummy_sprite; vm.pActor = &dummy_actor; vm.pData = &dummy_actor.t_data[0]; } // verification that the event actually exists happens elsewhere static FORCE_INLINE int32_t VM_EventInlineInternal__(int const eventNum, int const spriteNum, int const playerNum, int const playerDist = -1, int32_t returnValue = 0) { vmstate_t const newVMstate = { spriteNum, playerNum, playerDist, 0, &sprite[spriteNum&(MAXSPRITES-1)], &actor[spriteNum&(MAXSPRITES-1)].t_data[0], g_player[playerNum&(MAXPLAYERS-1)].ps, &actor[spriteNum&(MAXSPRITES-1)] }; auto &globalReturn = aGameVars[g_returnVarID].global; struct { vmstate_t vm; intptr_t globalReturn; int eventNum; intptr_t const *insptr; } const saved = { vm, globalReturn, g_currentEvent, insptr }; vm = newVMstate; g_currentEvent = eventNum; insptr = apScript + apScriptEvents[eventNum]; globalReturn = returnValue; double const t = timerGetHiTicks(); if ((unsigned)spriteNum >= MAXSPRITES) VM_DummySprite(); if ((unsigned)playerNum >= (unsigned)g_mostConcurrentPlayers) vm.pPlayer = g_player[0].ps; VM_Execute(true); if (vm.flags & VM_KILL) VM_DeleteSprite(vm.spriteNum, vm.playerNum); g_eventTotalMs[eventNum] += timerGetHiTicks()-t; g_eventCalls[eventNum]++; // restoring these needs to happen after VM_DeleteSprite() due to event recursion returnValue = globalReturn; vm = saved.vm; globalReturn = saved.globalReturn; g_currentEvent = saved.eventNum; insptr = saved.insptr; return returnValue; } #endif // the idea here is that the compiler inlines the call to VM_EventInlineInternal__() and gives us a set of // functions which are optimized further based on distance/return having values known at compile time int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist, int32_t const nReturn) { return VM_EventInlineInternal__(nEventID, spriteNum, playerNum, nDist, nReturn); } int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist) { return VM_EventInlineInternal__(nEventID, spriteNum, playerNum, nDist); } int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum) { return VM_EventInlineInternal__(nEventID, spriteNum, playerNum); } int32_t VM_ExecuteEventWithValue(int const nEventID, int const spriteNum, int const playerNum, int32_t const nReturn) { return VM_EventInlineInternal__(nEventID, spriteNum, playerNum, -1, nReturn); } static int VM_CheckSquished(void) { auto const pSector = (usectorptr_t)§or[vm.pSprite->sectnum]; if (pSector->lotag == ST_23_SWINGING_DOOR || (vm.pSprite->picnum == APLAYER && ud.noclip) || (pSector->lotag == ST_1_ABOVE_WATER && !A_CheckNoSE7Water(vm.pUSprite, vm.pSprite->sectnum, pSector->lotag, NULL))) return 0; int32_t floorZ = pSector->floorz; int32_t ceilZ = pSector->ceilingz; #ifdef YAX_ENABLE int16_t cb, fb; yax_getbunches(vm.pSprite->sectnum, &cb, &fb); if (cb >= 0 && (pSector->ceilingstat&512)==0) // if ceiling non-blocking... ceilZ -= ZOFFSET5; // unconditionally don't squish... yax_getneighborsect is slowish :/ if (fb >= 0 && (pSector->floorstat&512)==0) floorZ += ZOFFSET5; #endif if (vm.pSprite->pal == 1 ? (floorZ - ceilZ >= ZOFFSET5 || (pSector->lotag & 32768u)) : (floorZ - ceilZ >= ZOFFSET4)) return 0; P_DoQuote(QUOTE_SQUISHED, vm.pPlayer); if (A_CheckEnemySprite(vm.pSprite)) vm.pSprite->xvel = 0; #ifndef EDUKE32_STANDALONE if (EDUKE32_PREDICT_FALSE(vm.pSprite->pal == 1)) // frozen { vm.pActor->picnum = SHOTSPARK1; vm.pActor->extra = 1; return 0; } #endif return 1; } #if !defined LUNATIC GAMEEXEC_STATIC GAMEEXEC_INLINE void P_ForceAngle(DukePlayer_t *pPlayer) { int const nAngle = 128-(krand()&255); pPlayer->q16horiz += F16(64); pPlayer->return_to_center = 9; pPlayer->q16rotscrnang = fix16_from_int(nAngle >> 1); pPlayer->q16look_ang = pPlayer->q16rotscrnang; } #endif // wow, this function sucks int A_Dodge(spritetype * const); int A_Dodge(spritetype * const pSprite) { if (A_CheckEnemySprite(pSprite) && pSprite->extra <= 0) // hack return 0; vec2_t const msin = { sintable[(pSprite->ang + 512) & 2047], sintable[pSprite->ang & 2047] }; for (native_t nexti, SPRITES_OF_STAT_SAFE(STAT_PROJECTILE, i, nexti)) //weapons list { if (OW(i) == i) continue; vec2_t const b = { SX(i) - pSprite->x, SY(i) - pSprite->y }; vec2_t const v = { sintable[(SA(i) + 512) & 2047], sintable[SA(i) & 2047] }; if (((msin.x * b.x) + (msin.y * b.y) >= 0) && ((v.x * b.x) + (v.y * b.y) < 0)) { if (klabs((v.x * b.y) - (v.y * b.x)) < 65536 << 6) { pSprite->ang -= 512+(krand()&1024); return 1; } } } return 0; } int A_GetFurthestAngle(int const spriteNum, int const angDiv) { auto const pSprite = (uspriteptr_t)&sprite[spriteNum]; if (pSprite->picnum != APLAYER && (AC_COUNT(actor[spriteNum].t_data)&63) > 2) return (pSprite->ang + 1024) & 2047; int furthestAngle = 0; int const angIncs = tabledivide32_noinline(2048, angDiv); int32_t greatestDist = INT32_MIN; hitdata_t hit; for (native_t j = pSprite->ang; j < (2048 + pSprite->ang); j += angIncs) { vec3_t origin = *(const vec3_t *)pSprite; origin.z -= ZOFFSET3; hitscan(&origin, pSprite->sectnum, sintable[(j + 512) & 2047], sintable[j & 2047], 0, &hit, CLIPMASK1); int const hitDist = klabs(hit.pos.x-pSprite->x) + klabs(hit.pos.y-pSprite->y); if (hitDist > greatestDist) { greatestDist = hitDist; furthestAngle = j; } } return furthestAngle & 2047; } int A_FurthestVisiblePoint(int const spriteNum, uspriteptr_t const ts, vec2_t * const vect) { if (AC_COUNT(actor[spriteNum].t_data)&63) return -1; auto const pnSprite = (uspriteptr_t)&sprite[spriteNum]; hitdata_t hit; int const angincs = 128; // ((!g_netServer && ud.multimode < 2) && ud.player_skill < 3) ? 2048 / 2 : tabledivide32_noinline(2048, 1 + (krand() & 1)); for (native_t j = ts->ang; j < (2048 + ts->ang); j += (angincs /*-(krand()&511)*/)) { vec3_t origin = *(const vec3_t *)ts; origin.z -= ZOFFSET2; hitscan(&origin, ts->sectnum, sintable[(j + 512) & 2047], sintable[j & 2047], 16384 - (krand() & 32767), &hit, CLIPMASK1); if (hit.sect < 0) continue; int const d = FindDistance2D(hit.pos.x - ts->x, hit.pos.y - ts->y); int const da = FindDistance2D(hit.pos.x - pnSprite->x, hit.pos.y - pnSprite->y); if (d < da) { if (cansee(hit.pos.x, hit.pos.y, hit.pos.z, hit.sect, pnSprite->x, pnSprite->y, pnSprite->z - ZOFFSET2, pnSprite->sectnum)) { vect->x = hit.pos.x; vect->y = hit.pos.y; return hit.sect; } } } return -1; } void VM_GetZRange(int const spriteNum, int32_t * const ceilhit, int32_t * const florhit, int const wallDist) { auto const pSprite = &sprite[spriteNum]; int const ocstat = pSprite->cstat; pSprite->cstat = 0; pSprite->z -= ACTOR_FLOOR_OFFSET; getzrange(&pSprite->pos, pSprite->sectnum, &actor[spriteNum].ceilingz, ceilhit, &actor[spriteNum].floorz, florhit, wallDist, CLIPMASK0); pSprite->z += ACTOR_FLOOR_OFFSET; pSprite->cstat = ocstat; } void A_GetZLimits(int const spriteNum) { auto const pSprite = &sprite[spriteNum]; int32_t ceilhit, florhit; int const clipDist = A_GetClipdist(spriteNum, -1); auto const oceilz = actor[spriteNum].ceilingz; VM_GetZRange(spriteNum, &ceilhit, &florhit, pSprite->statnum == STAT_PROJECTILE ? clipDist << 3 : clipDist); actor[spriteNum].flags &= ~SFLAG_NOFLOORSHADOW; if ((florhit&49152) == 49152 && (sprite[florhit&(MAXSPRITES-1)].cstat&48) == 0) { auto const hitspr = (uspriteptr_t)&sprite[florhit&(MAXSPRITES-1)]; florhit &= (MAXSPRITES-1); // If a non-projectile would fall onto non-frozen enemy OR an enemy onto a player... if ((A_CheckEnemySprite(hitspr) && hitspr->pal != 1 && pSprite->statnum != STAT_PROJECTILE) || (hitspr->picnum == APLAYER && A_CheckEnemySprite(pSprite))) { actor[spriteNum].flags |= SFLAG_NOFLOORSHADOW; // No shadows on actors pSprite->xvel = -256; // SLIDE_ABOVE_ENEMY A_SetSprite(spriteNum, CLIPMASK0); } else if (pSprite->statnum == STAT_PROJECTILE && hitspr->picnum == APLAYER && pSprite->owner==florhit) { actor[spriteNum].ceilingz = sector[pSprite->sectnum].ceilingz; actor[spriteNum].floorz = sector[pSprite->sectnum].floorz; } } // in E1L1, the dumpster fire sprites break after calling this function because the cardboard boxes // are a few units higher than the fire and are detected as the "ceiling" // unfortunately, this trips the "ifgapzl 16 break" in "state firestate" if ((ceilhit&49152) == 49152 && (sprite[ceilhit&(MAXSPRITES-1)].cstat&48) == 0) { if (pSprite->z >= actor[spriteNum].floorz) actor[spriteNum].ceilingz = oceilz; } } void A_Fall(int const spriteNum) { auto const pSprite = &sprite[spriteNum]; int spriteGravity = g_spriteGravity; if (EDUKE32_PREDICT_FALSE(G_CheckForSpaceFloor(pSprite->sectnum))) spriteGravity = 0; else if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER || EDUKE32_PREDICT_FALSE(G_CheckForSpaceCeiling(pSprite->sectnum))) spriteGravity = g_spriteGravity/6; int32_t ceilhit, florhit; VM_GetZRange(spriteNum, &ceilhit, &florhit, A_GetClipdist(spriteNum, -1)); #ifdef YAX_ENABLE int fbunch = (sector[pSprite->sectnum].floorstat&512) ? -1 : yax_getbunch(pSprite->sectnum, YAX_FLOOR); #endif if (pSprite->z < actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET #ifdef YAX_ENABLE || fbunch >= 0 #endif ) { if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER && pSprite->zvel > 3122) pSprite->zvel = 3144; pSprite->z += pSprite->zvel = min(ACTOR_MAXFALLINGZVEL, pSprite->zvel+spriteGravity); } #ifdef YAX_ENABLE if (fbunch >= 0) setspritez(spriteNum, &pSprite->pos); else #endif if (pSprite->z >= actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET) { pSprite->z = actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET; pSprite->zvel = 0; } } int __fastcall G_GetAngleDelta(int currAngle, int newAngle) { currAngle &= 2047; newAngle &= 2047; if (klabs(currAngle-newAngle) < 1024) { // Printf("G_GetAngleDelta() returning %d\n",na-a); return newAngle-currAngle; } if (newAngle > 1024) newAngle -= 2048; if (currAngle > 1024) currAngle -= 2048; // Printf("G_GetAngleDelta() returning %d\n",na-a); return newAngle-currAngle; } fix16_t __fastcall G_GetQ16AngleDelta(fix16_t oldAngle, fix16_t newAngle) { if (fix16_abs(fix16_sub(oldAngle, newAngle)) < fix16_from_int(1024)) return fix16_sub(newAngle, oldAngle); if (newAngle > fix16_from_int(1024)) newAngle = fix16_sub(newAngle, fix16_from_int(2048)); if (oldAngle > fix16_from_int(1024)) oldAngle = fix16_sub(oldAngle, fix16_from_int(2048)); return fix16_sub(newAngle, oldAngle); } GAMEEXEC_STATIC void VM_AlterAng(int32_t const moveFlags) { int const elapsedTics = (AC_COUNT(vm.pData))&31; #if !defined LUNATIC if (EDUKE32_PREDICT_FALSE((unsigned)AC_MOVE_ID(vm.pData) >= (unsigned)g_scriptSize-1)) { AC_MOVE_ID(vm.pData) = 0; Printf(OSD_ERROR "bad moveptr for actor %d (%d)!\n", vm.spriteNum, vm.pUSprite->picnum); return; } auto const moveptr = apScript + AC_MOVE_ID(vm.pData); auto &hvel = moveptr[0]; auto &vvel = moveptr[1]; #else auto &hvel = vm.pActor->mv.hvel; auto &vvel = vm.pActor->mv.vvel; #endif vm.pSprite->xvel += (hvel - vm.pSprite->xvel)/5; if (vm.pSprite->zvel < 648) vm.pSprite->zvel += ((vvel<<4) - vm.pSprite->zvel)/5; if (A_CheckEnemySprite(vm.pSprite) && vm.pSprite->extra <= 0) // hack return; if (moveFlags&seekplayer) { int const spriteAngle = vm.pSprite->ang; int const holoDukeSprite = vm.pPlayer->holoduke_on; // NOTE: looks like 'owner' is set to target sprite ID... vm.pSprite->owner = (holoDukeSprite >= 0 && cansee(sprite[holoDukeSprite].x, sprite[holoDukeSprite].y, sprite[holoDukeSprite].z, sprite[holoDukeSprite].sectnum, vm.pSprite->x, vm.pSprite->y, vm.pSprite->z, vm.pSprite->sectnum)) ? holoDukeSprite : vm.pPlayer->i; int const goalAng = (sprite[vm.pSprite->owner].picnum == APLAYER) ? getangle(vm.pActor->lastv.x - vm.pSprite->x, vm.pActor->lastv.y - vm.pSprite->y) : getangle(sprite[vm.pSprite->owner].x - vm.pSprite->x, sprite[vm.pSprite->owner].y - vm.pSprite->y); if (vm.pSprite->xvel && vm.pSprite->picnum != DRONE) { int const angDiff = G_GetAngleDelta(spriteAngle, goalAng); if (elapsedTics < 2) { if (klabs(angDiff) < 256) { int const angInc = 128-(krand()&256); vm.pSprite->ang += angInc; if (A_GetHitscanRange(vm.spriteNum) < 844) vm.pSprite->ang -= angInc; } } else if (elapsedTics > 18 && elapsedTics < GAMETICSPERSEC) // choose { if (klabs(angDiff >> 2) < 128) vm.pSprite->ang = goalAng; else vm.pSprite->ang += angDiff >> 2; } } else vm.pSprite->ang = goalAng; } if (elapsedTics < 1) { if (moveFlags&furthestdir) { vm.pSprite->ang = A_GetFurthestAngle(vm.spriteNum, 2); vm.pSprite->owner = vm.pPlayer->i; } if (moveFlags&fleeenemy) vm.pSprite->ang = A_GetFurthestAngle(vm.spriteNum, 2); } } static inline void VM_AddAngle(int const shift, int const goalAng) { int angDiff = G_GetAngleDelta(vm.pSprite->ang, goalAng) >> shift; if ((angDiff > -8 && angDiff < 0) || (angDiff < 8 && angDiff > 0)) angDiff <<= 1; vm.pSprite->ang += angDiff; } static inline void VM_FacePlayer(int const shift) { VM_AddAngle(shift, (vm.pPlayer->newowner >= 0) ? getangle(vm.pPlayer->opos.x - vm.pSprite->x, vm.pPlayer->opos.y - vm.pSprite->y) : getangle(vm.pPlayer->pos.x - vm.pSprite->x, vm.pPlayer->pos.y - vm.pSprite->y)); } static inline int32_t VM_GetCeilZOfSlope(void) { vec2_t const vect = vm.pSprite->pos.vec2; int const sectnum = vm.pSprite->sectnum; return yax_getceilzofslope(sectnum, vect); } #ifndef EDUKE32_STANDALONE static inline int32_t VM_GetFlorZOfSlope(void) { vec2_t const vect = vm.pSprite->pos.vec2; int const sectnum = vm.pSprite->sectnum; return yax_getflorzofslope(sectnum, vect); } #endif static int32_t A_GetWaterZOffset(int spritenum); GAMEEXEC_STATIC void VM_Move(void) { auto const movflagsptr = &AC_MOVFLAGS(vm.pSprite, &actor[vm.spriteNum]); // NOTE: test against -1 commented out and later revived in source history // XXX: Does its presence/absence break anything? Where are movflags with all bits set created? int const movflags = (*movflagsptr == (remove_pointer_t)-1) ? 0 : *movflagsptr; int const deadflag = (A_CheckEnemySprite(vm.pSprite) && vm.pSprite->extra <= 0); AC_COUNT(vm.pData)++; if (AC_MOVE_ID(vm.pData) == 0 || movflags == 0) { if (deadflag || (vm.pActor->bpos.x != vm.pSprite->x) || (vm.pActor->bpos.y != vm.pSprite->y)) setsprite(vm.spriteNum, &vm.pSprite->pos); return; } if (!deadflag) { if (movflags & face_player) VM_FacePlayer(2); if (movflags & spin) vm.pSprite->ang += sintable[((AC_COUNT(vm.pData) << 3) & 2047)] >> 6; if (movflags & face_player_slow) VM_FacePlayer(4); if ((movflags & jumptoplayer_bits) == jumptoplayer_bits) { if (AC_COUNT(vm.pData) < 16) vm.pSprite->zvel -= (sintable[(512 + (AC_COUNT(vm.pData) << 4)) & 2047] >> 5); } if (movflags & face_player_smart) { vec2_t const vect = { vm.pPlayer->pos.x + (vm.pPlayer->vel.x / 768), vm.pPlayer->pos.y + (vm.pPlayer->vel.y / 768) }; VM_AddAngle(2, getangle(vect.x - vm.pSprite->x, vect.y - vm.pSprite->y)); } } #if !defined LUNATIC if (EDUKE32_PREDICT_FALSE((unsigned)AC_MOVE_ID(vm.pData) >= (unsigned)g_scriptSize-1)) { AC_MOVE_ID(vm.pData) = 0; Printf(OSD_ERROR "clearing bad moveptr for actor %d (%d)\n", vm.spriteNum, vm.pUSprite->picnum); return; } auto const moveptr = apScript + AC_MOVE_ID(vm.pData); auto &hvel = moveptr[0]; auto &vvel = moveptr[1]; #else auto &hvel = vm.pActor->mv.hvel; auto &vvel = vm.pActor->mv.vvel; #endif if (movflags & geth) vm.pSprite->xvel += (hvel - vm.pSprite->xvel) >> 1; if (movflags & getv) vm.pSprite->zvel += (16 * vvel - vm.pSprite->zvel) >> 1; if (movflags&dodgebullet && !deadflag) A_Dodge(vm.pSprite); if (vm.pSprite->picnum != APLAYER) VM_AlterAng(movflags); if (vm.pSprite->xvel > -6 && vm.pSprite->xvel < 6) vm.pSprite->xvel = 0; int badguyp = A_CheckEnemySprite(vm.pSprite); if (vm.pSprite->xvel || vm.pSprite->zvel) { int spriteXvel = vm.pSprite->xvel; int angDiff = vm.pSprite->ang; #ifndef EDUKE32_STANDALONE if (badguyp && (FURY || vm.pSprite->picnum != ROTATEGUN)) { if (!FURY && (vm.pSprite->picnum == DRONE || vm.pSprite->picnum == COMMANDER) && vm.pSprite->extra > 0) { if (vm.pSprite->picnum == COMMANDER) { int32_t nSectorZ; // NOTE: COMMANDER updates both actor[].floorz and // .ceilingz regardless of its zvel. vm.pActor->floorz = nSectorZ = VM_GetFlorZOfSlope(); if (vm.pSprite->z > nSectorZ-ZOFFSET3) { vm.pSprite->z = nSectorZ-ZOFFSET3; vm.pSprite->zvel = 0; } vm.pActor->ceilingz = nSectorZ = VM_GetCeilZOfSlope(); if (vm.pSprite->z < nSectorZ+(80<<8)) { vm.pSprite->z = nSectorZ+(80<<8); vm.pSprite->zvel = 0; } } else { int32_t nSectorZ; // The DRONE updates either .floorz or .ceilingz, not both. if (vm.pSprite->zvel > 0) { vm.pActor->floorz = nSectorZ = VM_GetFlorZOfSlope(); if (vm.pSprite->z > nSectorZ-(30<<8)) vm.pSprite->z = nSectorZ-(30<<8); } else { vm.pActor->ceilingz = nSectorZ = VM_GetCeilZOfSlope(); if (vm.pSprite->z < nSectorZ+(50<<8)) { vm.pSprite->z = nSectorZ+(50<<8); vm.pSprite->zvel = 0; } } } } else if ((FURY && badguyp) || vm.pSprite->picnum != ORGANTIC) #else if (badguyp) { #endif { // All other actors besides ORGANTIC don't update .floorz or // .ceilingz here. if (vm.pSprite->zvel > 0) { if (vm.pSprite->z > vm.pActor->floorz) vm.pSprite->z = vm.pActor->floorz; vm.pSprite->z += A_GetWaterZOffset(vm.spriteNum); } else if (vm.pSprite->zvel < 0) { int const l = VM_GetCeilZOfSlope(); if (vm.pSprite->z < l+(66<<8)) { vm.pSprite->z = l+(66<<8); vm.pSprite->zvel >>= 1; } } } if (vm.playerDist < 960 && vm.pSprite->xrepeat > 16) { spriteXvel = -(1024 - vm.playerDist); angDiff = getangle(vm.pPlayer->pos.x - vm.pSprite->x, vm.pPlayer->pos.y - vm.pSprite->y); if (vm.playerDist < 512) { vm.pPlayer->vel.x = 0; vm.pPlayer->vel.y = 0; } else { vm.pPlayer->vel.x = mulscale16(vm.pPlayer->vel.x, vm.pPlayer->runspeed - 0x2000); vm.pPlayer->vel.y = mulscale16(vm.pPlayer->vel.y, vm.pPlayer->runspeed - 0x2000); } } else #ifndef EDUKE32_STANDALONE if (FURY || (vm.pSprite->picnum != DRONE && vm.pSprite->picnum != SHARK && vm.pSprite->picnum != COMMANDER)) #endif { if (vm.pPlayer->actorsqu == vm.spriteNum) return; if (!A_CheckSpriteFlags(vm.spriteNum, SFLAG_SMOOTHMOVE)) { if (AC_COUNT(vm.pData) & 1) return; spriteXvel <<= 1; } } } else if (vm.pSprite->picnum == APLAYER) if (vm.pSprite->z < vm.pActor->ceilingz+ZOFFSET5) vm.pSprite->z = vm.pActor->ceilingz+ZOFFSET5; vec3_t const vect = { (spriteXvel * (sintable[(angDiff + 512) & 2047])) >> 14, (spriteXvel * (sintable[angDiff & 2047])) >> 14, vm.pSprite->zvel }; vm.pActor->movflag = A_MoveSprite(vm.spriteNum, &vect, (A_CheckSpriteFlags(vm.spriteNum, SFLAG_NOCLIP) ? 0 : CLIPMASK0)); } if (!badguyp) return; vm.pSprite->shade += (sector[vm.pSprite->sectnum].ceilingstat & 1) ? (sector[vm.pSprite->sectnum].ceilingshade - vm.pSprite->shade) >> 1 : (sector[vm.pSprite->sectnum].floorshade - vm.pSprite->shade) >> 1; } static void P_AddWeaponMaybeSwitch(DukePlayer_t * const ps, int const weaponNum) { if ((ps->weaponswitch & (1|4)) == (1|4)) { int const playerNum = P_Get(ps->i); int new_wchoice = -1; int curr_wchoice = -1; for (native_t i=0; i<=FREEZE_WEAPON && (new_wchoice < 0 || curr_wchoice < 0); i++) { int w = g_player[playerNum].wchoice[i]; if (w == KNEE_WEAPON) w = FREEZE_WEAPON; else w--; if (w == ps->curr_weapon) curr_wchoice = i; if (w == weaponNum) new_wchoice = i; } P_AddWeapon(ps, weaponNum, (new_wchoice < curr_wchoice)); } else { P_AddWeapon(ps, weaponNum, (ps->weaponswitch & 1)); } } #if defined LUNATIC void P_AddWeaponMaybeSwitchI(int32_t snum, int32_t weap) { P_AddWeaponMaybeSwitch(g_player[snum].ps, weap); } #else static void P_AddWeaponAmmoCommon(DukePlayer_t * const pPlayer, int const weaponNum, int const nAmount) { P_AddAmmo(pPlayer, weaponNum, nAmount); if (PWEAPON(vm.playerNum, pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON && (pPlayer->gotweapon & (1 << weaponNum))) P_AddWeaponMaybeSwitch(pPlayer, weaponNum); } static void VM_AddWeapon(DukePlayer_t * const pPlayer, int const weaponNum, int const nAmount) { if (EDUKE32_PREDICT_FALSE((unsigned)weaponNum >= MAX_WEAPONS)) { CON_ERRPRINTF("invalid weapon %d\n", weaponNum); return; } if ((pPlayer->gotweapon & (1 << weaponNum)) == 0) { P_AddWeaponMaybeSwitch(pPlayer, weaponNum); } else if (pPlayer->ammo_amount[weaponNum] >= pPlayer->max_ammo_amount[weaponNum]) { vm.flags |= VM_NOEXECUTE; return; } P_AddWeaponAmmoCommon(pPlayer, weaponNum, nAmount); } static void VM_AddAmmo(DukePlayer_t * const pPlayer, int const weaponNum, int const nAmount) { if (EDUKE32_PREDICT_FALSE((unsigned)weaponNum >= MAX_WEAPONS)) { CON_ERRPRINTF("invalid weapon %d\n", weaponNum); return; } if (pPlayer->ammo_amount[weaponNum] >= pPlayer->max_ammo_amount[weaponNum]) { vm.flags |= VM_NOEXECUTE; return; } P_AddWeaponAmmoCommon(pPlayer, weaponNum, nAmount); } static void VM_AddInventory(DukePlayer_t * const pPlayer, int const itemNum, int const nAmount) { switch (itemNum) { case GET_STEROIDS: case GET_SCUBA: case GET_HOLODUKE: case GET_JETPACK: case GET_HEATS: case GET_FIRSTAID: case GET_BOOTS: pPlayer->inven_icon = inv_to_icon[itemNum]; pPlayer->inv_amount[itemNum] = nAmount; break; case GET_SHIELD: { int16_t & shield_amount = pPlayer->inv_amount[GET_SHIELD]; shield_amount = min(shield_amount + nAmount, pPlayer->max_shield_amount); break; } case GET_ACCESS: switch (vm.pSprite->pal) { case 0: pPlayer->got_access |= 1; break; case 21: pPlayer->got_access |= 2; break; case 23: pPlayer->got_access |= 4; break; } break; default: CON_ERRPRINTF("invalid inventory item %d\n", itemNum); break; } } #endif static int A_GetVerticalVel(actor_t const * const pActor) { #ifdef LUNATIC return pActor->mv.vvel; #else int32_t moveScriptOfs = AC_MOVE_ID(pActor->t_data); return ((unsigned) moveScriptOfs < (unsigned) g_scriptSize - 1) ? apScript[moveScriptOfs + 1] : 0; #endif } static int32_t A_GetWaterZOffset(int const spriteNum) { auto const pSprite = (uspriteptr_t)&sprite[spriteNum]; auto const pActor = &actor[spriteNum]; if (sector[pSprite->sectnum].lotag == ST_1_ABOVE_WATER) { if (A_CheckSpriteFlags(spriteNum, SFLAG_NOWATERDIP)) return 0; // fix for flying/jumping monsters getting stuck in water if ((AC_MOVFLAGS(pSprite, pActor) & jumptoplayer_only) || (G_TileHasActor(pSprite->picnum) && A_GetVerticalVel(pActor) != 0)) return 0; return ACTOR_ONWATER_ADDZ; } return 0; } static void VM_Fall(int const spriteNum, spritetype * const pSprite) { int spriteGravity = g_spriteGravity; pSprite->xoffset = pSprite->yoffset = 0; if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER || EDUKE32_PREDICT_FALSE(G_CheckForSpaceCeiling(pSprite->sectnum))) spriteGravity = g_spriteGravity/6; else if (EDUKE32_PREDICT_FALSE(G_CheckForSpaceFloor(pSprite->sectnum))) spriteGravity = 0; if (!actor[spriteNum].cgg-- || (sector[pSprite->sectnum].floorstat&2)) actor[spriteNum].cgg = 3; A_GetZLimits(spriteNum); if (pSprite->z < actor[spriteNum].floorz-ACTOR_FLOOR_OFFSET) { // Free fall. pSprite->zvel = min(pSprite->zvel+spriteGravity, ACTOR_MAXFALLINGZVEL); int newZ = pSprite->z + pSprite->zvel; #ifdef YAX_ENABLE if (yax_getbunch(pSprite->sectnum, YAX_FLOOR) >= 0 && (sector[pSprite->sectnum].floorstat & 512) == 0) setspritez(spriteNum, &pSprite->pos); else #endif if (newZ > actor[spriteNum].floorz - ACTOR_FLOOR_OFFSET) newZ = actor[spriteNum].floorz - ACTOR_FLOOR_OFFSET; pSprite->z = newZ; return; } // Preliminary new z position of the actor. int newZ = actor[spriteNum].floorz - ACTOR_FLOOR_OFFSET; if (A_CheckEnemySprite(pSprite) || (pSprite->picnum == APLAYER && pSprite->owner >= 0)) { if (pSprite->zvel > 3084 && pSprite->extra <= 1) { // I'm guessing this DRONE check is from a beta version of the game // where they crashed into the ground when killed #ifndef EDUKE32_STANDALONE if (!FURY && !(pSprite->picnum == APLAYER && pSprite->extra > 0) && pSprite->pal != 1 && pSprite->picnum != DRONE) { A_DoGuts(spriteNum,JIBS6,15); A_PlaySound(SQUISHED,spriteNum); A_Spawn(spriteNum,BLOODPOOL); } #endif actor[spriteNum].picnum = SHOTSPARK1; actor[spriteNum].extra = 1; pSprite->zvel = 0; } else if (pSprite->zvel > 2048 && sector[pSprite->sectnum].lotag != ST_1_ABOVE_WATER) { int16_t newsect = pSprite->sectnum; pushmove(&pSprite->pos, &newsect, 128, 4<<8, 4<<8, CLIPMASK0); if ((unsigned)newsect < MAXSECTORS) changespritesect(spriteNum, newsect); A_PlaySound(THUD, spriteNum); } } if (sector[pSprite->sectnum].lotag == ST_1_ABOVE_WATER && actor[spriteNum].floorz == yax_getflorzofslope(pSprite->sectnum, pSprite->pos.vec2)) { pSprite->z = newZ + A_GetWaterZOffset(spriteNum); return; } pSprite->z = newZ; pSprite->zvel = 0; } static int32_t VM_ResetPlayer(int const playerNum, int32_t vmFlags, int32_t const resetFlags) { if (!g_netServer && ud.multimode < 2 && !(resetFlags & 2)) { #if 0 // Who thought that allowing a script to do this shit is a good idea??? This needs to be sorted out later and implemented properly. if (!(resetFlags & 8)) { if (resetFlags & 4) { inputState.keyFlushChars(); inputState.ClearAllInput(); FX_StopAllSounds(); if (G_LoadPlayerMaybeMulti(*g_quickload) != 0) { g_quickload->reset(); goto QuickLoadFailure; } } else if (!(resetFlags & 1)) { M_StartControlPanel(false); M_SetMenu(NAME_ConfirmPlayerReset); /* case MENU_RESETPLAYER: videoFadeToBlack(1); Bsprintf(tempbuf, "Load last game:\n\"%s\"", g_quickload->name); Menu_DrawVerifyPrompt(origin.x, origin.y, tempbuf, 2); break; */ } } else #endif { g_player[playerNum].ps->gm = MODE_RESTART; } vmFlags |= VM_NOEXECUTE; } else { if (playerNum == myconnectindex) { CAMERADIST = 0; CAMERACLOCK = (int32_t) totalclock; } if (g_fakeMultiMode) P_ResetMultiPlayer(playerNum); #ifndef NETCODE_DISABLE if (g_netServer) { P_ResetMultiPlayer(playerNum); Net_SpawnPlayer(playerNum); } #endif } P_UpdateScreenPal(g_player[playerNum].ps); //AddLog("EOF: resetplayer"); return vmFlags; } void G_GetTimeDate(int32_t * const pValues) { time_t timeStruct; time(&timeStruct); struct tm *pTime = localtime(&timeStruct); // Printf("Time&date: %s\n",asctime (ti)); pValues[0] = pTime->tm_sec; pValues[1] = pTime->tm_min; pValues[2] = pTime->tm_hour; pValues[3] = pTime->tm_mday; pValues[4] = pTime->tm_mon; pValues[5] = pTime->tm_year+1900; pValues[6] = pTime->tm_wday; pValues[7] = pTime->tm_yday; } static int G_StartTrackSlot(int const volumeNum, int const levelNum) { if ((unsigned)volumeNum <= MAXVOLUMES && (unsigned)levelNum < MAXLEVELS) { int trackNum = MAXLEVELS*volumeNum + levelNum; return S_TryPlaySpecialMusic(trackNum); } return 1; } #ifndef LUNATIC static int G_StartTrackSlotWrap(int const volumeNum, int const levelNum) { if (EDUKE32_PREDICT_FALSE(G_StartTrackSlot(volumeNum, levelNum))) { CON_ERRPRINTF("invalid level %d or null music for volume %d level %d\n", levelNum, volumeNum, levelNum); return 1; } return 0; } #else int G_StartTrack(int const levelNum) { return G_StartTrackSlot(ud.volume_number, levelNum); } #endif LUNATIC_EXTERN void G_ShowView(vec3_t vec, fix16_t a, fix16_t horiz, int sect, int ix1, int iy1, int ix2, int iy2, int unbiasedp) { int x1 = min(ix1, ix2); int x2 = max(ix1, ix2); int y1 = min(iy1, iy2); int y2 = max(iy1, iy2); if (!unbiasedp) { // The showview command has a rounding bias towards zero, // e.g. floor((319*1680)/320) == 1674 x1 = scale(x1,xdim,320); y1 = scale(y1,ydim,200); x2 = scale(x2,xdim,320); y2 = scale(y2,ydim,200); } else { // This will map the maximum 320-based coordinate to the // maximum real screen coordinate: // floor((319*1679)/319) == 1679 x1 = scale(x1,xdim-1,319); y1 = scale(y1,ydim-1,199); x2 = scale(x2,xdim-1,319); y2 = scale(y2,ydim-1,199); } horiz = fix16_clamp(horiz, F16(HORIZ_MIN), F16(HORIZ_MAX)); int const viewingRange = viewingrange; int const yxAspect = yxaspect; videoSetViewableArea(x1,y1,x2,y2); renderSetAspect(viewingRange, yxAspect); int const smoothratio = calc_smoothratio(totalclock, ototalclock); G_DoInterpolations(smoothratio); screen->BeginScene(); if (!display_mirror) G_HandleMirror(vec.x, vec.y, vec.z, a, horiz, smoothratio); #ifdef POLYMER if (videoGetRenderMode() == REND_POLYMER) polymer_setanimatesprites(G_DoSpriteAnimations, vec.x, vec.y, vec.z, fix16_to_int(a), smoothratio); #endif yax_preparedrawrooms(); renderDrawRoomsQ16(vec.x, vec.y, vec.z, a, horiz, sect); yax_drawrooms(G_DoSpriteAnimations, sect, 0, smoothratio); display_mirror = 2; G_DoSpriteAnimations(vec.x, vec.y, vec.z, fix16_to_int(a), smoothratio); display_mirror = 0; renderDrawMasks(); screen->FinishScene(); G_RestoreInterpolations(); G_UpdateScreenArea(); renderSetAspect(viewingRange, yxAspect); } void Screen_Play(void) { bool running = true; inputState.ClearAllInput(); do { gameHandleEvents(); ototalclock = totalclock + 1; // pause game like ANMs if (!G_FPSLimit()) continue; videoClearScreen(0); if (VM_OnEventWithReturn(EVENT_SCREEN, -1, myconnectindex, inputState.CheckAllInput())) running = false; videoNextPage(); inputState.ClearAllInput(); } while (running); } static void SetArray(int const arrayNum, int const arrayIndex, int const newValue) { if (EDUKE32_PREDICT_FALSE((unsigned)arrayNum >= (unsigned)g_gameArrayCount || (unsigned)arrayIndex >= (unsigned)aGameArrays[arrayNum].size)) { Printf(OSD_ERROR "Gv_SetVar(): tried to set invalid array %d or index out of bounds from " "sprite %d (%d), player %d\n", (int)arrayNum, vm.spriteNum, vm.pUSprite->picnum, vm.playerNum); vm.flags |= VM_RETURN; return; } auto &arr = aGameArrays[arrayNum]; if (EDUKE32_PREDICT_FALSE(arr.flags & GAMEARRAY_READONLY)) { Printf(OSD_ERROR "Tried to set value in read-only array `%s'", arr.szLabel); vm.flags |= VM_RETURN; return; } switch (arr.flags & GAMEARRAY_TYPE_MASK) { case 0: arr.pValues[arrayIndex] = newValue; break; case GAMEARRAY_INT16: ((int16_t *)arr.pValues)[arrayIndex] = newValue; break; case GAMEARRAY_INT8: ((int8_t *)arr.pValues)[arrayIndex] = newValue; break; case GAMEARRAY_UINT16: ((uint16_t *)arr.pValues)[arrayIndex] = newValue; break; case GAMEARRAY_UINT8: ((int8_t *)arr.pValues)[arrayIndex] = newValue; break; case GAMEARRAY_BITMAP: { uint32_t const mask = pow2char[arrayIndex&7]; uint8_t &value = ((uint8_t *)arr.pValues)[arrayIndex>>3]; value = (value & ~mask) | (-!!newValue & mask); break; } } } static void ResizeArray(int const arrayNum, int const newSize) { auto &arr = aGameArrays[arrayNum]; int const oldSize = arr.size; if (newSize == oldSize || newSize < 0) return; #if 0 Printf(OSDTEXT_GREEN "CON_RESIZEARRAY: resizing array %s from %d to %d\n", array.szLabel, array.size, newSize); #endif if (newSize == 0) { Xaligned_free(arr.pValues); arr.pValues = nullptr; arr.size = 0; return; } size_t const oldBytes = Gv_GetArrayAllocSizeForCount(arrayNum, oldSize); size_t const newBytes = Gv_GetArrayAllocSizeForCount(arrayNum, newSize); auto const oldArray = arr.pValues; auto const newArray = (intptr_t *)Xaligned_alloc(ARRAY_ALIGNMENT, newBytes); if (oldSize != 0) Bmemcpy(newArray, oldArray, min(oldBytes, newBytes)); if (newSize > oldSize) Bmemset((char *)newArray + oldBytes, 0, newBytes - oldBytes); arr.pValues = newArray; arr.size = newSize; Xaligned_free(oldArray); } #if !defined LUNATIC #if defined __GNUC__ || defined __clang__ // # define CON_USE_COMPUTED_GOTO does not work anymore with some of the changes. #endif #ifdef CON_USE_COMPUTED_GOTO # define vInstruction(KEYWORDID) VINST_ ## KEYWORDID # define vmErrorCase VINST_CON_OPCODE_END # define eval(INSTRUCTION) { goto *jumpTable[min(INSTRUCTION, CON_OPCODE_END)]; } # define dispatch_unconditionally(...) { g_tw = tw = *insptr; eval((VM_DECODE_INST(tw))) } # define dispatch(...) { if (!vm_execution_depth || vm.flags & (VM_RETURN|VM_KILL|VM_NOEXECUTE)) return; dispatch_unconditionally(__VA_ARGS__); } # define abort_after_error(...) return # define vInstructionPointer(KEYWORDID) &&VINST_ ## KEYWORDID # define COMMA , # define JUMP_TABLE_ARRAY_LITERAL { TRANSFORM_SCRIPT_KEYWORDS_LIST(vInstructionPointer, COMMA) } #else # define vInstruction(KEYWORDID) case KEYWORDID # define vmErrorCase default # define dispatch_unconditionally(...) continue # define dispatch(...) continue # define eval(INSTRUCTION) switch(INSTRUCTION) # define abort_after_error(...) continue // non-threaded dispatch handles this in the loop condition in VM_Execute() #endif #if defined _MSC_VER #define VM_ASSERT(condition, fmt, ...) \ do \ { \ if (EDUKE32_PREDICT_FALSE(!(condition))) \ { \ CON_ERRPRINTF(fmt, __VA_ARGS__); \ abort_after_error(); \ } \ } while (0) #else #define VM_ASSERT(condition, ...) \ do \ { \ if (EDUKE32_PREDICT_FALSE(!(condition))) \ { \ CON_ERRPRINTF(__VA_ARGS__); \ abort_after_error(); \ } \ } while (0) #endif GAMEEXEC_STATIC void VM_Execute(int const loop /*= false*/) { // be careful when changing this--the assignment used as a condition doubles as the nullptr check! auto branch = [&](int const x) { if (x || ((insptr = (intptr_t *)insptr[1]) && (VM_DECODE_INST(*insptr) == CON_ELSE))) { insptr += 2; VM_Execute(); } }; int vm_execution_depth = loop; #ifdef CON_USE_COMPUTED_GOTO static void *const jumpTable[] = JUMP_TABLE_ARRAY_LITERAL; #else do { #endif int32_t tw = *insptr; g_tw = tw; eval(VM_DECODE_INST(tw)) { vInstruction(CON_LEFTBRACE): { insptr++, vm_execution_depth++; dispatch_unconditionally(); } vInstruction(CON_RIGHTBRACE): { insptr++, vm_execution_depth--; dispatch(); } vInstruction(CON_ELSE): { insptr = (intptr_t *)insptr[1]; dispatch_unconditionally(); } vInstruction(CON_STATE): { auto tempscrptr = &insptr[2]; insptr = (intptr_t *)insptr[1]; VM_Execute(true); insptr = tempscrptr; } dispatch(); #ifdef CON_DISCRETE_VAR_ACCESS vInstruction(CON_IFVARE_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw == *insptr); dispatch(); vInstruction(CON_IFVARN_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw != *insptr); dispatch(); vInstruction(CON_IFVARAND_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw & *insptr); dispatch(); vInstruction(CON_IFVAROR_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw | *insptr); dispatch(); vInstruction(CON_IFVARXOR_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw ^ *insptr); dispatch(); vInstruction(CON_IFVAREITHER_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw || *insptr); dispatch(); vInstruction(CON_IFVARBOTH_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw && *insptr); dispatch(); vInstruction(CON_IFVARG_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw > *insptr); dispatch(); vInstruction(CON_IFVARGE_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw >= *insptr); dispatch(); vInstruction(CON_IFVARL_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw < *insptr); dispatch(); vInstruction(CON_IFVARLE_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch(tw <= *insptr); dispatch(); vInstruction(CON_IFVARA_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch((uint32_t)tw > (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARAE_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch((uint32_t)tw >= (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARB_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch((uint32_t)tw < (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARBE_GLOBAL): insptr++; tw = aGameVars[*insptr++].global; branch((uint32_t)tw <= (uint32_t)*insptr); dispatch(); vInstruction(CON_SETVAR_GLOBAL): insptr++; aGameVars[*insptr].global = insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ADDVAR_GLOBAL): insptr++; aGameVars[*insptr].global += insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SUBVAR_GLOBAL): insptr++; aGameVars[*insptr].global -= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_MULVAR_GLOBAL): insptr++; aGameVars[*insptr].global *= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ANDVAR_GLOBAL): insptr++; aGameVars[*insptr].global &= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_XORVAR_GLOBAL): insptr++; aGameVars[*insptr].global ^= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ORVAR_GLOBAL): insptr++; aGameVars[*insptr].global |= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SHIFTVARL_GLOBAL): insptr++; aGameVars[*insptr].global <<= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SHIFTVARR_GLOBAL): insptr++; aGameVars[*insptr].global >>= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_IFVARE_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw == *insptr); dispatch(); vInstruction(CON_IFVARN_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw != *insptr); dispatch(); vInstruction(CON_IFVARAND_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw & *insptr); dispatch(); vInstruction(CON_IFVAROR_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw | *insptr); dispatch(); vInstruction(CON_IFVARXOR_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw ^ *insptr); dispatch(); vInstruction(CON_IFVAREITHER_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw || *insptr); dispatch(); vInstruction(CON_IFVARBOTH_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw && *insptr); dispatch(); vInstruction(CON_IFVARG_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw > *insptr); dispatch(); vInstruction(CON_IFVARGE_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw >= *insptr); dispatch(); vInstruction(CON_IFVARL_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw < *insptr); dispatch(); vInstruction(CON_IFVARLE_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch(tw <= *insptr); dispatch(); vInstruction(CON_IFVARA_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch((uint32_t)tw > (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARAE_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch((uint32_t)tw >= (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARB_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch((uint32_t)tw < (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARBE_ACTOR): insptr++; tw = aGameVars[*insptr++].pValues[vm.spriteNum & (MAXSPRITES-1)]; branch((uint32_t)tw <= (uint32_t)*insptr); dispatch(); vInstruction(CON_SETVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] = insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ADDVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] += insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SUBVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] -= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_MULVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] *= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ANDVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] &= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_XORVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] ^= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ORVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] |= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SHIFTVARL_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] <<= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SHIFTVARR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] >>= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_IFVARE_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw == *insptr); dispatch(); vInstruction(CON_IFVARN_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw != *insptr); dispatch(); vInstruction(CON_IFVARAND_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw & *insptr); dispatch(); vInstruction(CON_IFVAROR_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw | *insptr); dispatch(); vInstruction(CON_IFVARXOR_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw ^ *insptr); dispatch(); vInstruction(CON_IFVAREITHER_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw || *insptr); dispatch(); vInstruction(CON_IFVARBOTH_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw && *insptr); dispatch(); vInstruction(CON_IFVARG_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw > *insptr); dispatch(); vInstruction(CON_IFVARGE_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw >= *insptr); dispatch(); vInstruction(CON_IFVARL_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw < *insptr); dispatch(); vInstruction(CON_IFVARLE_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch(tw <= *insptr); dispatch(); vInstruction(CON_IFVARA_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch((uint32_t)tw > (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARAE_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch((uint32_t)tw >= (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARB_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch((uint32_t)tw < (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARBE_PLAYER): insptr++; tw = aGameVars[*insptr++].pValues[vm.playerNum & (MAXPLAYERS-1)]; branch((uint32_t)tw <= (uint32_t)*insptr); dispatch(); vInstruction(CON_SETVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] = insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ADDVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] += insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SUBVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] -= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_MULVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] *= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ANDVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] &= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_XORVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] ^= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_ORVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] |= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SHIFTVARL_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] <<= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_SHIFTVARR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] >>= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_WHILEVARN_GLOBAL): { auto const savedinsptr = &insptr[2]; do { insptr = savedinsptr; tw = (aGameVars[insptr[-1]].global != *insptr); branch(tw); } while (tw); dispatch(); } vInstruction(CON_WHILEVARL_GLOBAL): { auto const savedinsptr = &insptr[2]; do { insptr = savedinsptr; tw = (aGameVars[insptr[-1]].global < *insptr); branch(tw); } while (tw); dispatch(); } vInstruction(CON_WHILEVARN_ACTOR): { auto const savedinsptr = &insptr[2]; auto &v = aGameVars[savedinsptr[-1]].pValues[vm.spriteNum & (MAXSPRITES-1)]; do { insptr = savedinsptr; tw = (v != *insptr); branch(tw); } while (tw); dispatch(); } vInstruction(CON_WHILEVARL_ACTOR): { auto const savedinsptr = &insptr[2]; auto &v = aGameVars[savedinsptr[-1]].pValues[vm.spriteNum & (MAXSPRITES-1)]; do { insptr = savedinsptr; tw = (v < *insptr); branch(tw); } while (tw); dispatch(); } vInstruction(CON_WHILEVARN_PLAYER): { auto const savedinsptr = &insptr[2]; auto &v = aGameVars[savedinsptr[-1]].pValues[vm.playerNum & (MAXPLAYERS-1)]; do { insptr = savedinsptr; tw = (v != *insptr); branch(tw); } while (tw); dispatch(); } vInstruction(CON_WHILEVARL_PLAYER): { auto const savedinsptr = &insptr[2]; auto &v = aGameVars[savedinsptr[-1]].pValues[vm.playerNum & (MAXPLAYERS-1)]; do { insptr = savedinsptr; tw = (v < *insptr); branch(tw); } while (tw); dispatch(); } vInstruction(CON_MODVAR_GLOBAL): insptr++; aGameVars[*insptr].global %= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_MODVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] %= insptr[1]; insptr += 2; dispatch(); vInstruction(CON_MODVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] %= insptr[1]; insptr += 2; dispatch(); #endif vInstruction(CON_IFVARAND): insptr++; tw = Gv_GetVar(*insptr++); branch(tw & *insptr); dispatch(); vInstruction(CON_IFVAROR): insptr++; tw = Gv_GetVar(*insptr++); branch(tw | *insptr); dispatch(); vInstruction(CON_IFVARXOR): insptr++; tw = Gv_GetVar(*insptr++); branch(tw ^ *insptr); dispatch(); vInstruction(CON_IFVAREITHER): insptr++; tw = Gv_GetVar(*insptr++); branch(tw || *insptr); dispatch(); vInstruction(CON_IFVARBOTH): insptr++; tw = Gv_GetVar(*insptr++); branch(tw && *insptr); dispatch(); vInstruction(CON_IFRND): branch(rnd(*(++insptr))); dispatch(); vInstruction(CON_IFVARG): insptr++; tw = Gv_GetVar(*insptr++); branch(tw > *insptr); dispatch(); vInstruction(CON_IFVARGE): insptr++; tw = Gv_GetVar(*insptr++); branch(tw >= *insptr); dispatch(); vInstruction(CON_IFVARL): insptr++; tw = Gv_GetVar(*insptr++); branch(tw < *insptr); dispatch(); vInstruction(CON_IFVARLE): insptr++; tw = Gv_GetVar(*insptr++); branch(tw <= *insptr); dispatch(); vInstruction(CON_IFVARA): insptr++; tw = Gv_GetVar(*insptr++); branch((uint32_t)tw > (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARAE): insptr++; tw = Gv_GetVar(*insptr++); branch((uint32_t)tw >= (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARB): insptr++; tw = Gv_GetVar(*insptr++); branch((uint32_t)tw < (uint32_t)*insptr); dispatch(); vInstruction(CON_IFVARBE): insptr++; tw = Gv_GetVar(*insptr++); branch((uint32_t)tw <= (uint32_t)*insptr); dispatch(); vInstruction(CON_SETVARVAR): insptr++; { tw = *insptr++; int const nValue = Gv_GetVar(*insptr++); if ((aGameVars[tw].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) == 0) aGameVars[tw].global = nValue; else Gv_SetVar(tw, nValue); } dispatch(); vInstruction(CON_ADDVARVAR): insptr++; tw = *insptr++; Gv_AddVar(tw, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_SUBVARVAR): insptr++; tw = *insptr++; Gv_SubVar(tw, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_ANDVARVAR): insptr++; tw = *insptr++; Gv_AndVar(tw, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_XORVARVAR): insptr++; tw = *insptr++; Gv_XorVar(tw, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_ORVARVAR): insptr++; tw = *insptr++; Gv_OrVar(tw, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_SHIFTVARVARL): insptr++; tw = *insptr++; Gv_ShiftVarL(tw, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_SHIFTVARVARR): insptr++; tw = *insptr++; Gv_ShiftVarR(tw, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_MULVARVAR): insptr++; tw = *insptr++; Gv_MulVar(tw, Gv_GetVar(*insptr++)); dispatch(); #ifdef CON_DISCRETE_VAR_ACCESS vInstruction(CON_DIVVAR_GLOBAL): insptr++; aGameVars[*insptr].global = tabledivide32(aGameVars[*insptr].global, insptr[1]); insptr += 2; dispatch(); vInstruction(CON_DIVVAR_PLAYER): { insptr++; auto &v = aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS - 1)]; v = tabledivide32(v, insptr[1]); insptr += 2; dispatch(); } vInstruction(CON_DIVVAR_ACTOR): { insptr++; auto &v = aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES - 1)]; v = tabledivide32(v, insptr[1]); insptr += 2; dispatch(); } #endif vInstruction(CON_DIVVARVAR): insptr++; { tw = *insptr++; int const nValue = Gv_GetVar(*insptr++); VM_ASSERT(nValue, "divide by zero!\n"); Gv_DivVar(tw, nValue); dispatch(); } vInstruction(CON_IFVARE): insptr++; tw = Gv_GetVar(*insptr++); branch(tw == *insptr); dispatch(); vInstruction(CON_IFVARN): insptr++; tw = Gv_GetVar(*insptr++); branch(tw != *insptr); dispatch(); vInstruction(CON_IFVARVARE): insptr++; tw = Gv_GetVar(*insptr++); tw = (tw == Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARN): insptr++; tw = Gv_GetVar(*insptr++); tw = (tw != Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARG): insptr++; tw = Gv_GetVar(*insptr++); tw = (tw > Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARGE): insptr++; tw = Gv_GetVar(*insptr++); tw = (tw >= Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARL): insptr++; tw = Gv_GetVar(*insptr++); tw = (tw < Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARLE): insptr++; tw = Gv_GetVar(*insptr++); tw = (tw <= Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARA): insptr++; tw = Gv_GetVar(*insptr++); tw = ((uint32_t)tw > (uint32_t)Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARAE): insptr++; tw = Gv_GetVar(*insptr++); tw = ((uint32_t)tw >= (uint32_t)Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARB): insptr++; tw = Gv_GetVar(*insptr++); tw = ((uint32_t)tw < (uint32_t)Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARBE): insptr++; tw = Gv_GetVar(*insptr++); tw = ((uint32_t)tw <= (uint32_t)Gv_GetVar(*insptr++)); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARAND): insptr++; tw = Gv_GetVar(*insptr++); tw &= Gv_GetVar(*insptr++); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVAROR): insptr++; tw = Gv_GetVar(*insptr++); tw |= Gv_GetVar(*insptr++); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARXOR): insptr++; tw = Gv_GetVar(*insptr++); tw ^= Gv_GetVar(*insptr++); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVAREITHER): insptr++; tw = Gv_GetVar(*insptr++); tw = (Gv_GetVar(*insptr++) || tw); insptr--; branch(tw); dispatch(); vInstruction(CON_IFVARVARBOTH): insptr++; tw = Gv_GetVar(*insptr++); tw = (Gv_GetVar(*insptr++) && tw); insptr--; branch(tw); dispatch(); vInstruction(CON_WHILEVARN): { auto const savedinsptr = &insptr[2]; do { insptr = savedinsptr; branch((tw = (Gv_GetVar(insptr[-1]) != *insptr))); } while (tw); dispatch(); } vInstruction(CON_WHILEVARVARN): { auto const savedinsptr = &insptr[2]; do { insptr = savedinsptr; tw = Gv_GetVar(insptr[-1]); tw = (tw != Gv_GetVar(*insptr++)); insptr--; branch(tw); } while (tw); dispatch(); } vInstruction(CON_WHILEVARL): { auto const savedinsptr = &insptr[2]; do { insptr = savedinsptr; branch((tw = (Gv_GetVar(insptr[-1]) < *insptr))); } while (tw); dispatch(); } vInstruction(CON_WHILEVARVARL): { auto const savedinsptr = &insptr[2]; do { insptr = savedinsptr; tw = Gv_GetVar(insptr[-1]); tw = (tw < Gv_GetVar(*insptr++)); insptr--; branch(tw); } while (tw); dispatch(); } vInstruction(CON_SETVAR): Gv_SetVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_ADDVAR): Gv_AddVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_SUBVAR): Gv_SubVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_MULVAR): Gv_MulVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_DIVVAR): Gv_DivVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_ANDVAR): Gv_AndVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_XORVAR): Gv_XorVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_ORVAR): Gv_OrVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_SHIFTVARL): Gv_ShiftVarL(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_SHIFTVARR): Gv_ShiftVarR(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_MODVAR): Gv_ModVar(insptr[1], insptr[2]); insptr += 3; dispatch(); vInstruction(CON_MODVARVAR): insptr++; { tw = *insptr++; int const nValue = Gv_GetVar(*insptr++); VM_ASSERT(nValue, "mod by zero!\n"); Gv_ModVar(tw, nValue); dispatch(); } vInstruction(CON_RANDVAR): insptr++; Gv_SetVar(*insptr, mulscale16(krand(), insptr[1] + 1)); insptr += 2; dispatch(); #ifdef CON_DISCRETE_VAR_ACCESS vInstruction(CON_RANDVAR_GLOBAL): insptr++; aGameVars[*insptr].global = mulscale16(krand(), insptr[1] + 1); insptr += 2; dispatch(); vInstruction(CON_RANDVAR_PLAYER): insptr++; aGameVars[*insptr].pValues[vm.playerNum & (MAXPLAYERS-1)] = mulscale16(krand(), insptr[1] + 1); insptr += 2; dispatch(); vInstruction(CON_RANDVAR_ACTOR): insptr++; aGameVars[*insptr].pValues[vm.spriteNum & (MAXSPRITES-1)] = mulscale16(krand(), insptr[1] + 1); insptr += 2; dispatch(); #endif vInstruction(CON_RANDVARVAR): insptr++; tw = *insptr++; Gv_SetVar(tw, mulscale16(krand(), Gv_GetVar(*insptr++) + 1)); dispatch(); vInstruction(CON_SETPLAYER): insptr++; { int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum; int const labelNum = *insptr++; int const lParm2 = (PlayerLabels[labelNum].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0; VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum); VM_SetPlayer(playerNum, labelNum, lParm2, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETPLAYER): insptr++; { int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum; int const labelNum = *insptr++; int const lParm2 = (PlayerLabels[labelNum].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0; VM_ASSERT((unsigned)playerNum < MAXPLAYERS, "invalid player %d\n", playerNum); Gv_SetVar(*insptr++, VM_GetPlayer(playerNum, labelNum, lParm2)); dispatch(); } vInstruction(CON_SETWALL): insptr++; { tw = *insptr++; int const wallNum = Gv_GetVar(tw); int const labelNum = *insptr++; int const newValue = Gv_GetVar(*insptr++); auto const &wallLabel = WallLabels[labelNum]; VM_ASSERT((unsigned)wallNum < MAXWALLS, "invalid wall %d\n", wallNum); if (wallLabel.offset == -1 || wallLabel.flags & LABEL_WRITEFUNC) { VM_SetWall(wallNum, labelNum, newValue); dispatch(); } VM_SetStruct(wallLabel.flags, (intptr_t *)((char *)&wall[wallNum] + wallLabel.offset), newValue); dispatch(); } vInstruction(CON_GETWALL): insptr++; { tw = *insptr++; int const wallNum = Gv_GetVar(tw); int const labelNum = *insptr++; auto const &wallLabel = WallLabels[labelNum]; VM_ASSERT((unsigned)wallNum < MAXWALLS, "invalid wall %d\n", wallNum); Gv_SetVar(*insptr++, (wallLabel.offset != -1 && (wallLabel.flags & LABEL_READFUNC) != LABEL_READFUNC) ? VM_GetStruct(wallLabel.flags, (intptr_t *)((char *)&wall[wallNum] + wallLabel.offset)) : VM_GetWall(wallNum, labelNum)); dispatch(); } vInstruction(CON_SETACTORVAR): vInstruction(CON_GETACTORVAR): insptr++; { int const lSprite = Gv_GetVar(*insptr++); int const lVar1 = *insptr++; int const lVar2 = *insptr++; VM_ASSERT((unsigned)lSprite < MAXSPRITES, "invalid sprite %d\n", lSprite); if (VM_DECODE_INST(tw) == CON_SETACTORVAR) Gv_SetVar(lVar1, Gv_GetVar(lVar2), lSprite, vm.playerNum); else Gv_SetVar(lVar2, Gv_GetVar(lVar1, lSprite, vm.playerNum)); dispatch(); } vInstruction(CON_SETPLAYERVAR): vInstruction(CON_GETPLAYERVAR): insptr++; { int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum; int const lVar1 = *insptr++; int const lVar2 = *insptr++; VM_ASSERT((unsigned)playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", playerNum); if (VM_DECODE_INST(tw) == CON_SETPLAYERVAR) Gv_SetVar(lVar1, Gv_GetVar(lVar2), vm.spriteNum, playerNum); else Gv_SetVar(lVar2, Gv_GetVar(lVar1, vm.spriteNum, playerNum)); dispatch(); } vInstruction(CON_SETACTOR): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; int const lParm2 = (ActorLabels[labelNum].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0; auto const &actorLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES && ((actorLabel.flags & LABEL_HASPARM2) == 0 || (unsigned)lParm2 < (unsigned)actorLabel.maxParm2), "%s[%d] invalid for sprite %d\n", actorLabel.name, lParm2, spriteNum); VM_SetSprite(spriteNum, labelNum, lParm2, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETACTOR): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; int const lParm2 = (ActorLabels[labelNum].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0; auto const &actorLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES && ((actorLabel.flags & LABEL_HASPARM2) == 0 || (unsigned)lParm2 < (unsigned)actorLabel.maxParm2), "%s[%d] invalid for sprite %d\n", actorLabel.name, lParm2, spriteNum); Gv_SetVar(*insptr++, VM_GetSprite(spriteNum, labelNum, lParm2)); dispatch(); } vInstruction(CON_SETACTORSTRUCT): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &actorLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); VM_SetStruct(actorLabel.flags, (intptr_t *)((char *)&actor[spriteNum] + actorLabel.offset), Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETACTORSTRUCT): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &actorLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(*insptr++, VM_GetStruct(actorLabel.flags, (intptr_t *)((char *)&actor[spriteNum] + actorLabel.offset))); dispatch(); } vInstruction(CON_SETSPRITESTRUCT): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &spriteLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); VM_SetStruct(spriteLabel.flags, (intptr_t *)((char *)&sprite[spriteNum] + spriteLabel.offset), Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETSPRITESTRUCT): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &spriteLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(*insptr++, VM_GetStruct(spriteLabel.flags, (intptr_t *)((char *)&sprite[spriteNum] + spriteLabel.offset))); dispatch(); } vInstruction(CON_SETSPRITEEXT): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &spriteExtLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); VM_SetStruct(spriteExtLabel.flags, (intptr_t *)((char *)&spriteext[spriteNum] + spriteExtLabel.offset), Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETSPRITEEXT): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &spriteExtLabel = ActorLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(*insptr++, VM_GetStruct(spriteExtLabel.flags, (intptr_t *)((char *)&spriteext[spriteNum] + spriteExtLabel.offset))); dispatch(); } vInstruction(CON_SETTSPR): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &tsprLabel = TsprLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); VM_SetStruct(tsprLabel.flags, (intptr_t *)((char *)spriteext[spriteNum].tspr + tsprLabel.offset), Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETTSPR): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const labelNum = *insptr++; auto const &tsprLabel = TsprLabels[labelNum]; VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(*insptr++, VM_GetStruct(tsprLabel.flags, (intptr_t *)((char *)spriteext[spriteNum].tspr + tsprLabel.offset))); dispatch(); } vInstruction(CON_SETSECTOR): insptr++; { int const sectNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->sectnum; int const labelNum = *insptr++; auto const §Label = SectorLabels[labelNum]; int const newValue = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum); if (sectLabel.offset == -1 || sectLabel.flags & LABEL_WRITEFUNC) { VM_SetSector(sectNum, labelNum, newValue); dispatch(); } VM_SetStruct(sectLabel.flags, (intptr_t *)((char *)§or[sectNum] + sectLabel.offset), newValue); dispatch(); } vInstruction(CON_GETSECTOR): insptr++; { int const sectNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->sectnum; int const labelNum = *insptr++; auto const §Label = SectorLabels[labelNum]; VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum); Gv_SetVar(*insptr++, (sectLabel.offset != -1 && (sectLabel.flags & LABEL_READFUNC) != LABEL_READFUNC) ? VM_GetStruct(sectLabel.flags, (intptr_t *)((char *)§or[sectNum] + sectLabel.offset)) : VM_GetSector(sectNum, labelNum)); dispatch(); } vInstruction(CON_RETURN): vm.flags |= VM_RETURN; #if !defined CON_USE_COMPUTED_GOTO fallthrough__; #endif vInstruction(CON_ENDSWITCH): vInstruction(CON_ENDA): vInstruction(CON_BREAK): vInstruction(CON_ENDS): vInstruction(CON_ENDEVENT): return; vInstruction(CON_JUMP): // this is used for event chaining insptr++; tw = Gv_GetVar(*insptr++); insptr = (intptr_t *)(tw + apScript); dispatch(); vInstruction(CON_SWITCH): insptr++; { // command format: // variable ID to check // script offset to 'end' // count of case statements // script offset to default case (null if none) // For each case: value, ptr to code int const lValue = Gv_GetVar(*insptr++); int const endOffset = *insptr++; int const numCases = *insptr++; auto lpDefault = insptr++; auto lpCases = insptr; int left = 0; int right = numCases - 1; insptr += numCases << 1; do { int const lCheckCase = (left + right) >> 1; if (lpCases[lCheckCase << 1] > lValue) right = lCheckCase - 1; else if (lpCases[lCheckCase << 1] < lValue) left = lCheckCase + 1; else if (lpCases[lCheckCase << 1] == lValue) { // fake a 2-d Array insptr = (intptr_t *)(lpCases[(lCheckCase << 1) + 1] + &apScript[0]); VM_Execute(true); goto matched; } if (right - left < 0) break; } while (1); if (*lpDefault) { insptr = (intptr_t *)(*lpDefault + &apScript[0]); VM_Execute(true); } matched: insptr = (intptr_t *)(endOffset + (intptr_t)&apScript[0]); dispatch(); } vInstruction(CON_FOR): // special-purpose iteration insptr++; { int const returnVar = *insptr++; int const iterType = *insptr++; int const nIndex = iterType <= ITER_DRAWNSPRITES ? 0 : Gv_GetVar(*insptr++); auto const pEnd = insptr + *insptr; auto const pNext = ++insptr; auto execute = [&](int index) { Gv_SetVar(returnVar, index); insptr = pNext; VM_Execute(); return !!(vm.flags & VM_RETURN); }; switch (iterType) { case ITER_ALLSPRITES: for (native_t jj = 0; jj < MAXSPRITES; ++jj) { if (sprite[jj].statnum == MAXSTATUS) continue; if (execute(jj)) return; } break; case ITER_ALLSPRITESBYSTAT: for (native_t statNum = 0; statNum < MAXSTATUS; ++statNum) { for (native_t kk, SPRITES_OF_STAT_SAFE(statNum, jj, kk)) if (execute(jj)) return; } break; case ITER_ALLSPRITESBYSECT: for (native_t sectNum = 0; sectNum < numsectors; ++sectNum) { for (native_t kk, SPRITES_OF_SECT_SAFE(sectNum, jj, kk)) if (execute(jj)) return; } break; case ITER_ALLSECTORS: for (native_t jj = 0; jj < numsectors; ++jj) if (execute(jj)) return; break; case ITER_ALLWALLS: for (native_t jj = 0; jj < numwalls; ++jj) if (execute(jj)) return; break; case ITER_ACTIVELIGHTS: #ifdef POLYMER for (native_t jj = 0; jj < PR_MAXLIGHTS; ++jj) { if (!prlights[jj].flags.active) continue; if (execute(jj)) return; } #endif break; case ITER_DRAWNSPRITES: for (native_t jj = 0; jj < spritesortcnt; jj++) if (execute(jj)) return; break; case ITER_SPRITESOFSECTOR: if ((unsigned)nIndex >= MAXSECTORS) goto badindex; for (native_t kk, SPRITES_OF_SECT_SAFE(nIndex, jj, kk)) if (execute(jj)) return; break; case ITER_SPRITESOFSTATUS: if ((unsigned)nIndex >= MAXSTATUS) goto badindex; for (native_t kk, SPRITES_OF_STAT_SAFE(nIndex, jj, kk)) if (execute(jj)) return; break; case ITER_WALLSOFSECTOR: if ((unsigned)nIndex >= MAXSECTORS) goto badindex; for (native_t jj = sector[nIndex].wallptr, endwall = jj + sector[nIndex].wallnum - 1; jj <= endwall; jj++) if (execute(jj)) return; break; case ITER_LOOPOFWALL: if ((unsigned)nIndex >= (unsigned)numwalls) goto badindex; { int jj = nIndex; do { if (execute(jj)) return; jj = wall[jj].point2; } while (jj != nIndex); } break; case ITER_RANGE: for (native_t jj = 0; jj < nIndex; jj++) if (execute(jj)) return; break; badindex: Printf(OSD_ERROR "Line %d, for %s: index %d out of range!\n", VM_DECODE_LINE_NUMBER(g_tw), iter_tokens[iterType].token, nIndex); vm.flags |= VM_RETURN; dispatch(); } insptr = pEnd; } dispatch(); vInstruction(CON_REDEFINEQUOTE): insptr++; { int const strIndex = *insptr++; int const XstrIndex = *insptr++; quoteMgr.CopyExQuote(strIndex, XstrIndex); dispatch(); } vInstruction(CON_GETTHISPROJECTILE): insptr++; { tw = *insptr++; int const spriteNum = (tw != g_thisActorVarID) ? Gv_GetVar(tw) : vm.spriteNum; int const labelNum = *insptr++; Gv_SetVar(*insptr++, VM_GetActiveProjectile(spriteNum, labelNum)); dispatch(); } vInstruction(CON_SETTHISPROJECTILE): insptr++; { tw = *insptr++; int const spriteNum = (tw != g_thisActorVarID) ? Gv_GetVar(tw) : vm.spriteNum; int const labelNum = *insptr++; VM_SetActiveProjectile(spriteNum, labelNum, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_IFCANSHOOTTARGET): { #define CHECK_PICNUM(x) \ if ((unsigned)x < MAXSPRITES && sprite[x].picnum == vm.pSprite->picnum) \ { \ branch(false); \ dispatch(); \ } if (vm.playerDist > 1024) { int16_t temphit; auto checkHitSprite = [&](int x) { vm.pSprite->ang += x; tw = A_CheckHitSprite(vm.spriteNum, &temphit); vm.pSprite->ang -= x; }; if ((tw = A_CheckHitSprite(vm.spriteNum, &temphit)) == (1 << 30)) { branch(true); dispatch(); } int dist = 768; int angDiff = 16; if (A_CheckEnemySprite(vm.pSprite) && vm.pSprite->xrepeat > 56) { dist = 3084; angDiff = 48; } if (tw > dist) { CHECK_PICNUM(temphit); checkHitSprite(angDiff); if (tw > dist) { CHECK_PICNUM(temphit); checkHitSprite(-angDiff); if (tw > 768) { CHECK_PICNUM(temphit); branch(true); dispatch(); } } } branch(false); dispatch(); } branch(true); #undef CHECK_PICNUM } dispatch(); vInstruction(CON_IFCANSEETARGET): 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, vm.pPlayer->pos.z /*-((krand()&41)<<8)*/, sprite[vm.pPlayer->i].sectnum); branch(tw); if (tw) vm.pActor->timetosleep = SLEEPTIME; dispatch(); vInstruction(CON_IFACTION): branch(AC_ACTION_ID(vm.pData) == *(++insptr)); dispatch(); vInstruction(CON_IFACTIONCOUNT): branch(AC_ACTION_COUNT(vm.pData) >= *(++insptr)); dispatch(); vInstruction(CON_IFACTOR): branch(vm.pSprite->picnum == *(++insptr)); dispatch(); vInstruction(CON_IFACTORNOTSTAYPUT): branch(vm.pActor->stayput == -1); dispatch(); vInstruction(CON_IFAI): branch(AC_AI_ID(vm.pData) == *(++insptr)); dispatch(); vInstruction(CON_IFBULLETNEAR): branch(A_Dodge(vm.pSprite) == 1); dispatch(); vInstruction(CON_IFCEILINGDISTL): branch((vm.pSprite->z - vm.pActor->ceilingz) <= (*(++insptr) << 8)); dispatch(); vInstruction(CON_IFCLIENT): branch(g_netClient != NULL); dispatch(); vInstruction(CON_IFCOUNT): branch(AC_COUNT(vm.pData) >= *(++insptr)); dispatch(); vInstruction(CON_IFDEAD): branch(vm.pSprite->extra <= 0); dispatch(); vInstruction(CON_IFFLOORDISTL): branch((vm.pActor->floorz - vm.pSprite->z) <= (*(++insptr) << 8)); dispatch(); vInstruction(CON_IFGAPZL): branch(((vm.pActor->floorz - vm.pActor->ceilingz) >> 8) < *(++insptr)); dispatch(); vInstruction(CON_IFHITSPACE): branch(TEST_SYNC_KEY(g_player[vm.playerNum].input->bits, SK_OPEN)); dispatch(); vInstruction(CON_IFHITWEAPON): branch(A_IncurDamage(vm.spriteNum) >= 0); dispatch(); vInstruction(CON_IFINSPACE): branch(G_CheckForSpaceCeiling(vm.pSprite->sectnum)); dispatch(); vInstruction(CON_IFINWATER): branch(sector[vm.pSprite->sectnum].lotag == ST_2_UNDERWATER); dispatch(); vInstruction(CON_IFONWATER): branch(sector[vm.pSprite->sectnum].lotag == ST_1_ABOVE_WATER && klabs(vm.pSprite->z - sector[vm.pSprite->sectnum].floorz) < ZOFFSET5); dispatch(); vInstruction(CON_IFMOVE): branch(AC_MOVE_ID(vm.pData) == *(++insptr)); dispatch(); vInstruction(CON_IFMULTIPLAYER): branch((g_netServer || g_netClient || ud.multimode > 1)); dispatch(); vInstruction(CON_IFOUTSIDE): branch(sector[vm.pSprite->sectnum].ceilingstat & 1); dispatch(); vInstruction(CON_IFPLAYBACKON): branch(false); dispatch(); vInstruction(CON_IFPLAYERSL): branch(numplayers < *(++insptr)); dispatch(); vInstruction(CON_IFSERVER): branch(g_netServer != NULL); dispatch(); vInstruction(CON_IFSQUISHED): branch(VM_CheckSquished()); dispatch(); vInstruction(CON_IFSTRENGTH): branch(vm.pSprite->extra <= *(++insptr)); dispatch(); vInstruction(CON_IFSPAWNEDBY): vInstruction(CON_IFWASWEAPON): branch(vm.pActor->picnum == *(++insptr)); dispatch(); vInstruction(CON_IFPDISTL): branch(vm.playerDist < *(++insptr)); if (vm.playerDist > MAXSLEEPDIST && vm.pActor->timetosleep == 0) vm.pActor->timetosleep = SLEEPTIME; dispatch(); vInstruction(CON_IFPDISTG): branch(vm.playerDist > *(++insptr)); if (vm.playerDist > MAXSLEEPDIST && vm.pActor->timetosleep == 0) vm.pActor->timetosleep = SLEEPTIME; dispatch(); vInstruction(CON_IFRESPAWN): if (A_CheckEnemySprite(vm.pSprite)) branch(ud.respawn_monsters); else if (A_CheckInventorySprite(vm.pSprite)) branch(ud.respawn_inventory); else branch(ud.respawn_items); dispatch(); vInstruction(CON_IFINOUTERSPACE): branch(G_CheckForSpaceFloor(vm.pSprite->sectnum)); dispatch(); vInstruction(CON_IFNOTMOVING): branch((vm.pActor->movflag & 49152) > 16384); dispatch(); vInstruction(CON_IFCANSEE): { auto pSprite = (uspriteptr_t)&sprite[vm.pPlayer->i]; // select sprite for monster to target // if holoduke is on, let them target holoduke first. // #ifndef EDUKE32_STANDALONE if (!FURY && vm.pPlayer->holoduke_on >= 0) { pSprite = (uspriteptr_t)&sprite[vm.pPlayer->holoduke_on]; tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - (krand() & (ZOFFSET5 - 1)), vm.pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum); if (tw == 0) { // they can't see player's holoduke // check for player... pSprite = (uspriteptr_t)&sprite[vm.pPlayer->i]; } } #endif // can they see player, (or player's holoduke) tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - (krand() & ((47 << 8))), vm.pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z - (24 << 8), pSprite->sectnum); if (tw == 0) { // search around for target player // also modifies 'target' x&y if found.. tw = (A_FurthestVisiblePoint(vm.spriteNum, pSprite, &vm.pActor->lastv) != -1); } else { // else, they did see it. // save where we were looking... vm.pActor->lastv.x = pSprite->x; vm.pActor->lastv.y = pSprite->y; } if (tw && (vm.pSprite->statnum == STAT_ACTOR || vm.pSprite->statnum == STAT_STANDABLE)) vm.pActor->timetosleep = SLEEPTIME; branch(tw); dispatch(); } vInstruction(CON_AI): insptr++; // Following changed to use pointersizes AC_AI_ID(vm.pData) = *insptr++; // Ai AC_ACTION_ID(vm.pData) = *(apScript + AC_AI_ID(vm.pData)); // Action // NOTE: "if" check added in r1155. It used to be a pointer though. if (AC_AI_ID(vm.pData)) AC_MOVE_ID(vm.pData) = *(apScript + AC_AI_ID(vm.pData) + 1); // move vm.pSprite->hitag = *(apScript + AC_AI_ID(vm.pData) + 2); // move flags AC_COUNT(vm.pData) = 0; AC_ACTION_COUNT(vm.pData) = 0; AC_CURFRAME(vm.pData) = 0; if (!A_CheckEnemySprite(vm.pSprite) || vm.pSprite->extra > 0) // hack if (vm.pSprite->hitag & random_angle) vm.pSprite->ang = krand() & 2047; dispatch(); vInstruction(CON_ACTION): insptr++; AC_ACTION_COUNT(vm.pData) = 0; AC_CURFRAME(vm.pData) = 0; AC_ACTION_ID(vm.pData) = *insptr++; dispatch(); vInstruction(CON_ADDSTRENGTH): insptr++; vm.pSprite->extra += *insptr++; dispatch(); vInstruction(CON_STRENGTH): insptr++; vm.pSprite->extra = *insptr++; dispatch(); vInstruction(CON_IFGOTWEAPONCE): insptr++; if ((g_gametypeFlags[ud.coop] & GAMETYPE_WEAPSTAY) && (g_netServer || ud.multimode > 1)) { if (*insptr == 0) { int j = 0; for (; j < vm.pPlayer->weapreccnt; ++j) if (vm.pPlayer->weaprecs[j] == vm.pSprite->picnum) break; branch(j < vm.pPlayer->weapreccnt && vm.pSprite->owner == vm.spriteNum); dispatch(); } else if (vm.pPlayer->weapreccnt < MAX_WEAPONS) { vm.pPlayer->weaprecs[vm.pPlayer->weapreccnt++] = vm.pSprite->picnum; branch(vm.pSprite->owner == vm.spriteNum); dispatch(); } } branch(false); dispatch(); vInstruction(CON_GETLASTPAL): insptr++; if (vm.pSprite->picnum == APLAYER) vm.pSprite->pal = g_player[P_GetP(vm.pSprite)].ps->palookup; else { if (vm.pSprite->pal == 1 && vm.pSprite->extra == 0) // hack for frozen vm.pSprite->extra++; vm.pSprite->pal = vm.pActor->tempang; } vm.pActor->tempang = 0; dispatch(); vInstruction(CON_TOSSWEAPON): insptr++; // NOTE: assumes that current actor is APLAYER P_DropWeapon(P_GetP(vm.pSprite)); dispatch(); vInstruction(CON_MIKESND): insptr++; VM_ASSERT((unsigned)vm.pSprite->yvel < MAXSOUNDS, "invalid sound %d\n", vm.pUSprite->yvel); if (!S_CheckSoundPlaying(vm.pSprite->yvel)) A_PlaySound(vm.pSprite->yvel, vm.spriteNum); dispatch(); vInstruction(CON_PKICK): insptr++; if ((g_netServer || ud.multimode > 1) && vm.pSprite->picnum == APLAYER) { if (g_player[otherp].ps->quick_kick == 0) g_player[otherp].ps->quick_kick = 14; } else if (vm.pSprite->picnum != APLAYER && vm.pPlayer->quick_kick == 0) vm.pPlayer->quick_kick = 14; dispatch(); vInstruction(CON_SIZETO): insptr++; tw = (*insptr++ - vm.pSprite->xrepeat) << 1; vm.pSprite->xrepeat += ksgn(tw); if ((vm.pSprite->picnum == APLAYER && vm.pSprite->yrepeat < 36) || *insptr < vm.pSprite->yrepeat || ((vm.pSprite->yrepeat * (tilesiz[vm.pSprite->picnum].y + 8)) << 2) < (vm.pActor->floorz - vm.pActor->ceilingz)) { tw = ((*insptr) - vm.pSprite->yrepeat) << 1; if (klabs(tw)) vm.pSprite->yrepeat += ksgn(tw); } insptr++; dispatch(); vInstruction(CON_SIZEAT): insptr++; vm.pSprite->xrepeat = (uint8_t)*insptr++; vm.pSprite->yrepeat = (uint8_t)*insptr++; dispatch(); vInstruction(CON_IFACTORSOUND): insptr++; { int const spriteNum = Gv_GetVar(*insptr++); int const soundNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum); insptr--; branch(A_CheckSoundPlaying(spriteNum, soundNum)); } dispatch(); vInstruction(CON_IFSOUND): insptr++; VM_ASSERT((unsigned)*insptr < MAXSOUNDS, "invalid sound %d\n", (int32_t)*insptr); branch(S_CheckSoundPlaying(*insptr)); // VM_DoConditional(SoundOwner[*insptr][0].ow == vm.spriteNum); dispatch(); vInstruction(CON_STOPACTORSOUND): insptr++; { int const spriteNum = Gv_GetVar(*insptr++); int const soundNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum); if (A_CheckSoundPlaying(spriteNum, soundNum)) S_StopEnvSound(soundNum, spriteNum); dispatch(); } vInstruction(CON_ACTORSOUND): insptr++; { int const spriteNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.spriteNum; int const soundNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum); A_PlaySound(soundNum, spriteNum); dispatch(); } vInstruction(CON_SETACTORSOUNDPITCH): insptr++; { int const spriteNum = Gv_GetVar(*insptr++); int const soundNum = Gv_GetVar(*insptr++); int const newPitch = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum); S_ChangeSoundPitch(soundNum, spriteNum, newPitch); dispatch(); } vInstruction(CON_TIP): insptr++; vm.pPlayer->tipincs = GAMETICSPERSEC; dispatch(); vInstruction(CON_FALL): insptr++; VM_Fall(vm.spriteNum, vm.pSprite); dispatch(); vInstruction(CON_NULLOP): insptr++; dispatch(); vInstruction(CON_ADDAMMO): insptr++; { int const weaponNum = *insptr++; int const addAmount = *insptr++; VM_AddAmmo(vm.pPlayer, weaponNum, addAmount); dispatch(); } vInstruction(CON_MONEY): insptr++; A_SpawnMultiple(vm.spriteNum, MONEY, *insptr++); dispatch(); vInstruction(CON_MAIL): insptr++; A_SpawnMultiple(vm.spriteNum, MAIL, *insptr++); dispatch(); vInstruction(CON_SLEEPTIME): insptr++; vm.pActor->timetosleep = (int16_t)*insptr++; dispatch(); vInstruction(CON_PAPER): insptr++; A_SpawnMultiple(vm.spriteNum, PAPER, *insptr++); dispatch(); vInstruction(CON_ADDKILLS): insptr++; P_AddKills(vm.pPlayer, *insptr++); vm.pActor->stayput = -1; dispatch(); vInstruction(CON_LOTSOFGLASS): insptr++; #ifndef EDUKE32_STANDALONE if (!FURY) A_SpawnGlass(vm.spriteNum, *insptr++); #else insptr++; #endif dispatch(); vInstruction(CON_SPAWNWALLGLASS): insptr++; { #ifndef EDUKE32_STANDALONE if (!FURY) { int const wallNum = Gv_GetVar(*insptr++); int const numShards = Gv_GetVar(*insptr++); A_SpawnWallGlass(vm.spriteNum, wallNum, numShards); } #else Gv_GetVar(*insptr++); Gv_GetVar(*insptr++); #endif } dispatch(); vInstruction(CON_SPAWNWALLSTAINEDGLASS): insptr++; { #ifndef EDUKE32_STANDALONE if (!FURY) { int const wallNum = Gv_GetVar(*insptr++); int const numShards = Gv_GetVar(*insptr++); A_SpawnRandomGlass(vm.spriteNum, wallNum, numShards); } #else Gv_GetVar(*insptr++); Gv_GetVar(*insptr++); #endif } dispatch(); vInstruction(CON_SPAWNCEILINGGLASS): insptr++; { #ifndef EDUKE32_STANDALONE if (!FURY) { int const sectNum = Gv_GetVar(*insptr++); int const numShards = Gv_GetVar(*insptr++); A_SpawnCeilingGlass(vm.spriteNum, sectNum, numShards); } #else Gv_GetVar(*insptr++); Gv_GetVar(*insptr++); #endif } dispatch(); vInstruction(CON_KILLIT): insptr++; vm.flags |= VM_KILL; return; vInstruction(CON_DEBUG): insptr++; buildprint(*insptr++, "\n"); dispatch(); vInstruction(CON_ENDOFGAME): vInstruction(CON_ENDOFLEVEL): insptr++; vm.pPlayer->timebeforeexit = *insptr++; vm.pPlayer->customexitsound = -1; ud.eog = 1; dispatch(); vInstruction(CON_ADDPHEALTH): insptr++; { if (vm.pPlayer->newowner >= 0) G_ClearCameraView(vm.pPlayer); int newHealth = sprite[vm.pPlayer->i].extra; #ifndef EDUKE32_STANDALONE if (!FURY && vm.pSprite->picnum == ATOMICHEALTH) { if (newHealth > 0) newHealth += *insptr; if (newHealth > (vm.pPlayer->max_player_health << 1)) newHealth = (vm.pPlayer->max_player_health << 1); } else #endif { if (newHealth > vm.pPlayer->max_player_health && *insptr > 0) { insptr++; dispatch(); } else { if (newHealth > 0) newHealth += *insptr; if (newHealth > vm.pPlayer->max_player_health && *insptr > 0) newHealth = vm.pPlayer->max_player_health; } } if (newHealth < 0) newHealth = 0; if (ud.god == 0) { if (*insptr > 0) { #ifndef EDUKE32_STANDALONE if (!FURY && (newHealth - *insptr) < (vm.pPlayer->max_player_health >> 2) && newHealth >= (vm.pPlayer->max_player_health >> 2)) A_PlaySound(DUKE_GOTHEALTHATLOW, vm.pPlayer->i); #endif vm.pPlayer->last_extra = newHealth; } sprite[vm.pPlayer->i].extra = newHealth; } } insptr++; dispatch(); vInstruction(CON_MOVE): insptr++; AC_COUNT(vm.pData) = 0; AC_MOVE_ID(vm.pData) = *insptr++; vm.pSprite->hitag = *insptr++; if (!A_CheckEnemySprite(vm.pSprite) || vm.pSprite->extra > 0) // hack if (vm.pSprite->hitag & random_angle) vm.pSprite->ang = krand() & 2047; dispatch(); vInstruction(CON_ADDWEAPON): insptr++; { int const weaponNum = Gv_GetVar(*insptr++); VM_AddWeapon(vm.pPlayer, weaponNum, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_SETASPECT): insptr++; { int const xRange = Gv_GetVar(*insptr++); renderSetAspect(xRange, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_SSP): insptr++; { int const spriteNum = Gv_GetVar(*insptr++); int const clipType = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); A_SetSprite(spriteNum, clipType); dispatch(); } vInstruction(CON_ACTIVATEBYSECTOR): insptr++; { int const sectNum = Gv_GetVar(*insptr++); int const spriteNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum); G_ActivateBySector(sectNum, spriteNum); dispatch(); } vInstruction(CON_OPERATESECTORS): insptr++; { int const sectNum = Gv_GetVar(*insptr++); int const spriteNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum); G_OperateSectors(sectNum, spriteNum); dispatch(); } vInstruction(CON_OPERATEACTIVATORS): insptr++; { int const nTag = Gv_GetVar(*insptr++); int const playerNum = (*insptr++ == g_thisActorVarID) ? vm.playerNum : Gv_GetVar(insptr[-1]); VM_ASSERT((unsigned)playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", playerNum); G_OperateActivators(nTag, playerNum); dispatch(); } vInstruction(CON_CANSEESPR): insptr++; { int const nSprite1 = Gv_GetVar(*insptr++); int const nSprite2 = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)nSprite1 < MAXSPRITES && (unsigned)nSprite2 < MAXSPRITES, "invalid sprite %d\n", (unsigned)nSprite1 >= MAXSPRITES ? nSprite1 : nSprite2); int const nResult = cansee(sprite[nSprite1].x, sprite[nSprite1].y, sprite[nSprite1].z, sprite[nSprite1].sectnum, sprite[nSprite2].x, sprite[nSprite2].y, sprite[nSprite2].z, sprite[nSprite2].sectnum); Gv_SetVar(*insptr++, nResult); dispatch(); } vInstruction(CON_OPERATERESPAWNS): insptr++; G_OperateRespawns(Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_OPERATEMASTERSWITCHES): insptr++; G_OperateMasterSwitches(Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_CHECKACTIVATORMOTION): insptr++; aGameVars[g_returnVarID].global = G_CheckActivatorMotion(Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_INSERTSPRITEQ): insptr++; A_AddToDeleteQueue(vm.spriteNum); dispatch(); vInstruction(CON_QSTRLEN): insptr++; { int const gameVar = *insptr++; int const quoteNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)quoteNum < MAXQUOTES, "invalid quote %d\n", quoteNum); Gv_SetVar(gameVar, strlen(quoteMgr.GetQuote(quoteNum))); dispatch(); } vInstruction(CON_QSTRDIM): insptr++; { int const widthVar = *insptr++; int const heightVar = *insptr++; struct { int32_t tileNum; vec3_t vect; int32_t blockAngle, quoteNum, orientation; vec2_t offset, between; int32_t f; vec2_t bound[2]; } v; Gv_FillWithVars(v); if (EDUKE32_PREDICT_FALSE(v.tileNum < 0 || v.tileNum + 127 >= MAXTILES)) CON_ERRPRINTF("invalid base tilenum %d\n", v.tileNum); else if ((unsigned)v.quoteNum >= MAXQUOTES) CON_ERRPRINTF("invalid quote %d\n", v.quoteNum); else { vec2_t dim = G_ScreenTextSize(v.tileNum, v.vect.x, v.vect.y, v.vect.z, v.blockAngle, quoteMgr.GetQuote(v.quoteNum), 2 | v.orientation, 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, v.bound[1].y); Gv_SetVar(widthVar, dim.x); Gv_SetVar(heightVar, dim.y); } dispatch(); } vInstruction(CON_HEADSPRITESTAT): insptr++; { int const gameVar = *insptr++; int const statNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)statNum < MAXSTATUS, "invalid status list %d\n", statNum); Gv_SetVar(gameVar, headspritestat[statNum]); dispatch(); } vInstruction(CON_PREVSPRITESTAT): insptr++; { int const gameVar = *insptr++; int const spriteNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(gameVar, prevspritestat[spriteNum]); dispatch(); } vInstruction(CON_NEXTSPRITESTAT): insptr++; { int const gameVar = *insptr++; int const spriteNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(gameVar, nextspritestat[spriteNum]); dispatch(); } vInstruction(CON_HEADSPRITESECT): insptr++; { int const gameVar = *insptr++; int const sectNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum); Gv_SetVar(gameVar, headspritesect[sectNum]); dispatch(); } vInstruction(CON_PREVSPRITESECT): insptr++; { int const gameVar = *insptr++; int const spriteNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(gameVar, prevspritesect[spriteNum]); dispatch(); } vInstruction(CON_NEXTSPRITESECT): insptr++; { int const gameVar = *insptr++; int const spriteNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)spriteNum < MAXSPRITES, "invalid sprite %d\n", spriteNum); Gv_SetVar(gameVar, nextspritesect[spriteNum]); dispatch(); } vInstruction(CON_GETKEYNAME): insptr++; { int const quoteIndex = Gv_GetVar(*insptr++); int const gameFunc = Gv_GetVar(*insptr++); int funcPos = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)quoteIndex < MAXQUOTES, "invalid quote %d\n", quoteIndex); VM_ASSERT((unsigned)gameFunc < NUM_ACTIONS, "invalid function %d\n", gameFunc); auto bindings = Bindings.GetKeysForCommand(C_CON_GetButtonFunc(gameFunc)); if ((unsigned)funcPos >= bindings.Size()) funcPos = 0; quoteMgr.InitializeQuote(quoteIndex, funcPos >= bindings.Size()? "???" : KB_ScanCodeToString(bindings[funcPos])); dispatch(); } vInstruction(CON_GETGAMEFUNCBIND): insptr++; { int const quoteIndex = Gv_GetVar(*insptr++); int const gameFunc = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)quoteIndex < MAXQUOTES, "invalid quote %d\n", quoteIndex); VM_ASSERT((unsigned)gameFunc < NUM_ACTIONS, "invalid function %d\n", gameFunc); auto binding = C_CON_GetBoundKeyForLastInput(gameFunc); if (binding.Len()) quoteMgr.FormatQuote(quoteIndex, "(%s)", binding.GetChars()); dispatch(); } vInstruction(CON_QSUBSTR): insptr++; { struct { int32_t outputQuote, inputQuote, quotePos, quoteLength; } v; Gv_FillWithVars(v); if (EDUKE32_PREDICT_FALSE((unsigned)v.outputQuote >= MAXQUOTES || (unsigned)v.inputQuote >= MAXQUOTES )) { CON_ERRPRINTF("invalid quote %d\n", v.inputQuote >= MAXQUOTES ? v.inputQuote : v.outputQuote); abort_after_error(); } TArray output; char const *pInput = quoteMgr.GetQuote(v.inputQuote); while (*pInput && v.quotePos--) pInput++; while ((*pInput) && v.quoteLength--) { output.Push(*pInput); pInput++; } output.Push(0); quoteMgr.InitializeQuote(v.outputQuote, output.Data()); dispatch(); } vInstruction(CON_QSTRCMP): insptr++; { int const quote1 = Gv_GetVar(*insptr++); int const quote2 = Gv_GetVar(*insptr++); int const gameVar = *insptr++; Gv_SetVar(gameVar, strcmp(quoteMgr.GetQuote(quote1), quoteMgr.GetQuote(quote2))); dispatch(); } vInstruction(CON_GETPNAME): vInstruction(CON_QSTRNCAT): vInstruction(CON_QSTRCAT): vInstruction(CON_QSTRCPY): vInstruction(CON_QGETSYSSTR): insptr++; { int const q = Gv_GetVar(*insptr++); int j; if (VM_DECODE_INST(tw) == CON_GETPNAME && *insptr == g_thisActorVarID) { j = vm.playerNum; insptr++; } else j = Gv_GetVar(*insptr++); switch (VM_DECODE_INST(tw)) { case CON_GETPNAME: VM_ASSERT((unsigned)q < MAXQUOTES, "invalid quote %d\n", q); if (g_player[j].user_name[0]) quoteMgr.InitializeQuote(q, g_player[j].user_name); else quoteMgr.FormatQuote(q, "%d", j); break; case CON_QGETSYSSTR: VM_ASSERT((unsigned)q < MAXQUOTES, "invalid quote %d\n", q); switch (j) { case STR_MAPNAME: case STR_MAPFILENAME: { if (G_HaveUserMap()) { quoteMgr.FormatQuote(q, "%s", boardfilename); break; } int const levelNum = ud.volume_number * MAXLEVELS + ud.level_number; const char *pName; if (EDUKE32_PREDICT_FALSE((unsigned)levelNum >= ARRAY_SIZE(mapList))) { CON_ERRPRINTF("out of bounds map number (vol=%d, lev=%d)\n", ud.volume_number, ud.level_number); abort_after_error(); } pName = j == STR_MAPNAME ? mapList[levelNum].DisplayName() : mapList[levelNum].fileName.GetChars(); VM_ASSERT(pName != nullptr, "attempted access to %s of non-existent map (vol=%d, lev=%d)", j == STR_MAPNAME ? "name" : "file name", ud.volume_number, ud.level_number); quoteMgr.InitializeQuote(q, j == STR_MAPNAME ? mapList[levelNum].DisplayName() : mapList[levelNum].fileName.GetChars()); break; } case STR_PLAYERNAME: VM_ASSERT((unsigned)vm.playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", vm.playerNum); quoteMgr.InitializeQuote(q, g_player[vm.playerNum].user_name); break; case STR_VERSION: Bsprintf(tempbuf, GAMENAME " %s", GetGitDescription()); quoteMgr.InitializeQuote(q, tempbuf); break; case STR_GAMETYPE: quoteMgr.InitializeQuote(q, g_gametypeNames[ud.coop]); break; case STR_VOLUMENAME: if (G_HaveUserMap()) { quoteMgr.InitializeQuote(q, ""); break; } VM_ASSERT((unsigned)ud.volume_number < MAXVOLUMES, "invalid volume %d\n", ud.volume_number); // length is no longer limited so a check is needed. quoteMgr.InitializeQuote(q, gVolumeNames[ud.volume_number]); break; case STR_YOURTIME: quoteMgr.InitializeQuote(q, G_PrintYourTime()); break; case STR_PARTIME: quoteMgr.InitializeQuote(q, G_PrintParTime()); break; case STR_DESIGNERTIME: quoteMgr.InitializeQuote(q, G_PrintDesignerTime()); break; case STR_BESTTIME: quoteMgr.InitializeQuote(q, G_PrintBestTime()); break; case STR_USERMAPFILENAME: quoteMgr.FormatQuote(q, "%s", boardfilename); break; default: CON_ERRPRINTF("invalid string index %d or %d\n", q, j); abort_after_error(); } break; case CON_QSTRCAT: quoteMgr.AppendQuote(q, j); break; case CON_QSTRNCAT: quoteMgr.AppendQuote(q, j, Gv_GetVar(*insptr++)); break; case CON_QSTRCPY: if (q != j) quoteMgr.CopyQuote(q, j); break; default: CON_ERRPRINTF("invalid quote %d\n", q < MAXQUOTES? j : q); abort_after_error(); } dispatch(); } vInstruction(CON_CHANGESPRITESECT): insptr++; { int const spriteNum = Gv_GetVar(*insptr++); int const sectNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)spriteNum < MAXSPRITES && (unsigned)sectNum < MAXSECTORS, "invalid parameters: %d, %d\n", spriteNum, sectNum); if (sprite[spriteNum].sectnum == sectNum) dispatch(); changespritesect(spriteNum, sectNum); dispatch(); } vInstruction(CON_CHANGESPRITESTAT): insptr++; { int const spriteNum = Gv_GetVar(*insptr++); int const statNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)spriteNum < MAXSPRITES && (unsigned)statNum < MAXSTATUS, "invalid parameters: %d, %d\n", spriteNum, statNum); if (sprite[spriteNum].statnum == statNum) dispatch(); /* initialize actor data when changing to an actor statnum because there's usually garbage left over from being handled as a hard coded object */ if (sprite[spriteNum].statnum > STAT_ZOMBIEACTOR && (statNum == STAT_ACTOR || statNum == STAT_ZOMBIEACTOR)) { auto pActor = &actor[spriteNum]; auto pSprite = &sprite[spriteNum]; Bmemset(&pActor->t_data, 0, sizeof pActor->t_data); pActor->lastv = { 0, 0 }; pActor->timetosleep = 0; pActor->cgg = 0; pActor->movflag = 0; pActor->tempang = 0; pActor->dispicnum = 0; pActor->flags = 0; pSprite->hitag = 0; if (G_TileHasActor(pSprite->picnum)) { auto actorptr = g_tile[pSprite->picnum].execPtr; // offsets AC_ACTION_ID(pActor->t_data) = actorptr[1]; AC_MOVE_ID(pActor->t_data) = actorptr[2]; AC_MOVFLAGS(pSprite, pActor) = actorptr[3]; // ai bits (movflags) } } changespritestat(spriteNum, statNum); dispatch(); } vInstruction(CON_STARTLEVEL): insptr++; // skip command { // from 'level' cheat in game.c (about line 6250) int const volumeNum = Gv_GetVar(*insptr++); int const levelNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)volumeNum < MAXVOLUMES && (unsigned)levelNum < MAXLEVELS, "invalid parameters: %d, %d\n", volumeNum, levelNum); ud.m_volume_number = ud.volume_number = volumeNum; m_level_number = ud.level_number = levelNum; // if (numplayers > 1 && g_netServer) // Net_NewGame(volnume,levnume); //else { g_player[myconnectindex].ps->gm |= MODE_EOL; ud.display_bonus_screen = 0; } // MODE_RESTART; dispatch(); } vInstruction(CON_MYOSX): vInstruction(CON_MYOSPALX): vInstruction(CON_MYOS): vInstruction(CON_MYOSPAL): insptr++; { struct { vec2_t pos; int32_t tilenum, shade, orientation; } v; Gv_FillWithVars(v); switch (VM_DECODE_INST(tw)) { case CON_MYOS: VM_DrawTile(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation); break; case CON_MYOSPAL: VM_DrawTilePal(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation, Gv_GetVar(*insptr++)); break; case CON_MYOSX: VM_DrawTileSmall(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation); break; case CON_MYOSPALX: VM_DrawTilePalSmall(v.pos.x, v.pos.y, v.tilenum, v.shade, v.orientation, Gv_GetVar(*insptr++)); break; } dispatch(); } vInstruction(CON_DISPLAYRAND): insptr++; Gv_SetVar(*insptr++, system_15bit_rand()); dispatch(); vInstruction(CON_DRAGPOINT): insptr++; { int const wallNum = Gv_GetVar(*insptr++); vec2_t n; Gv_FillWithVars(n); VM_ASSERT((unsigned)wallNum < (unsigned)numwalls, "invalid wall %d\n", wallNum); dragpoint(wallNum, n.x, n.y, 0); dispatch(); } vInstruction(CON_LDIST): vInstruction(CON_DIST): insptr++; { int const out = *insptr++; vec2_t in; Gv_FillWithVars(in); VM_ASSERT((unsigned)in.x < MAXSPRITES && (unsigned)in.y < MAXSPRITES, "invalid sprite %d, %d\n", in.x, in.y); Gv_SetVar(out, (VM_DECODE_INST(tw) == CON_LDIST ? ldist : dist)(&sprite[in.x], &sprite[in.y])); dispatch(); } vInstruction(CON_GETANGLE): vInstruction(CON_GETINCANGLE): insptr++; { int const out = *insptr++; vec2_t in; Gv_FillWithVars(in); Gv_SetVar(out, (VM_DECODE_INST(tw) == CON_GETANGLE ? getangle : G_GetAngleDelta)(in.x, in.y)); dispatch(); } vInstruction(CON_MULSCALE): vInstruction(CON_DIVSCALE): insptr++; { int const out = *insptr++; vec3_t in; Gv_FillWithVars(in); if (VM_DECODE_INST(tw) == CON_MULSCALE) Gv_SetVar(out, mulscale(in.x, in.y, in.z)); else Gv_SetVar(out, divscale(in.x, in.y, in.z)); dispatch(); } vInstruction(CON_SCALEVAR): insptr++; { int const out = *insptr++; vec3_t in; Gv_FillWithVars(in); Gv_SetVar(out, scale(in.x, in.y, in.z)); dispatch(); } vInstruction(CON_INITTIMER): insptr++; G_InitTimer(Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_NEXTSECTORNEIGHBORZ): insptr++; { int32_t params[4]; Gv_FillWithVars(params); aGameVars[g_returnVarID].global = nextsectorneighborz(params[0], params[1], params[2], params[3]); } dispatch(); vInstruction(CON_MOVESECTOR): insptr++; A_MoveSector(Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_TIME): insptr += 2; dispatch(); vInstruction(CON_ESPAWN): vInstruction(CON_EQSPAWN): vInstruction(CON_QSPAWN): insptr++; { int const tileNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum); int const spriteNum = A_Spawn(vm.spriteNum, tileNum); switch (VM_DECODE_INST(tw)) { case CON_EQSPAWN: if (spriteNum != -1) A_AddToDeleteQueue(spriteNum); fallthrough__; case CON_ESPAWN: aGameVars[g_returnVarID].global = spriteNum; break; case CON_QSPAWN: if (spriteNum != -1) A_AddToDeleteQueue(spriteNum); break; } dispatch(); } vInstruction(CON_SHOOT): vInstruction(CON_ESHOOT): insptr++; { int j = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum); j = A_Shoot(vm.spriteNum, j); if (VM_DECODE_INST(tw) == CON_ESHOOT) aGameVars[g_returnVarID].global = j; dispatch(); } vInstruction(CON_EZSHOOT): vInstruction(CON_ZSHOOT): insptr++; { int const zvel = (int16_t)Gv_GetVar(*insptr++); int j = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum); j = A_ShootWithZvel(vm.spriteNum, j, zvel); if (VM_DECODE_INST(tw) == CON_EZSHOOT) aGameVars[g_returnVarID].global = j; dispatch(); } vInstruction(CON_CMENU): insptr++; // Well, sorry, but - no. //Menu_Change(Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_SOUND): vInstruction(CON_STOPSOUND): vInstruction(CON_SOUNDONCE): vInstruction(CON_GLOBALSOUND): vInstruction(CON_SCREENSOUND): insptr++; { int const soundNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)soundNum < MAXSOUNDS, "invalid sound %d\n", soundNum); switch (VM_DECODE_INST(tw)) { case CON_SOUNDONCE: if (!S_CheckSoundPlaying(soundNum)) { fallthrough__; case CON_SOUND: A_PlaySound((int16_t)soundNum, vm.spriteNum); } dispatch(); case CON_GLOBALSOUND: A_PlaySound((int16_t)soundNum, g_player[screenpeek].ps->i); dispatch(); case CON_STOPSOUND: if (S_CheckSoundPlaying(soundNum)) S_StopSound((int16_t)soundNum); dispatch(); case CON_SCREENSOUND: S_PlaySound(soundNum, CHAN_AUTO, CHANF_UI); dispatch(); } } dispatch(); vInstruction(CON_STARTCUTSCENE): vInstruction(CON_IFCUTSCENE): insptr++; { int const nQuote = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)nQuote < MAXQUOTES, "invalid quote %d\n", nQuote); if (VM_DECODE_INST(tw) == CON_IFCUTSCENE) { insptr--; branch(g_animPtr == Anim_Find(quoteMgr.GetQuote(nQuote))); dispatch(); } tw = vm.pPlayer->palette; inputState.ClearAllInput(); Anim_Play(quoteMgr.GetQuote(nQuote)); P_SetGamePalette(vm.pPlayer, tw, Pal_DontResetFade); dispatch(); } vInstruction(CON_STARTSCREEN): insptr++; inputState.ClearAllInput(); Screen_Play(); dispatch(); vInstruction(CON_GUNIQHUDID): insptr++; { tw = Gv_GetVar(*insptr++); if (EDUKE32_PREDICT_FALSE((unsigned)tw >= MAXUNIQHUDID - 1)) CON_ERRPRINTF("invalid value %d\n", (int)tw); else guniqhudid = tw; dispatch(); } vInstruction(CON_SAVEGAMEVAR): vInstruction(CON_READGAMEVAR): { int32_t nValue = 0; insptr++; FString section = currentGame + ".Gamevars"; GameConfig->SetSection(section, true); switch (VM_DECODE_INST(tw)) { case CON_SAVEGAMEVAR: { nValue = Gv_GetVar(*insptr); FStringf vs("%d", nValue); GameConfig->SetValueForKey(aGameVars[*insptr++].szLabel, vs); break; } case CON_READGAMEVAR: { auto str = GameConfig->GetValueForKey(aGameVars[*insptr].szLabel); if (str) nValue = (int)strtoll(str, nullptr, 0); Gv_SetVar(*insptr++, nValue); break; } } dispatch(); } vInstruction(CON_SHOWVIEW): vInstruction(CON_SHOWVIEWUNBIASED): vInstruction(CON_SHOWVIEWQ16): vInstruction(CON_SHOWVIEWQ16UNBIASED): insptr++; { struct { vec3_t vec; int32_t params[3]; vec2_t scrn[2]; } v; Gv_FillWithVars(v); 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"); VM_ASSERT((unsigned)v.params[2] < MAXSECTORS, "invalid sector %d\n", v.params[2]); if (VM_DECODE_INST(tw) != CON_SHOWVIEWQ16 && VM_DECODE_INST(tw) != CON_SHOWVIEWQ16UNBIASED) { v.params[0] <<= 16; v.params[1] <<= 16; } 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, (VM_DECODE_INST(tw) != CON_SHOWVIEW && VM_DECODE_INST(tw) != CON_SHOWVIEWQ16)); dispatch(); } vInstruction(CON_ROTATESPRITEA): vInstruction(CON_ROTATESPRITE16): vInstruction(CON_ROTATESPRITE): insptr++; { struct { vec3_t pos; int32_t ang, tilenum, shade, pal, orientation; } v; Gv_FillWithVars(v); int32_t alpha = (VM_DECODE_INST(tw) == CON_ROTATESPRITEA) ? Gv_GetVar(*insptr++) : 0; vec2_t bound[2]; Gv_FillWithVars(bound); if (VM_DECODE_INST(tw) != CON_ROTATESPRITE16 && !(v.orientation & ROTATESPRITE_FULL16)) { v.pos.x <<= 16; v.pos.y <<= 16; } VM_ASSERT((unsigned)v.tilenum < MAXTILES, "invalid tilenum %d\n", v.tilenum); int32_t blendidx = 0; NEG_ALPHA_TO_BLEND(alpha, blendidx, v.orientation); 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, blendidx, bound[0].x, bound[0].y, bound[1].x, bound[1].y); dispatch(); } vInstruction(CON_GAMETEXT): vInstruction(CON_GAMETEXTZ): insptr++; { struct { int32_t tilenum; vec2_t pos; int32_t nQuote, shade, pal, orientation; vec2_t bound[2]; } v; Gv_FillWithVars(v); int32_t z = (VM_DECODE_INST(tw) == CON_GAMETEXTZ) ? Gv_GetVar(*insptr++) : 65536; if (EDUKE32_PREDICT_FALSE(v.tilenum < 0 || v.tilenum + 127 >= MAXTILES)) { CON_ERRPRINTF("invalid base tilenum %d\n", v.tilenum); abort_after_error(); } if (z <= 0) { CON_ERRPRINTF("Bad text size (<= 0)"); z = 65536; } VM_ASSERT((unsigned)v.nQuote < MAXQUOTES, "invalid quote %d\n", v.nQuote); G_PrintGameText(v.tilenum, v.pos.x >> 1, v.pos.y, quoteMgr.GetQuote(v.nQuote), v.shade, v.pal, v.orientation & (ROTATESPRITE_MAX - 1), v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y, z, 0); dispatch(); } vInstruction(CON_DIGITALNUMBER): vInstruction(CON_DIGITALNUMBERZ): insptr++; { struct { int32_t tilenum; vec2_t pos; int32_t nQuote, shade, pal, orientation; vec2_t bound[2]; } v; Gv_FillWithVars(v); int32_t const nZoom = (VM_DECODE_INST(tw) == CON_DIGITALNUMBERZ) ? Gv_GetVar(*insptr++) : 65536; // NOTE: '-' not taken into account, but we have rotatesprite() bound check now anyway VM_ASSERT(v.tilenum >= 0 && v.tilenum + 9 < MAXTILES, "invalid base tilenum %d\n", v.tilenum); 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, v.bound[0].y, v.bound[1].x, v.bound[1].y, nZoom); dispatch(); } vInstruction(CON_MINITEXT): insptr++; { struct { vec2_t pos; int32_t nQuote, shade, pal; } v; Gv_FillWithVars(v); VM_ASSERT((unsigned)v.nQuote < MAXQUOTES, "invalid quote %d\n", v.nQuote); minitextshade(v.pos.x, v.pos.y, quoteMgr.GetQuote(v.nQuote), v.shade, v.pal, 2 + 8 + 16); dispatch(); } vInstruction(CON_SCREENTEXT): insptr++; { struct { int32_t tilenum; vec3_t v; int32_t blockangle, charangle, nQuote, shade, pal, orientation, alpha; vec2_t spacing, between; int32_t nFlags; vec2_t bound[2]; } v; Gv_FillWithVars(v); VM_ASSERT(v.tilenum >= 0 && v.tilenum + 127 < MAXTILES, "invalid base tilenum %d\n", v.tilenum); VM_ASSERT((unsigned)v.nQuote < MAXQUOTES, "invalid quote %d\n", v.nQuote); G_ScreenText(v.tilenum, v.v.x, v.v.y, v.v.z, v.blockangle, v.charangle, quoteMgr.GetQuote(v.nQuote), v.shade, v.pal, 2 | (v.orientation & (ROTATESPRITE_MAX - 1)), v.alpha, v.spacing.x, v.spacing.y, v.between.x, v.between.y, v.nFlags, v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y); dispatch(); } vInstruction(CON_GETZRANGE): insptr++; { struct { vec3_t vect; int32_t sectNum; } v; Gv_FillWithVars(v); int const ceilzvar = *insptr++; int const ceilhitvar = *insptr++; int const florzvar = *insptr++; int const florhitvar = *insptr++; struct { int32_t walldist, clipmask; } v2; Gv_FillWithVars(v2); VM_ASSERT((unsigned)v.sectNum < MAXSECTORS, "invalid sector %d\n", v.sectNum); int32_t ceilz, ceilhit, florz, florhit; getzrange(&v.vect, v.sectNum, &ceilz, &ceilhit, &florz, &florhit, v2.walldist, v2.clipmask); Gv_SetVar(ceilzvar, ceilz); Gv_SetVar(ceilhitvar, ceilhit); Gv_SetVar(florzvar, florz); Gv_SetVar(florhitvar, florhit); dispatch(); } vInstruction(CON_SECTSETINTERPOLATION): vInstruction(CON_SECTCLEARINTERPOLATION): insptr++; { int const sectnum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)sectnum < MAXSECTORS, "invalid sector %d\n", sectnum); if (VM_DECODE_INST(tw) == CON_SECTSETINTERPOLATION) Sect_SetInterpolation(sectnum); else Sect_ClearInterpolation(sectnum); dispatch(); } vInstruction(CON_CALCHYPOTENUSE): insptr++; { int32_t returnVar = *insptr++; vec2_t da; Gv_FillWithVars(da); int64_t const hypsq = (int64_t)da.x * da.x + (int64_t)da.y * da.y; Gv_SetVar(returnVar, (hypsq > (int64_t)INT32_MAX) ? (int32_t)sqrt((double)hypsq) : ksqrt((uint32_t)hypsq)); dispatch(); } vInstruction(CON_LINEINTERSECT): vInstruction(CON_RAYINTERSECT): insptr++; { struct { vec3_t vec[2]; vec2_t vec2[2]; } v; Gv_FillWithVars(v); int const intxvar = *insptr++; int const intyvar = *insptr++; int const intzvar = *insptr++; int const retvar = *insptr++; vec3_t in; 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, v.vec[1].z, v.vec2[0].x, v.vec2[0].y, v.vec2[1].x, v.vec2[1].y, &in.x, &in.y, &in.z); Gv_SetVar(retvar, ret); if (ret) { Gv_SetVar(intxvar, in.x); Gv_SetVar(intyvar, in.y); Gv_SetVar(intzvar, in.z); } dispatch(); } vInstruction(CON_CLIPMOVE): vInstruction(CON_CLIPMOVENOSLIDE): insptr++; { typedef struct { int32_t w, f, c; } vec3dist_t; int const returnVar = *insptr++; int const xReturn = *insptr++; int const yReturn = *insptr++; insptr -= 2; typedef struct { vec3_t vec3; int32_t sectNum32; vec2_t vec2; vec3dist_t dist; int32_t clipMask; } clipmoveparams_t; int32_t const sectReturn = insptr[offsetof(clipmoveparams_t, sectNum32) / sizeof(int32_t)]; clipmoveparams_t v; Gv_FillWithVars(v); int16_t sectNum = v.sectNum32; VM_ASSERT((unsigned)sectNum < MAXSECTORS, "invalid sector %d\n", sectNum); Gv_SetVar( returnVar, clipmovex(&v.vec3, §Num, v.vec2.x, v.vec2.y, v.dist.w, v.dist.f, v.dist.c, v.clipMask, (VM_DECODE_INST(tw) == CON_CLIPMOVENOSLIDE))); Gv_SetVar(sectReturn, v.sectNum32); Gv_SetVar(xReturn, v.vec3.x); Gv_SetVar(yReturn, v.vec3.y); dispatch(); } vInstruction(CON_HITSCAN): insptr++; { struct { vec3_t origin; int32_t sectnum; vec3_t vect; } v; Gv_FillWithVars(v); int const sectReturn = *insptr++; int const wallReturn = *insptr++; int const spriteReturn = *insptr++; int const xReturn = *insptr++; int const yReturn = *insptr++; int const zReturn = *insptr++; int const clipType = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)v.sectnum < MAXSECTORS, "invalid sector %d\n", v.sectnum); hitdata_t hit; hitscan(&v.origin, v.sectnum, v.vect.x, v.vect.y, v.vect.z, &hit, clipType); Gv_SetVar(sectReturn, hit.sect); Gv_SetVar(wallReturn, hit.wall); Gv_SetVar(spriteReturn, hit.sprite); Gv_SetVar(xReturn, hit.pos.x); Gv_SetVar(yReturn, hit.pos.y); Gv_SetVar(zReturn, hit.pos.z); dispatch(); } vInstruction(CON_CANSEE): insptr++; { struct { vec3_t vec1; int32_t firstSector; vec3_t vec2; int32_t secondSector; } v; Gv_FillWithVars(v); int const returnVar = *insptr++; VM_ASSERT((unsigned)v.firstSector < (unsigned)numsectors && (unsigned)v.secondSector < (unsigned)numsectors, "invalid sector %d\n", (unsigned)v.firstSector >= (unsigned)numsectors ? v.firstSector : v.secondSector); Gv_SetVar(returnVar, cansee(v.vec1.x, v.vec1.y, v.vec1.z, v.firstSector, v.vec2.x, v.vec2.y, v.vec2.z, v.secondSector)); dispatch(); } vInstruction(CON_ROTATEPOINT): insptr++; { struct { vec2_t point[2]; int32_t angle; } v; Gv_FillWithVars(v); int const xReturn = *insptr++; int const yReturn = *insptr++; vec2_t result; rotatepoint(v.point[0], v.point[1], v.angle, &result); Gv_SetVar(xReturn, result.x); Gv_SetVar(yReturn, result.y); dispatch(); } vInstruction(CON_NEARTAG): insptr++; { // neartag(int32_t x, int32_t y, int32_t z, short sectnum, short ang, //Starting position & angle // short *neartagsector, //Returns near sector if sector[].tag != 0 // short *neartagwall, //Returns near wall if wall[].tag != 0 // short *neartagsprite, //Returns near sprite if sprite[].tag != 0 // int32_t *neartaghitdist, //Returns actual distance to object (scale: 1024=largest grid size) // int32_t neartagrange, //Choose maximum distance to scan (scale: 1024=largest grid size) // char tagsearch) //1-lotag only, 2-hitag only, 3-lotag&hitag struct { vec3_t point; int32_t sectNum, nAngle; } v; Gv_FillWithVars(v); int const sectReturn = *insptr++; int const wallReturn = *insptr++; int const spriteReturn = *insptr++; int const distReturn = *insptr++; struct { int32_t tagRange, tagSearch; } v2; Gv_FillWithVars(v2); VM_ASSERT((unsigned)v.sectNum < MAXSECTORS, "invalid sector %d\n", v.sectNum); int16_t neartagsector, neartagwall, neartagsprite; int32_t neartaghitdist; neartag(v.point.x, v.point.y, v.point.z, v.sectNum, v.nAngle, &neartagsector, &neartagwall, &neartagsprite, &neartaghitdist, v2.tagRange, v2.tagSearch, NULL); Gv_SetVar(sectReturn, neartagsector); Gv_SetVar(wallReturn, neartagwall); Gv_SetVar(spriteReturn, neartagsprite); Gv_SetVar(distReturn, neartaghitdist); dispatch(); } vInstruction(CON_GETTIMEDATE): insptr++; { int32_t values[8]; G_GetTimeDate(values); for (int value : values) Gv_SetVar(*insptr++, value); dispatch(); } vInstruction(CON_MOVESPRITE): insptr++; { struct { int32_t spriteNum; vec3_t vect; int32_t clipType; } v; Gv_FillWithVars(v); VM_ASSERT((unsigned)v.spriteNum < MAXSPRITES, "invalid sprite %d\n", v.spriteNum); Gv_SetVar(*insptr++, A_MoveSprite(v.spriteNum, &v.vect, v.clipType)); dispatch(); } vInstruction(CON_SETSPRITE): insptr++; { struct { int32_t spriteNum; vec3_t vect; } v; Gv_FillWithVars(v); VM_ASSERT((unsigned)v.spriteNum < MAXSPRITES, "invalid sprite %d\n", v.spriteNum); setsprite(v.spriteNum, &v.vect); dispatch(); } vInstruction(CON_GETFLORZOFSLOPE): vInstruction(CON_GETCEILZOFSLOPE): insptr++; { struct { int32_t sectNum; vec2_t vect; } v; Gv_FillWithVars(v); VM_ASSERT((unsigned)v.sectNum < MAXSECTORS, "invalid sector %d\n", v.sectNum); Gv_SetVar(*insptr++, (VM_DECODE_INST(tw) == CON_GETFLORZOFSLOPE ? yax_getflorzofslope : yax_getceilzofslope)(v.sectNum, v.vect)); dispatch(); } vInstruction(CON_UPDATESECTOR): insptr++; { vec2_t vect = { 0, 0 }; Gv_FillWithVars(vect); int const returnVar = *insptr++; int16_t sectNum = Gv_GetVar(returnVar); if ((unsigned)sectNum >= MAXSECTORS) sectNum = vm.pSprite->sectnum; updatesector(vect.x, vect.y, §Num); Gv_SetVar(returnVar, sectNum); dispatch(); } vInstruction(CON_UPDATESECTORZ): insptr++; { vec3_t vect = { 0, 0, 0 }; Gv_FillWithVars(vect); int const returnVar = *insptr++; int16_t sectNum = Gv_GetVar(returnVar); if ((unsigned)sectNum >= MAXSECTORS) sectNum = vm.pSprite->sectnum; updatesectorz(vect.x, vect.y, vect.z, §Num); Gv_SetVar(returnVar, sectNum); dispatch(); } vInstruction(CON_UPDATESECTORNEIGHBOR): insptr++; { vec2_t vect = { 0, 0 }; Gv_FillWithVars(vect); int const returnVar = *insptr++; int16_t sectNum = Gv_GetVar(returnVar); if ((unsigned)sectNum >= MAXSECTORS) sectNum = vm.pSprite->sectnum; updatesectorneighbor(vect.x, vect.y, §Num, getsectordist(vect, sectNum)); Gv_SetVar(returnVar, sectNum); dispatch(); } vInstruction(CON_UPDATESECTORNEIGHBORZ): insptr++; { vec3_t vect = { 0, 0, 0 }; Gv_FillWithVars(vect); int const returnVar = *insptr++; int16_t sectNum = Gv_GetVar(returnVar); if ((unsigned)sectNum >= MAXSECTORS) sectNum = vm.pSprite->sectnum; updatesectorneighborz(vect.x, vect.y, vect.z, §Num, getsectordist({vect.x, vect.y}, sectNum)); Gv_SetVar(returnVar, sectNum); dispatch(); } vInstruction(CON_CAPIA): { insptr++; int const nQuote = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)nQuote < MAXQUOTES, "invalid quote %d\n", nQuote); //communityapiUnlockAchievement(quoteMgr.GetQuote(v.nQuote)); dispatch(); } vInstruction(CON_CAPIS): { insptr++; int const nQuote = Gv_GetVar(*insptr++); int const value = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)nQuote < MAXQUOTES, "invalid quote %d\n", nQuote); //communityapiSetStat(quoteMgr.GetQuote(nQuote), value); dispatch(); } vInstruction(CON_SPAWN): insptr++; VM_ASSERT((unsigned)vm.pSprite->sectnum < MAXSECTORS, "invalid sector %d\n", vm.pUSprite->sectnum); A_Spawn(vm.spriteNum, *insptr++); dispatch(); vInstruction(CON_RESETACTIONCOUNT): insptr++; AC_ACTION_COUNT(vm.pData) = 0; dispatch(); vInstruction(CON_DEBRIS): insptr++; { #ifndef EDUKE32_STANDALONE if (!FURY) { int debrisTile = *insptr++; if ((unsigned)vm.pSprite->sectnum < MAXSECTORS) for (native_t cnt = (*insptr) - 1; cnt >= 0; cnt--) { int const tileOffset = (vm.pSprite->picnum == BLIMP && debrisTile == SCRAP1) ? 0 : (krand() % 3); int const spriteNum = A_InsertSprite( vm.pSprite->sectnum, vm.pSprite->x + (krand() & 255) - 128, vm.pSprite->y + (krand() & 255) - 128, vm.pSprite->z - (8 << 8) - (krand() & 8191), debrisTile + tileOffset, vm.pSprite->shade, 32 + (krand() & 15), 32 + (krand() & 15), krand() & 2047, (krand() & 127) + 32, -(krand() & 2047), vm.spriteNum, 5); sprite[spriteNum].yvel = (vm.pSprite->picnum == BLIMP && debrisTile == SCRAP1) ? g_blimpSpawnItems[cnt % 14] : -1; sprite[spriteNum].pal = vm.pSprite->pal; } } #else insptr++; #endif insptr++; } dispatch(); vInstruction(CON_COUNT): insptr++; AC_COUNT(vm.pData) = (int16_t)*insptr++; dispatch(); vInstruction(CON_CSTATOR): insptr++; vm.pSprite->cstat |= (int16_t)*insptr++; dispatch(); vInstruction(CON_CLIPDIST): insptr++; vm.pSprite->clipdist = (int16_t)*insptr++; dispatch(); vInstruction(CON_CSTAT): insptr++; vm.pSprite->cstat = (int16_t)*insptr++; dispatch(); vInstruction(CON_SAVENN): vInstruction(CON_SAVE): insptr++; insptr++; // skip the slot. I will not allow the script to do targeted saving without user control. g_saveRequested = true; // cannot save right here. dispatch(); vInstruction(CON_QUAKE): insptr++; g_earthquakeTime = Gv_GetVar(*insptr++); A_PlaySound(EARTHQUAKE, g_player[screenpeek].ps->i); dispatch(); vInstruction(CON_RESETPLAYER): insptr++; vm.flags = VM_ResetPlayer(vm.playerNum, vm.flags, 0); dispatch(); vInstruction(CON_RESETPLAYERFLAGS): insptr++; vm.flags = VM_ResetPlayer(vm.playerNum, vm.flags, Gv_GetVar(*insptr++)); dispatch(); vInstruction(CON_RESETCOUNT): insptr++; AC_COUNT(vm.pData) = 0; dispatch(); vInstruction(CON_ADDINVENTORY): insptr += 2; VM_AddInventory(vm.pPlayer, insptr[-1], *insptr); insptr++; dispatch(); vInstruction(CON_HITRADIUS): insptr++; { int32_t params[5]; Gv_FillWithVars(params); A_RadiusDamage(vm.spriteNum, params[0], params[1], params[2], params[3], params[4]); } dispatch(); vInstruction(CON_IFP): { int const moveFlags = *(++insptr); int nResult = 0; int const playerXVel = sprite[vm.pPlayer->i].xvel; int const syncBits = g_player[vm.playerNum].input->bits; if (((moveFlags & pducking) && vm.pPlayer->on_ground && TEST_SYNC_KEY(syncBits, SK_CROUCH)) || ((moveFlags & pfalling) && vm.pPlayer->jumping_counter == 0 && !vm.pPlayer->on_ground && vm.pPlayer->vel.z > 2048) || ((moveFlags & pjumping) && vm.pPlayer->jumping_counter > 348) || ((moveFlags & pstanding) && playerXVel >= 0 && playerXVel < 8) || ((moveFlags & pwalking) && playerXVel >= 8 && !TEST_SYNC_KEY(syncBits, SK_RUN)) || ((moveFlags & prunning) && playerXVel >= 8 && TEST_SYNC_KEY(syncBits, SK_RUN)) || ((moveFlags & phigher) && vm.pPlayer->pos.z < (vm.pSprite->z - (48 << 8))) || ((moveFlags & pwalkingback) && playerXVel <= -8 && !TEST_SYNC_KEY(syncBits, SK_RUN)) || ((moveFlags & prunningback) && playerXVel <= -8 && TEST_SYNC_KEY(syncBits, SK_RUN)) || ((moveFlags & pkicking) && (vm.pPlayer->quick_kick > 0 || (PWEAPON(vm.playerNum, vm.pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON && vm.pPlayer->kickback_pic > 0))) || ((moveFlags & pshrunk) && sprite[vm.pPlayer->i].xrepeat < 32) || ((moveFlags & pjetpack) && vm.pPlayer->jetpack_on) || ((moveFlags & ponsteroids) && vm.pPlayer->inv_amount[GET_STEROIDS] > 0 && vm.pPlayer->inv_amount[GET_STEROIDS] < 400) || ((moveFlags & ponground) && vm.pPlayer->on_ground) || ((moveFlags & palive) && sprite[vm.pPlayer->i].xrepeat > 32 && sprite[vm.pPlayer->i].extra > 0 && vm.pPlayer->timebeforeexit == 0) || ((moveFlags & pdead) && sprite[vm.pPlayer->i].extra <= 0)) nResult = 1; else if ((moveFlags & pfacing)) { nResult = (vm.pSprite->picnum == APLAYER && (g_netServer || ud.multimode > 1)) ? G_GetAngleDelta(fix16_to_int(g_player[otherp].ps->q16ang), getangle(vm.pPlayer->pos.x - g_player[otherp].ps->pos.x, vm.pPlayer->pos.y - g_player[otherp].ps->pos.y)) : G_GetAngleDelta(fix16_to_int(vm.pPlayer->q16ang), getangle(vm.pSprite->x - vm.pPlayer->pos.x, vm.pSprite->y - vm.pPlayer->pos.y)); nResult = (nResult > -128 && nResult < 128); } branch(nResult); } dispatch(); vInstruction(CON_GUTS): #ifndef EDUKE32_STANDALONE if (!FURY) A_DoGuts(vm.spriteNum, insptr[1], insptr[2]); #endif insptr += 3; dispatch(); vInstruction(CON_WACKPLAYER): insptr++; P_ForceAngle(vm.pPlayer); dispatch(); vInstruction(CON_FLASH): insptr++; vm.pSprite->shade = -127; vm.pPlayer->visibility = -127; dispatch(); vInstruction(CON_SAVEMAPSTATE): G_SaveMapState(); insptr++; dispatch(); vInstruction(CON_LOADMAPSTATE): G_RestoreMapState(); insptr++; dispatch(); vInstruction(CON_CLEARMAPSTATE): insptr++; { int const levelNum = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)levelNum < MAXVOLUMES * MAXLEVELS, "invalid map number %d\n", levelNum); G_FreeMapState(levelNum); } dispatch(); vInstruction(CON_STOPALLSOUNDS): insptr++; if (screenpeek == vm.playerNum) FX_StopAllSounds(); dispatch(); vInstruction(CON_STOPALLMUSIC): insptr++; Mus_Stop(); dispatch(); vInstruction(CON_OPERATE): insptr++; if (sector[vm.pSprite->sectnum].lotag == 0) { int16_t foundSect, foundWall, foundSprite; int32_t foundDist; neartag(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - ZOFFSET5, vm.pSprite->sectnum, vm.pSprite->ang, &foundSect, &foundWall, &foundSprite, &foundDist, 768, 4 + 1, NULL); if (foundSect >= 0 && isanearoperator(sector[foundSect].lotag)) if ((sector[foundSect].lotag & 0xff) == ST_23_SWINGING_DOOR || sector[foundSect].floorz == sector[foundSect].ceilingz) if ((sector[foundSect].lotag & (16384u | 32768u)) == 0) { int32_t j; for (SPRITES_OF_SECT(foundSect, j)) if (sprite[j].picnum == ACTIVATOR) break; if (j == -1) G_OperateSectors(foundSect, vm.spriteNum); } } dispatch(); vInstruction(CON_SPRITEPAL): insptr++; if (vm.pSprite->picnum != APLAYER) vm.pActor->tempang = vm.pSprite->pal; vm.pSprite->pal = *insptr++; dispatch(); vInstruction(CON_CACTOR): insptr++; vm.pSprite->picnum = *insptr++; dispatch(); vInstruction(CON_PALFROM): insptr++; VM_ASSERT((unsigned)vm.playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", vm.playerNum); { palette_t const pal = { uint8_t(insptr[1]), uint8_t(insptr[2]), uint8_t(insptr[3]), uint8_t(insptr[0]) }; insptr += 4; P_PalFrom(vm.pPlayer, pal.f, pal.r, pal.g, pal.b); } dispatch(); vInstruction(CON_SCREENPAL): insptr++; { int32_t params[4]; Gv_FillWithVars(params); videoFadePalette(params[0], params[1], params[2], params[3]); } dispatch(); vInstruction(CON_SECTOROFWALL): insptr++; tw = *insptr++; Gv_SetVar(tw, sectorofwall(Gv_GetVar(*insptr++))); dispatch(); vInstruction(CON_QSPRINTF): insptr++; { int const outputQuote = Gv_GetVar(*insptr++); int const inputQuote = Gv_GetVar(*insptr++); auto inBuf = quoteMgr.GetQuote(inputQuote); int32_t arg[32]; TArray outBuf; int const quoteLen = Bstrlen(inBuf); int inputPos = 0; int outputPos = 0; int argIdx = 0; while (VM_DECODE_INST(*insptr) != CON_NULLOP && argIdx < 32) arg[argIdx++] = Gv_GetVar(*insptr++); int numArgs = argIdx; insptr++; // skip the NOP argIdx = 0; do { while (inputPos < quoteLen && inBuf[inputPos] != '%') outBuf.Push(inBuf[inputPos++]); if (inBuf[inputPos] == '%') { inputPos++; switch (inBuf[inputPos]) { case 'l': if (inBuf[inputPos + 1] != 'd') { // write the % and l outBuf.Push(inBuf[inputPos - 1]); outBuf.Push(inBuf[inputPos++]); break; } inputPos++; fallthrough__; case 'd': { if (argIdx >= numArgs) goto finish_qsprintf; char buf[16]; Bsprintf(buf, "%d", arg[argIdx++]); int const bufLen = Bstrlen(buf); outputPos = outBuf.Reserve(bufLen); memcpy(&outBuf[outputPos], buf, bufLen); outputPos += bufLen; inputPos++; } break; case 's': { if (argIdx >= numArgs) goto finish_qsprintf; auto quoteArg = quoteMgr.GetQuote(arg[argIdx]); int const argLen = (int)strlen(quoteArg); outputPos = outBuf.Reserve(argLen); memcpy(&outBuf[outputPos], quoteArg, argLen); outputPos += argLen; argIdx++; inputPos++; } break; default: outBuf.Push(inBuf[inputPos - 1]); break; } } } while (inputPos < quoteLen); finish_qsprintf: outBuf.Push(0); quoteMgr.InitializeQuote(outputQuote, outBuf.Data()); dispatch(); } vInstruction(CON_ADDLOGVAR): insptr++; { int32_t m = 1; char szBuf[256]; int32_t lVarID = *insptr; if ((lVarID >= g_gameVarCount) || lVarID < 0) { if (*insptr == MAXGAMEVARS) // addlogvar for a constant? Har. insptr++; // else if (*insptr > g_gameVarCount && (*insptr < (MAXGAMEVARS<<1)+MAXGAMEVARS+1+MAXGAMEARRAYS)) else if (*insptr & (MAXGAMEVARS << 2)) { int32_t index; lVarID ^= (MAXGAMEVARS << 2); if (lVarID & GV_FLAG_NEGATIVE) { m = -m; lVarID ^= GV_FLAG_NEGATIVE; } insptr++; index = Gv_GetVar(*insptr++); if (EDUKE32_PREDICT_TRUE((unsigned)index < (unsigned)aGameArrays[lVarID].size)) { Printf(OSDTEXT_GREEN "CONLOGVAR: L=%d %s[%d] =%d\n", VM_DECODE_LINE_NUMBER(g_tw), aGameArrays[lVarID].szLabel, index, (int32_t)(m * Gv_GetArrayValue(lVarID, index))); dispatch(); } else { CON_ERRPRINTF("invalid array index\n"); abort_after_error(); } } else if (*insptr & (MAXGAMEVARS << 3)) { // FIXME FIXME FIXME if ((lVarID & (MAXGAMEVARS - 1)) == g_structVarIDs + STRUCT_ACTORVAR) { auto const oinsptr = insptr++; int32_t index = Gv_GetVar(*insptr++); insptr = oinsptr; if (EDUKE32_PREDICT_FALSE((unsigned)index >= MAXSPRITES - 1)) { CON_ERRPRINTF("invalid array index\n"); abort_after_error(); } Printf(OSDTEXT_GREEN "CONLOGVAR: L=%d %d %d\n", VM_DECODE_LINE_NUMBER(g_tw), index, Gv_GetVar(*insptr++, index, vm.playerNum)); dispatch(); } } else if (EDUKE32_PREDICT_TRUE(*insptr & GV_FLAG_NEGATIVE)) { m = -m; lVarID ^= GV_FLAG_NEGATIVE; } else { // invalid varID CON_ERRPRINTF("invalid variable\n"); abort_after_error(); } } Bsprintf(tempbuf, "CONLOGVAR: L=%d %s ", VM_DECODE_LINE_NUMBER(g_tw), aGameVars[lVarID].szLabel); if (aGameVars[lVarID].flags & GAMEVAR_READONLY) { Bsprintf(szBuf, " (read-only)"); Bstrcat(tempbuf, szBuf); } if (aGameVars[lVarID].flags & GAMEVAR_PERPLAYER) { Bsprintf(szBuf, " (Per Player. Player=%d)", vm.playerNum); } else if (aGameVars[lVarID].flags & GAMEVAR_PERACTOR) { Bsprintf(szBuf, " (Per Actor. Actor=%d)", vm.spriteNum); } else { Bsprintf(szBuf, " (Global)"); } Bstrcat(tempbuf, szBuf); Bsprintf(szBuf, " =%d\n", Gv_GetVar(lVarID) * m); Bstrcat(tempbuf, szBuf); Printf(OSDTEXT_GREEN "%s", tempbuf); insptr++; dispatch(); } vInstruction(CON_SQRT): insptr++; { // syntax sqrt int const sqrtval = ksqrt((uint32_t)Gv_GetVar(*insptr++)); Gv_SetVar(*insptr++, sqrtval); dispatch(); } vInstruction(CON_FINDNEARACTOR): vInstruction(CON_FINDNEARSPRITE): vInstruction(CON_FINDNEARACTOR3D): vInstruction(CON_FINDNEARSPRITE3D): insptr++; { // syntax findnearactorvar // gets the sprite ID of the nearest actor within max dist // that is of into // -1 for none found // int const decodedInst = VM_DECODE_INST(tw); int const actorsOnly = (decodedInst == CON_FINDNEARACTOR || decodedInst == CON_FINDNEARACTOR3D); auto const dist_funcptr = (decodedInst == CON_FINDNEARACTOR || decodedInst == CON_FINDNEARSPRITE) ? &ldist : &dist; int const findTile = *insptr++; int maxDist = Gv_GetVar(*insptr++); int const returnVar = *insptr++; int findStatnum = actorsOnly ? STAT_ACTOR : MAXSTATUS - 1; int foundSprite = -1; do { int spriteNum = headspritestat[findStatnum]; // all sprites while ((unsigned)spriteNum < MAXSPRITES) { if (sprite[spriteNum].picnum == findTile && spriteNum != vm.spriteNum) { int const foundDist = dist_funcptr(vm.pSprite, &sprite[spriteNum]); if (foundDist < maxDist) { maxDist = foundDist; foundSprite = spriteNum; } } spriteNum = nextspritestat[spriteNum]; } if (actorsOnly) break; } while (findStatnum--); Gv_SetVar(returnVar, foundSprite); dispatch(); } vInstruction(CON_FINDNEARACTORZ): vInstruction(CON_FINDNEARSPRITEZ): insptr++; { // syntax findnearactorvar // gets the sprite ID of the nearest actor within max dist // that is of into // -1 for none found // int const actorsOnly = (VM_DECODE_INST(tw) == CON_FINDNEARACTORZ); int const findTile = *insptr++; int maxDist = Gv_GetVar(*insptr++); int const maxZDist = Gv_GetVar(*insptr++); int const returnVar = *insptr++; int findStatnum = actorsOnly ? STAT_ACTOR : MAXSTATUS - 1; int foundSprite = -1; do { int spriteNum = headspritestat[findStatnum]; // all sprites while ((unsigned)spriteNum < MAXSPRITES) { if (sprite[spriteNum].picnum == findTile && spriteNum != vm.spriteNum) { int const foundDist = ldist(vm.pSprite, &sprite[spriteNum]); if (foundDist < maxDist && klabs(vm.pSprite->z - sprite[spriteNum].z) < maxZDist) { maxDist = foundDist; foundSprite = spriteNum; } } spriteNum = nextspritestat[spriteNum]; } if (actorsOnly) break; } while (findStatnum--); Gv_SetVar(returnVar, foundSprite); dispatch(); } vInstruction(CON_FINDPLAYER): { int32_t tw; insptr++; aGameVars[g_returnVarID].global = A_FindPlayer(vm.pSprite, &tw); Gv_SetVar(*insptr++, tw); dispatch(); } vInstruction(CON_FINDOTHERPLAYER): { int32_t tw; insptr++; aGameVars[g_returnVarID].global = P_FindOtherPlayer(vm.playerNum, &tw); Gv_SetVar(*insptr++, tw); dispatch(); } vInstruction(CON_GETINPUT): insptr++; { int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum; int const labelNum = *insptr++; Gv_SetVar(*insptr++, VM_GetPlayerInput(playerNum, labelNum)); dispatch(); } vInstruction(CON_SETINPUT): insptr++; { int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.playerNum; int const labelNum = *insptr++; VM_SetPlayerInput(playerNum, labelNum, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETUSERDEF): insptr++; { int const labelNum = *insptr++; int const lParm2 = (UserdefsLabels[labelNum].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0; Gv_SetVar(*insptr++, VM_GetUserdef(labelNum, lParm2)); dispatch(); } vInstruction(CON_GETTILEDATA): insptr++; { int const tileNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->picnum; int const labelNum = *insptr++; Gv_SetVar(*insptr++, VM_GetTileData(tileNum, labelNum)); dispatch(); } vInstruction(CON_SETTILEDATA): insptr++; { int const tileNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(insptr[-1]) : vm.pSprite->picnum; int const labelNum = *insptr++; VM_SetTileData(tileNum, labelNum, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_SETUSERDEF): insptr++; { int const labelNum = *insptr++; int const lParm2 = (UserdefsLabels[labelNum].flags & LABEL_HASPARM2) ? Gv_GetVar(*insptr++) : 0; VM_SetUserdef(labelNum, lParm2, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETPROJECTILE): insptr++; { tw = Gv_GetVar(*insptr++); int const labelNum = *insptr++; Gv_SetVar(*insptr++, VM_GetProjectile(tw, labelNum)); dispatch(); } vInstruction(CON_SETPROJECTILE): insptr++; { tw = Gv_GetVar(*insptr++); int const labelNum = *insptr++; VM_SetProjectile(tw, labelNum, Gv_GetVar(*insptr++)); dispatch(); } vInstruction(CON_GETANGLETOTARGET): insptr++; // vm.pActor->lastvx and lastvy are last known location of target. Gv_SetVar(*insptr++, getangle(vm.pActor->lastv.x - vm.pSprite->x, vm.pActor->lastv.y - vm.pSprite->y)); dispatch(); vInstruction(CON_ANGOFF): insptr++; spriteext[vm.spriteNum].angoff = Gv_GetVar(*insptr++); dispatch(); vInstruction(CON_LOCKPLAYER): insptr++; vm.pPlayer->transporter_hold = Gv_GetVar(*insptr++); dispatch(); vInstruction(CON_CHECKAVAILWEAPON): vInstruction(CON_CHECKAVAILINVEN): { insptr++; int const playerNum = (*insptr++ != g_thisActorVarID) ? Gv_GetVar(*insptr) : vm.playerNum; VM_ASSERT((unsigned)playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", playerNum); if (VM_DECODE_INST(tw) == CON_CHECKAVAILWEAPON) P_CheckWeapon(g_player[playerNum].ps); else P_SelectNextInvItem(g_player[playerNum].ps); dispatch(); } vInstruction(CON_GETPLAYERANGLE): insptr++; Gv_SetVar(*insptr++, fix16_to_int(vm.pPlayer->q16ang)); dispatch(); vInstruction(CON_GETACTORANGLE): insptr++; Gv_SetVar(*insptr++, vm.pSprite->ang); dispatch(); vInstruction(CON_SETPLAYERANGLE): insptr++; vm.pPlayer->q16ang = fix16_from_int(Gv_GetVar(*insptr++) & 2047); dispatch(); vInstruction(CON_SETACTORANGLE): insptr++; vm.pSprite->ang = Gv_GetVar(*insptr++) & 2047; dispatch(); vInstruction(CON_KLABS): if ((aGameVars[insptr[1]].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) == 0) aGameVars[insptr[1]].global = klabs(aGameVars[insptr[1]].global); else Gv_SetVar(insptr[1], klabs(Gv_GetVar(insptr[1]))); insptr += 2; dispatch(); vInstruction(CON_SETARRAY): insptr++; { tw = *insptr++; int const arrayIndex = Gv_GetVar(*insptr++); int const newValue = Gv_GetVar(*insptr++); SetArray(tw, arrayIndex, newValue); dispatch(); } vInstruction(CON_GETARRAYSEQUENCE): { insptr++; int32_t const arrayNum = *insptr++; int32_t const arraySize = (aGameArrays[arrayNum].flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(aGameArrays[arrayNum].size) : aGameArrays[arrayNum].size; int32_t const sequenceSize = *insptr++; int32_t const copySize = min(sequenceSize, arraySize); // warning? auto const insptrbak = insptr; for (int arrayIndex = 0; arrayIndex < copySize; ++arrayIndex) { int32_t const gameVar = *insptr++; int32_t const newValue = Gv_GetArrayValue(arrayNum, arrayIndex); Gv_SetVar(gameVar, newValue); } insptr = insptrbak + sequenceSize; dispatch(); } vInstruction(CON_SETARRAYSEQUENCE): { insptr++; int32_t const arrayNum = *insptr++; int32_t const sequenceSize = *insptr++; ResizeArray(arrayNum, sequenceSize); for (int arrayIndex = 0; arrayIndex < sequenceSize; ++arrayIndex) { int32_t const gameVar = *insptr++; int32_t const newValue = Gv_GetVar(gameVar); SetArray(arrayNum, arrayIndex, newValue); } dispatch(); } vInstruction(CON_READARRAYFROMFILE): insptr++; { int const arrayNum = *insptr++; int const quoteFilename = *insptr++; VM_ASSERT((unsigned)quoteFilename < MAXQUOTES, "invalid quote %d\n", quoteFilename); FStringf IniSection("%s.UserStrings", LumpFilter.GetChars()); auto IniKey = quoteMgr.GetQuote(quoteFilename); if (!GameConfig->SetSection(IniSection)) { dispatch(); } auto base64 = GameConfig->GetValueForKey(IniKey); if (base64 == nullptr) { dispatch(); } auto decoded = base64_decode(base64); FileReader fr; size_t const filelength = decoded.length(); size_t const numElements = Gv_GetArrayCountForAllocSize(arrayNum, filelength); if (numElements > 0) { size_t const newBytes = Gv_GetArrayAllocSizeForCount(arrayNum, numElements); size_t const readBytes = min(newBytes, filelength); size_t const oldBytes = Gv_GetArrayAllocSize(arrayNum); intptr_t *&pValues = aGameArrays[arrayNum].pValues; if (newBytes != oldBytes) { Xaligned_free(pValues); pValues = (intptr_t *)Xaligned_alloc(ARRAY_ALIGNMENT, newBytes); } aGameArrays[arrayNum].size = numElements; uintptr_t const flags = aGameArrays[arrayNum].flags; switch (flags & GAMEARRAY_SIZE_MASK) { case 0: #ifdef BITNESS64 { void *const pArray = Xcalloc(1, newBytes); memcpy(pArray, decoded.c_str(), readBytes); if (flags & GAMEARRAY_UNSIGNED) { for (unative_t i = 0; i < numElements; ++i) pValues[i] = ((uint32_t *)pArray)[i]; } else { for (unative_t i = 0; i < numElements; ++i) pValues[i] = ((int32_t *)pArray)[i]; } Xfree(pArray); break; } #endif default: memset((char *)pValues + readBytes, 0, newBytes - readBytes); memcpy(pValues, decoded.c_str(), readBytes); break; } } dispatch(); } vInstruction(CON_WRITEARRAYTOFILE): insptr++; { int const arrayNum = *insptr++; int const quoteFilename = *insptr++; VM_ASSERT((unsigned)quoteFilename < MAXQUOTES, "invalid quote %d\n", quoteFilename); // No, we are not writing stuff to an arbitrary file on the hard drive! This is a first grade exploit for doing serious damage. // Instead, encode the data as BASE64 and write it to the config file, // which doesn't create a wide open door for exploits. FStringf IniSection("%s.UserStrings", LumpFilter.GetChars()); auto IniKey = quoteMgr.GetQuote(quoteFilename); BufferWriter bw; switch (aGameArrays[arrayNum].flags & GAMEARRAY_SIZE_MASK) { case 0: #ifdef BITNESS64 { size_t const numElements = aGameArrays[arrayNum].size; size_t const numDiskBytes = numElements * sizeof(int32_t); int32_t *const pArray = (int32_t *)Xmalloc(numDiskBytes); for (unative_t k = 0; k < numElements; ++k) pArray[k] = Gv_GetArrayValue(arrayNum, k); bw.Write(pArray, numDiskBytes); Xfree(pArray); break; } #endif default: bw.Write(aGameArrays[arrayNum].pValues, Gv_GetArrayAllocSize(arrayNum)); break; } auto base64 = base64_encode(bw.GetBuffer()->Data(), bw.GetBuffer()->Size()); if (GameConfig->SetSection(IniSection, true)) { GameConfig->SetValueForKey(IniKey, base64.c_str()); } dispatch(); } vInstruction(CON_GETARRAYSIZE): insptr++; tw = *insptr++; Gv_SetVar(*insptr++, (aGameArrays[tw].flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(aGameArrays[tw].size) : aGameArrays[tw].size); dispatch(); vInstruction(CON_RESIZEARRAY): insptr++; { tw = *insptr++; int const newSize = Gv_GetVar(*insptr++); ResizeArray(tw, newSize); dispatch(); } vInstruction(CON_COPY): insptr++; { int const srcArray = *insptr++; int srcArrayIndex = Gv_GetVar(*insptr++); //, vm.spriteNum, vm.playerNum); int const destArray = *insptr++; int destArrayIndex = Gv_GetVar(*insptr++); int numElements = Gv_GetVar(*insptr++); auto &src = aGameArrays[srcArray]; auto &dest = aGameArrays[destArray]; int const srcArraySize = (src.flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(src.size) : src.size; int const destArraySize = (dest.flags & GAMEARRAY_VARSIZE) ? Gv_GetVar(dest.size) : dest.size; if (EDUKE32_PREDICT_FALSE(srcArrayIndex > srcArraySize || destArrayIndex > destArraySize)) dispatch(); if ((srcArrayIndex + numElements) > srcArraySize) numElements = srcArraySize - srcArrayIndex; if ((destArrayIndex + numElements) > destArraySize) numElements = destArraySize - destArrayIndex; // Switch depending on the source array type. int const srcInc = 1 << (int)!!(EDUKE32_PREDICT_FALSE(src.flags & GAMEARRAY_STRIDE2)); int const destInc = 1 << (int)!!(EDUKE32_PREDICT_FALSE(dest.flags & GAMEARRAY_STRIDE2)); // matching array types, no BITMAPs, no STRIDE2 flag if ((src.flags & GAMEARRAY_SIZE_MASK) == (dest.flags & GAMEARRAY_SIZE_MASK) && !((src.flags | dest.flags) & GAMEARRAY_BITMAP) && (srcInc & destInc) == 1) { Bmemcpy(dest.pValues + destArrayIndex, src.pValues + srcArrayIndex, numElements * Gv_GetArrayElementSize(srcArray)); dispatch(); } switch (dest.flags & GAMEARRAY_TYPE_MASK) { case 0: for (; numElements > 0; --numElements) { dest.pValues[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++); destArrayIndex += destInc; } break; case GAMEARRAY_INT16: for (; numElements > 0; --numElements) { ((int16_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++); destArrayIndex += destInc; } break; case GAMEARRAY_INT8: for (; numElements > 0; --numElements) { ((int8_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++); destArrayIndex += destInc; } break; case GAMEARRAY_UINT16: for (; numElements > 0; --numElements) { ((uint16_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++); destArrayIndex += destInc; } break; case GAMEARRAY_UINT8: for (; numElements > 0; --numElements) { ((uint8_t *)dest.pValues)[destArrayIndex] = Gv_GetArrayValue(srcArray, srcArrayIndex++); destArrayIndex += destInc; } break; case GAMEARRAY_BITMAP: for (; numElements > 0; --numElements) { uint32_t const newValue = Gv_GetArrayValue(srcArray, srcArrayIndex++); uint32_t const mask = 1 << (destArrayIndex & 7); uint8_t & value = ((uint8_t *)dest.pValues)[destArrayIndex >> 3]; value = (value & ~mask) | (-!!newValue & mask); destArrayIndex += destInc; } break; } dispatch(); } vInstruction(CON_SWAPARRAYS): insptr++; { auto &array1 = aGameArrays[*insptr++]; auto &array2 = aGameArrays[*insptr++]; swapptr(&array1.size, &array2.size); swapptr(&array1.pValues, &array2.pValues); dispatch(); } vInstruction(CON_DISPLAYRANDVAR): insptr++; Gv_SetVar(*insptr, mulscale15(system_15bit_rand(), insptr[1] + 1)); insptr += 2; dispatch(); vInstruction(CON_CLAMP): insptr++; { tw = *insptr++; int const min = Gv_GetVar(*insptr++); Gv_SetVar(tw, clamp2(Gv_GetVar(tw), min, Gv_GetVar(*insptr++))); } dispatch(); vInstruction(CON_GETCLOSESTCOL): insptr++; { tw = *insptr++; int32_t const rgb = Gv_GetVar(*insptr++); Gv_GetVar(*insptr++); Gv_SetVar(tw, ColorMatcher.Pick(rgb & 0xFF, (rgb >> 8) & 0xFF, (rgb >> 16) & 0xFF)); } dispatch(); vInstruction(CON_DRAWLINE256): insptr++; { struct { vec2_t pos[2]; int32_t index; } v; Gv_FillWithVars(v); renderDrawLine(v.pos[0].x, v.pos[0].y, v.pos[1].x, v.pos[1].y, v.index); } dispatch(); vInstruction(CON_DRAWLINERGB): insptr++; { struct { vec2_t pos[2]; int32_t index, rgb; } v; Gv_FillWithVars(v); palette_t const p = { (uint8_t)(v.rgb & 0xFF), (uint8_t)((v.rgb >> 8) & 0xFF), (uint8_t)((v.rgb >> 16) & 0xFF), (uint8_t)v.index }; drawlinergb(v.pos[0].x, v.pos[0].y, v.pos[1].x, v.pos[1].y, p); } dispatch(); vInstruction(CON_INV): if ((aGameVars[insptr[1]].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) == 0) aGameVars[insptr[1]].global = -aGameVars[insptr[1]].global; else Gv_SetVar(insptr[1], -Gv_GetVar(insptr[1])); insptr += 2; dispatch(); vInstruction(CON_DISPLAYRANDVARVAR): insptr++; tw = *insptr++; Gv_SetVar(tw, mulscale15(system_15bit_rand(), Gv_GetVar(*insptr++) + 1)); dispatch(); vInstruction(CON_GMAXAMMO): insptr++; tw = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)tw < MAX_WEAPONS, "invalid weapon %d\n", (int)tw); Gv_SetVar(*insptr++, vm.pPlayer->max_ammo_amount[tw]); dispatch(); vInstruction(CON_SMAXAMMO): insptr++; tw = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)tw < MAX_WEAPONS, "invalid weapon %d\n", (int)tw); vm.pPlayer->max_ammo_amount[tw] = Gv_GetVar(*insptr++); dispatch(); vInstruction(CON_DIVR): // div round to nearest insptr++; { tw = *insptr++; int const dividend = Gv_GetVar(tw); int const divisor = Gv_GetVar(*insptr++); if (EDUKE32_PREDICT_FALSE(!divisor)) { CON_CRITICALERRPRINTF("divide by zero!\n"); abort_after_error(); } Gv_SetVar(tw, tabledivide32((dividend + ksgn(dividend) * klabs(divisor / 2)), divisor)); dispatch(); } vInstruction(CON_DIVRU): // div round away from zero insptr++; { tw = *insptr++; int const dividend = Gv_GetVar(tw); int const divisor = Gv_GetVar(*insptr++); if (EDUKE32_PREDICT_FALSE(!divisor)) { CON_CRITICALERRPRINTF("divide by zero!\n"); abort_after_error(); } Gv_SetVar(tw, tabledivide32((dividend + ksgn(dividend) * klabs(divisor) + 1), divisor)); dispatch(); } vInstruction(CON_SIN): insptr++; tw = *insptr++; Gv_SetVar(tw, sintable[Gv_GetVar(*insptr++) & 2047]); dispatch(); vInstruction(CON_COS): insptr++; tw = *insptr++; Gv_SetVar(tw, sintable[(Gv_GetVar(*insptr++) + 512) & 2047]); dispatch(); vInstruction(CON_SPGETLOTAG): insptr++; aGameVars[g_lotagVarID].global = vm.pSprite->lotag; dispatch(); vInstruction(CON_SPGETHITAG): insptr++; aGameVars[g_hitagVarID].global = vm.pSprite->hitag; dispatch(); vInstruction(CON_SECTGETLOTAG): insptr++; aGameVars[g_lotagVarID].global = sector[vm.pSprite->sectnum].lotag; dispatch(); vInstruction(CON_SECTGETHITAG): insptr++; aGameVars[g_hitagVarID].global = sector[vm.pSprite->sectnum].hitag; dispatch(); vInstruction(CON_GETTEXTUREFLOOR): insptr++; aGameVars[g_textureVarID].global = sector[vm.pSprite->sectnum].floorpicnum; dispatch(); vInstruction(CON_STARTTRACK): insptr++; G_StartTrackSlotWrap(ud.volume_number, Gv_GetVar(*(insptr++))); dispatch(); vInstruction(CON_STARTTRACKSLOT): insptr++; { int const volumeNum = Gv_GetVar(*(insptr++)); int const levelNum = Gv_GetVar(*(insptr++)); G_StartTrackSlotWrap(volumeNum == -1 ? MAXVOLUMES : volumeNum, levelNum); } dispatch(); vInstruction(CON_SWAPTRACKSLOT): insptr++; { int const volumeNum = Gv_GetVar(*(insptr++)); int const levelNum = Gv_GetVar(*(insptr++)); if (volumeNum == ud.music_episode && levelNum == ud.music_level) dispatch(); #if 0 // This looks not only a LITTLE broken. I think it's better to disable so that the position hackery can be taken out of the equation. // This is the best ASS can do right now. Better implementation pending. int32_t position = S_GetMusicPosition(); if (!G_StartTrackSlotWrap(volumeNum == -1 ? MAXVOLUMES : volumeNum, levelNum)) S_SetMusicPosition(position); #endif } dispatch(); vInstruction(CON_PRELOADTRACKSLOTFORSWAP): // ASS can't even handle this command right now. insptr++; Gv_GetVar(*(insptr++)); Gv_GetVar(*(insptr++)); dispatch(); vInstruction(CON_SETMUSICPOSITION): insptr++; Gv_GetVar(*(insptr++)); dispatch(); vInstruction(CON_GETMUSICPOSITION): insptr += 2; dispatch(); vInstruction(CON_ACTIVATECHEAT): insptr++; tw = Gv_GetVar(*(insptr++)); VM_ASSERT(numplayers == 1 && (g_player[myconnectindex].ps->gm & MODE_GAME), "not in a single-player game.\n"); osdcmd_cheatsinfo_stat.cheatnum = tw; dispatch(); vInstruction(CON_SETGAMEPALETTE): insptr++; P_SetGamePalette(vm.pPlayer, Gv_GetVar(*(insptr++)), Pal_DontResetFade); dispatch(); vInstruction(CON_GETTEXTURECEILING): insptr++; aGameVars[g_textureVarID].global = sector[vm.pSprite->sectnum].ceilingpicnum; dispatch(); vInstruction(CON_IFPHEALTHL): insptr++; branch(sprite[vm.pPlayer->i].extra < *insptr); dispatch(); vInstruction(CON_IFPINVENTORY): insptr++; switch (*insptr++) { case GET_STEROIDS: case GET_SHIELD: case GET_SCUBA: case GET_HOLODUKE: case GET_HEATS: case GET_FIRSTAID: case GET_BOOTS: case GET_JETPACK: tw = (vm.pPlayer->inv_amount[insptr[-1]] != *insptr); break; case GET_ACCESS: switch (vm.pSprite->pal) { case 0: tw = (vm.pPlayer->got_access & 1); break; case 21: tw = (vm.pPlayer->got_access & 2); break; case 23: tw = (vm.pPlayer->got_access & 4); break; } break; default: tw = 0; CON_ERRPRINTF("invalid inventory item %d\n", (int32_t) * (insptr - 1)); dispatch(); } branch(tw); dispatch(); vInstruction(CON_PSTOMP): insptr++; if (vm.pPlayer->knee_incs == 0 && sprite[vm.pPlayer->i].xrepeat >= 40) if (cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - ZOFFSET6, vm.pSprite->sectnum, vm.pPlayer->pos.x, vm.pPlayer->pos.y, vm.pPlayer->pos.z + ZOFFSET2, sprite[vm.pPlayer->i].sectnum)) { int numPlayers = g_mostConcurrentPlayers - 1; for (; numPlayers >= 0; --numPlayers) { if (g_player[numPlayers].ps->actorsqu == vm.spriteNum) break; } if (numPlayers == -1) { if (vm.pPlayer->weapon_pos == 0) vm.pPlayer->weapon_pos = -1; vm.pPlayer->actorsqu = vm.spriteNum; vm.pPlayer->knee_incs = 1; } } dispatch(); vInstruction(CON_IFAWAYFROMWALL): { int16_t otherSectnum = vm.pSprite->sectnum; tw = 0; #define IFAWAYDIST 108 updatesector(vm.pSprite->x + IFAWAYDIST, vm.pSprite->y + IFAWAYDIST, &otherSectnum); if (otherSectnum == vm.pSprite->sectnum) { updatesector(vm.pSprite->x - IFAWAYDIST, vm.pSprite->y - IFAWAYDIST, &otherSectnum); if (otherSectnum == vm.pSprite->sectnum) { updatesector(vm.pSprite->x + IFAWAYDIST, vm.pSprite->y - IFAWAYDIST, &otherSectnum); if (otherSectnum == vm.pSprite->sectnum) { updatesector(vm.pSprite->x - IFAWAYDIST, vm.pSprite->y + IFAWAYDIST, &otherSectnum); if (otherSectnum == vm.pSprite->sectnum) tw = 1; } } } branch(tw); #undef IFAWAYDIST } dispatch(); vInstruction(CON_QUOTE): insptr++; VM_ASSERT((unsigned)vm.playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", vm.playerNum); P_DoQuote(*(insptr++) | MAXQUOTES, vm.pPlayer); dispatch(); vInstruction(CON_USERQUOTE): insptr++; tw = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)tw < MAXQUOTES, "invalid quote %d\n", (int)tw); G_AddUserQuote(quoteMgr.GetQuote(tw)); dispatch(); vInstruction(CON_ECHO): insptr++; tw = Gv_GetVar(*insptr++); VM_ASSERT((unsigned)tw < MAXQUOTES, "invalid quote %d\n", (int)tw); Printf("%s\n", quoteMgr.GetQuote(tw)); dispatch(); vInstruction(CON_RESPAWNHITAG): insptr++; switch (DYNAMICTILEMAP(vm.pSprite->picnum)) { #ifndef EDUKE32_STANDALONE case FEM1__STATIC: case FEM2__STATIC: case FEM3__STATIC: case FEM4__STATIC: case FEM5__STATIC: case FEM6__STATIC: case FEM7__STATIC: case FEM8__STATIC: case FEM9__STATIC: case FEM10__STATIC: case PODFEM1__STATIC: case NAKED1__STATIC: case STATUE__STATIC: if (!FURY) { if (vm.pSprite->yvel) G_OperateRespawns(vm.pSprite->yvel); break; } fallthrough__; #endif default: if (vm.pSprite->hitag >= 0) G_OperateRespawns(vm.pSprite->hitag); break; } dispatch(); vInstruction(CON_IFSPRITEPAL): insptr++; branch(vm.pSprite->pal == *insptr); dispatch(); vInstruction(CON_IFANGDIFFL): insptr++; tw = klabs(G_GetAngleDelta(fix16_to_int(vm.pPlayer->q16ang), vm.pSprite->ang)); branch(tw <= *insptr); dispatch(); vInstruction(CON_IFNOSOUNDS): branch(!A_CheckAnySoundPlaying(vm.spriteNum)); dispatch(); vInstruction(CON_SPRITEFLAGS): insptr++; vm.pActor->flags = Gv_GetVar(*insptr++); dispatch(); vInstruction(CON_GETTICKS): insptr++; Gv_SetVar(*insptr++, timerGetTicks()); dispatch(); vInstruction(CON_GETCURRADDRESS): insptr++; tw = *insptr++; Gv_SetVar(tw, (intptr_t)(insptr - apScript)); dispatch(); vmErrorCase: // you're not supposed to be here VM_ScriptInfo(insptr, 64); debug_break(); G_GameExit("An error has occurred in the " GAMENAME " virtual machine.\n\n"); #if 0 "If you are an end user, please e-mail the file " GAMENAMELOWERCASE ".log\n" "along with links to any mods you're using to development@voidpoint.com.\n\n" "If you are a developer, please attach all of your script files\n" "along with instructions on how to reproduce this error.\n\n" "Thank you!"); #endif } #ifndef CON_USE_COMPUTED_GOTO } while (vm_execution_depth && (vm.flags & (VM_RETURN|VM_KILL|VM_NOEXECUTE)) == 0); #endif } // NORECURSE void A_LoadActor(int const spriteNum) { vm.spriteNum = spriteNum; // Sprite ID vm.pSprite = &sprite[spriteNum]; // Pointer to sprite structure vm.pActor = &actor[spriteNum]; if (g_tile[vm.pSprite->picnum].loadPtr == NULL) return; vm.pData = &actor[spriteNum].t_data[0]; // Sprite's 'extra' data vm.playerNum = -1; // Player ID vm.playerDist = -1; // Distance vm.pPlayer = g_player[0].ps; vm.flags &= ~(VM_RETURN|VM_KILL|VM_NOEXECUTE); if ((unsigned)vm.pSprite->sectnum >= MAXSECTORS) { A_DeleteSprite(vm.spriteNum); return; } insptr = g_tile[vm.pSprite->picnum].loadPtr; VM_Execute(true); insptr = NULL; if (vm.flags & VM_KILL) A_DeleteSprite(vm.spriteNum); } #endif void VM_UpdateAnim(int const spriteNum, int32_t * const pData) { #if !defined LUNATIC size_t const actionofs = AC_ACTION_ID(pData); auto const actionptr = (actionofs != 0 && actionofs + (ACTION_PARAM_COUNT-1) < (unsigned) g_scriptSize) ? &apScript[actionofs] : NULL; if (actionptr != NULL) { int const action_frames = actionptr[ACTION_NUMFRAMES]; int const action_incval = actionptr[ACTION_INCVAL]; int const action_delay = actionptr[ACTION_DELAY]; #else int const action_frames = actor[spriteNum].ac.numframes; int const action_incval = actor[spriteNum].ac.incval; int const action_delay = actor[spriteNum].ac.delay; #endif auto actionticsptr = &AC_ACTIONTICS(&sprite[spriteNum], &actor[spriteNum]); *actionticsptr += TICSPERFRAME; if (*actionticsptr > action_delay) { *actionticsptr = 0; AC_ACTION_COUNT(pData)++; AC_CURFRAME(pData) += action_incval; } if (klabs(AC_CURFRAME(pData)) >= klabs(action_frames * action_incval)) AC_CURFRAME(pData) = 0; } } // NORECURSE void A_Execute(int const spriteNum, int const playerNum, int const playerDist) { // for some reason this is faster than using the C++ syntax; e.g vm = vmstate_t{ ... } vmstate_t const tempvm = { spriteNum, playerNum, playerDist, 0, &sprite[spriteNum], &actor[spriteNum].t_data[0], g_player[playerNum].ps, &actor[spriteNum] }; vm = tempvm; #ifndef NETCODE_DISABLE if (g_netClient) { #if 0 if (A_CheckSpriteFlags(spriteNum, SFLAG_NULL)) { A_DeleteSprite(spriteNum); return; } #endif // [75] The server should not overwrite its own randomseed randomseed = ticrandomseed; } #endif if (EDUKE32_PREDICT_FALSE((unsigned)vm.pSprite->sectnum >= MAXSECTORS)) { if (A_CheckEnemySprite(vm.pSprite)) P_AddKills(vm.pPlayer, 1); A_DeleteSprite(vm.spriteNum); return; } VM_UpdateAnim(vm.spriteNum, vm.pData); int const picnum = vm.pSprite->picnum; double t = timerGetHiTicks(); #ifdef LUNATIC int32_t killit=0; if (L_IsInitialized(&g_ElState) && El_HaveActor(picnum)) killit = (El_CallActor(&g_ElState, picnum, spriteNum, playerNum, playerDist)==1); #else insptr = 4 + (g_tile[vm.pSprite->picnum].execPtr); VM_Execute(true); insptr = NULL; #endif t = timerGetHiTicks()-t; g_actorTotalMs[picnum] += t; g_actorMinMs[picnum] = min(g_actorMinMs[picnum], t); g_actorMaxMs[picnum] = max(g_actorMaxMs[picnum], t); g_actorCalls[picnum]++; #ifdef LUNATIC if (!killit) #else if ((vm.flags & VM_KILL) == 0) #endif { VM_Move(); if (vm.pSprite->statnum == STAT_ACTOR) { if (A_CheckEnemySprite(vm.pSprite)) { if (vm.pSprite->xrepeat > 60 || (ud.respawn_monsters == 1 && vm.pSprite->extra <= 0)) return; } else if (EDUKE32_PREDICT_FALSE(ud.respawn_items == 1 && (vm.pSprite->cstat & 32768))) return; if (A_CheckSpriteFlags(vm.spriteNum, SFLAG_USEACTIVATOR) && sector[vm.pSprite->sectnum].lotag & 16384) changespritestat(vm.spriteNum, STAT_ZOMBIEACTOR); else if (vm.pActor->timetosleep > 1) vm.pActor->timetosleep--; else if (vm.pActor->timetosleep == 1) { // hack for 1.3D fire sprites #ifndef EDUKE32_STANDALONE if (!FURY && EDUKE32_PREDICT_FALSE(g_scriptVersion == 13 && (vm.pSprite->picnum == FIRE || vm.pSprite->picnum == FIRE2))) return; #endif changespritestat(vm.spriteNum, STAT_ZOMBIEACTOR); } } #ifndef EDUKE32_STANDALONE else if (!FURY && vm.pSprite->statnum == STAT_STANDABLE) { switch (DYNAMICTILEMAP(vm.pSprite->picnum)) { case RUBBERCAN__STATIC: case EXPLODINGBARREL__STATIC: case WOODENHORSE__STATIC: case HORSEONSIDE__STATIC: case CANWITHSOMETHING__STATIC: case FIREBARREL__STATIC: case NUKEBARREL__STATIC: case NUKEBARRELDENTED__STATIC: case NUKEBARRELLEAKED__STATIC: case TRIPBOMB__STATIC: case EGG__STATIC: if (vm.pActor->timetosleep > 1) vm.pActor->timetosleep--; else if (vm.pActor->timetosleep == 1) changespritestat(vm.spriteNum, STAT_ZOMBIEACTOR); default: break; } } #endif } else VM_DeleteSprite(spriteNum, playerNum); } void G_SaveMapState(void) { int const levelNum = ud.volume_number * MAXLEVELS + ud.level_number; map_t *const pMapInfo = &g_mapInfo[levelNum]; if (pMapInfo->savedstate == NULL) { pMapInfo->savedstate = (mapstate_t *) Xaligned_alloc(ACTOR_VAR_ALIGNMENT, sizeof(mapstate_t)); Bmemset(pMapInfo->savedstate, 0, sizeof(mapstate_t)); } mapstate_t *save = pMapInfo->savedstate; if (save == NULL) return; save->numwalls = numwalls; Bmemcpy(&save->wall[0],&wall[0],sizeof(walltype)*MAXWALLS); save->numsectors = numsectors; Bmemcpy(&save->sector[0],§or[0],sizeof(sectortype)*MAXSECTORS); Bmemcpy(&save->sprite[0],&sprite[0],sizeof(spritetype)*MAXSPRITES); // If we're in EVENT_ANIMATESPRITES, we'll be saving pointer values to disk :-/ #if !defined LUNATIC if (EDUKE32_PREDICT_FALSE(g_currentEvent == EVENT_ANIMATESPRITES)) Printf("Line %d: savemapstate called from EVENT_ANIMATESPRITES. WHY?\n", VM_DECODE_LINE_NUMBER(g_tw)); #endif Bmemcpy(&save->spriteext[0],&spriteext[0],sizeof(spriteext_t)*MAXSPRITES); #ifndef NEW_MAP_FORMAT Bmemcpy(&save->wallext[0],&wallext[0],sizeof(wallext_t)*MAXWALLS); #endif save->numsprites = Numsprites; save->tailspritefree = tailspritefree; Bmemcpy(&save->headspritesect[0],&headspritesect[0],sizeof(headspritesect)); Bmemcpy(&save->prevspritesect[0],&prevspritesect[0],sizeof(prevspritesect)); Bmemcpy(&save->nextspritesect[0],&nextspritesect[0],sizeof(nextspritesect)); Bmemcpy(&save->headspritestat[0],&headspritestat[0],sizeof(headspritestat)); Bmemcpy(&save->prevspritestat[0],&prevspritestat[0],sizeof(prevspritestat)); Bmemcpy(&save->nextspritestat[0],&nextspritestat[0],sizeof(nextspritestat)); #ifdef YAX_ENABLE save->numyaxbunches = numyaxbunches; # if !defined NEW_MAP_FORMAT Bmemcpy(save->yax_bunchnum, yax_bunchnum, sizeof(yax_bunchnum)); Bmemcpy(save->yax_nextwall, yax_nextwall, sizeof(yax_nextwall)); # endif #endif Bmemcpy(&save->actor[0],&actor[0],sizeof(actor_t)*MAXSPRITES); save->g_cyclerCnt = g_cyclerCnt; Bmemcpy(save->g_cyclers, g_cyclers, sizeof(g_cyclers)); Bmemcpy(&save->g_playerSpawnPoints[0],&g_playerSpawnPoints[0],sizeof(g_playerSpawnPoints)); save->g_animWallCnt = g_animWallCnt; Bmemcpy(&save->SpriteDeletionQueue[0],&SpriteDeletionQueue[0],sizeof(SpriteDeletionQueue)); save->g_spriteDeleteQueuePos = g_spriteDeleteQueuePos; Bmemcpy(&save->animwall[0],&animwall[0],sizeof(animwall)); Bmemcpy(&save->origins[0],&g_origins[0],sizeof(g_origins)); Bmemcpy(&save->g_mirrorWall[0],&g_mirrorWall[0],sizeof(g_mirrorWall)); Bmemcpy(&save->g_mirrorSector[0],&g_mirrorSector[0],sizeof(g_mirrorSector)); save->g_mirrorCount = g_mirrorCount; save->show2dsector = show2dsector; save->g_cloudCnt = g_cloudCnt; Bmemcpy(&save->g_cloudSect[0],&g_cloudSect[0],sizeof(g_cloudSect)); save->g_cloudX = g_cloudX; save->g_cloudY = g_cloudY; save->pskyidx = g_pskyidx; Bmemcpy(&save->g_animateGoal[0],&g_animateGoal[0],sizeof(g_animateGoal)); Bmemcpy(&save->g_animateVel[0],&g_animateVel[0],sizeof(g_animateVel)); save->g_animateCnt = g_animateCnt; Bmemcpy(&save->g_animateSect[0],&g_animateSect[0],sizeof(g_animateSect)); G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_FWD); Bmemcpy(&save->g_animatePtr[0],&g_animatePtr[0],sizeof(g_animatePtr)); G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK); { EDUKE32_STATIC_ASSERT(sizeof(save->g_animatePtr) == sizeof(g_animatePtr)); } save->g_playerSpawnCnt = g_playerSpawnCnt; save->g_earthquakeTime = g_earthquakeTime; save->randomseed = randomseed; save->g_globalRandom = g_globalRandom; #if !defined LUNATIC for (native_t i=g_gameVarCount-1; i>=0; i--) { if (aGameVars[i].flags & GAMEVAR_NORESET) continue; if (aGameVars[i].flags & GAMEVAR_PERPLAYER) { if (!save->vars[i]) save->vars[i] = (intptr_t *)Xaligned_alloc(PLAYER_VAR_ALIGNMENT, MAXPLAYERS * sizeof(intptr_t)); Bmemcpy(&save->vars[i][0], aGameVars[i].pValues, sizeof(intptr_t) * MAXPLAYERS); } else if (aGameVars[i].flags & GAMEVAR_PERACTOR) { if (!save->vars[i]) save->vars[i] = (intptr_t *)Xaligned_alloc(ACTOR_VAR_ALIGNMENT, MAXSPRITES * sizeof(intptr_t)); Bmemcpy(&save->vars[i][0], aGameVars[i].pValues, sizeof(intptr_t) * MAXSPRITES); } else save->vars[i] = (intptr_t *)aGameVars[i].global; } for (native_t i=g_gameArrayCount-1; i>=0; i--) { if ((aGameArrays[i].flags & GAMEARRAY_RESTORE) == 0) continue; save->arraysiz[i] = aGameArrays[i].size; Xaligned_free(save->arrays[i]); save->arrays[i] = (intptr_t *)Xaligned_alloc(ARRAY_ALIGNMENT, Gv_GetArrayAllocSize(i)); Bmemcpy(&save->arrays[i][0], aGameArrays[i].pValues, Gv_GetArrayAllocSize(i)); } #else int32_t slen; const char *svcode = El_SerializeGamevars(&slen, levelNum); if (slen < 0) { El_OnError("ERROR: savemapstate: serialization failed!"); } else { char *savecode = Xstrdup(svcode); Xfree(save->savecode); save->savecode = savecode; } #endif ototalclock = totalclock; } void G_RestoreMapState(void) { int const levelNum = ud.volume_number * MAXLEVELS + ud.level_number; mapstate_t *pSavedState = g_mapInfo[levelNum].savedstate; if (pSavedState != NULL) { int playerHealth[MAXPLAYERS]; for (native_t i=0; ii].extra; pub = NUMPAGES; pus = NUMPAGES; G_UpdateScreenArea(); numwalls = pSavedState->numwalls; Bmemcpy(&wall[0],&pSavedState->wall[0],sizeof(walltype)*MAXWALLS); #ifndef NEW_MAP_FORMAT Bmemcpy(&wallext[0],&pSavedState->wallext[0],sizeof(wallext_t)*MAXWALLS); #endif numsectors = pSavedState->numsectors; Bmemcpy(§or[0],&pSavedState->sector[0],sizeof(sectortype)*MAXSECTORS); Bmemcpy(&sprite[0],&pSavedState->sprite[0],sizeof(spritetype)*MAXSPRITES); Bmemcpy(&spriteext[0],&pSavedState->spriteext[0],sizeof(spriteext_t)*MAXSPRITES); // If we're restoring from EVENT_ANIMATESPRITES, all spriteext[].tspr // will be overwritten, so NULL them. #if !defined LUNATIC if (EDUKE32_PREDICT_FALSE(g_currentEvent == EVENT_ANIMATESPRITES)) { Printf("Line %d: loadmapstate called from EVENT_ANIMATESPRITES. WHY?\n", VM_DECODE_LINE_NUMBER(g_tw)); for (native_t i=0; inumsprites; tailspritefree = pSavedState->tailspritefree; Bmemcpy(&headspritesect[0],&pSavedState->headspritesect[0],sizeof(headspritesect)); Bmemcpy(&prevspritesect[0],&pSavedState->prevspritesect[0],sizeof(prevspritesect)); Bmemcpy(&nextspritesect[0],&pSavedState->nextspritesect[0],sizeof(nextspritesect)); Bmemcpy(&headspritestat[0],&pSavedState->headspritestat[0],sizeof(headspritestat)); Bmemcpy(&prevspritestat[0],&pSavedState->prevspritestat[0],sizeof(prevspritestat)); Bmemcpy(&nextspritestat[0],&pSavedState->nextspritestat[0],sizeof(nextspritestat)); #ifdef YAX_ENABLE numyaxbunches = pSavedState->numyaxbunches; # if !defined NEW_MAP_FORMAT Bmemcpy(yax_bunchnum, pSavedState->yax_bunchnum, sizeof(yax_bunchnum)); Bmemcpy(yax_nextwall, pSavedState->yax_nextwall, sizeof(yax_nextwall)); # endif #endif Bmemcpy(&actor[0],&pSavedState->actor[0],sizeof(actor_t)*MAXSPRITES); g_cyclerCnt = pSavedState->g_cyclerCnt; Bmemcpy(g_cyclers, pSavedState->g_cyclers, sizeof(g_cyclers)); Bmemcpy(&g_playerSpawnPoints[0],&pSavedState->g_playerSpawnPoints[0],sizeof(g_playerSpawnPoints)); g_animWallCnt = pSavedState->g_animWallCnt; Bmemcpy(&SpriteDeletionQueue[0],&pSavedState->SpriteDeletionQueue[0],sizeof(SpriteDeletionQueue)); g_spriteDeleteQueuePos = pSavedState->g_spriteDeleteQueuePos; Bmemcpy(&animwall[0],&pSavedState->animwall[0],sizeof(animwall)); Bmemcpy(&g_origins[0],&pSavedState->origins[0],sizeof(g_origins)); Bmemcpy(&g_mirrorWall[0],&pSavedState->g_mirrorWall[0],sizeof(g_mirrorWall)); Bmemcpy(&g_mirrorSector[0],&pSavedState->g_mirrorSector[0],sizeof(g_mirrorSector)); g_mirrorCount = pSavedState->g_mirrorCount; show2dsector = pSavedState->show2dsector; g_cloudCnt = pSavedState->g_cloudCnt; Bmemcpy(&g_cloudSect[0],&pSavedState->g_cloudSect[0],sizeof(g_cloudSect)); g_cloudX = pSavedState->g_cloudX; g_cloudY = pSavedState->g_cloudY; g_pskyidx = pSavedState->pskyidx; Bmemcpy(&g_animateGoal[0],&pSavedState->g_animateGoal[0],sizeof(g_animateGoal)); Bmemcpy(&g_animateVel[0],&pSavedState->g_animateVel[0],sizeof(g_animateVel)); g_animateCnt = pSavedState->g_animateCnt; Bmemcpy(&g_animateSect[0],&pSavedState->g_animateSect[0],sizeof(g_animateSect)); Bmemcpy(&g_animatePtr[0],&pSavedState->g_animatePtr[0],sizeof(g_animatePtr)); G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK); g_playerSpawnCnt = pSavedState->g_playerSpawnCnt; g_earthquakeTime = pSavedState->g_earthquakeTime; randomseed = pSavedState->randomseed; g_globalRandom = pSavedState->g_globalRandom; #if !defined LUNATIC for (native_t i=g_gameVarCount-1; i>=0; i--) { if (aGameVars[i].flags & GAMEVAR_NORESET) continue; if (aGameVars[i].flags & GAMEVAR_PERPLAYER) { if (!pSavedState->vars[i]) continue; Bmemcpy(aGameVars[i].pValues, pSavedState->vars[i], sizeof(intptr_t) * MAXPLAYERS); } else if (aGameVars[i].flags & GAMEVAR_PERACTOR) { if (!pSavedState->vars[i]) continue; Bmemcpy(aGameVars[i].pValues, pSavedState->vars[i], sizeof(intptr_t) * MAXSPRITES); } else aGameVars[i].global = (intptr_t)pSavedState->vars[i]; } for (native_t i=g_gameArrayCount-1; i>=0; i--) { if ((aGameArrays[i].flags & GAMEARRAY_RESTORE) == 0) continue; aGameArrays[i].size = pSavedState->arraysiz[i]; Xaligned_free(aGameArrays[i].pValues); aGameArrays[i].pValues = (intptr_t *) Xaligned_alloc(ARRAY_ALIGNMENT, Gv_GetArrayAllocSize(i)); Bmemcpy(aGameArrays[i].pValues, pSavedState->arrays[i], Gv_GetArrayAllocSize(i)); } Gv_RefreshPointers(); #else if (pSavedState->savecode) { El_RestoreGamevars(pSavedState->savecode); } #endif // Update g_player[].ps->i (sprite indices of players) to be consistent // with just loaded sprites. // Otherwise, crashes may ensue: e.g. WGR2 SVN r391, map spiderden: // - walk forward (to door leading to other level "Shadowpine Forest") // - in new level, walk backward to get back to the Spider Den // - walk backward to the door leading to Shadowpine Forest --> crash. for (native_t SPRITES_OF(STAT_PLAYER, i)) { int32_t snum = P_Get(i); Bassert((unsigned)snum < MAXPLAYERS); g_player[snum].ps->i = i; } for (native_t i=0; ii].extra = playerHealth[i]; if (g_player[myconnectindex].ps->over_shoulder_on != 0) { CAMERADIST = 0; CAMERACLOCK = 0; g_player[myconnectindex].ps->over_shoulder_on = 1; } screenpeek = myconnectindex; #ifndef EDUKE32_STANDALONE if (adult_lockout) { for (native_t x=g_animWallCnt-1; x>=0; x--) switch (DYNAMICTILEMAP(wall[animwall[x].wallnum].picnum)) { case FEMPIC1__STATIC: wall[animwall[x].wallnum].picnum = BLANKSCREEN; break; case FEMPIC2__STATIC: case FEMPIC3__STATIC: wall[animwall[x].wallnum].picnum = SCREENBREAK6; break; } } #if 0 else { for (native_t x=g_numAnimWalls-1; x>=0; x--) if (wall[animwall[x].wallnum].extra >= 0) wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra; } #endif #endif #ifdef YAX_ENABLE yax_update(numyaxbunches > 0 ? 2 : 1); #endif G_ResetInterpolations(); Net_ResetPrediction(); G_ClearFIFO(); G_ResetTimers(0); } } #ifdef LUNATIC void VM_FallSprite(int32_t i) { VM_Fall(i, &sprite[i]); } int32_t VM_ResetPlayer2(int32_t snum, int32_t flags) { return VM_ResetPlayer(snum, 0, flags); } int32_t VM_CheckSquished2(int32_t i, int32_t snum) { vm.spriteNum = i; vm.pSprite = &sprite[i]; vm.playerNum = snum; vm.pPlayer = g_player[snum].ps; return VM_CheckSquished(); } #endif // MYOS* CON commands. void VM_DrawTileGeneric(int32_t x, int32_t y, int32_t zoom, int32_t tilenum, int32_t shade, int32_t orientation, int32_t p) { orientation &= (ROTATESPRITE_MAX-1); int const rotAngle = (orientation&4) ? 1024 : 0; if (!(orientation&ROTATESPRITE_FULL16)) { x<<=16; y<<=16; } rotatesprite_win(x, y, zoom, rotAngle, tilenum, shade, p, 2|orientation); } #if !defined LUNATIC void VM_DrawTile(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation) { auto const pPlayer = g_player[screenpeek].ps; int32_t tilePal = pPlayer->cursectnum >= 0 ? sector[pPlayer->cursectnum].floorpal : 0; VM_DrawTileGeneric(x, y, 65536, tilenum, shade, orientation, tilePal); } void VM_DrawTileSmall(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation) { auto const pPlayer = g_player[screenpeek].ps; int32_t tilePal = pPlayer->cursectnum >= 0 ? sector[pPlayer->cursectnum].floorpal : 0; VM_DrawTileGeneric(x, y, 32768, tilenum, shade, orientation, tilePal); } #endif END_DUKE_NS