//-------------------------------------------------------------------------
/*
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 "colmatch.h"
#include "compat.h"

#include "duke3d.h"

#include "anim.h"
#include "input.h"
#include "menus.h"
#include "osdcmds.h"
#include "savegame.h"
#include "scriplib.h"

#include "debugbreak.h"

BEGIN_RR_NS

#if KRANDDEBUG
# define GAMEEXEC_INLINE
# define GAMEEXEC_STATIC
#else
# define GAMEEXEC_INLINE inline
# define GAMEEXEC_STATIC static
#endif

vmstate_t vm;

enum vmflags_t
{
    VM_RETURN       = 0x00000001,
    VM_KILL         = 0x00000002,
    VM_NOEXECUTE    = 0x00000004,
    VM_SAFEDELETE   = 0x00000008
};

int32_t g_tw;
int32_t g_errorLineNum;

intptr_t const *insptr;

// for timing events and actors
uint32_t g_actorCalls[MAXTILES];
double g_actorTotalMs[MAXTILES], g_actorMinMs[MAXTILES], g_actorMaxMs[MAXTILES];

GAMEEXEC_STATIC void VM_Execute(native_t loop);

# include "gamestructures.cpp"

#define VM_CONDITIONAL(xxx)                                                                                            \
    {                                                                                                                  \
        if ((xxx) || ((insptr = (intptr_t *)*(insptr + 1)) && (((*insptr) & VM_INSTMASK) == CON_ELSE)))                \
        {                                                                                                              \
            insptr += 2;                                                                                               \
            VM_Execute(0);                                                                                             \
        }                                                                                                              \
    }

void VM_ScriptInfo(intptr_t const *ptr, int range)
{
    if (!apScript || (!vm.pSprite && !vm.pPlayer))
        return;

    if (ptr)
    {
        initprintf("\n");

        for (auto pScript = max<intptr_t const *>(ptr - (range >> 1), apScript),
                  p_end   = min<intptr_t const *>(ptr + (range >> 1), apScript + g_scriptSize);
             pScript < p_end;
             ++pScript)
        {
            initprintf("%5d: %3d: ", (int32_t)(pScript - apScript), (int32_t)(pScript - ptr));

            if (*pScript >> 12 && (*pScript & VM_INSTMASK) < CON_END)
                initprintf("%5d %s\n", (int32_t)(*pScript >> 12), VM_GetKeywordForID(*pScript & VM_INSTMASK));
            else
                initprintf("%d\n", (int32_t)*pScript);
        }

        initprintf("\n");
    }

    if (ptr == insptr)
    {
        if (vm.pUSprite)
            initprintf("current actor: %d (%d)\n", vm.spriteNum, vm.pUSprite->picnum);

        initprintf("g_errorLineNum: %d, g_tw: %d\n", g_errorLineNum, g_tw);
    }
}

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);
}

static int32_t VM_CheckSquished(void)
{
    if (RR)
        return 0;

    usectortype const * const pSector = (usectortype *)&sector[vm.pSprite->sectnum];

    if (pSector->lotag == ST_23_SWINGING_DOOR ||
        (vm.pSprite->picnum == APLAYER && ud.noclip))
        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;

    if (EDUKE32_PREDICT_FALSE(vm.pSprite->pal == 1)) // frozen
    {
        vm.pActor->picnum = SHOTSPARK1;
        vm.pActor->extra  = 1;
        return 0;
    }

    return 1;
}

GAMEEXEC_STATIC GAMEEXEC_INLINE void P_ForceAngle(DukePlayer_t *pPlayer)
{
    int const nAngle = 128-(krand2()&255);

    pPlayer->q16horiz           += F16(64);
    pPlayer->return_to_center = 9;
    pPlayer->rotscrnang       = nAngle >> 1;
    pPlayer->look_ang         = pPlayer->rotscrnang;
}

// wow, this function sucks
int32_t A_Dodge(spritetype * const);
int32_t A_Dodge(spritetype * const pSprite)
{
    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 || SECT(i) != pSprite->sectnum)
            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+(krand2()&1024);
                return 1;
            }
        }
    }

    return 0;
}

int32_t A_GetFurthestAngle(int const spriteNum, int const angDiv)
{
    uspritetype *const pSprite = (uspritetype *)&sprite[spriteNum];

    if (pSprite->picnum != APLAYER && (AC_COUNT(actor[spriteNum].t_data)&63) > 2)
        return pSprite->ang + 1024;

    int32_t   furthestAngle = 0;
    int32_t   greatestDist  = INT32_MIN;
    int const angIncs       = tabledivide32_noinline(2048, angDiv);
    hitdata_t hit;

    for (native_t j = pSprite->ang; j < (2048 + pSprite->ang); j += angIncs)
    {
        pSprite->z -= ZOFFSET3;
        hitscan((const vec3_t *)pSprite, pSprite->sectnum, sintable[(j + 512) & 2047], sintable[j & 2047], 0, &hit, CLIPMASK1);
        pSprite->z += ZOFFSET3;

        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, uspritetype * const ts, vec2_t * const vect)
{
    if (AC_COUNT(actor[spriteNum].t_data)&63)
        return -1;

    const uspritetype *const pnSprite = (uspritetype *)&sprite[spriteNum];

    hitdata_t hit;
    int const angincs = ((!g_netServer && ud.multimode < 2) && ud.player_skill < 3) ? 2048 / 2 : tabledivide32_noinline(2048, 1 + (krand2() & 1));

    for (native_t j = ts->ang; j < (2048 + ts->ang); j += (angincs-(krand2()&511)))
    {
        ts->z -= ZOFFSET2;
        hitscan((const vec3_t *)ts, ts->sectnum, sintable[(j + 512) & 2047], sintable[j & 2047], 16384 - (krand2() & 32767), &hit, CLIPMASK1);
        ts->z += ZOFFSET2;

        if (hit.sect < 0)
            continue;

        int const d  = klabs(hit.pos.x - ts->x) + klabs(hit.pos.y - ts->y);
        int const da = klabs(hit.pos.x - pnSprite->x) + klabs(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;
}

static void VM_GetZRange(int const spriteNum, int32_t * const ceilhit, int32_t * const florhit, int const wallDist)
{
    uspritetype *const pSprite = (uspritetype *)&sprite[spriteNum];
    vec3_t const tempVect = {
        pSprite->x, pSprite->y, pSprite->z - ZOFFSET
    };
    getzrange(&tempVect, pSprite->sectnum, &actor[spriteNum].ceilingz, ceilhit, &actor[spriteNum].floorz, florhit, wallDist, CLIPMASK0);
}

void A_GetZLimits(int const spriteNum)
{
    spritetype *const pSprite = &sprite[spriteNum];
    int32_t           ceilhit, florhit;

    if (pSprite->statnum == STAT_PLAYER || pSprite->statnum == STAT_STANDABLE || pSprite->statnum == STAT_ZOMBIEACTOR
        || pSprite->statnum == STAT_ACTOR || pSprite->statnum == STAT_PROJECTILE)
    {
        VM_GetZRange(spriteNum, &ceilhit, &florhit, (pSprite->statnum == STAT_PROJECTILE) ? 4 : 127);
        actor[spriteNum].flags &= ~SFLAG_NOFLOORSHADOW;

        if ((florhit&49152) == 49152 && (sprite[florhit&(MAXSPRITES-1)].cstat&48) == 0)
        {
            uspritetype const * const hitspr = (uspritetype *)&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;
            }
        }
    }
    else
    {
        actor[spriteNum].ceilingz = sector[pSprite->sectnum].ceilingz;
        actor[spriteNum].floorz   = sector[pSprite->sectnum].floorz;
    }
}

void A_Fall(int const spriteNum)
{
    spritetype *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;
    
    if (RRRA && spriteGravity == g_spriteGravity)
    {
        if (pSprite->picnum == BIKERB || pSprite->picnum == CHEERB)
            spriteGravity >>= 2;
        else if (pSprite->picnum == BIKERBV2)
            spriteGravity >>= 3;
    }

    if (pSprite->statnum == STAT_ACTOR || pSprite->statnum == STAT_PLAYER || pSprite->statnum == STAT_ZOMBIEACTOR
        || pSprite->statnum == STAT_STANDABLE)
    {
        int32_t ceilhit, florhit;
        VM_GetZRange(spriteNum, &ceilhit, &florhit, 127);
    }
    else
    {
        actor[spriteNum].ceilingz = sector[pSprite->sectnum].ceilingz;
        actor[spriteNum].floorz   = sector[pSprite->sectnum].floorz;
    }

#ifdef YAX_ENABLE
    int fbunch = (sector[pSprite->sectnum].floorstat&512) ? -1 : yax_getbunch(pSprite->sectnum, YAX_FLOOR);
#endif

    if (pSprite->z < actor[spriteNum].floorz-ZOFFSET
#ifdef YAX_ENABLE
            || fbunch >= 0
#endif
       )
    {
        if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER && pSprite->zvel > 3122)
            pSprite->zvel = 3144;
        if (pSprite->zvel < 6144)
            pSprite->zvel += spriteGravity;
        else pSprite->zvel = 6144;
        pSprite->z += pSprite->zvel;
    }

#ifdef YAX_ENABLE
    if (fbunch >= 0)
        setspritez(spriteNum, (vec3_t *)pSprite);
    else
#endif
        if (pSprite->z >= actor[spriteNum].floorz-ZOFFSET)
        {
            pSprite->z = actor[spriteNum].floorz-ZOFFSET;
            pSprite->zvel = 0;
        }
}

int32_t __fastcall G_GetAngleDelta(int32_t currAngle, int32_t newAngle)
{
    currAngle &= 2047;
    newAngle &= 2047;

    if (klabs(currAngle-newAngle) < 1024)
    {
//        OSD_Printf("G_GetAngleDelta() returning %d\n",na-a);
        return newAngle-currAngle;
    }

    if (newAngle > 1024)
        newAngle -= 2048;
    if (currAngle > 1024)
        currAngle -= 2048;

//    OSD_Printf("G_GetAngleDelta() returning %d\n",na-a);
    return newAngle-currAngle;
}

GAMEEXEC_STATIC void VM_AlterAng(int32_t const moveFlags)
{
    int const elapsedTics = (AC_COUNT(vm.pData))&31;

    const intptr_t *moveptr;
    if (EDUKE32_PREDICT_FALSE((unsigned)AC_MOVE_ID(vm.pData) >= (unsigned)g_scriptSize-1))

    {
        AC_MOVE_ID(vm.pData) = 0;
        OSD_Printf(OSD_ERROR "bad moveptr for actor %d (%d)!\n", vm.spriteNum, vm.pUSprite->picnum);
        return;
    }

    moveptr = apScript + AC_MOVE_ID(vm.pData);

    vm.pSprite->xvel += (moveptr[0] - vm.pSprite->xvel)/5;
    if (vm.pSprite->zvel < 648)
        vm.pSprite->zvel += ((moveptr[1]<<4) - vm.pSprite->zvel)/5;

    if (RRRA && (moveFlags&windang))
        vm.pSprite->ang = g_windDir;
    else 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 = (!RR && 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-(krand2()&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 = 0;

    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));
}

////////// TROR get*zofslope //////////
// These rather belong into the engine.

static int32_t VM_GetCeilZOfSlope(void)
{
    vec2_t const vect     = *(vec2_t *)vm.pSprite;
    int const    sectnum  = vm.pSprite->sectnum;

#ifdef YAX_ENABLE
    if ((sector[sectnum].ceilingstat&512)==0)
    {
        int const nsect = yax_getneighborsect(vect.x, vect.y, sectnum, YAX_CEILING);
        if (nsect >= 0)
            return getceilzofslope(nsect, vect.x, vect.y);
    }
#endif
    return getceilzofslope(sectnum, vect.x, vect.y);
}

static int32_t VM_GetFlorZOfSlope(void)
{
    vec2_t const vect    = *(vec2_t *)vm.pSprite;
    int const    sectnum = vm.pSprite->sectnum;

#ifdef YAX_ENABLE
    if ((sector[sectnum].floorstat&512)==0)
    {
        int const nsect = yax_getneighborsect(vect.x, vect.y, sectnum, YAX_FLOOR);
        if (nsect >= 0)
            return getflorzofslope(nsect, vect.x, vect.y);
    }
#endif
    return getflorzofslope(sectnum, vect.x, vect.y);
}

////////////////////

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 == (std::remove_pointer<decltype(movflagsptr)>::type)-1) ? 0 : *movflagsptr;
    
    AC_COUNT(vm.pData)++;

    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)
    {
        int const goalAng = (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);

        vm.pSprite->ang += ksgn(G_GetAngleDelta(vm.pSprite->ang, goalAng)) << 5;
    }

    if (RRRA && (movflags&antifaceplayerslow))
    {
        int goalAng = (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);
        goalAng = (goalAng+1024)&2047;

        vm.pSprite->ang += ksgn(G_GetAngleDelta(vm.pSprite->ang, goalAng)) << 5;
    }

    if ((movflags&jumptoplayer_bits) == jumptoplayer_bits)
    {
        if (AC_COUNT(vm.pData) < 16)
            vm.pSprite->zvel -= (RRRA && vm.pSprite->picnum == CHEER) ? (sintable[(512+(AC_COUNT(vm.pData)<<4))&2047]/40)
            : (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 (RRRA && (vm.pSprite->picnum == RABBIT || vm.pSprite->picnum == MAMA))
    {
        if(movflags&jumptoplayer_only)
        {
            if (AC_COUNT(vm.pData) < 8)
                vm.pSprite->zvel -= sintable[(512+(AC_COUNT(vm.pData)<<4))&2047]/(vm.pSprite->picnum == RABBIT ? 30 : 35);
        }
        if(movflags&justjump2)
        {
            if (AC_COUNT(vm.pData) < 8)
                vm.pSprite->zvel -= sintable[(512+(AC_COUNT(vm.pData)<<4))&2047]/(vm.pSprite->picnum == RABBIT ? 24 : 28);
        }
    }

    if (RRRA && (movflags&windang))
    {
        if (AC_COUNT(vm.pData) < 8)
            vm.pSprite->zvel -= sintable[(512+(AC_COUNT(vm.pData)<<4))&2047]/24;
    }
    
    if (AC_MOVE_ID(vm.pData) == 0 || movflags == 0)
    {
        if ((A_CheckEnemySprite(vm.pSprite) && vm.pSprite->extra <= 0) || (vm.pActor->bpos.x != vm.pSprite->x) || (vm.pActor->bpos.y != vm.pSprite->y))
        {
            vm.pActor->bpos.x = vm.pSprite->x;
            vm.pActor->bpos.y = vm.pSprite->y;
            setsprite(vm.spriteNum, (vec3_t *)vm.pSprite);
        }
        if (RR && A_CheckEnemySprite(vm.pSprite) && vm.pSprite->extra <= 0)
        {
            vm.pSprite->shade += (sector[vm.pSprite->sectnum].ceilingstat & 1) ? ((g_shadedSector[vm.pSprite->sectnum] == 1 ? 16 : sector[vm.pSprite->sectnum].ceilingshade) - vm.pSprite->shade) >> 1
                                                                 : (sector[vm.pSprite->sectnum].floorshade - vm.pSprite->shade) >> 1;
        }
        return;
    }

    if (EDUKE32_PREDICT_FALSE((unsigned)AC_MOVE_ID(vm.pData) >= (unsigned)g_scriptSize-1))
    {
        AC_MOVE_ID(vm.pData) = 0;
        OSD_Printf(OSD_ERROR "clearing bad moveptr for actor %d (%d)\n", vm.spriteNum, vm.pUSprite->picnum);
        return;
    }

    intptr_t const * const moveptr = apScript + AC_MOVE_ID(vm.pData);

    if (movflags & geth)
        vm.pSprite->xvel += ((moveptr[0]) - vm.pSprite->xvel) >> 1;
    if (movflags & getv)
        vm.pSprite->zvel += ((moveptr[1] << 4) - vm.pSprite->zvel) >> 1;

    if (movflags&dodgebullet)
        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;

        if (badguyp && (vm.pSprite->picnum != ROTATEGUN || RR))
        {
            if ((vm.pSprite->picnum == DRONE || (!RR && vm.pSprite->picnum == COMMANDER)) && vm.pSprite->extra > 0)
            {
                if (!RR && 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();
                        int const zDiff = RRRA ? (28<<8) : (30<<8);
                        if (vm.pSprite->z > nSectorZ-zDiff)
                            vm.pSprite->z = nSectorZ-zDiff;
                    }
                    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 (vm.pSprite->picnum != ORGANTIC || RR)
            {
                // 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 if (vm.pSprite->picnum != DRONE && vm.pSprite->picnum != SHARK
                && ((!RR && vm.pSprite->picnum != COMMANDER)
                    || (RR && vm.pSprite->picnum != UFO1)
                    || (RR && !RRRA && vm.pSprite->picnum != UFO2 && vm.pSprite->picnum != UFO3 && vm.pSprite->picnum != UFO4 && vm.pSprite->picnum != UFO5)))
            {
                if (vm.pPlayer->actorsqu == vm.spriteNum)
                    return;

                if (vm.pActor->bpos.z != vm.pSprite->z || (!g_netServer && ud.multimode < 2 && ud.player_skill < 2))
                {
                    if (AC_COUNT(vm.pData)&1) return;
                    spriteXvel <<= 1;
                }
                else
                {
                    if (AC_COUNT(vm.pData)&3) return;
                    spriteXvel <<= 2;
                }
            }
        }
        else if (vm.pSprite->picnum == APLAYER)
            if (vm.pSprite->z < vm.pActor->ceilingz+ZOFFSET5)
                vm.pSprite->z = vm.pActor->ceilingz+ZOFFSET5;

        if (RRRA)
        {
            if (sector[vm.pSprite->sectnum].lotag != ST_1_ABOVE_WATER)
            {
                switch (DYNAMICTILEMAP(vm.pSprite->picnum))
                {
                case MINIONBOAT__STATICRR:
                case HULK__STATICRR:
                case CHEERBOAT__STATICRR:
                    spriteXvel >>= 1;
                    break;
                }
            }
            else
            {
                switch (DYNAMICTILEMAP(vm.pSprite->picnum))
                {
                case BIKERB__STATICRR:
                case BIKERBV2__STATICRR:
                case CHEERB__STATICRR:
                    spriteXvel >>= 1;
                    break;
                }
            }
        }

        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, CLIPMASK0);
    }

    if (!badguyp)
        return;

    vm.pSprite->shade += (sector[vm.pSprite->sectnum].ceilingstat & 1) ? ((g_shadedSector[vm.pSprite->sectnum] == 1 ? 16 : sector[vm.pSprite->sectnum].ceilingshade) - vm.pSprite->shade) >> 1
                                                                 : (sector[vm.pSprite->sectnum].floorshade - vm.pSprite->shade) >> 1;

    if (sector[vm.pSprite->sectnum].floorpicnum == MIRROR)
        A_DeleteSprite(vm.spriteNum);
}

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_AddWeapon(pPlayer, weaponNum);
    }
    else if (pPlayer->ammo_amount[weaponNum] >= pPlayer->max_ammo_amount[weaponNum])
    {
        vm.flags |= VM_NOEXECUTE;
        return;
    }

    P_AddAmmo(pPlayer, weaponNum, nAmount);

    if (pPlayer->curr_weapon == KNEE_WEAPON && (pPlayer->gotweapon & (1<<weaponNum)))
        P_AddWeapon(pPlayer, weaponNum);
}

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_AddAmmo(pPlayer, weaponNum, nAmount);

    if (pPlayer->curr_weapon == KNEE_WEAPON && (pPlayer->gotweapon & (1<<weaponNum)))
        P_AddWeapon(pPlayer, weaponNum);
}

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:
        if (RR)
        {
            switch (vm.pSprite->lotag)
            {
                case 100: pPlayer->keys[1] = 1; break;
                case 101: pPlayer->keys[2] = 1; break;
                case 102: pPlayer->keys[3] = 1; break;
                case 103: pPlayer->keys[4] = 1; break;
            }
        }
        else
        {
            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;
    }
}

static int32_t A_GetWaterZOffset(int const spriteNum)
{
    uspritetype const *const pSprite = (uspritetype *)&sprite[spriteNum];

    if (sector[pSprite->sectnum].lotag == ST_1_ABOVE_WATER)
    {
        if (RRRA)
        {
            switch (DYNAMICTILEMAP(pSprite->picnum))
            {
                case HULKBOAT__STATICRR:
                    return (12<<8);
                case MINIONBOAT__STATICRR:
                    return (3<<8);
                case CHEERBOAT__STATICRR:
                case EMPTYBOAT__STATICRR:
                    return (6<<8);
            }
        }
        if (A_CheckSpriteFlags(spriteNum, SFLAG_NOWATERDIP))
            return 0;

        return ACTOR_ONWATER_ADDZ;
    }

    return 0;
}

static void VM_Fall(int const spriteNum, spritetype * const pSprite)
{
    int spriteGravity = g_spriteGravity;
    int hitSprite = 0;

    pSprite->xoffset = pSprite->yoffset = 0;

    if (RR)
    {
        if (RRRA)
        {
            if (sector[vm.pSprite->sectnum].lotag == 801)
            {
                if (vm.pSprite->picnum == ROCK)
                {
                    A_Spawn(vm.spriteNum, ROCK2);
                    A_Spawn(vm.spriteNum, ROCK2);
                    vm.flags |= VM_SAFEDELETE;
                }
            }
            else if (sector[vm.pSprite->sectnum].lotag == 802)
            {
                if (vm.pSprite->picnum != APLAYER && A_CheckEnemySprite(vm.pSprite) && vm.pSprite->z == vm.pActor->floorz - ZOFFSET)
                {
                    A_DoGuts(vm.spriteNum, JIBS6, 5);
                    A_PlaySound(SQUISHED, vm.spriteNum);
                    vm.flags |= VM_SAFEDELETE;
                }
            }
            else if (sector[vm.pSprite->sectnum].lotag == 803)
            {
                if (vm.pSprite->picnum == ROCK2)
                {
                    vm.flags |= VM_SAFEDELETE;
                }
            }
        }
        if (sector[vm.pSprite->sectnum].lotag == 800)
        {
            if (vm.pSprite->picnum == AMMO)
            {
                vm.flags |= VM_SAFEDELETE;
                return;
            }
            if (vm.pSprite->picnum != APLAYER && (A_CheckEnemySprite(vm.pSprite) || vm.pSprite->picnum == COW) && g_spriteExtra[vm.spriteNum] < 128)
            {
                vm.pSprite->z = vm.pActor->floorz-ZOFFSET;
                vm.pSprite->zvel = 8000;
                vm.pSprite->extra = 0;
                g_spriteExtra[vm.spriteNum]++;
                hitSprite = 1;
            }
            else if (vm.pSprite->picnum != APLAYER)
            {
                if (!g_spriteExtra[vm.spriteNum])
                    vm.flags |= VM_SAFEDELETE;
                return;
            }
            vm.pActor->picnum = SHOTSPARK1;
            vm.pActor->extra = 1;
        }
        if (RRRA && EDUKE32_PREDICT_TRUE(sector[vm.pSprite->sectnum].lotag < 800 || sector[vm.pSprite->sectnum].lotag > 803)
            && (sector[vm.pSprite->sectnum].floorpicnum == RRTILE7820 || sector[vm.pSprite->sectnum].floorpicnum == RRTILE7768))
        {
            if (vm.pSprite->picnum != MINION && vm.pSprite->pal != 19)
            {
                if ((krand2()&3) == 1)
                {
                    vm.pActor->picnum = SHOTSPARK1;
                    vm.pActor->extra = 5;
                }
            }
        }
    }

    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 <= 0 || (sector[pSprite->sectnum].floorstat&2))
    {
        A_GetZLimits(spriteNum);
        actor[spriteNum].cgg = 6;
    }
    else actor[spriteNum].cgg--;

    if (pSprite->z < actor[spriteNum].floorz-ZOFFSET)
    {
        // Free fall.
        pSprite->zvel += spriteGravity;
        pSprite->z += pSprite->zvel;

#ifdef YAX_ENABLE
        if (yax_getbunch(pSprite->sectnum, YAX_FLOOR) >= 0 && (sector[pSprite->sectnum].floorstat & 512) == 0)
            setspritez(spriteNum, (vec3_t *)pSprite);
#endif

        if (pSprite->zvel > 6144) pSprite->zvel = 6144;
        return;
    }

    pSprite->z = actor[spriteNum].floorz - ZOFFSET;

    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
            if (!(pSprite->picnum == APLAYER && pSprite->extra > 0) && pSprite->pal != 1 && pSprite->picnum != DRONE)
            {
                A_PlaySound(SQUISHED,spriteNum);
                if (hitSprite)
                {
                    A_DoGuts(spriteNum,JIBS6,5);
                }
                else
                {
                    A_DoGuts(spriteNum,JIBS6,15);
                    A_Spawn(spriteNum,BLOODPOOL);
                }
            }
            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((vec3_t *)pSprite, &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)
    {
        pSprite->z += A_GetWaterZOffset(spriteNum);
        return;
    }

    pSprite->zvel = 0;
}

static int32_t VM_ResetPlayer(int const playerNum, int32_t vmFlags)
{
    //AddLog("resetplayer");
    if (!g_netServer && ud.multimode < 2)
    {
        if (g_quickload && g_quickload->isValid() && ud.recstat != 2)
        {
            Menu_Open(playerNum);
            KB_ClearKeyDown(sc_Space);
            I_AdvanceTriggerClear();
            Menu_Change(MENU_RESETPLAYER);
        }
        else
            g_player[playerNum].ps->gm = MODE_RESTART;
        vmFlags |= VM_NOEXECUTE;
    }
    else
    {
        if (playerNum == myconnectindex)
        {
            CAMERADIST = 0;
            CAMERACLOCK = (int32_t) totalclock;
        }

        //if (g_fakeMultiMode)
            P_ResetPlayer(playerNum);
#ifndef NETCODE_DISABLE
        //if (g_netServer)
        //{
        //    P_ResetPlayer(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);

    // initprintf("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;
}

void Screen_Play(void)
{
    int32_t running = 1;

    I_ClearAllInput();

    do
    {
        G_HandleAsync();

        ototalclock = totalclock + 1; // pause game like ANMs

        if (!G_FPSLimit())
            continue;

        videoClearScreen(0);
        if (I_CheckAllInput())
            running = 0;

        videoNextPage();
        I_ClearAllInput();
    } while (running);
}

GAMEEXEC_STATIC void VM_Execute(native_t loop)
{
    native_t            tw      = *insptr;
    DukePlayer_t *const pPlayer = vm.pPlayer;

    // jump directly into the loop, skipping branches during the first iteration
    goto skip_check;

    while (loop)
    {
        if (vm.flags & (VM_RETURN | VM_KILL | VM_NOEXECUTE))
            break;

        tw = *insptr;

    skip_check:
        //      Bsprintf(g_szBuf,"Parsing: %d",*insptr);
        //      AddLog(g_szBuf);

        g_errorLineNum = tw >> 12;
        g_tw           = tw &= VM_INSTMASK;

        if (tw == CON_LEFTBRACE)
        {
            insptr++, loop++;
            continue;
        }
        else if (tw == CON_RIGHTBRACE)
        {
            insptr++, loop--;
            continue;
        }
        else if (tw == CON_ELSE)
        {
            insptr = (intptr_t *)*(insptr + 1);
            continue;
        }
        else if (tw == CON_STATE)
        {
            intptr_t const *const tempscrptr = insptr + 2;
            insptr                           = (intptr_t *)*(insptr + 1);
            VM_Execute(1);
            insptr = tempscrptr;
            continue;
        }

        switch (tw)
        {
            case CON_ENDA:
            case CON_BREAK:
            case CON_ENDS: return;

            case CON_IFRND: VM_CONDITIONAL(rnd(*(++insptr))); continue;

            case CON_IFCANSHOOTTARGET:
            {
                if (vm.playerDist > 1024)
                {
                    int16_t temphit;

                    if ((tw = A_CheckHitSprite(vm.spriteNum, &temphit)) == (1 << 30))
                    {
                        VM_CONDITIONAL(1);
                        continue;
                    }

                    int dist    = 768;
                    int angDiff = 16;

                    if (A_CheckEnemySprite(vm.pSprite) && vm.pSprite->xrepeat > 56)
                    {
                        dist    = 3084;
                        angDiff = 48;
                    }

#define CHECK(x)                                                                                                                                     \
    if (x >= 0 && sprite[x].picnum == vm.pSprite->picnum)                                                                                            \
    {                                                                                                                                                \
        VM_CONDITIONAL(0);                                                                                                                           \
        continue;                                                                                                                                    \
    }
#define CHECK2(x)                                                                                                                                    \
    do                                                                                                                                               \
    {                                                                                                                                                \
        vm.pSprite->ang += x;                                                                                                                        \
        tw = A_CheckHitSprite(vm.spriteNum, &temphit);                                                                                               \
        vm.pSprite->ang -= x;                                                                                                                        \
    } while (0)

                    if (tw > dist)
                    {
                        CHECK(temphit);
                        CHECK2(angDiff);

                        if (tw > dist)
                        {
                            CHECK(temphit);
                            CHECK2(-angDiff);

                            if (tw > 768)
                            {
                                CHECK(temphit);
                                VM_CONDITIONAL(1);
                                continue;
                            }
                        }
                    }
                    VM_CONDITIONAL(0);
                    continue;
                }
                VM_CONDITIONAL(1);
            }
                continue;

            case CON_IFCANSEETARGET:
                tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - ((krand2() & 41) << 8), vm.pSprite->sectnum, pPlayer->pos.x, pPlayer->pos.y,
                            pPlayer->pos.z /*-((krand2()&41)<<8)*/, sprite[pPlayer->i].sectnum);
                VM_CONDITIONAL(tw);
                if (tw)
                    vm.pActor->timetosleep = SLEEPTIME;
                continue;

            case CON_IFNOCOVER:
                tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z, vm.pSprite->sectnum, pPlayer->pos.x, pPlayer->pos.y,
                            pPlayer->pos.z, sprite[pPlayer->i].sectnum);
                VM_CONDITIONAL(tw);
                if (tw)
                    vm.pActor->timetosleep = SLEEPTIME;
                continue;

            case CON_IFACTORNOTSTAYPUT: VM_CONDITIONAL(vm.pActor->actorstayput == -1); continue;

            case CON_IFCANSEE:
            {
                uspritetype *pSprite = (uspritetype *)&sprite[pPlayer->i];

// select sprite for monster to target
// if holoduke is on, let them target holoduke first.
//
                if (!RR && pPlayer->holoduke_on >= 0)
                {
                    pSprite = (uspritetype *)&sprite[pPlayer->holoduke_on];
                    tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - (krand2() & (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 = (uspritetype *)&sprite[pPlayer->i];
                    }
                }
                // can they see player, (or player's holoduke)
                tw = cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - (krand2() & ((47 << 8))), vm.pSprite->sectnum, pSprite->x, pSprite->y,
                            pSprite->z - (RR ? (28 << 8) : (24 << 8)), pSprite->sectnum);

                if (tw == 0)
                {
                    // search around for target player

                    // also modifies 'target' x&y if found..

                    tw = 1;
                    if (A_FurthestVisiblePoint(vm.spriteNum, pSprite, &vm.pActor->lastv) == -1)
                        tw = 0;
                }
                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;

                VM_CONDITIONAL(tw);
                continue;
            }

            case CON_IFHITWEAPON: VM_CONDITIONAL(A_IncurDamage(vm.spriteNum) >= 0); continue;

            case CON_IFSQUISHED: VM_CONDITIONAL(VM_CheckSquished()); continue;

            case CON_IFDEAD: VM_CONDITIONAL(vm.pSprite->extra - (vm.pSprite->picnum == APLAYER) < 0); continue;

            case 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
                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 (vm.pSprite->hitag & random_angle)
                    vm.pSprite->ang = krand2() & 2047;
                continue;

            case CON_ACTION:
                insptr++;
                AC_ACTION_COUNT(vm.pData) = 0;
                AC_CURFRAME(vm.pData)     = 0;
                AC_ACTION_ID(vm.pData)    = *insptr++;
                continue;

            case CON_IFPDISTL:
                VM_CONDITIONAL(vm.playerDist < *(++insptr));
                if (vm.playerDist > MAXSLEEPDIST && vm.pActor->timetosleep == 0)
                    vm.pActor->timetosleep = SLEEPTIME;
                continue;

            case CON_IFPDISTG:
                VM_CONDITIONAL(vm.playerDist > *(++insptr));
                if (vm.playerDist > MAXSLEEPDIST && vm.pActor->timetosleep == 0)
                    vm.pActor->timetosleep = SLEEPTIME;
                continue;

            case CON_ADDSTRENGTH:
                insptr++;
                vm.pSprite->extra += *insptr++;
                continue;

            case CON_STRENGTH:
                insptr++;
                vm.pSprite->extra = *insptr++;
                continue;

            case CON_SMACKSPRITE:
                insptr++;
                if (krand2()&1)
                    vm.pSprite->ang = (vm.pSprite->ang-(512+(krand2()&511)))&2047;
                else
                    vm.pSprite->ang = (vm.pSprite->ang+(512+(krand2()&511)))&2047;
                continue;

            case CON_FAKEBUBBA:
                insptr++;
                switch (++g_fakeBubbaCnt)
                {
                case 1:
                    A_Spawn(vm.spriteNum, PIG);
                    break;
                case 2:
                    A_Spawn(vm.spriteNum, MINION);
                    break;
                case 3:
                    A_Spawn(vm.spriteNum, CHEER);
                    break;
                case 4:
                    A_Spawn(vm.spriteNum, VIXEN);
                    G_OperateActivators(666, vm.playerNum);
                    break;
                }
                continue;

            case CON_RNDMOVE:
                insptr++;
                vm.pSprite->ang = krand2()&2047;
                vm.pSprite->xvel = 25;
                continue;

            case CON_MAMATRIGGER:
                insptr++;
                G_OperateActivators(667, vm.playerNum);
                continue;

            case CON_MAMASPAWN:
                insptr++;
                if (g_mamaSpawnCnt)
                {
                    g_mamaSpawnCnt--;
                    A_Spawn(vm.spriteNum, RABBIT);
                }
                continue;

            case CON_MAMAQUAKE:
                insptr++;
                if (vm.pSprite->pal == 31)
                    g_earthquakeTime = 4;
                else if(vm.pSprite->pal == 32)
                    g_earthquakeTime = 6;
                continue;

            case CON_GARYBANJO:
                insptr++;
                if (g_banjoSong == 0)
                {
                    switch (krand2()&3)
                    {
                    case 3:
                        g_banjoSong = 262;
                        break;
                    case 0:
                        g_banjoSong = 272;
                        break;
                    default:
                        g_banjoSong = 273;
                        break;
                    }
                    A_PlaySound(g_banjoSong, vm.spriteNum);
                }
                else if (!S_CheckSoundPlaying(vm.spriteNum, g_banjoSong))
                    A_PlaySound(g_banjoSong, vm.spriteNum);
                continue;
            case CON_MOTOLOOPSND:
                insptr++;
                if (!S_CheckSoundPlaying(vm.spriteNum, 411))
                    A_PlaySound(411, vm.spriteNum);
                continue;

            case CON_IFGOTWEAPONCE:
                insptr++;

                if ((g_gametypeFlags[ud.coop] & GAMETYPE_WEAPSTAY) && (g_netServer || ud.multimode > 1))
                {
                    if (*insptr == 0)
                    {
                        int j = 0;
                        for (; j < pPlayer->weapreccnt; ++j)
                            if (pPlayer->weaprecs[j] == vm.pSprite->picnum)
                                break;

                        VM_CONDITIONAL(j < pPlayer->weapreccnt && vm.pSprite->owner == vm.spriteNum);
                        continue;
                    }
                    else if (pPlayer->weapreccnt < MAX_WEAPON_RECS-1)
                    {
                        pPlayer->weaprecs[pPlayer->weapreccnt++] = vm.pSprite->picnum;
                        VM_CONDITIONAL(vm.pSprite->owner == vm.spriteNum);
                        continue;
                    }
                }
                VM_CONDITIONAL(0);
                continue;

            case CON_GETLASTPAL:
                insptr++;
                if (vm.pSprite->picnum == APLAYER)
                    vm.pSprite->pal = g_player[P_GetP(vm.pSprite)].ps->palookup;
                else
                    vm.pSprite->pal = vm.pActor->tempang;
                vm.pActor->tempang = 0;
                continue;

            case CON_TOSSWEAPON:
                insptr++;
                // NOTE: assumes that current actor is APLAYER
                P_DropWeapon(P_GetP(vm.pSprite));
                continue;

            case CON_MIKESND:
                insptr++;
                if (EDUKE32_PREDICT_FALSE(((unsigned)vm.pSprite->yvel >= MAXSOUNDS)))
                {
                    CON_ERRPRINTF("invalid sound %d\n", vm.pUSprite->yvel);
                    continue;
                }
                if (!S_CheckSoundPlaying(vm.spriteNum, vm.pSprite->yvel))
                    A_PlaySound(vm.pSprite->yvel, vm.spriteNum);
                continue;

            case 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 && pPlayer->quick_kick == 0)
                    pPlayer->quick_kick = 14;
                continue;

            case 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++;

                continue;

            case CON_SIZEAT:
                insptr++;
                vm.pSprite->xrepeat = (uint8_t)*insptr++;
                vm.pSprite->yrepeat = (uint8_t)*insptr++;
                continue;

            case CON_SHOOT:
                insptr++;
                if (EDUKE32_PREDICT_FALSE((unsigned)vm.pSprite->sectnum >= (unsigned)numsectors))
                {
                    CON_ERRPRINTF("invalid sector %d\n", vm.pUSprite->sectnum);
                    continue;
                }
                A_Shoot(vm.spriteNum, *insptr++);
                continue;

            case CON_IFSOUNDID:
                insptr++;
                VM_CONDITIONAL((int16_t)*insptr == g_ambientLotag[vm.pSprite->ang]);
                continue;

            case CON_IFSOUNDDIST:
                insptr++;
                if (*insptr == 0)
                {
                    VM_CONDITIONAL(g_ambientHitag[vm.pSprite->ang] > vm.playerDist);
                }
                else if (*insptr == 1)
                {
                    VM_CONDITIONAL(g_ambientHitag[vm.pSprite->ang] < vm.playerDist);
                }
                else
                {
                    VM_CONDITIONAL(0);
                }

                continue;

            case CON_SOUNDTAG:
                insptr++;
                A_PlaySound(g_ambientLotag[vm.pSprite->ang], vm.spriteNum);
                continue;

            case CON_SOUNDTAGONCE:
                insptr++;
                if (!S_CheckSoundPlaying(vm.spriteNum, g_ambientLotag[vm.pSprite->ang]))
                    A_PlaySound(g_ambientLotag[vm.pSprite->ang], vm.spriteNum);
                continue;

            case CON_SOUNDONCE:
                if (EDUKE32_PREDICT_FALSE((unsigned)*(++insptr) >= MAXSOUNDS))
                {
                    CON_ERRPRINTF("invalid sound %d\n", (int32_t)*insptr++);
                    continue;
                }

                if (!S_CheckSoundPlaying(vm.spriteNum, *insptr++))
                    A_PlaySound(*(insptr - 1), vm.spriteNum);

                continue;

            case CON_STOPSOUND:
                if (EDUKE32_PREDICT_FALSE((unsigned)*(++insptr) >= MAXSOUNDS))
                {
                    CON_ERRPRINTF("invalid sound %d\n", (int32_t)*insptr);
                    insptr++;
                    continue;
                }
                if (S_CheckSoundPlaying(vm.spriteNum, *insptr))
                    S_StopSound((int16_t)*insptr);
                insptr++;
                continue;

            case CON_GLOBALSOUND:
                if (EDUKE32_PREDICT_FALSE((unsigned)*(++insptr) >= MAXSOUNDS))
                {
                    CON_ERRPRINTF("invalid sound %d\n", (int32_t)*insptr);
                    insptr++;
                    continue;
                }
                if (vm.playerNum == screenpeek || (g_gametypeFlags[ud.coop] & GAMETYPE_COOPSOUND)
#ifdef SPLITSCREEN_MOD_HACKS
                    || (g_fakeMultiMode == 2)
#endif
                    )
                    A_PlaySound(*insptr, g_player[screenpeek].ps->i);
                insptr++;
                continue;

            case CON_SMACKBUBBA:
                insptr++;
                if (!RRRA || vm.pSprite->pal != 105)
                {
                    for (bssize_t TRAVERSE_CONNECT(playerNum))
                        g_player[playerNum].ps->gm = MODE_EOL;
                    if (++ud.level_number > 6)
                        ud.level_number = 0;
                    ud.m_level_number = ud.level_number;
                }
                continue;

            case CON_MAMAEND:
                insptr++;
                g_player[myconnectindex].ps->level_end_timer = 150;
                continue;

            case CON_IFACTORHEALTHG:
                insptr++;
                VM_CONDITIONAL(vm.pSprite->extra > (int16_t)*insptr);
                continue;

            case CON_IFACTORHEALTHL:
                insptr++;
                VM_CONDITIONAL(vm.pSprite->extra < (int16_t)*insptr);
                continue;

            case CON_SOUND:
                if (EDUKE32_PREDICT_FALSE((unsigned)*(++insptr) >= MAXSOUNDS))
                {
                    CON_ERRPRINTF("invalid sound %d\n", (int32_t)*insptr);
                    insptr++;
                    continue;
                }
                A_PlaySound(*insptr++, vm.spriteNum);
                continue;

            case CON_TIP:
                insptr++;
                pPlayer->tipincs = GAMETICSPERSEC;
                continue;

            case CON_IFTIPCOW:
                if (g_spriteExtra[vm.spriteNum] == 1)
                {
                    g_spriteExtra[vm.spriteNum]++;
                    VM_CONDITIONAL(1);
                }
                else
                    VM_CONDITIONAL(0);
                continue;

            case CON_IFHITTRUCK:
                if (g_spriteExtra[vm.spriteNum] == 1)
                {
                    g_spriteExtra[vm.spriteNum]++;
                    VM_CONDITIONAL(1);
                }
                else
                    VM_CONDITIONAL(0);
                continue;

            case CON_TEARITUP:
                insptr++;
                for (bssize_t SPRITES_OF_SECT(vm.pSprite->sectnum, spriteNum))
                {
                    if (sprite[spriteNum].picnum == DESTRUCTO)
                    {
                        actor[spriteNum].picnum = SHOTSPARK1;
                        actor[spriteNum].extra = 1;
                    }
                }
                continue;

            case CON_FALL:
                insptr++;
                VM_Fall(vm.spriteNum, vm.pSprite);
                continue;

            case CON_NULLOP: insptr++; continue;

            case CON_ADDAMMO:
                insptr++;
                {
                    int const weaponNum = *insptr++;
                    int const addAmount = *insptr++;

                    VM_AddAmmo(pPlayer, weaponNum, addAmount);

                    continue;
                }

            case CON_MONEY:
                insptr++;
                A_SpawnMultiple(vm.spriteNum, MONEY, *insptr++);
                continue;

            case CON_MAIL:
                insptr++;
                A_SpawnMultiple(vm.spriteNum, RR ? MONEY : MAIL, *insptr++);
                continue;

            case CON_SLEEPTIME:
                insptr++;
                vm.pActor->timetosleep = (int16_t)*insptr++;
                continue;

            case CON_PAPER:
                insptr++;
                A_SpawnMultiple(vm.spriteNum, RR ? MONEY : PAPER, *insptr++);
                continue;

            case CON_ADDKILLS:
                insptr++;
                if ((g_spriteExtra[vm.spriteNum] < 1 || g_spriteExtra[vm.spriteNum] == 128)
                    && A_CheckSpriteFlags(vm.spriteNum, SFLAG_KILLCOUNT))
                    P_AddKills(pPlayer, *insptr);
                insptr++;
                vm.pActor->actorstayput = -1;
                continue;

            case CON_LOTSOFGLASS:
                insptr++;
                A_SpawnGlass(vm.spriteNum, *insptr++);
                continue;

            case CON_KILLIT:
                insptr++;
                vm.flags |= VM_KILL;
                return;

            case CON_ADDWEAPON:
                insptr++;
                {
                    int const weaponNum = *insptr++;
                    VM_AddWeapon(pPlayer, weaponNum, *insptr++);
                    continue;
                }

            case CON_DEBUG:
                insptr++;
                buildprint(*insptr++, "\n");
                continue;

            case CON_ENDOFGAME:
                insptr++;
                pPlayer->timebeforeexit  = *insptr++;
                pPlayer->customexitsound = -1;
                ud.eog                   = 1;
                continue;

            case CON_ISDRUNK:
                insptr++;
                {
                    pPlayer->drink_amt += *insptr;

                    int newHealth = sprite[pPlayer->i].extra;

                    if (newHealth > 0)
                        newHealth += *insptr;
                    if (newHealth > (pPlayer->max_player_health << 1))
                        newHealth = (pPlayer->max_player_health << 1);
                    if (newHealth < 0)
                        newHealth = 0;

                    if (ud.god == 0)
                    {
                        if (*insptr > 0)
                        {
                            if ((newHealth - *insptr) < (pPlayer->max_player_health >> 2) && newHealth >= (pPlayer->max_player_health >> 2))
                                A_PlaySound(DUKE_GOTHEALTHATLOW, pPlayer->i);
                            pPlayer->last_extra = newHealth;
                        }

                        sprite[pPlayer->i].extra = newHealth;
                    }
                    if (pPlayer->drink_amt > 100)
                        pPlayer->drink_amt = 100;

                    if (sprite[pPlayer->i].extra >= pPlayer->max_player_health)
                    {
                        sprite[pPlayer->i].extra = pPlayer->max_player_health;
                        pPlayer->last_extra = pPlayer->max_player_health;
                    }
                }
                insptr++;
                continue;

            case CON_STRAFELEFT:
                insptr++;
                {
                    vec3_t const vect = { sintable[(vm.pSprite->ang+1024)&2047]>>10, sintable[(vm.pSprite->ang+512)&2047]>>10, vm.pSprite->zvel };
                    A_MoveSprite(vm.spriteNum, &vect, CLIPMASK0);
                }
                continue;

            case CON_STRAFERIGHT:
                insptr++;
                {
                    vec3_t const vect = { sintable[(vm.pSprite->ang-0)&2047]>>10, sintable[(vm.pSprite->ang-512)&2047]>>10, vm.pSprite->zvel };
                    A_MoveSprite(vm.spriteNum, &vect, CLIPMASK0);
                }
                continue;

            case CON_LARRYBIRD:
                insptr++;
                pPlayer->pos.z = sector[sprite[pPlayer->i].sectnum].ceilingz;
                sprite[pPlayer->i].z = pPlayer->pos.z;
                continue;

            case CON_DESTROYIT:
                insptr++;
                {
                    int16_t hitag, lotag, spr, jj, k, nextk;
                    hitag = 0;
                    for (SPRITES_OF_SECT(vm.pSprite->sectnum,k))
                    {
                        if (sprite[k].picnum == RRTILE63)
                        {
                            lotag = sprite[k].lotag;
                            spr = k;
                            if (sprite[k].hitag)
                                hitag = sprite[k].hitag;
                        }
                    }
                    for (SPRITES_OF(100, jj))
                    {
                        spritetype const *js = &sprite[jj];
                        if (hitag && hitag == js->hitag)
                        {
                            for (SPRITES_OF_SECT(js->sectnum,k))
                            {
                                if (sprite[k].picnum == DESTRUCTO)
                                {
                                    actor[k].picnum = SHOTSPARK1;
                                    actor[k].extra = 1;
                                }
                            }
                        }
                        if (sprite[spr].sectnum != js->sectnum && lotag == js->lotag)
                        {
                            int16_t const sectnum = sprite[spr].sectnum;
                            int16_t const wallstart = sector[sectnum].wallptr;
                            int16_t const wallend = wallstart + sector[sectnum].wallnum;
                            int16_t const wallstart2 = sector[js->sectnum].wallptr;
                            //int16_t const wallend2 = wallstart2 + sector[js->sectnum].wallnum;
                            for (bssize_t wi = wallstart, wj = wallstart2; wi < wallend; wi++, wj++)
                            {
                                wall[wi].picnum = wall[wj].picnum;
                                wall[wi].overpicnum = wall[wj].overpicnum;
                                wall[wi].shade = wall[wj].shade;
                                wall[wi].xrepeat = wall[wj].xrepeat;
                                wall[wi].yrepeat = wall[wj].yrepeat;
                                wall[wi].xpanning = wall[wj].xpanning;
                                wall[wi].ypanning = wall[wj].ypanning;
                                if (RRRA && wall[wi].nextwall != -1)
                                {
                                    wall[wi].cstat = 0;
                                    wall[wall[wi].nextwall].cstat = 0;
                                }
                            }
                            sector[sectnum].floorz = sector[js->sectnum].floorz;
                            sector[sectnum].ceilingz = sector[js->sectnum].ceilingz;
                            sector[sectnum].ceilingstat = sector[js->sectnum].ceilingstat;
                            sector[sectnum].floorstat = sector[js->sectnum].floorstat;
                            sector[sectnum].ceilingpicnum = sector[js->sectnum].ceilingpicnum;
                            sector[sectnum].ceilingheinum = sector[js->sectnum].ceilingheinum;
                            sector[sectnum].ceilingshade = sector[js->sectnum].ceilingshade;
                            sector[sectnum].ceilingpal = sector[js->sectnum].ceilingpal;
                            sector[sectnum].ceilingxpanning = sector[js->sectnum].ceilingxpanning;
                            sector[sectnum].ceilingypanning = sector[js->sectnum].ceilingypanning;
                            sector[sectnum].floorpicnum = sector[js->sectnum].floorpicnum;
                            sector[sectnum].floorheinum = sector[js->sectnum].floorheinum;
                            sector[sectnum].floorshade = sector[js->sectnum].floorshade;
                            sector[sectnum].floorpal = sector[js->sectnum].floorpal;
                            sector[sectnum].floorxpanning = sector[js->sectnum].floorxpanning;
                            sector[sectnum].floorypanning = sector[js->sectnum].floorypanning;
                            sector[sectnum].visibility = sector[js->sectnum].visibility;
                            g_sectorExtra[sectnum] = g_sectorExtra[js->sectnum];
                            sector[sectnum].lotag = sector[js->sectnum].lotag;
                            sector[sectnum].hitag = sector[js->sectnum].hitag;
                            sector[sectnum].extra = sector[js->sectnum].extra;
                        }
                    }
                    for (SPRITES_OF_SECT_SAFE(vm.pSprite->sectnum, k, nextk))
                    {
                        switch (DYNAMICTILEMAP(sprite[k].picnum))
                        {
                            case DESTRUCTO__STATICRR:
                            case RRTILE63__STATICRR:
                            case TORNADO__STATICRR:
                            case APLAYER__STATIC:
                            case COOT__STATICRR:
                                break;
                            default:
                                A_DeleteSprite(k);
                                break;
                        }
                    }
                }
                continue;

            case CON_ISEAT:
                insptr++;

                {
                    pPlayer->eat_amt += *insptr;
                    if (pPlayer->eat_amt > 100)
                        pPlayer->eat_amt = 100;

                    pPlayer->drink_amt -= *insptr;
                    if (pPlayer->drink_amt < 0)
                        pPlayer->drink_amt = 0;

                    int newHealth = sprite[pPlayer->i].extra;

                    if (vm.pSprite->picnum != ATOMICHEALTH)
                    {
                        if (newHealth > pPlayer->max_player_health && *insptr > 0)
                        {
                            insptr++;
                            continue;
                        }
                        else
                        {
                            if (newHealth > 0)
                                newHealth += (*insptr)*3;
                            if (newHealth > pPlayer->max_player_health && *insptr > 0)
                                newHealth = pPlayer->max_player_health;
                        }
                    }
                    else
                    {
                        if (newHealth > 0)
                            newHealth += *insptr;
                        if (newHealth > (pPlayer->max_player_health << 1))
                            newHealth = (pPlayer->max_player_health << 1);
                    }

                    if (newHealth < 0)
                        newHealth = 0;

                    if (ud.god == 0)
                    {
                        if (*insptr > 0)
                        {
                            if ((newHealth - *insptr) < (pPlayer->max_player_health >> 2) && newHealth >= (pPlayer->max_player_health >> 2))
                                A_PlaySound(DUKE_GOTHEALTHATLOW, pPlayer->i);
                            pPlayer->last_extra = newHealth;
                        }

                        sprite[pPlayer->i].extra = newHealth;
                    }
                }

                insptr++;
                continue;

            case CON_ADDPHEALTH:
                insptr++;

                {
                    if (!RR && pPlayer->newowner >= 0)
                        G_ClearCameraView(pPlayer);

                    int newHealth = sprite[pPlayer->i].extra;

                    if (vm.pSprite->picnum != ATOMICHEALTH)
                    {
                        if (newHealth > pPlayer->max_player_health && *insptr > 0)
                        {
                            insptr++;
                            continue;
                        }
                        else
                        {
                            if (newHealth > 0)
                                newHealth += *insptr;
                            if (newHealth > pPlayer->max_player_health && *insptr > 0)
                                newHealth = pPlayer->max_player_health;
                        }
                    }
                    else
                    {
                        if (newHealth > 0)
                            newHealth += *insptr;
                        if (newHealth > (pPlayer->max_player_health << 1))
                            newHealth = (pPlayer->max_player_health << 1);
                    }

                    if (newHealth < 0)
                        newHealth = 0;

                    if (ud.god == 0)
                    {
                        if (*insptr > 0)
                        {
                            if ((newHealth - *insptr) < (pPlayer->max_player_health >> 2) && newHealth >= (pPlayer->max_player_health >> 2))
                                A_PlaySound(DUKE_GOTHEALTHATLOW, pPlayer->i);
                            pPlayer->last_extra = newHealth;
                        }

                        sprite[pPlayer->i].extra = newHealth;
                    }
                }

                insptr++;
                continue;

            case CON_MOVE:
                insptr++;
                AC_COUNT(vm.pData)   = 0;
                AC_MOVE_ID(vm.pData) = *insptr++;
                vm.pSprite->hitag    = *insptr++;
                if (vm.pSprite->hitag & random_angle)
                    vm.pSprite->ang = krand2() & 2047;
                continue;

            case CON_SPAWN:
                insptr++;
                if ((unsigned)vm.pSprite->sectnum >= MAXSECTORS)
                {
                    CON_ERRPRINTF("invalid sector %d\n", vm.pUSprite->sectnum);
                    insptr++;
                    continue;
                }
                A_Spawn(vm.spriteNum, *insptr++);
                continue;

            case CON_IFWASWEAPON:
            case CON_IFSPAWNEDBY:
                insptr++;
                VM_CONDITIONAL(vm.pActor->picnum == *insptr);
                continue;

            case CON_IFAI:
                insptr++;
                VM_CONDITIONAL(AC_AI_ID(vm.pData) == *insptr);
                continue;

            case CON_IFACTION:
                insptr++;
                VM_CONDITIONAL(AC_ACTION_ID(vm.pData) == *insptr);
                continue;

            case CON_IFACTIONCOUNT:
                insptr++;
                VM_CONDITIONAL(AC_ACTION_COUNT(vm.pData) >= *insptr);
                continue;

            case CON_RESETACTIONCOUNT:
                insptr++;
                AC_ACTION_COUNT(vm.pData) = 0;
                continue;

            case CON_DEBRIS:
                insptr++;
                {
                    int debrisTile = *insptr++;

                    if ((unsigned)vm.pSprite->sectnum < MAXSECTORS)
                        for (native_t cnt = (*insptr) - 1; cnt >= 0; cnt--)
                        {
                            int const tileOffset = ((RR || vm.pSprite->picnum == BLIMP) && debrisTile == SCRAP1) ? 0 : (krand2() % 3);

                            int32_t const r1 = krand2(), r2 = krand2(), r3 = krand2(), r4 = krand2(), r5 = krand2(), r6 = krand2(), r7 = krand2(), r8 = krand2();
                            int const spriteNum = A_InsertSprite(vm.pSprite->sectnum, vm.pSprite->x + (r8 & 255) - 128,
                                                                 vm.pSprite->y + (r7 & 255) - 128, vm.pSprite->z - (8 << 8) - (r6 & 8191),
                                                                 debrisTile + tileOffset, vm.pSprite->shade, 32 + (r5 & 15), 32 + (r4 & 15),
                                                                 r3 & 2047, (r2 & 127) + 32, -(r1 & 2047), vm.spriteNum, 5);

                            sprite[spriteNum].yvel = ((RR || vm.pSprite->picnum == BLIMP) && debrisTile == SCRAP1) ? g_blimpSpawnItems[cnt % 14] : -1;
                            sprite[spriteNum].pal  = vm.pSprite->pal;
                        }
                    insptr++;
                }
                continue;

            case CON_COUNT:
                insptr++;
                AC_COUNT(vm.pData) = (int16_t)*insptr++;
                continue;

            case CON_CSTATOR:
                insptr++;
                vm.pSprite->cstat |= (int16_t)*insptr++;
                continue;

            case CON_CLIPDIST:
                insptr++;
                vm.pSprite->clipdist = (int16_t)*insptr++;
                continue;

            case CON_CSTAT:
                insptr++;
                vm.pSprite->cstat = (int16_t)*insptr++;
                continue;

            case CON_NEWPIC:
                insptr++;
                vm.pSprite->picnum = (int16_t)*insptr++;
                continue;

            case CON_IFMOVE:
                insptr++;
                VM_CONDITIONAL(AC_MOVE_ID(vm.pData) == *insptr);
                continue;

            case CON_RESETPLAYER:
                insptr++;
                vm.flags = VM_ResetPlayer(vm.playerNum, vm.flags);
                continue;

            case CON_IFCOOP:
                VM_CONDITIONAL(GTFLAGS(GAMETYPE_COOP) || numplayers > 2);
                continue;

            case CON_IFONMUD:
                VM_CONDITIONAL(sector[vm.pSprite->sectnum].floorpicnum == RRTILE3073
                               && klabs(vm.pSprite->z - sector[vm.pSprite->sectnum].floorz) < ZOFFSET5);
                continue;

            case CON_IFONWATER:
                VM_CONDITIONAL(sector[vm.pSprite->sectnum].lotag == ST_1_ABOVE_WATER
                               && klabs(vm.pSprite->z - sector[vm.pSprite->sectnum].floorz) < ZOFFSET5);
                continue;

            case CON_IFMOTOFAST:
                VM_CONDITIONAL(pPlayer->moto_speed > 60);
                continue;

            case CON_IFONMOTO:
                VM_CONDITIONAL(pPlayer->on_motorcycle == 1);
                continue;

            case CON_IFONBOAT:
                VM_CONDITIONAL(pPlayer->on_boat == 1);
                continue;

            case CON_IFSIZEDOWN:
                vm.pSprite->xrepeat--;
                vm.pSprite->yrepeat--;
                VM_CONDITIONAL(vm.pSprite->xrepeat <= 5);
                continue;

            case CON_IFWIND:
                VM_CONDITIONAL(g_windTime > 0);
                continue;

            case CON_IFINWATER: VM_CONDITIONAL(sector[vm.pSprite->sectnum].lotag == ST_2_UNDERWATER); continue;

            case CON_IFCOUNT:
                insptr++;
                VM_CONDITIONAL(AC_COUNT(vm.pData) >= *insptr);
                continue;

            case CON_IFACTOR:
                insptr++;
                VM_CONDITIONAL(vm.pSprite->picnum == *insptr);
                continue;

            case CON_RESETCOUNT:
                insptr++;
                AC_COUNT(vm.pData) = 0;
                continue;

            case CON_ADDINVENTORY:
                insptr += 2;

                VM_AddInventory(pPlayer, *(insptr - 1), *insptr);

                insptr++;
                continue;

            case CON_HITRADIUS:
                A_RadiusDamage(vm.spriteNum, *(insptr + 1), *(insptr + 2), *(insptr + 3), *(insptr + 4), *(insptr + 5));
                insptr += 6;
                continue;

            case CON_IFP:
            {
                int const moveFlags  = *(++insptr);
                int       nResult    = 0;
                int const playerXVel = sprite[pPlayer->i].xvel;
                int const syncBits   = g_player[vm.playerNum].inputBits->bits;

                if (((moveFlags & pducking) && pPlayer->on_ground && (TEST_SYNC_KEY(syncBits, SK_CROUCH) ^ vm.pPlayer->crouch_toggle))
                    || ((moveFlags & pfalling) && pPlayer->jumping_counter == 0 && !pPlayer->on_ground && pPlayer->vel.z > 2048)
                    || ((moveFlags & pjumping) && 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) && 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)
                        && (pPlayer->quick_kick > 0
                            || (pPlayer->curr_weapon == KNEE_WEAPON && pPlayer->kickback_pic > 0)))
                    || ((moveFlags & pshrunk) && sprite[pPlayer->i].xrepeat < (RR ? 8 : 32))
                    || ((moveFlags & pjetpack) && pPlayer->jetpack_on)
                    || ((moveFlags & ponsteroids) && pPlayer->inv_amount[GET_STEROIDS] > 0 && pPlayer->inv_amount[GET_STEROIDS] < 400)
                    || ((moveFlags & ponground) && pPlayer->on_ground)
                    || ((moveFlags & palive) && sprite[pPlayer->i].xrepeat > (RR ? 8 : 32) && sprite[pPlayer->i].extra > 0 && pPlayer->timebeforeexit == 0)
                    || ((moveFlags & pdead) && sprite[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(pPlayer->pos.x - g_player[otherp].ps->pos.x, pPlayer->pos.y - g_player[otherp].ps->pos.y))
                      : G_GetAngleDelta(fix16_to_int(pPlayer->q16ang), getangle(vm.pSprite->x - pPlayer->pos.x, vm.pSprite->y - pPlayer->pos.y));

                    nResult = (nResult > -128 && nResult < 128);
                }
                VM_CONDITIONAL(nResult);
            }
                continue;

            case CON_IFSTRENGTH:
                insptr++;
                VM_CONDITIONAL(vm.pSprite->extra <= *insptr);
                continue;

            case CON_GUTS:
                A_DoGuts(vm.spriteNum, *(insptr + 1), *(insptr + 2));
                insptr += 3;
                continue;

            case CON_SLAPPLAYER:
                insptr++;
                P_ForceAngle(pPlayer);
                pPlayer->vel.x -= sintable[(fix16_to_int(pPlayer->q16ang)+512)&2047]<<7;
                pPlayer->vel.y -= sintable[fix16_to_int(pPlayer->q16ang)&2047]<<7;
                continue;

            case CON_WACKPLAYER:
                insptr++;
                if (RR)
                {
                    pPlayer->vel.x -= sintable[(fix16_to_int(pPlayer->q16ang)+512)&2047]<<7;
                    pPlayer->vel.y -= sintable[fix16_to_int(pPlayer->q16ang)&2047]<<7;
                    pPlayer->jumping_counter = 767;
                    pPlayer->jumping_toggle = 1;
                }
                else
                    P_ForceAngle(pPlayer);
                continue;

            case CON_IFGAPZL:
                insptr++;
                VM_CONDITIONAL(((vm.pActor->floorz - vm.pActor->ceilingz) >> 8) < *insptr);
                continue;

            case CON_IFHITSPACE: VM_CONDITIONAL(TEST_SYNC_KEY(g_player[vm.playerNum].inputBits->bits, SK_OPEN)); continue;

            case CON_IFOUTSIDE: VM_CONDITIONAL(sector[vm.pSprite->sectnum].ceilingstat & 1); continue;

            case CON_IFMULTIPLAYER: VM_CONDITIONAL((g_netServer || g_netClient || ud.multimode > 1)); continue;

            case 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);
                            }
                }
                continue;

            case CON_IFINSPACE: VM_CONDITIONAL(G_CheckForSpaceCeiling(vm.pSprite->sectnum)); continue;

            case CON_SPRITEPAL:
                insptr++;
                if (vm.pSprite->picnum != APLAYER)
                    vm.pActor->tempang = vm.pSprite->pal;
                vm.pSprite->pal        = *insptr++;
                continue;

            case CON_CACTOR:
                insptr++;
                vm.pSprite->picnum = *insptr++;
                continue;

            case CON_IFBULLETNEAR: VM_CONDITIONAL(A_Dodge(vm.pSprite) == 1); continue;

            case CON_IFRESPAWN:
                if (A_CheckEnemySprite(vm.pSprite))
                    VM_CONDITIONAL(ud.respawn_monsters)
                else if (A_CheckInventorySprite(vm.pSprite))
                    VM_CONDITIONAL(ud.respawn_inventory)
                else
                    VM_CONDITIONAL(ud.respawn_items)
                continue;

            case CON_IFFLOORDISTL:
                insptr++;
                VM_CONDITIONAL((vm.pActor->floorz - vm.pSprite->z) <= ((*insptr) << 8));
                continue;

            case CON_IFCEILINGDISTL:
                insptr++;
                VM_CONDITIONAL((vm.pSprite->z - vm.pActor->ceilingz) <= ((*insptr) << 8));
                continue;

            case CON_PALFROM:
                insptr++;
                if (EDUKE32_PREDICT_FALSE((unsigned)vm.playerNum >= (unsigned)g_mostConcurrentPlayers))
                {
                    CON_ERRPRINTF("invalid player %d\n", vm.playerNum);
                    insptr += 4;
                }
                else
                {
                    palette_t const pal = { (uint8_t) * (insptr + 1), (uint8_t) * (insptr + 2), (uint8_t) * (insptr + 3), (uint8_t) * (insptr) };
                    insptr += 4;
                    P_PalFrom(pPlayer, pal.f, pal.r, pal.g, pal.b);
                }
                continue;

            case CON_IFPHEALTHL:
                insptr++;
                VM_CONDITIONAL(sprite[pPlayer->i].extra < *insptr);
                continue;

            case CON_IFPINVENTORY:
                insptr++;

                switch (*insptr++)
                {
                    case GET_STEROIDS:
                    case GET_SCUBA:
                    case GET_HOLODUKE:
                    case GET_HEATS:
                    case GET_FIRSTAID:
                    case GET_BOOTS:
                    case GET_JETPACK: tw = (pPlayer->inv_amount[*(insptr - 1)] != *insptr); break;

                    case GET_SHIELD:
                        tw = (pPlayer->inv_amount[GET_SHIELD] != pPlayer->max_player_health); break;
                    case GET_ACCESS:
                        if (RR)
                        {
                            switch (vm.pSprite->lotag)
                            {
                                case 100: tw = pPlayer->keys[1]; break;
                                case 101: tw = pPlayer->keys[2]; break;
                                case 102: tw = pPlayer->keys[3]; break;
                                case 103: tw = pPlayer->keys[4]; break;
                            }
                        }
                        else
                        {
                            switch (vm.pSprite->pal)
                            {
                                case 0: tw  = (pPlayer->got_access & 1); break;
                                case 21: tw = (pPlayer->got_access & 2); break;
                                case 23: tw = (pPlayer->got_access & 4); break;
                            }
                        }
                        break;
                    default: tw = 0; CON_ERRPRINTF("invalid inventory item %d\n", (int32_t) * (insptr - 1));
                }

                VM_CONDITIONAL(tw);
                continue;

            case CON_PSTOMP:
                insptr++;
                if (pPlayer->knee_incs == 0 && sprite[pPlayer->i].xrepeat >= (RR ? 9 : 40))
                    if (cansee(vm.pSprite->x, vm.pSprite->y, vm.pSprite->z - ZOFFSET6, vm.pSprite->sectnum, pPlayer->pos.x, pPlayer->pos.y,
                               pPlayer->pos.z + ZOFFSET2, sprite[pPlayer->i].sectnum))
                    {
                        if (pPlayer->weapon_pos == 0)
                            pPlayer->weapon_pos = -1;

                        pPlayer->actorsqu  = vm.spriteNum;
                        pPlayer->knee_incs = 1;
                    }
                continue;

            case 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;
                        }
                    }
                }

                VM_CONDITIONAL(tw);

