code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 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.
*/
//-------------------------------------------------------------------------

// this is all the crap for accessing the game's structs through the CON VM
// I got a 3-4 fps gain by inlining these...

#include "compat.h"
#include "m32script.h"
#include "m32def.h"
#ifdef POLYMER
# include "prlights.h"
#endif

// how: bitfield: 1=set? 2=vars? 4=use spriteext[].tspr? (otherwise use tsprite[])
#define ACCESS_SET 1
#define ACCESS_USEVARS 2

/// This file is #included into other files, so don't define variables here!

static int32_t __fastcall VM_AccessWall(int32_t how, int32_t lVar1, int32_t labelNum, int32_t lVar2)
{
    int32_t lValue;
    int32_t i = (how&ACCESS_USEVARS) ? Gv_GetVar(lVar1) : lVar1;

    if (!m32_script_expertmode && (i<0 || i >= numwalls))
        goto badwall;

    if (how&ACCESS_SET)
    {
        if (!m32_script_expertmode && (WallLabels[labelNum].flags & 1))
            goto readonly;

        lValue = (how&ACCESS_USEVARS) ? Gv_GetVar(lVar2) : lVar2;

        asksave = 1;

        if (!m32_script_expertmode && (WallLabels[labelNum].min != 0 || WallLabels[labelNum].max != 0))
        {
            if (lValue < WallLabels[labelNum].min)
                lValue = WallLabels[labelNum].min;
            if (lValue > WallLabels[labelNum].max)
                lValue = WallLabels[labelNum].max;
        }

        switch (labelNum)
        {
        case WALL_X: wall[i].x=lValue; break;
        case WALL_Y: wall[i].y=lValue; break;
        case WALL_POINT2: wall[i].point2=lValue; break;
        case WALL_NEXTWALL: wall[i].nextwall=lValue; break;
        case WALL_NEXTSECTOR: wall[i].nextsector=lValue; break;
        case WALL_CSTAT:
#ifdef YAX_ENABLE__COMPAT
            if (!m32_script_expertmode)
                SET_PROTECT_BITS(wall[i].cstat, lValue, YAX_NEXTWALLBITS);
            else
#endif
                wall[i].cstat = lValue;
            break;
        case WALL_PICNUM: wall[i].picnum=lValue; break;
        case WALL_OVERPICNUM: wall[i].overpicnum=lValue; break;
        case WALL_SHADE: wall[i].shade=lValue; break;
        case WALL_PAL: wall[i].pal=lValue; break;
        case WALL_XREPEAT: wall[i].xrepeat=lValue; break;
        case WALL_YREPEAT: wall[i].yrepeat=lValue; break;
        case WALL_XPANNING: wall[i].xpanning=lValue; break;
        case WALL_YPANNING: wall[i].ypanning=lValue; break;
        case WALL_LOTAG:
#ifdef YAX_ENABLE__COMPAT
            if (!m32_script_expertmode && numyaxbunches>0 && yax_getnextwall(i,YAX_CEILING)>=0)
                goto yax_readonly;
#endif
            wall[i].lotag=lValue;
            break;
        case WALL_HITAG: wall[i].hitag=lValue; break;
        case WALL_EXTRA:
#ifdef YAX_ENABLE__COMPAT
            if (!m32_script_expertmode && numyaxbunches>0 && yax_getnextwall(i,YAX_FLOOR)>=0)
                goto yax_readonly;
#endif
            wall[i].extra=lValue;
            break;
        default:
            return -1;
        }

        return 0;
    }
    else
    {
        switch (labelNum)
        {
        case WALL_X: lValue=wall[i].x; break;
        case WALL_Y: lValue=wall[i].y; break;
        case WALL_POINT2: lValue=wall[i].point2; break;
        case WALL_NEXTWALL: lValue=wall[i].nextwall; break;
        case WALL_NEXTSECTOR: lValue=wall[i].nextsector; break;
        case WALL_CSTAT: lValue=wall[i].cstat; break;
        case WALL_PICNUM: lValue=wall[i].picnum; break;
        case WALL_OVERPICNUM: lValue=wall[i].overpicnum; break;
        case WALL_SHADE: lValue=wall[i].shade; break;
        case WALL_PAL: lValue=wall[i].pal; break;
        case WALL_XREPEAT: lValue=wall[i].xrepeat; break;
        case WALL_YREPEAT: lValue=wall[i].yrepeat; break;
        case WALL_XPANNING: lValue=wall[i].xpanning; break;
        case WALL_YPANNING: lValue=wall[i].ypanning; break;
        case WALL_LOTAG: lValue=wall[i].lotag; break;
        case WALL_HITAG: lValue=wall[i].hitag; break;
        case WALL_EXTRA: lValue=wall[i].extra; break;
        default:
            return -1;
        }

        if (how&ACCESS_USEVARS)
            Gv_SetVar(lVar2, lValue);

        return lValue;
    }

badwall:
    M32_ERROR("Invalid wall %d", i);
    return -1;
readonly:
    M32_ERROR("Wall structure member `%s' is read-only.", WallLabels[labelNum].name);
    return -1;
#ifdef YAX_ENABLE__COMPAT
yax_readonly:
    M32_ERROR("Wall structure member `%s' is read-only because it is used for TROR",
              WallLabels[labelNum].name);
    return -1;
#endif
}

// how: bitfield: 1=set? 2=vars?
static int32_t __fastcall VM_AccessSector(int32_t how, int32_t lVar1, int32_t labelNum, int32_t lVar2)
{
    int32_t lValue;
    int32_t i = (how&ACCESS_USEVARS) ? sprite[vm.spriteNum].sectnum : lVar1;

    if ((how&ACCESS_USEVARS) && lVar1 != M32_THISACTOR_VAR_ID)
        i = Gv_GetVar(lVar1);

    if (!m32_script_expertmode && (i<0 || i >= numsectors))
        goto badsector;

    if (how&ACCESS_SET)
    {
        if (!m32_script_expertmode && (SectorLabels[labelNum].flags & 1))
            goto readonly;

        lValue = (how&ACCESS_USEVARS) ? Gv_GetVar(lVar2) : lVar2;

        asksave = 1;

        if (!m32_script_expertmode && (SectorLabels[labelNum].min != 0 || SectorLabels[labelNum].max != 0))
        {
            if (lValue < SectorLabels[labelNum].min)
                lValue = SectorLabels[labelNum].min;
            if (lValue > SectorLabels[labelNum].max)
                lValue = SectorLabels[labelNum].max;
        }

        switch (labelNum)
        {
        case SECTOR_WALLPTR: sector[i].wallptr=lValue; break;
        case SECTOR_WALLNUM: sector[i].wallnum=lValue; break;
        case SECTOR_CEILINGZ: sector[i].ceilingz=lValue; break;
        case SECTOR_FLOORZ: sector[i].floorz=lValue; break;
        case SECTOR_CEILINGSTAT:
#ifdef YAX_ENABLE__COMPAT
            if (!m32_script_expertmode)
                SET_PROTECT_BITS(sector[i].ceilingstat, lValue, YAX_BIT);
            else
#endif
                sector[i].ceilingstat = lValue;
            break;
        case SECTOR_FLOORSTAT:
#ifdef YAX_ENABLE__COMPAT
            if (!m32_script_expertmode)
                SET_PROTECT_BITS(sector[i].floorstat, lValue, YAX_BIT);
            else
#endif
                sector[i].floorstat = lValue;
            break;
        case SECTOR_CEILINGPICNUM: sector[i].ceilingpicnum=lValue; break;
        case SECTOR_CEILINGSLOPE:
            setslope(i, 0, lValue);
            break;
        case SECTOR_CEILINGSHADE: sector[i].ceilingshade=lValue; break;
        case SECTOR_CEILINGPAL: sector[i].ceilingpal=lValue; break;
        case SECTOR_CEILINGXPANNING: sector[i].ceilingxpanning=lValue; break;
        case SECTOR_CEILINGYPANNING: sector[i].ceilingypanning=lValue; break;
        case SECTOR_FLOORPICNUM: sector[i].floorpicnum=lValue; break;
        case SECTOR_FLOORSLOPE:
            setslope(i, 1, lValue);
            break;
        case SECTOR_FLOORSHADE: sector[i].floorshade=lValue; break;
        case SECTOR_FLOORPAL: sector[i].floorpal=lValue; break;
        case SECTOR_FLOORXPANNING: sector[i].floorxpanning=lValue; break;
        case SECTOR_FLOORYPANNING: sector[i].floorypanning=lValue; break;
        case SECTOR_VISIBILITY: sector[i].visibility=lValue; break;
        case SECTOR_FOGPAL: sector[i].fogpal=lValue; break;
        case SECTOR_LOTAG: sector[i].lotag=lValue; break;
        case SECTOR_HITAG: sector[i].hitag=lValue; break;
        case SECTOR_EXTRA: sector[i].extra=lValue; break;
        default:
            return -1;
        }

        return 0;
    }
    else
    {
        switch (labelNum)
        {
        case SECTOR_WALLPTR: lValue=sector[i].wallptr; break;
        case SECTOR_WALLNUM: lValue=sector[i].wallnum; break;
        case SECTOR_CEILINGZ: lValue=sector[i].ceilingz; break;
        case SECTOR_FLOORZ: lValue=sector[i].floorz; break;
        case SECTOR_CEILINGSTAT: lValue=sector[i].ceilingstat; break;
        case SECTOR_FLOORSTAT: lValue=sector[i].floorstat; break;
        case SECTOR_CEILINGPICNUM: lValue=sector[i].ceilingpicnum; break;
        case SECTOR_CEILINGSLOPE: lValue=sector[i].ceilingheinum; break;
        case SECTOR_CEILINGSHADE: lValue=sector[i].ceilingshade; break;
        case SECTOR_CEILINGPAL: lValue=sector[i].ceilingpal; break;
        case SECTOR_CEILINGXPANNING: lValue=sector[i].ceilingxpanning; break;
        case SECTOR_CEILINGYPANNING: lValue=sector[i].ceilingypanning; break;
        case SECTOR_FLOORPICNUM: lValue=sector[i].floorpicnum; break;
        case SECTOR_FLOORSLOPE: lValue=sector[i].floorheinum; break;
        case SECTOR_FLOORSHADE: lValue=sector[i].floorshade; break;
        case SECTOR_FLOORPAL: lValue=sector[i].floorpal; break;
        case SECTOR_FLOORXPANNING: lValue=sector[i].floorxpanning; break;
        case SECTOR_FLOORYPANNING: lValue=sector[i].floorypanning; break;
        case SECTOR_VISIBILITY: lValue=sector[i].visibility; break;
        case SECTOR_FOGPAL: lValue=sector[i].fogpal; break;
        case SECTOR_LOTAG: lValue=sector[i].lotag; break;
        case SECTOR_HITAG: lValue=sector[i].hitag; break;
        case SECTOR_EXTRA: lValue=sector[i].extra; break;
        default:
            return -1;
        }

        if (how&ACCESS_USEVARS)
            Gv_SetVar(lVar2, lValue);

        return lValue;
    }

badsector:
    M32_ERROR("Invalid sector %d", i);
    return -1;
readonly:
    M32_ERROR("Sector structure member `%s' is read-only.", SectorLabels[labelNum].name);
    return -1;
}

// how: bitfield: 1=set? 2=vars?
static int32_t __fastcall VM_AccessSprite(int32_t how, int32_t lVar1, int32_t labelNum, int32_t lVar2)
{
    int32_t lValue;
    int32_t i = (how&ACCESS_USEVARS) ? vm.spriteNum : lVar1;

    if ((how&ACCESS_USEVARS) && lVar1 != M32_THISACTOR_VAR_ID)
        i = Gv_GetVar(lVar1);

    if ((unsigned)i >= MAXSPRITES)
        goto badsprite;

    if (how&ACCESS_SET)
    {
        if (!m32_script_expertmode && (SpriteLabels[labelNum].flags & 1))
            goto readonly;

        lValue = (how&ACCESS_USEVARS) ? Gv_GetVar(lVar2) : lVar2;

        asksave = 1;

        if (!m32_script_expertmode && (SpriteLabels[labelNum].min != 0 || SpriteLabels[labelNum].max != 0))
        {
            if (lValue < SpriteLabels[labelNum].min)
                lValue = SpriteLabels[labelNum].min;
            if (lValue > SpriteLabels[labelNum].max)
                lValue = SpriteLabels[labelNum].max;
        }

        switch (labelNum)
        {
        case SPRITE_X: sprite[i].x=lValue; break;
        case SPRITE_Y: sprite[i].y=lValue; break;
        case SPRITE_Z: sprite[i].z=lValue; break;
        case SPRITE_CSTAT: sprite[i].cstat = lValue; break;
        case SPRITE_PICNUM: sprite[i].picnum=lValue; break;
        case SPRITE_SHADE: sprite[i].shade=lValue; break;
        case SPRITE_PAL: sprite[i].pal=lValue; break;
        case SPRITE_CLIPDIST: sprite[i].clipdist=lValue; break;
        case SPRITE_BLEND: sprite[i].blend=lValue; break;
        case SPRITE_XREPEAT: sprite[i].xrepeat=lValue; break;
        case SPRITE_YREPEAT: sprite[i].yrepeat=lValue; break;
        case SPRITE_XOFFSET: sprite[i].xoffset=lValue; break;
        case SPRITE_YOFFSET: sprite[i].yoffset=lValue; break;
        case SPRITE_SECTNUM: changespritesect(i,lValue); break;
        case SPRITE_STATNUM: changespritestat(i,lValue); break;
        case SPRITE_ANG:
            sprite[i].ang = lValue&2047;
            break;
        case SPRITE_OWNER: sprite[i].owner=lValue; break;
        case SPRITE_XVEL: sprite[i].xvel=lValue; break;
        case SPRITE_YVEL: sprite[i].yvel=lValue; break;
        case SPRITE_ZVEL: sprite[i].zvel=lValue; break;
        case SPRITE_LOTAG: sprite[i].lotag=lValue; break;
        case SPRITE_HITAG: sprite[i].hitag=lValue; break;
        case SPRITE_EXTRA: sprite[i].extra=lValue; break;
        default:
            return -1;
        }

        return 0;
    }
    else
    {
        switch (labelNum)
        {
        case SPRITE_X: lValue=sprite[i].x; break;
        case SPRITE_Y: lValue=sprite[i].y; break;
        case SPRITE_Z: lValue=sprite[i].z; break;
        case SPRITE_CSTAT: lValue=sprite[i].cstat; break;
        case SPRITE_PICNUM: lValue=sprite[i].picnum; break;
        case SPRITE_SHADE: lValue=sprite[i].shade; break;
        case SPRITE_PAL: lValue=sprite[i].pal; break;
        case SPRITE_CLIPDIST: lValue=sprite[i].clipdist; break;
        case SPRITE_BLEND: lValue=sprite[i].blend; break;
        case SPRITE_XREPEAT: lValue=sprite[i].xrepeat; break;
        case SPRITE_YREPEAT: lValue=sprite[i].yrepeat; break;
        case SPRITE_XOFFSET: lValue=sprite[i].xoffset; break;
        case SPRITE_YOFFSET: lValue=sprite[i].yoffset; break;
        case SPRITE_SECTNUM: lValue=sprite[i].sectnum; break;
        case SPRITE_STATNUM: lValue=sprite[i].statnum; break;
        case SPRITE_ANG: lValue=sprite[i].ang; break;
        case SPRITE_OWNER: lValue=sprite[i].owner; break;
        case SPRITE_XVEL: lValue=sprite[i].xvel; break;
        case SPRITE_YVEL: lValue=sprite[i].yvel; break;
        case SPRITE_ZVEL: lValue=sprite[i].zvel; break;
        case SPRITE_LOTAG: lValue=sprite[i].lotag; break;
        case SPRITE_HITAG: lValue=sprite[i].hitag; break;
        case SPRITE_EXTRA: lValue=sprite[i].extra; break;
        default:
            return -1;
        }

        if (how&ACCESS_USEVARS)
            Gv_SetVar(lVar2, lValue);

        return lValue;
    }
badsprite:
    M32_ERROR("tried to set %s on invalid target sprite (%d)", SpriteLabels[labelNum].name, i);
    return -1;
readonly:
    M32_ERROR("sprite structure member `%s' is read-only.", SpriteLabels[labelNum].name);
    return -1;
}