#undef IFAWAYDIST
            }
                continue;

            case CON_QUOTE:
                insptr++;

                if (EDUKE32_PREDICT_FALSE((unsigned)(*insptr) >= MAXQUOTES) || apStrings[*insptr] == NULL)
                {
                    CON_ERRPRINTF("invalid quote %d\n", (int32_t)(*insptr));
                    insptr++;
                    continue;
                }

                if (EDUKE32_PREDICT_FALSE((unsigned)vm.playerNum >= MAXPLAYERS))
                {
                    CON_ERRPRINTF("invalid player %d\n", vm.playerNum);
                    insptr++;
                    continue;
                }

                P_DoQuote(*(insptr++) | MAXQUOTES, pPlayer);
                continue;

            case CON_IFINOUTERSPACE: VM_CONDITIONAL(G_CheckForSpaceFloor(vm.pSprite->sectnum)); continue;

            case CON_IFNOTMOVING: VM_CONDITIONAL((vm.pActor->movflag & 49152) > 16384); continue;

            case CON_RESPAWNHITAG:
                insptr++;
                switch (DYNAMICTILEMAP(vm.pSprite->picnum))
                {
                    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 PODFEM1__STATIC:
                        if (RR) break;
                        fallthrough__;
                    case FEM10__STATIC:
                    case NAKED1__STATIC:
                    case STATUE__STATIC:
                        if (vm.pSprite->yvel)
                            G_OperateRespawns(vm.pSprite->yvel);
                        break;
                    default:
                        if (vm.pSprite->hitag >= 0)
                            G_OperateRespawns(vm.pSprite->hitag);
                        break;
                }
                continue;

            case CON_IFSPRITEPAL:
                insptr++;
                VM_CONDITIONAL(vm.pSprite->pal == *insptr);
                continue;

            case CON_IFANGDIFFL:
                insptr++;
                tw = klabs(G_GetAngleDelta(fix16_to_int(pPlayer->q16ang), vm.pSprite->ang));
                VM_CONDITIONAL(tw <= *insptr);
                continue;

            case CON_IFNOSOUNDS: VM_CONDITIONAL(!A_CheckAnySoundPlaying(vm.spriteNum)); continue;

            default:  // you aren't supposed to be here!
                if (RR && ud.recstat == 2)
                {
                    vm.flags |= VM_KILL;
                    return;
                }
                debug_break();
                VM_ScriptInfo(insptr, 64);
                G_GameExit("An error has occurred in the " APPNAME " virtual machine.\n\n"
                           "If you are an end user, please e-mail the file " APPBASENAME ".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!");
                break;
        }
    }
}