// how: bitfield: 1=set? 2=vars? 4=use spriteext[].tspr? (otherwise use tsprite[])
static int32_t __fastcall VM_AccessTsprite(int32_t how, int32_t lVar1, int32_t labelNum, int32_t lVar2)
{
    int32_t lightp = (labelNum >= LIGHT_X);
    int32_t i = (how&ACCESS_USEVARS) ? vm.spriteNum : lVar1;
    tspriteptr_t datspr = nullptr;
    memberlabel_t const *dalabel = lightp ? &LightLabels[labelNum-LIGHT_X] : &SpriteLabels[labelNum];

    if ((how&ACCESS_USEVARS) && lVar1 != M32_THISACTOR_VAR_ID)
        i = Gv_GetVar(lVar1);

    if (!lightp)
    {
        if (i<0 || i>=spritesortcnt)
            goto badsprite;
        datspr = &tsprite[i];
    }
    else
    {
        // access Polymer light
#ifndef POLYMER
        M32_ERROR("Polymer not compiled in, accessing lights is invalid.");
        return -1;
#else
        if ((how&ACCESS_USEVARS) && lVar1 == M32_THISACTOR_VAR_ID)
        {
            if ((unsigned)i >= MAXSPRITES)
                goto badsprite;
            M32_ERROR("Polymer light access via current sprite not implemented.");
            return -1;
        }
        else
        {
// check whether videoGetRenderMode() == REND_POLYMER ?
            if ((unsigned)i >= PR_MAXLIGHTS)
            {
                M32_ERROR("invalid light index (%d)", i);
                return -1;
            }

            if (labelNum != LIGHT_ACTIVE && !prlights[i].flags.active)
            {
                M32_ERROR("light with index %d is inactive!", i);
                return -1;
            }
        }
#endif
    }

    if (how&ACCESS_SET)
    {
        int32_t lValue, damin, damax;

        if (!m32_script_expertmode && (dalabel->flags & 1))
            goto readonly;

        lValue = (how&ACCESS_USEVARS) ? Gv_GetVar(lVar2) : lVar2;

        damin = dalabel->min;
        damax = dalabel->max;

        if (!m32_script_expertmode && (damin|damax))
        {
            if (lValue < damin)
                lValue = damin;
            if (lValue > damax)
                lValue = damax;
        }

        switch (labelNum)
        {
        case SPRITE_X: datspr->x=lValue; break;
        case SPRITE_Y: datspr->y=lValue; break;
        case SPRITE_Z: datspr->z=lValue; break;
        case SPRITE_CSTAT: datspr->cstat = lValue; break;
        case SPRITE_PICNUM: datspr->picnum=lValue; break;
        case SPRITE_SHADE: datspr->shade=lValue; break;
        case SPRITE_PAL: datspr->pal=lValue; break;
        case SPRITE_CLIPDIST: datspr->clipdist=lValue; break;
        case SPRITE_BLEND: datspr->blend=lValue; break;
        case SPRITE_XREPEAT: datspr->xrepeat=lValue; break;
        case SPRITE_YREPEAT: datspr->yrepeat=lValue; break;
        case SPRITE_XOFFSET: datspr->xoffset=lValue; break;
        case SPRITE_YOFFSET: datspr->yoffset=lValue; break;
        case SPRITE_SECTNUM: datspr->sectnum=lValue; break;
        case SPRITE_STATNUM: datspr->statnum=lValue; break;
        case SPRITE_ANG:
            datspr->ang = lValue&2047;
            break;
        case SPRITE_OWNER: datspr->owner=lValue; break;
        case SPRITE_XVEL: datspr->xvel=lValue; break;
        case SPRITE_YVEL: datspr->yvel=lValue; break;
        case SPRITE_ZVEL: datspr->zvel=lValue; break;
        case SPRITE_LOTAG: datspr->lotag=lValue; break;
        case SPRITE_HITAG: datspr->hitag=lValue; break;
        case SPRITE_EXTRA: datspr->extra=lValue; break;
#ifdef POLYMER
        // lights
        case LIGHT_X: prlights[i].x = lValue; break;
        case LIGHT_Y: prlights[i].y = lValue; break;
        case LIGHT_Z: prlights[i].z = lValue; break;
        case LIGHT_HORIZ: prlights[i].horiz = lValue; break;
        case LIGHT_RANGE: prlights[i].range = lValue; break;
        case LIGHT_ANGLE: prlights[i].angle = lValue; break;
        case LIGHT_FADERADIUS: prlights[i].faderadius = lValue; break;
        case LIGHT_RADIUS: prlights[i].radius = lValue; break;
        case LIGHT_SECTOR: prlights[i].sector = lValue; break;
        case LIGHT_R: prlights[i].color[0] = lValue; break;
        case LIGHT_G: prlights[i].color[1] = lValue; break;
        case LIGHT_B: prlights[i].color[2] = lValue; break;
        case LIGHT_PRIORITY: prlights[i].priority = lValue; break;
        case LIGHT_TILENUM: prlights[i].tilenum = lValue; break;
        case LIGHT_MINSHADE: prlights[i].minshade = lValue; break;
        case LIGHT_MAXSHADE: prlights[i].maxshade = lValue; break;
#endif
        default:
            return -1;
        }

        return 0;
    }
    else
    {
        int32_t lValue;

        switch (labelNum)
        {
        case SPRITE_X: lValue=datspr->x; break;
        case SPRITE_Y: lValue=datspr->y; break;
        case SPRITE_Z: lValue=datspr->z; break;
        case SPRITE_CSTAT: lValue=datspr->cstat; break;
        case SPRITE_PICNUM: lValue=datspr->picnum; break;
        case SPRITE_SHADE: lValue=datspr->shade; break;
        case SPRITE_PAL: lValue=datspr->pal; break;
        case SPRITE_CLIPDIST: lValue=datspr->clipdist; break;
        case SPRITE_BLEND: lValue=datspr->blend; break;
        case SPRITE_XREPEAT: lValue=datspr->xrepeat; break;
        case SPRITE_YREPEAT: lValue=datspr->yrepeat; break;
        case SPRITE_XOFFSET: lValue=datspr->xoffset; break;
        case SPRITE_YOFFSET: lValue=datspr->yoffset; break;
        case SPRITE_SECTNUM: lValue=datspr->sectnum; break;
        case SPRITE_STATNUM: lValue=datspr->statnum; break;
        case SPRITE_ANG: lValue=datspr->ang; break;
        case SPRITE_OWNER: lValue=datspr->owner; break;
        case SPRITE_XVEL: lValue=datspr->xvel; break;
        case SPRITE_YVEL: lValue=datspr->yvel; break;
        case SPRITE_ZVEL: lValue=datspr->zvel; break;
        case SPRITE_LOTAG: lValue=datspr->lotag; break;
        case SPRITE_HITAG: lValue=datspr->hitag; break;
        case SPRITE_EXTRA: lValue=datspr->extra; break;
#ifdef POLYMER
        // lights
        case LIGHT_X: lValue = prlights[i].x; break;
        case LIGHT_Y: lValue = prlights[i].y; break;
        case LIGHT_Z: lValue = prlights[i].z; break;
        case LIGHT_HORIZ: lValue = prlights[i].horiz; break;
        case LIGHT_RANGE: lValue = prlights[i].range; break;
        case LIGHT_ANGLE: lValue = prlights[i].angle; break;
        case LIGHT_FADERADIUS: lValue = prlights[i].faderadius; break;
        case LIGHT_RADIUS: lValue = prlights[i].radius; break;
        case LIGHT_SECTOR: lValue = prlights[i].sector; break;
        case LIGHT_R: lValue = prlights[i].color[0]; break;
        case LIGHT_G: lValue = prlights[i].color[1]; break;
        case LIGHT_B: lValue = prlights[i].color[2]; break;
        case LIGHT_PRIORITY: lValue = prlights[i].priority; break;
        case LIGHT_TILENUM: lValue = prlights[i].tilenum; break;
        case LIGHT_MINSHADE: lValue = prlights[i].minshade; break;
        case LIGHT_MAXSHADE: lValue = prlights[i].maxshade; break;
        case LIGHT_ACTIVE: lValue = !!prlights[i].flags.active; break;
#endif
        default:
            return -1;
        }

        if (how&ACCESS_USEVARS)
            Gv_SetVar(lVar2, lValue);

        return lValue;
    }

badsprite:
    M32_ERROR("invalid target sprite (%d)", i);
    return -1;
readonly:
    M32_ERROR("structure member `%s' is read-only.", dalabel->name);
    return -1;
}

#undef ACCESS_SET
#undef ACCESS_USEVARS


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 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.
*/
//-------------------------------------------------------------------------

// This object is shared by the editors of *all* Build games!

#include "m32script.h"
#include "m32def.h"
#include "osd.h"
#include "keys.h"
#ifdef POLYMER
#include "polymer.h"
#endif

#define _m32vars_c_
#include "m32structures.cpp"

static void Gv_Clear(void)
{
    // only call this function ONCE...
    int32_t i=(MAXGAMEVARS-1);

    //AddLog("Gv_Clear");

    for (; i>=0; i--)
    {
        DO_FREE_AND_NULL(aGameVars[i].szLabel);

        if (aGameVars[i].dwFlags & GAMEVAR_USER_MASK)
            DO_FREE_AND_NULL(aGameVars[i].val.plValues);

        aGameVars[i].val.lValue = 0;
        aGameVars[i].dwFlags |= GAMEVAR_RESET;

        if (i >= MAXGAMEARRAYS)
            continue;

        gamearray_t *const gar = &aGameArrays[i];

        DO_FREE_AND_NULL(gar->szLabel);

        if (gar->dwFlags & GAMEARRAY_NORMAL)
            DO_FREE_AND_NULL(gar->vals);

        gar->dwFlags |= GAMEARRAY_RESET;
    }

    g_gameVarCount = g_gameArrayCount = 0;

    hash_init(&h_gamevars);
    hash_init(&h_arrays);
}

#define ASSERT_IMPLIES(x, y) Bassert(!(x) || (y))

void Gv_NewArray(const char *pszLabel, void *arrayptr, intptr_t asize, uint32_t dwFlags)
{
    ASSERT_IMPLIES(dwFlags&GAMEARRAY_VARSIZE, dwFlags&GAMEARRAY_READONLY);
    ASSERT_IMPLIES(dwFlags&GAMEARRAY_STRIDE2, dwFlags&GAMEARRAY_READONLY);
    ASSERT_IMPLIES(dwFlags&GAMEARRAY_TYPE_MASK,
                   g_gameArrayCount==0 || (dwFlags&(GAMEARRAY_READONLY|GAMEARRAY_WARN)));

    if (g_gameArrayCount >= MAXGAMEARRAYS)
    {
        C_CUSTOMERROR("too many arrays! (max: %d)", MAXGAMEARRAYS);
        return;
    }

    if (Bstrlen(pszLabel) > (MAXARRAYLABEL-1))
    {
        C_CUSTOMERROR("array name `%s' exceeds limit of %d characters.", pszLabel, MAXARRAYLABEL);
        return;
    }

    const int32_t i = hash_find(&h_arrays, pszLabel);

    if (i>=0 && !(aGameArrays[i].dwFlags & GAMEARRAY_RESET))
    {
        // found it it's a duplicate in error

        if (aGameArrays[i].dwFlags&GAMEARRAY_TYPE_MASK)
            C_CUSTOMWARNING("ignored redefining system array `%s'.", pszLabel);

//        C_ReportError(WARNING_DUPLICATEDEFINITION);
        return;
    }

    if (!(dwFlags&GAMEARRAY_VARSIZE) && !(dwFlags&GAMEARRAY_TYPE_MASK) && (asize<=0 || asize>65536))
    {
        // the dummy array with index 0 sets the size to 0 so that accidental accesses as array
        // will complain.
        C_CUSTOMERROR("invalid array size %d. Must be between 1 and 65536", (int)asize);
        return;
    }

    gamearray_t *const gar = &aGameArrays[g_gameArrayCount];

    if (gar->szLabel == NULL)
        gar->szLabel = (char *)Xcalloc(MAXARRAYLABEL, sizeof(char));
    if (gar->szLabel != pszLabel)
        Bstrcpy(gar->szLabel, pszLabel);

    if (!(dwFlags & GAMEARRAY_TYPE_MASK))
        gar->vals = (int32_t *)Xcalloc(asize, sizeof(int32_t));
    else
        gar->vals = arrayptr;

    gar->size = asize;
    gar->dwFlags = dwFlags & ~GAMEARRAY_RESET;

    hash_add(&h_arrays, gar->szLabel, g_gameArrayCount, 1);
    g_gameArrayCount++;
}

void Gv_NewVar(const char *pszLabel, intptr_t lValue, uint32_t dwFlags)
{
    int32_t i, j;

    //Bsprintf(g_szBuf,"Gv_NewVar(%s, %d, %X)",pszLabel, lValue, dwFlags);
    //AddLog(g_szBuf);

    if (g_gameVarCount >= MAXGAMEVARS)
    {
        C_CUSTOMERROR("too many gamevars! (max: %d)", MAXGAMEVARS);
        return;
    }

    if (Bstrlen(pszLabel) > (MAXVARLABEL-1))
    {
        C_CUSTOMERROR("variable name `%s' exceeds limit of %d characters.", pszLabel, MAXVARLABEL);
        return;
    }

    i = hash_find(&h_gamevars,pszLabel);

    if (i >= 0 && !(aGameVars[i].dwFlags & GAMEVAR_RESET))
    {
        // found it...
        if (aGameVars[i].dwFlags & GAMEVAR_PTR_MASK)
        {
            C_ReportError(-1);
            initprintf("%s:%d: warning: cannot redefine internal gamevar `%s'.\n",g_szScriptFileName,g_lineNumber,label+(g_numLabels<<6));
            return;
        }
        else if (!(aGameVars[i].dwFlags & GAMEVAR_SYSTEM))
        {
            // it's a duplicate in error
//            g_numCompilerWarnings++;
//            C_ReportError(WARNING_DUPLICATEDEFINITION);
            return;
        }
    }

    if (i == -1)
        i = g_gameVarCount;

    // Set values
    if ((aGameVars[i].dwFlags & GAMEVAR_SYSTEM) == 0)
    {
        if (aGameVars[i].szLabel == NULL)
            aGameVars[i].szLabel = (char *)Xcalloc(MAXVARLABEL, sizeof(uint8_t));
        if (aGameVars[i].szLabel != pszLabel)
            Bstrcpy(aGameVars[i].szLabel,pszLabel);
        aGameVars[i].dwFlags = dwFlags;

        if (aGameVars[i].dwFlags & GAMEVAR_USER_MASK)
        {
            // only free if not system
            DO_FREE_AND_NULL(aGameVars[i].val.plValues);
        }
    }

    // if existing is system, they only get to change default value....
    aGameVars[i].lDefault = lValue;
    aGameVars[i].dwFlags &= ~GAMEVAR_RESET;

    if (i == g_gameVarCount)
    {
        // we're adding a new one.
        hash_add(&h_gamevars, aGameVars[i].szLabel, g_gameVarCount++, 0);
    }

    if (aGameVars[i].dwFlags & GAMEVAR_PERBLOCK)
    {
        if (!aGameVars[i].val.plValues)
            aGameVars[i].val.plValues = (int32_t *)Xcalloc(1+MAXEVENTS+g_stateCount, sizeof(int32_t));
        for (j=0; j<1+MAXEVENTS+g_stateCount; j++)
            aGameVars[i].val.plValues[j] = lValue;
    }
    else aGameVars[i].val.lValue = lValue;
}

int32_t __fastcall Gv_GetVarN(int32_t id)  // 'N' for "no side-effects"... vars and locals only!
{
    if (id == M32_THISACTOR_VAR_ID)
        return vm.spriteNum;

    switch (id&M32_VARTYPE_MASK)
    {
    case M32_FLAG_VAR:
        id &= (MAXGAMEVARS-1);

        switch (aGameVars[id].dwFlags & (GAMEVAR_USER_MASK|GAMEVAR_PTR_MASK))
        {
        case 0:
            return aGameVars[id].val.lValue;
        case GAMEVAR_PERBLOCK:
            return aGameVars[id].val.plValues[vm.g_st];
        case GAMEVAR_FLOATPTR:
        case GAMEVAR_INTPTR:
            return *((int32_t *)aGameVars[id].val.lValue);
        case GAMEVAR_SHORTPTR:
            return *((int16_t *)aGameVars[id].val.lValue);
        case GAMEVAR_CHARPTR:
            return *((uint8_t *)aGameVars[id].val.lValue);
        default:
            M32_ERROR("Gv_GetVarN(): WTF??");
            return -1;
        }

    case M32_FLAG_LOCAL:
    {
        int32_t index = id&(MAXGAMEVARS-1);
        // no bounds checking since it's done at script compilation time
        return ((int32_t *)aGameArrays[M32_LOCAL_ARRAY_ID].vals)[index];
    }

    default:
        M32_ERROR("Gv_GetVarN(): invalid var code %0x08x", id);
        return -1;
    }
}

int32_t __fastcall Gv_GetVar(int32_t id)
{
    int32_t negateResult = !!(id&M32_FLAG_NEGATE);

    if (id == M32_THISACTOR_VAR_ID)
        return vm.spriteNum;

    id &= ~M32_FLAG_NEGATE;

    if ((id & M32_BITS_MASK) == M32_FLAG_CONSTANT)
    {
        switch (id&3)
        {
        case 0:
            return ((int16_t)(id>>16));
        case 1:
            return constants[(id>>16)&0xffff];
        case 2:
            return (labelval[(id>>16)&0xffff] ^ -negateResult) + negateResult;
        default:
            M32_ERROR("Gv_GetVarX() (constant): WTF??");
            return -1;
        }
    }

    switch (id&M32_VARTYPE_MASK)
    {
    case M32_FLAG_ARRAY:
    {
        int32_t index;

        index = (int32_t)((id>>16)&0xffff);
        if (!(id&M32_FLAG_CONSTANTINDEX))
            index = Gv_GetVarN(index);

        id &= (MAXGAMEARRAYS-1);

        const int32_t siz = Gv_GetArraySize(id);
        const gamearray_t *const gar = &aGameArrays[id];

        if (index < 0 || index >= siz)
        {
            M32_ERROR("Gv_GetVarX(): invalid array index (%s[%d])", gar->szLabel, index);
            return -1;
        }

        if (gar->dwFlags & GAMEARRAY_STRIDE2)
            index <<= 1;

        switch (gar->dwFlags & GAMEARRAY_TYPE_MASK)
        {
        case 0:
        case GAMEARRAY_INT32:
            return (((int32_t *)gar->vals)[index] ^ -negateResult) + negateResult;
        case GAMEARRAY_INT16:
            return (((int16_t *)gar->vals)[index] ^ -negateResult) + negateResult;
        case GAMEARRAY_UINT8:
            return (((uint8_t *)gar->vals)[index] ^ -negateResult) + negateResult;
        default:
            M32_ERROR("Gv_GetVarX() (array): WTF??");
            return -1;
        }
    }
    case M32_FLAG_STRUCT:
    {
        int32_t index, memberid;

        index = (id>>16)&0x7fff;
        if (!(id&M32_FLAG_CONSTANTINDEX))
            index = Gv_GetVarN(index);

        memberid = (id>>2)&63;

        switch (id&3)
        {
        case M32_SPRITE_VAR_ID:
            return (VM_AccessSprite(0, index, memberid, 0) ^ -negateResult) + negateResult;
        case M32_SECTOR_VAR_ID:
            return (VM_AccessSector(0, index, memberid, 0) ^ -negateResult) + negateResult;
        case M32_WALL_VAR_ID:
            return (VM_AccessWall(0, index, memberid, 0) ^ -negateResult) + negateResult;
        case M32_TSPRITE_VAR_ID:
            return (VM_AccessTsprite(0, index, memberid, 0) ^ -negateResult) + negateResult;
        default:
            M32_ERROR("Gv_GetVarX(): WTF??");
            return -1;
        }
    }
    case M32_FLAG_VAR:
    {
        id &= (MAXGAMEVARS-1);

        switch (aGameVars[id].dwFlags & (GAMEVAR_USER_MASK|GAMEVAR_PTR_MASK))
        {
        case 0:
            return (aGameVars[id].val.lValue ^ -negateResult) + negateResult;
        case GAMEVAR_PERBLOCK:
            return (aGameVars[id].val.plValues[vm.g_st] ^ -negateResult) + negateResult;
        case GAMEVAR_FLOATPTR:
        {
            union { int32_t ival; float fval; };

            fval = *(float *)aGameVars[id].val.plValues;
            if (negateResult)
                fval *= -1;
            return ival;
        }
        case GAMEVAR_INTPTR:
            return (*((int32_t *)aGameVars[id].val.lValue) ^ -negateResult) + negateResult;
        case GAMEVAR_SHORTPTR:
            return (*((int16_t *)aGameVars[id].val.lValue) ^ -negateResult) + negateResult;
        case GAMEVAR_CHARPTR:
            return (*((uint8_t *)aGameVars[id].val.lValue) ^ -negateResult) + negateResult;
        default:
            M32_ERROR("Gv_GetVarX(): WTF??");
            return -1;
        }
    }
    case M32_FLAG_LOCAL:
    {
        int32_t index = id&(MAXGAMEVARS-1);
        // no bounds checking since it's done at script compilation time
        return (((int32_t *)aGameArrays[M32_LOCAL_ARRAY_ID].vals)[index] ^ -negateResult) + negateResult;
    }
    }  // switch (id&M32_VARTYPE_MASK)

    return 0;  // never reached
}