// NORECURSE
void A_LoadActor(int32_t 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(1);
    insptr = NULL;

    if (vm.flags & VM_KILL)
        A_DeleteSprite(vm.spriteNum);
}

void VM_UpdateAnim(int spriteNum, int32_t *pData)
{
    size_t const actionofs = AC_ACTION_ID(pData);
    intptr_t 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];
        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 spriteNum, int playerNum, int playerDist)
{
    vmstate_t tempvm
    = { spriteNum, playerNum, playerDist, 0, &sprite[spriteNum], &actor[spriteNum].t_data[0], g_player[playerNum].ps, &actor[spriteNum] };
    vm = tempvm;

/*
    if (g_netClient && A_CheckSpriteFlags(spriteNum, SFLAG_NULL))
    {
        A_DeleteSprite(spriteNum);
        return;
    }
*/

    //if (g_netClient) // [75] The server should not overwrite its own randomseed
    //    randomseed = ticrandomseed;

    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);

    double t = timerGetHiTicks();
    int const picnum = vm.pSprite->picnum;
    insptr = 4 + (g_tile[vm.pSprite->picnum].execPtr);
    VM_Execute(1);
    insptr = NULL;

    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]++;

    if (vm.flags & VM_KILL)
    {
        VM_DeleteSprite(spriteNum, playerNum);
        return;
    }

    VM_Move();

    if (vm.pSprite->statnum != STAT_ACTOR)
    {
        if (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;
            }
        }
        goto safe_delete;
    }

    if (A_CheckEnemySprite(vm.pSprite))
    {
        if (vm.pSprite->xrepeat > 60 || (ud.respawn_monsters == 1 && vm.pSprite->extra <= 0))
            goto safe_delete;
    }
    else if (EDUKE32_PREDICT_FALSE(ud.respawn_items == 1 && (vm.pSprite->cstat & 32768)))
        goto safe_delete;

    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)
        changespritestat(vm.spriteNum, STAT_ZOMBIEACTOR);

safe_delete:
    if (vm.flags & VM_SAFEDELETE)
        A_DeleteSprite(spriteNum);
}
END_RR_NS