void __fastcall Gv_SetVar(int32_t id, int32_t lValue)
{
    switch (id&M32_VARTYPE_MASK)
    {
    case M32_FLAG_ARRAY:
    {
        int32_t index;

        index = (id>>16)&0xffff;
        if (!(id&M32_FLAG_CONSTANTINDEX))
            index = Gv_GetVarN(index);

        id &= (MAXGAMEARRAYS-1);

        const int32_t siz = Gv_GetArraySize(id);
        gamearray_t *const gar = &aGameArrays[id];

        if (index < 0 || index >= siz)
        {
            M32_ERROR("Gv_SetVarX(): invalid array index %s[%d], size=%d", gar->szLabel, index, siz);
            return;
        }

        // NOTE: GAMEARRAY_READONLY arrays can be modified in expert mode.
        Bassert((gar->dwFlags & GAMEARRAY_STRIDE2) == 0);

        switch (gar->dwFlags & GAMEARRAY_TYPE_MASK)
        {
        case 0:
        case GAMEARRAY_INT32:
            ((int32_t *)gar->vals)[index] = lValue;
            return;
        case GAMEARRAY_INT16:
            ((int16_t *)gar->vals)[index] = (int16_t)lValue;
            return;
        case GAMEARRAY_UINT8:
            ((uint8_t *)gar->vals)[index] = (uint8_t)lValue;
            return;
        default:
            M32_ERROR("Gv_SetVarX() (array): WTF??");
            return;
        }
        return;
    }
    case M32_FLAG_STRUCT:
    {
        int32_t index, memberid;

        index = (id>>16)&0x7fff;
        if (!(id&M32_FLAG_CONSTANTINDEX))
            index = Gv_GetVarN(index);

        memberid = (id>>2)&63;

        switch (id&3)
        {
        case M32_SPRITE_VAR_ID:
            VM_AccessSprite(1, index, memberid, lValue);
            return;
        case M32_SECTOR_VAR_ID:
            VM_AccessSector(1, index, memberid, lValue);
            return;
        case M32_WALL_VAR_ID:
            VM_AccessWall(1, index, memberid, lValue);
            return;
        case M32_TSPRITE_VAR_ID:
            VM_AccessTsprite(1, index, memberid, lValue);
            return;
        default:
            M32_ERROR("Gv_SetVarX(): WTF??");
            return;
        }
    }
    case M32_FLAG_VAR:
    {
        id &= (MAXGAMEVARS-1);

        switch (aGameVars[id].dwFlags & (GAMEVAR_USER_MASK|GAMEVAR_PTR_MASK))
        {
        case 0:
            aGameVars[id].val.lValue=lValue;
            return;
        case GAMEVAR_PERBLOCK:
            aGameVars[id].val.plValues[vm.g_st] = lValue;
            return;
        case GAMEVAR_FLOATPTR:
        {
            union { int32_t ival; float fval; };
            ival = lValue;

            if (fval!=fval || fval<-3.4e38 || fval > 3.4e38)
            {
                M32_ERROR("Gv_SetVarX(): tried to set float var to NaN or infinity");
                return;
            }
        }
        fallthrough__;
        case GAMEVAR_INTPTR:
            *((int32_t *)aGameVars[id].val.lValue)=(int32_t)lValue;
            return;
        case GAMEVAR_SHORTPTR:
            *((int16_t *)aGameVars[id].val.lValue)=(int16_t)lValue;
            return;
        case GAMEVAR_CHARPTR:
            *((uint8_t *)aGameVars[id].val.lValue)=(uint8_t)lValue;
            return;
        default:
            M32_ERROR("Gv_SetVarX(): WTF??");
            return;
        }
    }
    case M32_FLAG_LOCAL:
    {
        int32_t index = id&(MAXGAMEVARS-1);
        ((int32_t *)aGameArrays[M32_LOCAL_ARRAY_ID].vals)[index] = lValue;
        return;
    }
    }
}

static uint8_t alphakeys[] =
{
    KEYSC_SPACE,

    KEYSC_A, KEYSC_B, KEYSC_C, KEYSC_D, KEYSC_E, KEYSC_F, KEYSC_G, KEYSC_H,
    KEYSC_I, KEYSC_J, KEYSC_K, KEYSC_L, KEYSC_M, KEYSC_N, KEYSC_O, KEYSC_P,
    KEYSC_Q, KEYSC_R, KEYSC_S, KEYSC_T, KEYSC_U, KEYSC_V, KEYSC_W, KEYSC_X,
    KEYSC_Y, KEYSC_Z,
};

static uint8_t numberkeys[] =
{
    KEYSC_0, KEYSC_1, KEYSC_2, KEYSC_3, KEYSC_4, KEYSC_5, KEYSC_6, KEYSC_7,
    KEYSC_8, KEYSC_9,
};

static void Gv_AddSystemVars(void)
{
    // only call ONCE
    int32_t hlcnt_id, hlscnt_id;

    // special vars for struct access
    // MUST be at top and in this order!!!
    Gv_NewVar("sprite", -1, GAMEVAR_READONLY | GAMEVAR_SYSTEM | GAMEVAR_SPECIAL);
    Gv_NewVar("sector", -1, GAMEVAR_READONLY | GAMEVAR_SYSTEM | GAMEVAR_SPECIAL);
    Gv_NewVar("wall", -1, GAMEVAR_READONLY | GAMEVAR_SYSTEM | GAMEVAR_SPECIAL);
    Gv_NewVar("tsprite", -1, GAMEVAR_READONLY | GAMEVAR_SYSTEM | GAMEVAR_SPECIAL);
    Gv_NewVar("light", -1, GAMEVAR_READONLY | GAMEVAR_SYSTEM | GAMEVAR_SPECIAL);

    // these too have to be in here and in order!
    // keep in sync with m32script.h: IDs of special vars

    Gv_NewVar("I", 0, GAMEVAR_READONLY | GAMEVAR_SYSTEM);  // THISACTOR
    Gv_NewVar("RETURN", (intptr_t)&g_iReturnVar, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("LOTAG", 0, GAMEVAR_SYSTEM);
    Gv_NewVar("HITAG", 0, GAMEVAR_SYSTEM);
    Gv_NewVar("TEXTURE", 0, GAMEVAR_SYSTEM);
    Gv_NewVar("DOSCRSHOT", (intptr_t)&g_doScreenShot, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);

    Gv_NewVar("xdim",(intptr_t)&xdim, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("ydim",(intptr_t)&ydim, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("windowx1",(intptr_t)&windowxy1.x, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("windowx2",(intptr_t)&windowxy2.x, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("windowy1",(intptr_t)&windowxy1.y, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("windowy2",(intptr_t)&windowxy2.y, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("totalclock",(intptr_t)&totalclock, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);

    Gv_NewVar("viewingrange",(intptr_t)&viewingrange, GAMEVAR_SYSTEM | GAMEVAR_INTPTR | GAMEVAR_READONLY);
    Gv_NewVar("yxaspect",(intptr_t)&yxaspect, GAMEVAR_SYSTEM | GAMEVAR_INTPTR | GAMEVAR_READONLY);

///    Gv_NewVar("framerate",(intptr_t)&g_frameRate, GAMEVAR_SYSTEM | GAMEVAR_INTPTR | GAMEVAR_READONLY);
///    Gv_NewVar("display_mirror",(intptr_t)&display_mirror, GAMEVAR_SYSTEM | GAMEVAR_CHARPTR);

    Gv_NewVar("randomseed",(intptr_t)&randomseed, GAMEVAR_SYSTEM | GAMEVAR_INTPTR);

    Gv_NewVar("numwalls",(intptr_t)&numwalls, GAMEVAR_SYSTEM | GAMEVAR_SHORTPTR | GAMEVAR_READONLY);
    Gv_NewVar("numsectors",(intptr_t)&numsectors, GAMEVAR_SYSTEM | GAMEVAR_SHORTPTR | GAMEVAR_READONLY);
    Gv_NewVar("numsprites",(intptr_t)&Numsprites, GAMEVAR_SYSTEM | GAMEVAR_INTPTR | GAMEVAR_READONLY);
    {
        static int32_t numtiles;
        Gv_NewVar("numtiles",(intptr_t)&numtiles, GAMEVAR_SYSTEM | GAMEVAR_INTPTR | GAMEVAR_READONLY);
    }
#ifdef YAX_ENABLE
    Gv_NewVar("numbunches",(intptr_t)&numyaxbunches, GAMEVAR_SYSTEM | GAMEVAR_INTPTR | GAMEVAR_READONLY);
#endif

#ifdef USE_OPENGL
    Gv_NewVar("rendmode",(intptr_t)&rendmode, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
#endif

    // current position
    Gv_NewVar("posx",(intptr_t)&pos.x, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("posy",(intptr_t)&pos.y, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("posz",(intptr_t)&pos.z, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("ang",(intptr_t)&ang, GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("horiz",(intptr_t)&horiz, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("cursectnum",(intptr_t)&cursectnum, GAMEVAR_READONLY | GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("hardcoded_movement",(intptr_t)&g_doHardcodedMovement, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);

    Gv_NewVar("searchx",(intptr_t)&searchx, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("searchy",(intptr_t)&searchy, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("searchstat",(intptr_t)&searchstat, GAMEVAR_READONLY | GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("searchwall",(intptr_t)&searchwall, GAMEVAR_READONLY | GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("searchsector",(intptr_t)&searchsector, GAMEVAR_READONLY | GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("searchbottomwall",(intptr_t)&searchbottomwall, GAMEVAR_READONLY | GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);

    Gv_NewVar("pointhighlight",(intptr_t)&pointhighlight, GAMEVAR_SHORTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("linehighlight",(intptr_t)&linehighlight, GAMEVAR_SHORTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);

    hlcnt_id = g_gameVarCount;
    Gv_NewVar("highlightcnt",(intptr_t)&highlightcnt, GAMEVAR_SHORTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    hlscnt_id = g_gameVarCount;
    Gv_NewVar("highlightsectorcnt",(intptr_t)&highlightsectorcnt, GAMEVAR_SHORTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);

    // clipboard contents
    Gv_NewVar("temppicnum",(intptr_t)&temppicnum, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempcstat",(intptr_t)&tempcstat, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("templotag",(intptr_t)&templotag, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("temphitag",(intptr_t)&temphitag, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempextra",(intptr_t)&tempextra, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempshade",(intptr_t)&tempshade, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("temppal",(intptr_t)&temppal, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempvis",(intptr_t)&tempvis, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempxrepeat",(intptr_t)&tempxrepeat, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempyrepeat",(intptr_t)&tempyrepeat, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempxpanning",(intptr_t)&tempxpanning, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempypanning",(intptr_t)&tempypanning, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempxvel",(intptr_t)&tempxvel, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempyvel",(intptr_t)&tempyvel, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempzvel",(intptr_t)&tempzvel, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempstatnum",(intptr_t)&tempstatnum, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempblend",(intptr_t)&tempblend, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);
    Gv_NewVar("tempsectornum",(intptr_t)&tempsectornum, GAMEVAR_INTPTR|GAMEVAR_SYSTEM|GAMEVAR_READONLY);

    // starting position
    Gv_NewVar("startposx",(intptr_t)&startpos.x, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("startposy",(intptr_t)&startpos.y, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("startposz",(intptr_t)&startpos.z, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("startang",(intptr_t)&startang, GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("startsectnum",(intptr_t)&startsectnum, GAMEVAR_SHORTPTR | GAMEVAR_SYSTEM);

    Gv_NewVar("mousxplc",(intptr_t)&mousxplc, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("mousyplc",(intptr_t)&mousyplc, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("mousebits",(intptr_t)&g_mouseBits, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);

    Gv_NewVar("zoom",(intptr_t)&zoom, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("drawlinepat",(intptr_t)&m32_drawlinepat, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("halfxdim16", (intptr_t)&halfxdim16, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("midydim16", (intptr_t)&midydim16, GAMEVAR_READONLY | GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("ydim16",(intptr_t)&ydim16, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("m32_sideview",(intptr_t)&m32_sideview, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);

    Gv_NewVar("SV1",(intptr_t)&m32_sortvar1, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("SV2",(intptr_t)&m32_sortvar2, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);
    Gv_NewVar("spritesortcnt",(intptr_t)&spritesortcnt, GAMEVAR_INTPTR | GAMEVAR_SYSTEM | GAMEVAR_READONLY);

#ifdef POLYMER
    Gv_NewVar("pr_overrideparallax",(intptr_t)&pr_overrideparallax, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("pr_parallaxscale",(intptr_t)&pr_parallaxscale, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("pr_parallaxbias",(intptr_t)&pr_parallaxbias, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("pr_overridespecular",(intptr_t)&pr_overridespecular, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("pr_specularpower",(intptr_t)&pr_specularpower, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
    Gv_NewVar("pr_specularfactor",(intptr_t)&pr_specularfactor, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
#else
    {
        // dummy Polymer variables for non-Polymer builds
        static int32_t pr_overrideparallax = 0;
        static float pr_parallaxscale = 0.1f;
        static float pr_parallaxbias = 0.0f;
        static int32_t pr_overridespecular = 0;
        static float pr_specularpower = 15.0f;
        static float pr_specularfactor = 1.0f;

        Gv_NewVar("pr_overrideparallax",(intptr_t)&pr_overrideparallax, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
        Gv_NewVar("pr_parallaxscale",(intptr_t)&pr_parallaxscale, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
        Gv_NewVar("pr_parallaxbias",(intptr_t)&pr_parallaxbias, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
        Gv_NewVar("pr_overridespecular",(intptr_t)&pr_overridespecular, GAMEVAR_INTPTR | GAMEVAR_SYSTEM);
        Gv_NewVar("pr_specularpower",(intptr_t)&pr_specularpower, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
        Gv_NewVar("pr_specularfactor",(intptr_t)&pr_specularfactor, GAMEVAR_FLOATPTR | GAMEVAR_SYSTEM);
    }
#endif

    g_systemVarCount = g_gameVarCount;

    // must be first!
    Gv_NewArray(".LOCALS_BASE", NULL, 0, GAMEARRAY_INT32);

    Gv_NewArray("highlight", (void *)highlight, hlcnt_id,
                GAMEARRAY_READONLY|GAMEARRAY_INT16|GAMEARRAY_VARSIZE);
    Gv_NewArray("highlightsector", (void *)highlightsector, hlscnt_id,
                GAMEARRAY_READONLY|GAMEARRAY_INT16|GAMEARRAY_VARSIZE);

    Gv_NewArray("hsect", (void *)headspritesect, MAXSECTORS+1, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("psect", (void *)prevspritesect, MAXSPRITES, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("nsect", (void *)nextspritesect, MAXSPRITES, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("hstat", (void *)headspritestat, MAXSTATUS+1, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("pstat", (void *)prevspritestat, MAXSPRITES, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("nstat", (void *)nextspritestat, MAXSPRITES, GAMEARRAY_READONLY|GAMEARRAY_INT16);
#ifdef YAX_ENABLE
    Gv_NewArray("headsectbunchc", (void *)headsectbunch[0], YAX_MAXBUNCHES, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("nextsectbunchc", (void *)nextsectbunch[0], MAXSECTORS, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("headsectbunchf", (void *)headsectbunch[1], YAX_MAXBUNCHES, GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("nextsectbunchf", (void *)nextsectbunch[1], MAXSECTORS, GAMEARRAY_READONLY|GAMEARRAY_INT16);
#endif
    Gv_NewArray("editorcolors", (void *)editorcolors, 256, GAMEARRAY_READONLY|GAMEARRAY_UINT8);
    Gv_NewArray("tilesizx", (void *)&tilesiz[0].x, MAXTILES, GAMEARRAY_STRIDE2|GAMEARRAY_READONLY|GAMEARRAY_INT16);
    Gv_NewArray("tilesizy", (void *)&tilesiz[0].y, MAXTILES, GAMEARRAY_STRIDE2|GAMEARRAY_READONLY|GAMEARRAY_INT16);
//    Gv_NewArray("picsiz", (void *)picsiz, MAXTILES, GAMEARRAY_READONLY|GAMEARRAY_OFCHAR);
    Gv_NewArray("picanm", (void *)picanm, MAXTILES, GAMEARRAY_READONLY|GAMEARRAY_INT32);

    Gv_NewArray("show2dsector", (void *)show2dsector, (MAXSECTORS+7)>>3, GAMEARRAY_READONLY|GAMEARRAY_UINT8);
    Gv_NewArray("show2dwall", (void *)show2dwall, (MAXWALLS+7)>>3, GAMEARRAY_READONLY|GAMEARRAY_UINT8);
    Gv_NewArray("show2dsprite", (void *)show2dsprite, (MAXSPRITES+7)>>3, GAMEARRAY_READONLY|GAMEARRAY_UINT8);

    Gv_NewArray("keystatus", (void *)keystatus, 256, GAMEARRAY_WARN|GAMEARRAY_UINT8);
    Gv_NewArray("alphakeys", (void *)alphakeys, sizeof(alphakeys), GAMEARRAY_READONLY|GAMEARRAY_UINT8);
    Gv_NewArray("numberkeys", (void *)numberkeys, sizeof(numberkeys), GAMEARRAY_READONLY|GAMEARRAY_UINT8);

    g_systemArrayCount = g_gameArrayCount;
}

void Gv_Init(void)
{
    // only call ONCE

    Gv_Clear();
    Gv_AddSystemVars();
}

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 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.
*/
//-------------------------------------------------------------------------

#ifndef EDUKE32_MACROS_H_
#define EDUKE32_MACROS_H_

#include "mmulti.h"

// Macros, some from SW source

#define BGSTRETCH (ud.bgstretch ? 1024 : 0)

#define WIN_IS_PRESSED ( KB_KeyPressed( sc_RightWin ) || KB_KeyPressed( sc_LeftWin ) )
#define ALT_IS_PRESSED ( KB_KeyPressed( sc_RightAlt ) || KB_KeyPressed( sc_LeftAlt ) )
#define SHIFTS_IS_PRESSED ( KB_KeyPressed( sc_RightShift ) || KB_KeyPressed( sc_LeftShift ) )

#ifndef EDUKE32_STANDALONE
#define RANDOMSCRAP(s, i) A_InsertSprite(s->sectnum,s->x+(krand()&255)-128,s->y+(krand()&255)-128,s->z-ZOFFSET3-(krand()&8191),\
    SCRAP6+(krand()&15),-8,48,48,krand()&2047,(krand()&63)+64,-512-(krand()&2047),i,5)
#endif

#define GTFLAGS(x) (g_gametypeFlags[ud.coop] & x)

#define TRAVERSE_SPRITE_SECT(l, o, n)    (o) = (l); ((o) != -1) && ((n) = nextspritesect[o]); (o) = (n)
#define TRAVERSE_SPRITE_STAT(l, o, n)    (o) = (l); ((o) != -1) && ((n) = nextspritestat[o]); (o) = (n)
#define TRAVERSE_CONNECT(i)              i = 0; i != -1; i = connectpoint2[i]

#define TEST(flags,mask) ((flags) & (mask))
#define SET(flags,mask) ((flags) |= (mask))
#define RESET(flags,mask) ((flags) &= ~(mask))
#define FLIP(flags,mask) ((flags) ^= (mask))

// mask definitions

#define BIT(shift)     (1u<<(shift))

#define TEST_SYNC_KEY(bits, sync_num) (!!TEST((bits), BIT(sync_num)))

#ifndef EDUKE32_STANDALONE
#define AFLAMABLE(X) (X==BOX||X==TREE1||X==TREE2||X==TIRE||X==CONE)
#endif
#define rnd(X) ((krand()>>8)>=(255-(X)))

//
// NETWORK - REDEFINABLE SHARED (SYNC) KEYS BIT POSITIONS
//

#define SK_JUMP         0
#define SK_CROUCH       1
#define SK_FIRE         2
#define SK_AIM_UP       3
#define SK_AIM_DOWN     4
#define SK_RUN          5
#define SK_LOOK_LEFT    6
#define SK_LOOK_RIGHT   7
// weapons take up 4 bits...
#define SK_WEAPON_BITS  8
#define SK_WEAPON_BITS1 9
#define SK_WEAPON_BITS2 10
#define SK_WEAPON_BITS3 11
#define SK_WEAPON_MASK ((1 << SK_WEAPON_BITS) | (1 << SK_WEAPON_BITS1) | (1 << SK_WEAPON_BITS2) | (1 << SK_WEAPON_BITS3))
#define SK_STEROIDS     12
#define SK_LOOK_UP      13
#define SK_LOOK_DOWN    14
#define SK_NIGHTVISION  15
#define SK_MEDKIT       16
#define SK_MULTIFLAG    17
#define SK_CENTER_VIEW  18
#define SK_HOLSTER      19
#define SK_INV_LEFT     20
#define SK_PAUSE        21
#define SK_QUICK_KICK   22
#define SK_AIMMODE      23
#define SK_HOLODUKE     24
#define SK_JETPACK      25
#define SK_GAMEQUIT     26
#define SK_INV_RIGHT    27
#define SK_TURNAROUND   28
#define SK_OPEN         29
#define SK_INVENTORY    30
#define SK_ESCAPE       31

#define EK_MOVE_FORWARD         0
#define EK_MOVE_BACKWARD        1
#define EK_STRAFE_LEFT          2
#define EK_STRAFE_RIGHT         3
#define EK_TURN_LEFT            4
#define EK_TURN_RIGHT           5
#define EK_ALT_FIRE             6
#define EK_CHAT_MODE            7
#define EK_GAMEPAD_CENTERING    8
#define EK_GAMEPAD_AIM_ASSIST   9

// rotatesprite flags
#define ROTATE_SPRITE_TRANSLUCENT   (BIT(0))
#define ROTATE_SPRITE_VIEW_CLIP     (BIT(1)) // clip to view
#define ROTATE_SPRITE_YFLIP         (BIT(2))
#define ROTATE_SPRITE_IGNORE_START_MOST (BIT(3)) // don't clip to startumost
#define ROTATE_SPRITE_SCREEN_CLIP   (BIT(1)|BIT(3)) // use window
#define ROTATE_SPRITE_CORNER        (BIT(4)) // place sprite from upper left corner
#define ROTATE_SPRITE_TRANS_FLIP    (BIT(5))
#define ROTATE_SPRITE_NON_MASK      (BIT(6)) // non masked sprites
#define ROTATE_SPRITE_ALL_PAGES     (BIT(7)) // copies to all pages

#define RS_SCALE                    BIT(16)

// system defines for status bits
#define CEILING_STAT_PLAX           BIT(0)
#define CEILING_STAT_SLOPE          BIT(1)
#define CEILING_STAT_SWAPXY         BIT(2)
#define CEILING_STAT_SMOOSH         BIT(3)
#define CEILING_STAT_XFLIP          BIT(4)
#define CEILING_STAT_YFLIP          BIT(5)
#define CEILING_STAT_RELATIVE       BIT(6)
#define CEILING_STAT_TYPE_MASK     (BIT(7)|BIT(8))
#define CEILING_STAT_MASKED         BIT(7)
#define CEILING_STAT_TRANS          BIT(8)
#define CEILING_STAT_TRANS_FLIP     (BIT(7)|BIT(8))
#define CEILING_STAT_FAF_BLOCK_HITSCAN      BIT(15)

#define FLOOR_STAT_PLAX           BIT(0)
#define FLOOR_STAT_SLOPE          BIT(1)
#define FLOOR_STAT_SWAPXY         BIT(2)
#define FLOOR_STAT_SMOOSH         BIT(3)
#define FLOOR_STAT_XFLIP          BIT(4)
#define FLOOR_STAT_YFLIP          BIT(5)
#define FLOOR_STAT_RELATIVE       BIT(6)
#define FLOOR_STAT_TYPE_MASK     (BIT(7)|BIT(8))
#define FLOOR_STAT_MASKED         BIT(7)
#define FLOOR_STAT_TRANS          BIT(8)
#define FLOOR_STAT_TRANS_FLIP     (BIT(7)|BIT(8))
#define FLOOR_STAT_FAF_BLOCK_HITSCAN      BIT(15)

//cstat, bit 0: 1 = Blocking sprite (use with clipmove, getzrange)    "B"
//       bit 1: 1 = 50/50 transluscence, 0 = normal                   "T"
//       bit 2: 1 = x-flipped, 0 = normal                             "F"
//       bit 3: 1 = y-flipped, 0 = normal                             "F"
//       bits 5-4: 00 = FACE sprite (default)                         "R"
//                 01 = WALL sprite (like masked walls)
//                 10 = FLOOR sprite (parallel to ceilings&floors)
//                 11 = SPIN sprite (face sprite that can spin 2draw style - not done yet)
//       bit 6: 1 = 1-sided sprite, 0 = normal                        "1"
//       bit 7: 1 = Real centered centering, 0 = foot center          "C"
//       bit 8: 1 = Blocking sprite (use with hitscan)                "H"
//       bit 9: reserved
//       bit 10: reserved
//       bit 11: 1 = determine shade based only on its own shade member (see CON's spritenoshade command), i.e.
//                   don't take over shade from parallaxed ceiling/nonparallaxed floor
//                   (NOTE: implemented on the game side)
//       bit 12: reserved
//       bit 13: reserved
//       bit 14: reserved
//       bit 15: 1 = Invisible sprite, 0 = not invisible
#define CSTAT_SPRITE_NOSHADE        BIT(11)
#define CSTAT_SPRITE_BREAKABLE (CSTAT_SPRITE_BLOCK_HITSCAN)

#define SP(i)  sprite[i].yvel
#define SX(i)  sprite[i].x
#define SY(i)  sprite[i].y
#define SZ(i)  sprite[i].z
#define SS(i)  sprite[i].shade
#define PN(i)  sprite[i].picnum
#define SA(i)  sprite[i].ang
//#define SV  sprite[i].xvel
//#define ZV  sprite[i].zvel
//#define RX  sprite[i].xrepeat
//#define RY  sprite[i].yrepeat
#define OW(i)  sprite[i].owner
#define CS(i)  sprite[i].cstat
#define SH(i)  sprite[i].extra
//#define CX  sprite[i].xoffset
//#define CY  sprite[i].yoffset
//#define CD  sprite[i].clipdist
//#define PL  sprite[i].pal
#define SLT(i)  sprite[i].lotag
#define SHT(i)  sprite[i].hitag
#define SECT(i) sprite[i].sectnum

#define T1(i)  actor[i].t_data[0]
#define T2(i)  actor[i].t_data[1]
#define T3(i)  actor[i].t_data[2]
#define T4(i)  actor[i].t_data[3]
#define T5(i)  actor[i].t_data[4]
#define T6(i)  actor[i].t_data[5]

#endif


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 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 "compat.h"

#define SETUPFILENAME "mapster32.cfg"

#define TICSPERFRAME 3

// #define VULGARITY

//#define NUMOPTIONS 9
//char option[NUMOPTIONS] = {0,0,0,0,0,0,1,0,0};

char default_buildkeys[NUMBUILDKEYS] =
{
    0xc8,0xd0,0xcb,0xcd,0x2a,0x9d,0x1d,0x39,
    0x1e,0x2c,0xd1,0xc9,0x47,0x49,
    0x9c,0x1c,0xd,0xc,0xf,0x29
};

extern char changechar(char dachar, int32_t dadir, char smooshyalign, char boundcheck);

static char autospritehelp=0, autosecthelp=0;
//static int32_t lastmessagetime=-1;

static char tempbuf[1024];
static int32_t numsprite[MAXTILES], multisprite[MAXTILES];
static char lo[64];
static int16_t curwall=0, curwallnum=0;
static int16_t /*cursearchsprite=0,*/ cursearchspritenum=0, cursector_lotag=0, cursectornum=0;
static int16_t search_lotag=0,search_hitag=0;
static char wallsprite=0;
static char helpon=0;
//static char onwater=0;
static uint8_t onnames=4, usedcount=1;
static int16_t cursprite;
static int32_t ppointhighlight;
//static int32_t counter=0;
static uint8_t nosprites=0,purpleon=0,skill=4;
static uint8_t framerateon=1,shadepreview=0;

static int32_t autosavetimer;

static void SearchSectors(int32_t dir);
static inline void SpriteName(int16_t spritenum, char *lo2);
//static void PrintStatus(const char *string,int32_t num,char x,char y,char color);

static void EditSpriteData(int16_t spritenum);
static void EditWallData(int16_t wallnum);
static void EditSectorData(int16_t sectnum);

static int32_t updownunits=1024;

//int32_t intro=0;

//void ContextHelp(int16_t spritenum);
//void ResetKeys();

//#define KEY_PRESSED(sc) KB_KeyPressed((sc))

// This table defines the various zoom levels, the numbers being the pixel width
//   and height of the sprite when plotted on the screen. Probably zooms in too far
//   for some, but I'm a blind old git :-(

#define FUCKING_GOOD_EYESIGHT 16
static const int32_t ZoomToThumbSize[] =
{
    FUCKING_GOOD_EYESIGHT, 32, 64, 128, 192, 256, 384, 512
};

#define NUM_ZOOMS ARRAY_SSIZE(ZoomToThumbSize)
#define ZOOMSIZ(x) (ZoomToThumbSize[x])
#define INITIAL_ZOOM 2

typedef struct
{
    int32_t *pIds;  // ptr to list of tile Ids
    int32_t  nIds;  // num of tile ids
    char *szText;   // description to present to user.
    char  key1;     // key1 and key2 are two alternative keypresses used to
    char  key2;     //    select tile set. Bodge to do eary upper/lower case handling
    char  color1;   // 2d sprite color 1
    char  color2;   // 2d sprite color 2
} TileGroup;

#define MAX_TILE_GROUPS 32
#define MAX_TILE_GROUP_ENTRIES MAXUSERTILES

static TileGroup s_TileGroups[MAX_TILE_GROUPS];
static int32_t tilegroupItems;
static int32_t tilegroupActors;
static int32_t tile_groups = 0;

#define NUMPRINTABLES 94
#define MAX_ALPHABETS 32
// alphabet definitions struct for the sprite text renderer
typedef struct
{
    int16_t pic[NUMPRINTABLES];  // maps printable ASCII chars to tilenums
    int8_t xofs[NUMPRINTABLES];  // additional offset
    int8_t yofs[NUMPRINTABLES];
} alphabet_t;

static alphabet_t alphabets[MAX_ALPHABETS];
static int32_t numalphabets = 0;

#define FIRST_USER_ART_TILE 3584
// Some atomic tiles are sprinkled in the V1.3d's area but
//   this is where the main atomic tiles start :-
#define FIRST_ATOMIC_TILE   4096
#define FIRST_EXTENDED_TILE 6144
#define SECOND_EXTENDED_TILE 9216

extern int16_t localartfreq[MAXTILES];
extern int16_t localartlookup[MAXTILES], localartlookupnum;
extern int32_t lockclock;

static int32_t gs_spriteTagValue[3][7];
static char gs_spriteTagInterested[3][7];
static int16_t gs_cursprite=-1;

//int32_t g_musicSize=0;


code:
#include "compat.h"
#include "mdump.h"

#include <tchar.h>
LPCSTR MiniDumper::m_szAppName;

MiniDumper g_dumper("eduke32");

MiniDumper::MiniDumper(LPCSTR szAppName)
{
    // if this assert fires then you have two instances of MiniDumper
    // which is not allowed
    assert(m_szAppName == NULL);

    m_szAppName = szAppName ? _strdup(szAppName) : "Application";

    ::SetUnhandledExceptionFilter(TopLevelFilter);
}

LONG MiniDumper::TopLevelFilter(struct _EXCEPTION_POINTERS *pExceptionInfo)
{
    HMODULE hDll = NULL;
    char    szDbgHelpPath[_MAX_PATH];

    if (GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH))
    {
        auto pSlash = _tcsrchr(szDbgHelpPath, '\\');

        if (pSlash)
        {
            _tcscpy(pSlash + 1, "DBGHELP.DLL");
            hDll = ::LoadLibrary(szDbgHelpPath);
        }
    }

    if (hDll == NULL)
    {
        // load any version we can
        hDll = ::LoadLibrary("DBGHELP.DLL");
    }

    LONG    retval   = EXCEPTION_CONTINUE_SEARCH;
    LPCTSTR szResult = "DBGHELP.DLL not found";
    char    szScratch[1024];

    if (hDll)
    {
        auto pDump = MINIDUMPWRITEDUMP(::GetProcAddress(hDll, "MiniDumpWriteDump"));

        if (pDump)
        {
            char szDumpPath[_MAX_PATH];

            snprintf(szDumpPath, sizeof(szDumpPath), "%s_%u.dmp", m_szAppName, timeGetTime());

            HANDLE hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);

            if (hFile != INVALID_HANDLE_VALUE)
            {
                _MINIDUMP_EXCEPTION_INFORMATION ExInfo = { ::GetCurrentThreadId(), pExceptionInfo, NULL };

                // take a dump
                BOOL bOK = pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL);

                if (bOK)
                {
                    snprintf(szScratch, sizeof(szScratch), "Crash dump written to %s", szDumpPath);
                    szResult = szScratch;
                    retval   = EXCEPTION_EXECUTE_HANDLER;
                }
                else
                {
                    snprintf(szScratch, sizeof(szScratch), "Unable to write crash dump to %s: error %d.", szDumpPath, GetLastError());
                    szResult = szScratch;
                }

                ::CloseHandle(hFile);
            }
            else
            {
                snprintf(szScratch, sizeof(szScratch), "Unable to write crash dump to %s: error %d", szDumpPath, GetLastError());
                szResult = szScratch;
            }
        }
        else
        {
            szResult = "DBGHELP.DLL too old";
        }

        ::FreeLibrary(hDll);
    }

    ::MessageBox(NULL, szResult, m_szAppName, MB_OK);

    return retval;
}


code:
#define NEED_DBGHELP_H
#define NEED_MMSYSTEM_H
#include "windows_inc.h"

// based on dbghelp.h
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
									CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
									CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
									CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
									);

class MiniDumper
{
private:
	static LPCSTR m_szAppName;

	static LONG WINAPI TopLevelFilter( struct _EXCEPTION_POINTERS *pExceptionInfo );

public:
	MiniDumper( LPCSTR szAppName );
};