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 "baselayer.h"
#include "cache1d.h"
#include "common_game.h"
#include "common.h"
#include "compat.h"
#include "crc32.h"
#include "grpscan.h"
#include "scriptfile.h"

#include "vfs.h"

#ifndef EDUKE32_STANDALONE
// custom GRP support for the startup window, file format reflects the structure below
#define GAMELISTFILE "games.list"
//    name          crc             size      flags                 dependency
static internalgrpinfo_t const internalgrpfiles[] =
{
    { "Witchaven",   WH1_CRC,  11904, GAMEFLAG_WH1, 0 },
    { "Witchaven 2", WH2_CRC,  0,     GAMEFLAG_WH2, 0 }
};
#endif

struct grpfile_t *foundgrps = NULL;
struct grpinfo_t *listgrps = NULL;

static void LoadList(const char * filename)
{
    scriptfile *script = scriptfile_fromfile(filename);

    if (!script)
        return;

#ifndef EDUKE32_STANDALONE
    scriptfile_addsymbolvalue("GAMEFLAG_WH",  GAMEFLAG_WH1);
    scriptfile_addsymbolvalue("GAMEFLAG_WH2", GAMEFLAG_WH2);
#endif

    while (!scriptfile_eof(script))
    {
        enum
        {
            T_GRPINFO,
            T_GAMENAME,
            T_CRC,
            T_SIZE,
            T_DEPCRC,
            T_DEFNAME,
            T_FLAGS,
        };

        static const tokenlist profiletokens[] =
        {
            { "grpinfo",            T_GRPINFO },
        };

        int32_t token = getatoken(script,profiletokens,ARRAY_SIZE(profiletokens));
        switch (token)
        {
        case T_GRPINFO:
        {
            int32_t gsize = 0, gcrcval = 0, gflags = GAMEFLAG_WH1, gdepcrc = WH1_CRC;
            char *gname = NULL, *gdef = NULL;
            char *grpend = NULL;

            static const tokenlist grpinfotokens[] =
            {
                { "name",           T_GAMENAME },
                { "defname",        T_DEFNAME },
                { "crc",            T_CRC },
                { "dependency",     T_DEPCRC },
                { "size",           T_SIZE },
                { "flags",          T_FLAGS },

            };

            if (scriptfile_getbraces(script,&grpend)) break;

            while (script->textptr < grpend)
            {
                int32_t token = getatoken(script,grpinfotokens,ARRAY_SIZE(grpinfotokens));

                switch (token)
                {
                case T_GAMENAME:
                    scriptfile_getstring(script,&gname); break;
                case T_DEFNAME:
                    scriptfile_getstring(script,&gdef); break;

                case T_FLAGS:
                    scriptfile_getsymbol(script,&gflags); gflags &= GAMEFLAGMASK; break;
                case T_DEPCRC:
                    scriptfile_getsymbol(script,&gdepcrc); break;
                case T_CRC:
                    scriptfile_getsymbol(script,&gcrcval); break;
                case T_SIZE:
                    scriptfile_getnumber(script,&gsize); break;
                default:
                    break;
                }

                grpinfo_t * const fg = (grpinfo_t *)Xcalloc(1, sizeof(grpinfo_t));
                fg->next = listgrps;
                listgrps = fg;

                if (gname)
                    fg->name = Xstrdup(gname);

                fg->size = gsize;
                fg->crcval = gcrcval;
                fg->dependency = gdepcrc;
                fg->game = gflags;

                if (gdef)
                    fg->defname = dup_filename(gdef);
            }
            break;
        }

        default:
            break;
        }
    }

    scriptfile_close(script);
    scriptfile_clearsymbols();
}

static void LoadGameList(void)
{
#ifndef EDUKE32_STANDALONE
    for (size_t i = 0; i < ARRAY_SIZE(internalgrpfiles); i++)
    {
        grpinfo_t * const fg = (grpinfo_t *)Xcalloc(1, sizeof(grpinfo_t));

        fg->name = Xstrdup(internalgrpfiles[i].name);
        fg->crcval = internalgrpfiles[i].crcval;
        fg->size = internalgrpfiles[i].size;
        fg->game = internalgrpfiles[i].game;
        fg->dependency = internalgrpfiles[i].dependency;

        fg->next = listgrps;
        listgrps = fg;
    }
#endif

#ifdef USE_PHYSFS
    auto const base = PHYSFS_getBaseDir();
#else
    static char const base[] = "/";
#endif
    BUILDVFS_FIND_REC * const srch = klistpath(base, "*.grpinfo", BUILDVFS_FIND_FILE);

    for (BUILDVFS_FIND_REC *sidx = srch; sidx; sidx = sidx->next)
        LoadList(sidx->name);

    klistfree(srch);
}

static void FreeGameList(void)
{
    while (listgrps)
    {
        Xfree(listgrps->name);
        Xfree(listgrps->defname);

        grpinfo_t * const fg = listgrps->next;
        Xfree(listgrps);
        listgrps = fg;
    }
}

#define GRPCACHEFILE "grpfiles.cache"
static struct grpcache
{
    struct grpcache *next;
    int32_t size;
    int32_t mtime;
    int32_t crcval;
    char name[BMAX_PATH];
}
*grpcache = NULL, *usedgrpcache = NULL;

static int32_t LoadGroupsCache(void)
{
    struct grpcache *fg;

    int32_t fsize, fmtime, fcrcval;
    char *fname;

    scriptfile *script;

    script = scriptfile_fromfile(GRPCACHEFILE);
    if (!script) return -1;

    while (!scriptfile_eof(script))
    {
        if (scriptfile_getstring(script, &fname)) break;    // filename
        if (scriptfile_getnumber(script, &fsize)) break;    // filesize
        if (scriptfile_getnumber(script, &fmtime)) break;   // modification time
        if (scriptfile_getnumber(script, &fcrcval)) break;  // crc checksum

        fg = (struct grpcache *)Xcalloc(1, sizeof(struct grpcache));
        fg->next = grpcache;
        grpcache = fg;

        Bstrncpy(fg->name, fname, BMAX_PATH);
        fg->size = fsize;
        fg->mtime = fmtime;
        fg->crcval = fcrcval;
    }

    scriptfile_close(script);
    return 0;
}

static void FreeGroupsCache(void)
{
    while (grpcache)
    {
        struct grpcache * const fg = grpcache->next;
        Xfree(grpcache);
        grpcache = fg;
    }
}

static void RemoveGroup(grpfile_t *igrp)
{
    for (grpfile_t *prev = NULL, *grp = foundgrps; grp; grp=grp->next)
    {
        if (grp == igrp)
        {
            if (grp == foundgrps)
                foundgrps = grp->next;
            else
                prev->next = grp->next;

            Xfree((char *)grp->filename);
            Xfree(grp);

            return;
        }

        prev = grp;
    }
}

grpfile_t * FindGroup(int32_t crcval)
{
    grpfile_t *grp;

    for (grp = foundgrps; grp; grp=grp->next)
    {
        if (grp->type->crcval == crcval)
            return grp;
    }

    return NULL;
}

#ifndef USE_PHYSFS
static grpinfo_t const * FindGrpInfo(int32_t crcval, int32_t size)
{
    grpinfo_t *grpinfo;

    for (grpinfo = listgrps; grpinfo; grpinfo=grpinfo->next)
    {
        if (grpinfo->crcval == crcval && grpinfo->size == size)
            return grpinfo;
    }

    return NULL;
}

static void ProcessGroups(BUILDVFS_FIND_REC *srch)
{
    BUILDVFS_FIND_REC *sidx;
    struct grpcache *fg, *fgg;
    char *fn;
    struct Bstat st;

    static constexpr int ReadSize = 65536;

    auto buf = (uint8_t *)Xmalloc(ReadSize);

    for (sidx = srch; sidx; sidx = sidx->next)
    {
        for (fg = grpcache; fg; fg = fg->next)
        {
            if (!Bstrcmp(fg->name, sidx->name)) break;
        }

        if (fg)
        {
            if (findfrompath(sidx->name, &fn)) continue; // failed to resolve the filename
            if (Bstat(fn, &st))
            {
                Xfree(fn);
                continue;
            } // failed to stat the file
            Xfree(fn);
            if (fg->size == (int32_t)st.st_size && fg->mtime == (int32_t)st.st_mtime)
            {
                grpinfo_t const * const grptype = FindGrpInfo(fg->crcval, fg->size);
                if (grptype)
                {
                    grpfile_t * const grp = (grpfile_t *)Xcalloc(1, sizeof(grpfile_t));
                    grp->filename = Xstrdup(sidx->name);
                    grp->type = grptype;
                    grp->next = foundgrps;
                    foundgrps = grp;
                }

                fgg = (struct grpcache *)Xcalloc(1, sizeof(struct grpcache));
                strcpy(fgg->name, fg->name);
                fgg->size = fg->size;
                fgg->mtime = fg->mtime;
                fgg->crcval = fg->crcval;
                fgg->next = usedgrpcache;
                usedgrpcache = fgg;
                continue;
            }
        }

        {
            int32_t b, fh;
            int32_t crcval = 0;

            fh = openfrompath(sidx->name, BO_RDONLY|BO_BINARY, BS_IREAD);
            if (fh < 0) continue;
            if (Bfstat(fh, &st)) continue;

            initprintf(" Checksumming %s...", sidx->name);
            do
            {
                b = read(fh, buf, ReadSize);
                if (b > 0) crcval = Bcrc32((uint8_t *)buf, b, crcval);
            }
            while (b == ReadSize);
            close(fh);
            initprintf(" Done\n");

            grpinfo_t const * const grptype = FindGrpInfo(crcval, st.st_size);
            if (grptype)
            {
                grpfile_t * const grp = (grpfile_t *)Xcalloc(1, sizeof(grpfile_t));
                grp->filename = Xstrdup(sidx->name);
                grp->type = grptype;
                grp->next = foundgrps;
                foundgrps = grp;
            }

            fgg = (struct grpcache *)Xcalloc(1, sizeof(struct grpcache));
            Bstrncpy(fgg->name, sidx->name, BMAX_PATH);
            fgg->size = st.st_size;
            fgg->mtime = st.st_mtime;
            fgg->crcval = crcval;
            fgg->next = usedgrpcache;
            usedgrpcache = fgg;
        }
    }

    Xfree(buf);
}
#endif

int32_t ScanGroups(void)
{
#ifndef USE_PHYSFS
    struct grpcache *fg, *fgg;

    initprintf("Searching for game data...\n");

    LoadGameList();
    LoadGroupsCache();

    static char const *extensions[] =
    {
        "*.grp",
        "*.ssi",
        "*.dat",
    };

    for (char const *extension : extensions)
    {
        BUILDVFS_FIND_REC *srch = klistpath("/", extension, BUILDVFS_FIND_FILE);
        ProcessGroups(srch);
        klistfree(srch);
    }

    FreeGroupsCache();

    for (grpfile_t *grp = foundgrps; grp; grp=grp->next)
    {
        if (grp->type->dependency)
        {
            if (FindGroup(grp->type->dependency) == NULL) // couldn't find dependency
            {
                //initprintf("removing %s\n", grp->name);
                RemoveGroup(grp);
                grp = foundgrps;
                // start from the beginning so we can remove anything that depended on this grp
                continue;
            }
        }
    }

    if (usedgrpcache)
    {
        int32_t i = 0;
        buildvfs_FILE fp = buildvfs_fopen_write(GRPCACHEFILE);
        if (fp)
        {
            for (fg = usedgrpcache; fg; fg=fgg)
            {
                fgg = fg->next;
                fprintf(fp, "\"%s\" %d %d %d\n", fg->name, fg->size, fg->mtime, fg->crcval);
                Xfree(fg);
                i++;
            }
            buildvfs_fclose(fp);
        }
//        initprintf("Found %d recognized GRP %s.\n",i,i>1?"files":"file");

        return 0;
    }

    initprintf("Found no recognized game data!\n");
#endif

    return 0;
}

void FreeGroups(void)
{
    while (foundgrps)
    {
        Xfree((char *)foundgrps->filename);
        grpfile_t * const fg = foundgrps->next;
        Xfree(foundgrps);
        foundgrps = fg;
    }

    FreeGameList();
}


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 grpscan_h_
#define grpscan_h_

#ifdef __cplusplus
extern "C" {
#endif

#define MAXLISTNAMELEN 32

// List of internally-known GRP files
#define WH1_CRC      (int32_t)0x6117B2DD
#define WH2_CRC      (int32_t)0x0

typedef struct internalgrpinfo_t {
    char const *name;
    int32_t const crcval;
    int32_t const size;
    int32_t const game;
    int32_t const dependency;
    // char const *scriptname;
    // void(*postprocessing)(int32_t);
} internalgrpinfo_t;

typedef struct grpinfo_t {
	char *name;
	int32_t crcval;
	int32_t size;
	int32_t game;
    int32_t dependency;
    // char *scriptname;
    char *defname;
    // char *rtsname;
    // void (*postprocessing)(int32_t);
	struct grpinfo_t *next;
} grpinfo_t;

typedef struct grpfile_t {
	char *filename;
	struct grpinfo_t const *type;
	struct grpfile_t *next;
} grpfile_t;

extern grpfile_t *foundgrps;
extern grpinfo_t *listgrps;

extern grpfile_t * FindGroup(int32_t crcval);

int32_t ScanGroups(void);
void FreeGroups(void);

#ifdef __cplusplus
}
#endif

#endif

repo_name: orochi/nblood

license: mit


code:
/***************************************************************************
 *   WHINP.C  - main game code for Apogee engine                           *
 *                                                                         *
 ***************************************************************************/

#include "input.h"
#include "player.h"
#include "sound.h"
#include "objects.h"
#include "menu.h"
#include "witchaven.h"
#include "network.h"
#include "effects.h"
#include "keyboard.h"
#include "control.h"
#include "scancodes.h"
#include "view.h"
#include "config.h"
#include "control.h"

#define WEAPONONE    0x02

#define MAX_ANGULAR_VELOCITY 127
#define TURN_ACCELERATION_RATE 16
#define TURN_DECELERATION_RATE 12
#define MAX_STRAFE_VELOCITY 127
#define MAX_MOVEMENT_VELOCITY 201
#define MAX_MOUSE_MOVEMENT_VELOCITY 127
#define MOVEMENT_ACCELERATION_RATE 8
#define MOVEMENT_DECELERATION_RATE 2

void dosoundthing();
void nettypeletter();
void typeletter();

int soundcontrol;
int musiclevel;
int digilevel;
int soundtoggle;

extern int mapon;

bool followmode = false;
bool justplayed = false;
bool lopoint = false;
bool walktoggle = false;
int runningtime = 0;

int charsperline = 0;
char nettemp[80];
extern char typemessage[];
extern char typemessageleng, typemode;
extern char scantoasc[];
extern char scantoascwithshift[];
bool nettypemode = false;

extern char tempbuf[];

int mousecalibrate = 0;
int mousespeed = 3;
int mousxspeed = 3;
int mousyspeed = 3;

int32_t angvel, svel, vel;

extern int escapetomenu;

int32_t lockclock;

int oldhoriz;

#if 1
char joyb;
char oldjoyb;
char oldbstatus;
char butbit[] = {0x10, 0x20, 0x40, 0x80};
char mbutbit[] = {0x01, 0x02};

short jcalibration = 0;
short jctrx;
short jctry;
short jlowx, jhighx;
short jlowy, jhighy;
short jmovespeed = 16;
int joyx, joyy;
int joykeys[4];
int jstickenabled = 0;
int turnspeed = 16;
#endif

short mousekeys[2];

char option2[7];

extern char flashflag;

short oldmousestatus;
extern short compass;

short pitch, roll, yaw;

void initjstick()
{
    #if 0 // TODO
    jcalibration = 1;
    jstickenabled = 0;

    if (option2[3] == 0)
    {
        option2[3] = KEYFIRE;
    }
    if (option2[4] == 0)
    {
        option2[4] = KEYUSE;
    }
    if (option2[5] == 0)
    {
        option2[5] = KEYUSEP;
    }
    if (option2[6] == 0)
    {
        option2[6] = KEYSTRAFE;
    }

    joykeys[0] = option2[3];
    joykeys[1] = option2[4];
    joykeys[2] = option2[5];
    joykeys[3] = option2[6];
    #endif
}

void dophysics(Player* plr, int goalz, short flyupdn, int v)
{
    if (plr->orbactive[5] > 0)
    {
        if (v > 0)
        {
            if (plr->horiz > 125)
                plr->hvel -= (synctics << 6);
            else if (plr->horiz < 75)
                plr->hvel += (synctics << 6);
        }
        else
        {
            if (flyupdn > 0)
            {
                plr->hvel -= (synctics << 7);
            }
            if (flyupdn < 0)
            {
                plr->hvel += (synctics << 7);
            }
        }

        plr->hvel += Sin(lockclock << 4) >> 6;
        plr->fallz = 0;
    }
    else if (plr->z < goalz)
    {
        plr->hvel += GRAVITYCONSTANT;
        plr->onsomething &= ~(GROUNDBIT | PLATFORMBIT);
        plr->fallz += plr->hvel;
    }
    else if (plr->z > goalz)
    {
        plr->hvel -= ((plr->z - goalz) >> 6);
        plr->onsomething |= GROUNDBIT;
        plr->fallz = 0;
    }
    else
    {
        plr->fallz = 0;
    }

    plr->z += plr->hvel;

    if (plr->hvel > 0 && plr->z > goalz)
    {
        plr->hvel >>= 2;
    }
    else if (plr->onsomething != 0)
    {
        if (plr->hvel < 0 && plr->z < goalz)
        {
            plr->hvel = 0;
            plr->z = goalz;
        }
    }
    if (plr->z < sector[plr->sector].ceilingz)
    {
        plr->z = sector[plr->sector].ceilingz + (plr->height >> 2);
        plr->hvel = 0;
    }
    else if (plr->z > sector[plr->sector].floorz)
    {
        plr->z = sector[plr->sector].floorz - (plr->height >> 4);
        plr->hvel = 0;
    }
    #if 0
    if (plr->forcev != 0)
    {
        *xvect = (int)((plr->forcev * (int)sintable[(plr->forcea + 512) & kAngleMask]) >> 3);
        *yvect = (int)((plr->forcev * (int)sintable[plr->forcea]) >> 3);
        plr->forcev >>= 1;
    }
    #endif
}

void keytimerstuff()
{
    static int counter = 0;
    static int32_t turn = 0;

    if (BUTTON(gamefunc_Strafe))
    {
        if (BUTTON(gamefunc_Turn_Left))
        {
            svel += MOVEMENT_ACCELERATION_RATE;
            if (svel > MAX_STRAFE_VELOCITY) {
                svel = MAX_STRAFE_VELOCITY;
            }
        }
        if (BUTTON(gamefunc_Turn_Right))
        {
            svel -= MOVEMENT_ACCELERATION_RATE;
            if (svel < -MAX_STRAFE_VELOCITY - 1) {//Original says -128
                svel = -MAX_STRAFE_VELOCITY - 1;
            }
        }
    }
    else
    {
        if (BUTTON(gamefunc_Turn_Left))
        {
            angvel -= TURN_ACCELERATION_RATE;
            if (angvel < -MAX_ANGULAR_VELOCITY - 1) { //Original says -128
                angvel = -MAX_ANGULAR_VELOCITY - 1;
            }
        }
        if (BUTTON(gamefunc_Turn_Right))
        {
            angvel += TURN_ACCELERATION_RATE;
            if (angvel > MAX_ANGULAR_VELOCITY) {
                angvel = MAX_ANGULAR_VELOCITY;
            }
        }
    }

    if (BUTTON(gamefunc_Strafe_Left))
    {
        svel += MOVEMENT_ACCELERATION_RATE;
        if (svel > MAX_STRAFE_VELOCITY) {
            svel = MAX_STRAFE_VELOCITY;
        }
    }
    else if (BUTTON(gamefunc_Strafe_Right))
    {
        svel -= MOVEMENT_ACCELERATION_RATE;
        if (svel < -MAX_STRAFE_VELOCITY - 1) {//Original says -128
            svel = -MAX_STRAFE_VELOCITY - 1;
        }
    }

    if (BUTTON(gamefunc_Move_Forward))
    {
        vel += MOVEMENT_ACCELERATION_RATE;
        if (vel > MAX_MOVEMENT_VELOCITY) {
            vel = MAX_MOVEMENT_VELOCITY;
        }
    }

    if (BUTTON(gamefunc_Move_Backward))
    {
        vel -= MOVEMENT_ACCELERATION_RATE;
        if (vel < -MAX_MOVEMENT_VELOCITY) {
            vel = -MAX_MOVEMENT_VELOCITY;
        }
    }

    //Deceleration
    if (angvel < 0)
    {
        angvel += TURN_DECELERATION_RATE;
        if (angvel > 0) {
            angvel = 0;
        }
    }

    if (angvel > 0)
    {
        angvel -= TURN_DECELERATION_RATE;
        if (angvel < 0) {
            angvel = 0;
        }
    }

    if (svel < 0)
    {
        svel += MOVEMENT_DECELERATION_RATE;
        if (svel > 0) {
            svel = 0;
        }
    }

    if (svel > 0)
    {
        svel -= MOVEMENT_DECELERATION_RATE;
        if (svel < 0) {
            svel = 0;
        }
    }

    if (vel < 0)
    {
        vel += MOVEMENT_DECELERATION_RATE;
        if (vel > 0) {
            vel = 0;
        }
    }

    if (vel > 0)
    {
        vel -= MOVEMENT_DECELERATION_RATE;
        if (vel < 0) {
            vel = 0;
        }
    }
}

void processinput(Player* plr)
{
    short bstatus = 0;
    short mousx = 0;
    short mousy = 0;

    int32_t goalz, xvect, yvect;
    int32_t hihit, hiz, loz, lohit;
    int32_t dax, dax2, day, day2, odax, odax2, oday, oday2;
    int a, s, v;
    static int  mv;
    int32_t oldposx, oldposy;
    int32_t dist;
    int32_t feetoffground;
    short onsprite = -1;
    static short tempsectornum;
    short onground;

    a = angvel;
    s = svel;
    v = vel;

    ControlInfo info;
    CONTROL_ProcessBinds();
    CONTROL_GetInput(&info);

    //if backspace > 0 then type mode = 1
    //backspace = 0

    if (keystatus[sc_BackSpace] > 0)
    {
        if (netgame == 0)
        {
            if (typemode == 1)
            {
                typemode = 0;
                charsperline = 0;
                typemessageleng = 0;
            }
            else
            {
                typemode = 1;
            }
            keystatus[sc_BackSpace] = 0;
        }
        else
        {
            if (nettypemode)
            {
                nettypemode = false;
                charsperline = 0;
                typemessageleng = 0;
                strcpy(nettemp, "");
            }
            else
            {
                nettypemode = true;
                typemessageleng = 0;
            }
            keystatus[sc_BackSpace] = 0;
        }
    }

    if (typemode == 1)
    {
        typeletter();
    }

    if (nettypemode)
    {
        nettypeletter();
    }

    if (keystatus[sc_Escape] > 0)
    {
        keystatus[sc_Escape] = 0;
        if (plr->z < sector[plr->sector].floorz - ((PLAYERHEIGHT + 8) << 8)
            //     || sector[plr->sector].floorpicnum == LAVA
            //     || sector[plr->sector].floorpicnum == SLIME
            //     || sector[plr->sector].floorpicnum == WATER
            //     || sector[plr->sector].floorpicnum == HEALTHWATER
            //     || sector[plr->sector].floorpicnum == ANILAVA
            //     || sector[plr->sector].floorpicnum == LAVA1
            //     || sector[plr->sector].floorpicnum == LAVA2 )
            )
        {

            StatusMessage(360, "must be on the ground");
        }
        else
        {
            escapetomenu = 1;
            plr->z = sector[plr->sector].floorz - (PLAYERHEIGHT << 8);

            vec3_t pos;
            pos.x = plr->x;
            pos.y = plr->y;
            pos.z = plr->z + (plr->height << 8);

            setsprite(plr->spritenum, &pos);
            sprite[plr->spritenum].ang = plr->ang;

            visibility = 1024;
        }
    }

    if (followmode)
    {
        if (BUTTON(gamefunc_Turn_Left))
        {
            followx -= synctics << 5;
        }
        if (BUTTON(gamefunc_Turn_Right))
        {
            followx += synctics << 5;
        }
        if (BUTTON(gamefunc_Move_Forward))
        {
            followy -= synctics << 5;
        }
        if (BUTTON(gamefunc_Move_Backward))
        {
            followy += synctics << 5;
        }

        if (labs(followx) >= 65536 - 1024)
            followx = 65536 - 1024;

        if (labs(followy) >= 65536 - 1024)
            followy = 65536 - 1024;

        if (keystatus[sc_F] > 0)
        {
            keystatus[sc_F] = 0;
            followmode = false;
        }
        return;
    }

    if (keystatus[sc_F10] > 0) // F10 - brightness
    {
        keystatus[sc_F10] = 0;
        gbrightness = brightness++;

        if (brightness > 8)
        {
            brightness = 0;
            gbrightness = 0;
        }

        setbrightness(brightness);
    }

    if (v < -MAX_MOVEMENT_VELOCITY) v = -MAX_MOVEMENT_VELOCITY;
    else if (v > MAX_MOVEMENT_VELOCITY) v = MAX_MOVEMENT_VELOCITY;

    v += v >> 1;// SUPER MARIO BROTHERS

    if (s < -MAX_MOVEMENT_VELOCITY) s = -MAX_MOVEMENT_VELOCITY; // Yes, this is strafing, however, original code compares it against 201
    else if (s > MAX_MOVEMENT_VELOCITY) s = MAX_MOVEMENT_VELOCITY;

    if (a < -112) a = -112; //fhomolka 18/02/2021: I have no clue why it's 112 here, but 127 elsewhere
    else if (a > 112) a = 112;

    int i = 0;

    if (option[3] != 0)
    {
        // TODO		getmousevalues(&mousx, &mousy, &bstatus);
        if (BUTTON(gamefunc_Strafe))
        {
            i = s;
            i -= (mousx * mousxspeed);
        }
        else
        {
            i = a;
            i += (mousx * mousxspeed);
        }

        if (i < -MAX_STRAFE_VELOCITY - 1) i = -MAX_STRAFE_VELOCITY - 1; // original says -128
        if (i > MAX_STRAFE_VELOCITY) i = MAX_STRAFE_VELOCITY;

        if (BUTTON(gamefunc_Strafe))
            s = i;
        else
            a = i;

        // Les START - 07/24/95  - if key KEYLOOKING is held, mouse fwd/back looks up/down
        if (BUTTON(gamefunc_Mouse_Aiming)) // TODO - change all this to work correctly
//		if (keystatus[keys[KEYLOOKING]])
        {
            i = plr->horiz;
            i += (mousy >> 4);
            if (i < 100 - (YDIM >> 1)) i = 100 - (YDIM >> 1);
            else if (i > 100 + (YDIM >> 1)) i = 100 + (YDIM >> 1);
            plr->horiz = i;
        }
        //                         else mouse fwd/back moves fwd/back
        else
        {
            i = v;

            i -= (mousy * mousyspeed);
            if (i < -MAX_MOUSE_MOVEMENT_VELOCITY - 1) //fhomolka 18/02/2021: I have no clue why it's 128 here, but 201 elsewhere
            {
                i = -MAX_MOUSE_MOVEMENT_VELOCITY - 1;
            }
            else if (i > MAX_MOUSE_MOVEMENT_VELOCITY)
            {
                i = MAX_MOUSE_MOVEMENT_VELOCITY;
            }
            v = i;
        }

        #if 0 // TODO
        for (i = 0; i < 2; i++)
        {
            if (((bstatus & mbutbit[i]) == mbutbit[i])     // button is down
                && ((oldbstatus & mbutbit[i]) != mbutbit[i]))
            {
                keystatus[keys[mousekeys[i]]] = 1;
                if (mousekeys[i] == KEYRUN)
                {
                    keystatus[keys[KEYFWD]] = 1;
                }
            }
            else if (((bstatus & mbutbit[i]) == mbutbit[i])// button still down
                && ((oldbstatus & mbutbit[i]) == mbutbit[i]))
            {
                if (mousekeys[i] == KEYUSE              // ..one-time actions
                    || mousekeys[i] == KEYJUMP
                    || mousekeys[i] == KEYMAP
                    || mousekeys[i] == KEYUSEP
                    || mousekeys[i] == KEYCAST)
                {
                    keystatus[keys[mousekeys[i]]] = 0;
                }
            }
            else if (((bstatus & mbutbit[i]) != mbutbit[i])// button released
                && ((oldbstatus & mbutbit[i]) == mbutbit[i]))
            {
                keystatus[keys[mousekeys[i]]] = 0;
                if (mousekeys[i] == KEYRUN)
                {
                    keystatus[keys[KEYFWD]] = 0;
                }
            }
        }
        #endif

        oldbstatus = bstatus;
    }

 //   i = (int)totalclock - lockclock;
/*
    if (i > 255)
        i = 255;
*/
    //synctics = tics = i;

    int32_t tics = kTicksPerFrame;

    lockclock += kTicksPerFrame;//synctics;

    // TEMP
    synctics = 4;

    sprite[plr->spritenum].cstat ^= 1;

    vec3_t pos;
    pos.x = plr->x;
    pos.y = plr->y;
    pos.z = plr->z;

    getzrange(&pos, plr->sector, &hiz, &hihit, &loz, &lohit, 128L, CLIPMASK0);

    sprite[plr->spritenum].cstat ^= 1;

    if ((lohit & 0xc000) == 49152)
    {
        if ((sprite[lohit & 4095].z - plr->z) <= (PLAYERHEIGHT << 8))
            onsprite = (lohit & 4095);
    }
    else {
        onsprite = -1;
    }

    feetoffground = (sector[plr->sector].floorz - plr->z);

    if (BUTTON(gamefunc_Run) || v > 201)
    {
        if (v > 201)
            v = 201;

        v += v >> 1; // SUPER MARIO BROTHERS

        if (feetoffground > (32 << 8))
            tics += tics >> 1;
    }

    if (BUTTON(gamefunc_Look_Down))
    {
        if (plr->horiz > 100 - (YDIM >> 1))
        {
            plr->horiz -= (synctics << 1);
            autohoriz = 0;
        }
    }
    else if (BUTTON(gamefunc_Look_Up))
    {
        if (plr->horiz < 100 + (YDIM >> 1))
            plr->horiz += (synctics << 1);
        autohoriz = 0;
    }

    if (keystatus[sc_End] != 0)
        plr->orbactive[5] = -1;

    //     oldmousestatus=bstatus&0x01;

    //      if(keystatus[keys[KEYFIRE]] != 0 || keystatus[sc_RightControl] != 0 || (oldmousestatus) != 0 && hasshot == 0) {
    //      if (keystatus[keys[KEYFIRE]] != 0 || (oldmousestatus) != 0 && hasshot == 0) {

    if (BUTTON(gamefunc_Fire) && !plr->hasshot)
    {
        if (plr->currweaponfired == 0)
            plrfireweapon(plr);
    }

    // cast
    if (BUTTON(gamefunc_Cast_Spell) && !plr->orbshot && !plr->currweaponflip)
    {
        if (plr->orb[plr->currentorb] == 1 && plr->selectedgun == 0)
        {
            if (lvlspellcheck(plr))
            {
                plr->orbshot = true;
                activatedaorb(plr);
            }
        }
        if (plr->selectedgun != 0)
        {
            keystatus[WEAPONONE] = 1; // TODO FIXME
        }

        CONTROL_ClearButton(gamefunc_Cast_Spell);
    }

    if (BUTTON(gamefunc_Use_Potion))
    {
        CONTROL_ClearButton(gamefunc_Use_Potion);

        if (plr->potion[plr->currentpotion] > 0) {
            usapotion(plr);
        }
    }

    if (BUTTON(gamefunc_Open))
    {
        CONTROL_ClearButton(gamefunc_Open);

        if (netgame) {
            netdropflag();
        }
        else {
            plruse(plr);
        }
    }

    if ((sector[plr->sector].floorpicnum != LAVA
        || sector[plr->sector].floorpicnum != SLIME
        || sector[plr->sector].floorpicnum != WATER
        || sector[plr->sector].floorpicnum != HEALTHWATER
        || sector[plr->sector].floorpicnum != ANILAVA
        || sector[plr->sector].floorpicnum != LAVA1
        || sector[plr->sector].floorpicnum != LAVA2)
        && feetoffground <= (32 << 8))
    {
        v >>= 1;
    }

    if ((sector[plr->sector].floorpicnum == LAVA
        || sector[plr->sector].floorpicnum == SLIME
        || sector[plr->sector].floorpicnum == WATER
        || sector[plr->sector].floorpicnum == HEALTHWATER
        || sector[plr->sector].floorpicnum == ANILAVA
        || sector[plr->sector].floorpicnum == LAVA1
        || sector[plr->sector].floorpicnum == LAVA2)
        && plr->orbactive[5] < 0                                 //loz
        && plr->z >= sector[plr->sector].floorz - (plr->height << 8) - (8 << 8))
    {
        goalz = loz - (32 << 8);
        switch (sector[plr->sector].floorpicnum)
        {
            case ANILAVA:
            case LAVA:
            case LAVA1:
            case LAVA2:
            {
                if (plr->treasure[5] == 1)
                {
                    goalz = loz - (PLAYERHEIGHT << 8);
                    break;
                }
                else
                    v -= v >> 3;

                if (plr->invincibletime > 0 || plr->manatime > 0)
                    break;
                else
                {
                    if (lavasnd == -1)
                    {
                        lavasnd = SND_PlaySound(S_FIRELOOP1, 0, 0, 0, -1);
                    }
                    sethealth(-1);
                    startredflash(10);
                }
            }
            break;
            case WATER:
            {
                if (plr->treasure[4] == 1)
                {
                    goalz = loz - (PLAYERHEIGHT << 8);
                }
                else
                {
                    v -= v >> 3;
                }
            }
            break;
            case HEALTHWATER:
            if (plr->health < plr->maxhealth)
            {
                sethealth(1);
                startblueflash(5);
            }
            break;
        }
    }
    else if (plr->orbactive[5] > 0)
    {
        goalz = plr->z - (plr->height << 8);
        plr->hvel = 0;
    }
    else
        goalz = loz - (plr->height << 8);

    if (BUTTON(gamefunc_Jump))
    {
        if (plr->onsomething)
        {
            plr->hvel -= JUMPVEL;
            plr->onsomething = 0;
        }

        CONTROL_ClearButton(gamefunc_Jump);
    }

    if (BUTTON(gamefunc_Crouch))
    {
        if (goalz < ((sector[plr->sector].floorz) - (plr->height >> 3)))
        {
            goalz += (24 << 8);
        }
    }

    if (BUTTON(gamefunc_Map))
    {
        if (plr->dimension == 3)
        {
            plr->dimension = 2;

            StatusMessage(720, "map %d", mapon);
        }
        else
        {
            plr->dimension = 3;
            followmode = false;
        }

        CONTROL_ClearButton(gamefunc_Map);
    }

    // Map mode
    if (plr->dimension == 2)
    {
        if (BUTTON(gamefunc_Zoom_Out))
        {
            if (plr->zoom > 48)
                plr->zoom -= (plr->zoom >> 4);
        }
        if (BUTTON(gamefunc_Zoom_In))
        {
            if (plr->zoom < 4096)
                plr->zoom += (plr->zoom >> 4);
        }

        if (keystatus[sc_F] > 0)
        {
            if (!followmode)
            {
                StatusMessage(360, "Map %d", mapon);
                followmode = true;
                followx = 0;
                followy = 0;
            }
            else
            {
                followmode = false;
            }

            keystatus[sc_F] = 0;
        }
    }

    else if (plr->dimension == 3 && svga == 0)
    {
        if (BUTTON(gamefunc_Shrink_Screen) && plr->screensize > 64)
        {
            if (plr->screensize <= 320)
            {
                //updatepics();
            }
            plr->screensize -= 8;
            dax = (XDIM >> 1) - (plr->screensize >> 1);
            dax2 = dax + plr->screensize - 1;
            day = (STATUSSCREEN >> 1) - (((plr->screensize * STATUSSCREEN) / XDIM) >> 1);
            day2 = day + ((plr->screensize * STATUSSCREEN) / XDIM) - 1;

            videoSetViewableArea(dax, day, dax2, day2);

            odax = (YDIM >> 1) - ((plr->screensize + 8) >> 1);
            odax2 = dax + (plr->screensize + 8) - 1;
            oday = (STATUSSCREEN >> 1) - ((((plr->screensize + 8) * STATUSSCREEN) / XDIM) >> 1);
            oday2 = day + (((plr->screensize + 8) * STATUSSCREEN) / XDIM) - 1;
            permanentwritesprite(0, 0, BACKGROUND, 0, odax, oday, dax - 1, oday2, 0);
            permanentwritesprite(0, 0, BACKGROUND, 0, dax2 + 1, oday, odax2, oday2, 0);
            permanentwritesprite(0, 0, BACKGROUND, 0, dax, oday, dax2, day - 1, 0);
            permanentwritesprite(0, 0, BACKGROUND, 0, dax, day2 + 1, dax2, oday2, 0);

            if (plr->screensize == XDIM)
            {
                permanentwritesprite(0, 200 - 46, NEWSTATUSBAR, 0, 0, 0, XDIM - 1, YDIM - 1, 0);
                //updatepics();
            }
        }

        if (BUTTON(gamefunc_Enlarge_Screen) && plr->screensize <= XDIM)
        {
            plr->screensize += 8;
            if (plr->screensize > XDIM)
            {
                dax = day = 0;
                dax2 = XDIM - 1;
                day2 = YDIM - 1;
            }
            else
            {
                dax = (XDIM >> 1) - (plr->screensize >> 1);
                dax2 = dax + plr->screensize - 1;
                day = (STATUSSCREEN >> 1) - (((plr->screensize * STATUSSCREEN) / XDIM) >> 1);
                day2 = day + ((plr->screensize * STATUSSCREEN) / XDIM) - 1;
            }

            videoSetViewableArea(dax, day, dax2, day2);
        }
    }

    if (plr->dimension == 3 && svga == 1)
    {
        if (BUTTON(gamefunc_Shrink_Screen))
        {
            plr->screensize = 320;
            videoSetViewableArea(0, 0, 640 - 1, 372 - 1);
            overwritesprite(0, 372, SSTATUSBAR, 0, 0, 0);
            //updatepics();
        }

        if (BUTTON(gamefunc_Enlarge_Screen))
        {
            plr->screensize = 328;
            videoSetViewableArea(0, 0, 640 - 1, 480 - 1);
        }
    }

    onground = plr->onsomething;

    if (BUTTON(gamefunc_Fly_Up)) {
        dophysics(plr, goalz, 1, v);
    }
    else if (BUTTON(gamefunc_Fly_Down)) {
        dophysics(plr, goalz, -1, v);
    }
    else {
        dophysics(plr, goalz, 0, v);
    }

    if (!onground && plr->onsomething)
    {
        if (plr->fallz > 32768)
        {
            if (rand() % 2)
                playsound_loc(S_PLRPAIN1 + (rand() % 2), plr->x, plr->y);
            else
                playsound_loc(S_PUSH1 + (rand() % 2), plr->x, plr->y);

            sethealth(-(plr->fallz >> 13));
            plr->fallz = 0;
        }
        else if (plr->fallz > 8192)
        {
            playsound_loc(S_BREATH1 + (rand() % 2), plr->x, plr->y);
        }
    }

    if (ihaveflag > 0)
        v -= v >> 2;

    if (v != 0 || s != 0)
    {
        xvect = yvect = 0;
        if (v != 0)
        {
            xvect = (v * tics * Cos(plr->ang + 2048)) >> 3;
            yvect = (v * tics * Sin(plr->ang + 2048)) >> 3;
        }
        if (s != 0)
        {
            xvect += (s * tics * Cos(plr->ang + 1536)) >> 3;
            yvect += (s * tics * Sin(plr->ang + 1536)) >> 3;
        }

        oldposx = plr->x; oldposy = plr->y;

        clipmove_old(&plr->x, &plr->y, &plr->z, &plr->sector, xvect, yvect, 128, 4 << 8, 4 << 8, CLIPMASK0);

        if (plr->sector != tempsectornum)
        {
            if (lavasnd != -1)
            {
                switch (sector[plr->sector].floorpicnum)
                {
                    case ANILAVA:
                    case LAVA:
                    case LAVA1:
                    case LAVA2:
                    break;
                    default:
                    SND_StopLoop(lavasnd);
                    lavasnd = -1;
                    break;
                }
            }
            sectorsounds();
        }

        tempsectornum = plr->sector;

        // walking on sprite
        plr->horiz -= oldhoriz;

        dist = ksqrt((plr->x - oldposx) * (plr->x - oldposx) + (plr->y - oldposy) * (plr->y - oldposy));

        if (BUTTON(gamefunc_Run))
        {
            dist >>= 2;
        }

        if (dist > 0 && feetoffground <= (plr->height << 8) || onsprite != -1)
        {
            if (svga == 1)
                oldhoriz = (dist * Sin((int)totalclock << 5) >> 19) >> 2;
            else
                oldhoriz = (dist * Sin((int)totalclock << 5) >> 19) >> 1;

            plr->horiz += oldhoriz;
        }
        else
            oldhoriz = 0;

        if (plr->horiz > 200) plr->horiz = 200;
        if (plr->horiz < 0) plr->horiz = 0;

        if (onsprite != -1 && dist > 50 && lopoint && !justplayed)
        {
            switch (sprite[onsprite].picnum)
            {
                case WALLARROW:
                case OPENCHEST:
                case GIFTBOX:
                if (walktoggle)
                    playsound_loc(S_WOOD1, (plr->x + 3000), plr->y);
                else
                    playsound_loc(S_WOOD1, plr->x, (plr->y + 3000));
                walktoggle = !walktoggle;
                justplayed = true;
                break;
                case WOODPLANK:
                if (walktoggle)
                    playsound_loc(S_SOFTCHAINWALK, (plr->x + 3000), plr->y);
                else
                    playsound_loc(S_SOFTCHAINWALK, plr->x, (plr->y + 3000));
                walktoggle = !walktoggle;
                justplayed = true;

                break;
                case SQUAREGRATE:
                case SQUAREGRATE + 1:
                if (walktoggle)
                    playsound_loc(S_LOUDCHAINWALK, (plr->x + 3000), plr->y);
                else
                    playsound_loc(S_LOUDCHAINWALK, plr->x, (plr->y + 3000));
                walktoggle = !walktoggle;
                justplayed = true;
                break;
                case SPACEPLANK:
                if (walktoggle)
                    playsound_loc(S_SOFTCREAKWALK, (plr->x + 3000), plr->y);
                else
                    playsound_loc(S_SOFTCREAKWALK, plr->x, (plr->y + 3000));
                walktoggle = !walktoggle;
                justplayed = true;
                break;
                case RAT:
                playsound_loc(S_RATS1 + (rand() % 2), sprite[i].x, sprite[i].y);
                justplayed = true;
                deletesprite(i);
                break;
                case SPIDER:
                //STOMP
                playsound_loc(S_DEADSTEP, sprite[i].x, sprite[i].y);
                justplayed = true;
                newstatus(i, DIE);
                break;

                case FREDDEAD:
                case 1980:
                case 1981:
                case 1984:
                case 1979:
                case 1957:
                case 1955:
                case 1953:
                case 1952:
                case 1941:
                case 1940:
                playsound_loc(S_DEADSTEP, plr->x, plr->y);
                justplayed = true;
                break;

                default:
                break;
            }
        }

        if (!lopoint && oldhoriz == -2 && !justplayed)
            lopoint = true;

        if (lopoint && oldhoriz != -2 && justplayed)
        {
            lopoint = false;
            justplayed = false;
        }

        if (vel > 199 || vel < -199 && dist > 10)
            runningtime += kTimerTicks; // synctics;
        else
            runningtime -= kTimerTicks; //synctics;

        if (runningtime < -360)
            runningtime = 0;

        if (runningtime > 360)
        {
            SND_PlaySound(S_PLRPAIN1, 0, 0, 0, 0);
            runningtime = 0;
        }
    }
    if (a != 0)
    {
        plr->ang += ((a * /*synctics*/kTimerTicks) >> 4);
        plr->ang = (plr->ang + 2048) & kAngleMask;
    }

    //vec3_t pos;
    pos.x = plr->x;
    pos.y = plr->y;
    pos.z = plr->z + (plr->height << 8);

    setsprite(plr->spritenum, &pos);
    sprite[plr->spritenum].ang = plr->ang;

    if (sector[plr->sector].ceilingz > sector[plr->sector].floorz - (8 << 8))
        sethealth(-10);

    if (plr->health <= 0)
    {
        SND_CheckLoops();
        playerdead(plr);
    }

    if (BUTTON(gamefunc_Look_Straight))
    {
        autohoriz = 1;
        CONTROL_ClearButton(gamefunc_Look_Straight);
    }

    if (autohoriz == 1)
        autothehoriz(plr);

    singleshot(bstatus);

    weaponchange();
}

void autothehoriz(Player* plr)
{
    if (plr->horiz < 100)
        plr->horiz += synctics << 1;
    if (plr->horiz > 100)
        plr->horiz -= synctics << 1;
    if (plr->horiz >= 90 && plr->horiz <= 110)
        autohoriz = 0;
}

#if 0
int gimmer=0;
int gimmes=0;
int gimmev=0;
int gimmep=0;

void cheatkeys(Player* plr)
{

    int i;

    /*
        the cheat code is R S V P
    */

    if (keystatus[sc_R] > 0)
    {
        gimmer = 1;
        //          keystatus[sc_R]=0;
    }
    if (gimmer == 1 && keystatus[sc_S] > 0)
    {
        gimmes = 1;
        //          keystatus[sc_S]=0;
    }
    if (gimmes == 1 && keystatus[sc_V] > 0)
    {
        gimmev = 1;
        //          keystatus[sc_V]=0;
    }
    if (gimmev == 1 && keystatus[sc_P] > 0)
    {
        gimmep = 1;
        //          keystatus[sc_P]=0;
    }
    if (gimmep == 1)
    {
        gimmer = gimmes = gimmev = gimmep = 0;

        //        spiked=1;

        for (i = 0; i < MAXPOTIONS; i++)
        {
            plr->potion[i] = 9;
        }

        plr->weapon[1] = 1; plr->ammo[1] = 45; //DAGGER
        plr->weapon[2] = 1; plr->ammo[2] = 55; //MORNINGSTAR
        plr->weapon[3] = 1; plr->ammo[3] = 50; //SHORT SWORD
        plr->weapon[4] = 1; plr->ammo[4] = 80; //BROAD SWORD
        plr->weapon[5] = 1; plr->ammo[5] = 100; //BATTLE AXE
        plr->weapon[6] = 1; plr->ammo[6] = 50; // BOW
        plr->weapon[7] = 2; plr->ammo[7] = 40; //PIKE
        plr->weapon[8] = 1; plr->ammo[8] = 250; //TWO HANDED
        plr->weapon[9] = 1; plr->ammo[9] = 50;

        for (i = 0; i < 8; i++)
        {
            plr->orb[i] = 1;
            plr->orbammo[i] = 9;
        }

        currweapon = selectedgun = 4;
        plr->health = 0;
        sethealth(200);
        plr->armor = 150;
        plr->armortype = 3;
        plr->lvl = 7;
        plr->maxhealth = 200;
        plr->treasure[14] = 1;
        plr->treasure[15] = 1;
        plr->treasure[16] = 1;
        plr->treasure[17] = 1;
        updatepics();
    }

    //if(keystatus[sc_D] > 0 && keystatus[sc_O] > 0 && keystatus[sc_N] > 0)
    //if( keystatus[sc_F12] > 0 )
    //    invincibletime=65000;

    return;
    // OHM
    if (keystatus[sc_O] > 0 && keystatus[sc_H] > 0 && keystatus[sc_M] > 0)
        for (i = 0; i < MAXWEAPONS; i++)
        {
            plr->weapon[i] = 1;
            plr->ammo[i] = 999;
        }

    // BINGO
    if (keystatus[sc_B] > 0 && keystatus[sc_I] > 0 && keystatus[sc_N] > 0 && keystatus[sc_G] > 0 && keystatus[sc_O] > 0)
    {
        plr->health = 1;
        sethealth(99);
    }
}
#endif

extern char fancy[];

void nettypeletter()
{
    strcpy(nettemp, "");

    if (typemessageleng <= 40)
    {
        for (int i = 0; i < 128; i++)
        {
            if (keystatus[i] > 0)
            {
                nettemp[typemessageleng] = scantoasc[i];
                typemessageleng++;
                nettemp[typemessageleng] = '\0';
                keystatus[i] = 0;
            }
        }
        printext256(0, 0, 31, -1, Bstrupr(nettemp), 1);
    }
}

char typingbuffer[40];

void typeletter()
{
    int exit = 0;
    //	char temp[20];

    keystatus[sc_Return] = keystatus[sc_kpad_Enter] = 0;

    for (int i = 0; i < 128; i++)
        keystatus[i] = 0;

    typingbuffer[0] = '\0';
    typemessageleng = 0;
    //strcpy(temp, "");

    while (!exit)
    {
        handleevents();

        if (keystatus[sc_BackSpace])
        {
            if (typemessageleng)
            {
                typemessageleng--;
                typingbuffer[typemessageleng] = '\0';
            }
            keystatus[sc_BackSpace] = 0;
        }

        if (typemessageleng <= 10)
        {
            for (int i = 0; i < 128; i++)
            {
                if (keystatus[i] > 0
                    && keystatus[sc_BackSpace] == 0
                    && keystatus[sc_Escape] == 0
                    && keystatus[sc_Return] == 0
                    && keystatus[sc_kpad_Enter] == 0)
                {
                    for (int j = 0; j < 41; j++)
                    {
                        if (scantoasc[i] == ' ')
                            continue;

                        else if (scantoasc[i] == fancy[j])
                        {
                            typingbuffer[typemessageleng] = fancy[j];
                            typemessageleng++;
                            typingbuffer[typemessageleng] = '\0';
                            keystatus[i] = 0;
                        }
                        else
                        {
                            keystatus[i] = 0;
                        }
                    }
                }
            }
        }

        if (keystatus[sc_Escape] > 0)
        {
            exit = 1;
            keystatus[sc_Escape] = 0;
        }

        if (keystatus[sc_Return] > 0 || keystatus[sc_kpad_Enter] > 0)
        {
            exit = 2;
            keystatus[sc_Return] = keystatus[sc_kpad_Enter] = 0;
        }

        fancyfontscreen(18, 24, THEFONT, typingbuffer);
        videoNextPage();
    }

    if (exit == 2)
    {
        keystatus[sc_Return] = keystatus[sc_kpad_Enter] = 0;
    }

    checkcheat();
    lockclock = (int)totalclock;
    typemode = 0;
    typemessageleng = 0;
}

void checkcheat()
{
    Player* plr = &player[pyrn];

    Bstrupr(typingbuffer);

    if (strcmp(typingbuffer, "RSVP") == 0)
    {
        // TODO - this behaves differently in the demo (max health, ammo, magic and 9 of each potions etc)
        sethealth(-plr->health);
        plr->horiz = 200;
        //updatepics();
    }
    else if (strcmp(typingbuffer, "RAMBO") == 0)
    {
        sethealth(-plr->health);
        plr->horiz = 200;
        //updatepics();
    }
    else if (strcmp(typingbuffer, "IDKFA") == 0)
    {
        sethealth(-plr->health);
        plr->horiz = 200;
        //updatepics();
    }
    else if (strcmp(typingbuffer, "SCOOTER") == 0)
    {
        plr->weapon[1] = 1; plr->ammo[1] = 45;  // DAGGER
        plr->weapon[2] = 1; plr->ammo[2] = 55;  // MORNINGSTAR
        plr->weapon[3] = 1; plr->ammo[3] = 50;  // SHORT SWORD
        plr->weapon[4] = 1; plr->ammo[4] = 80;  // BROAD SWORD
        plr->weapon[5] = 1; plr->ammo[5] = 100; // BATTLE AXE
        plr->weapon[6] = 1; plr->ammo[6] = 50;  // BOW
        plr->weapon[7] = 2; plr->ammo[7] = 40;  // PIKE
        plr->weapon[8] = 1; plr->ammo[8] = 250; // TWO HANDED
        plr->weapon[9] = 1; plr->ammo[9] = 50;
        plr->currweapon = plr->selectedgun = 4;
        //updatepics();
    }
    else if (strcmp(typingbuffer, "MOMMY") == 0)
    {
        for (int i = 0; i < MAXPOTIONS; i++)
        {
            plr->potion[i] = 9;
        }

        //updatepics();
    }
    else if (strcmp(typingbuffer, "WANGO") == 0)
    {
        for (int i = 0; i < 8; i++)
        {
            plr->orb[i] = 1;
            plr->orbammo[i] = 9;
        }

        plr->health = 0;
        sethealth(200);
        plr->armor = 150;
        plr->armortype = 3;
        plr->lvl = 7;
        plr->maxhealth = 200;
        plr->treasure[14] = 1;
        plr->treasure[15] = 1;
        plr->treasure[16] = 1;
        plr->treasure[17] = 1;
        //updatepics();
    }
    else if (strcmp(typingbuffer, "DARKNESS") == 0)
    {
        // TODO - god mode
    }
    else if (strcmp(typingbuffer, "GOTHMOG") == 0)
    {
        // TODO - teleport
    }
    else if (strcmp(typingbuffer, "SPINACH") == 0)
    {
        plr->health = 0;
        sethealth(200);
    }

    strcpy(typingbuffer, "");
}

void typecheat(char ch)
{
    char tempbuf[40];
    charsperline = 40;

    for (int i = 0; i <= typemessageleng; i += charsperline)
    {
        for (int j = 0; j < charsperline; j++)
        {
            tempbuf[j] = typemessage[i + j];
        }

        if (typemessageleng < i + charsperline)
        {
            tempbuf[(typemessageleng - i)] = '-';
            tempbuf[(typemessageleng - i) + 1] = 0;
        }
        else
        {
            tempbuf[charsperline - 1] = 0;
        }
    }

    StatusMessage(360, tempbuf);
}

void dosoundthing()
{
    musiclevel = (wMIDIVol >> 3);
    digilevel = (wDIGIVol >> 11);

    if (BUTTON(gamefunc_Move_Forward))
        soundcontrol++;

    if (BUTTON(gamefunc_Move_Backward))
        soundcontrol--;

    if (soundcontrol > 1)
        soundcontrol = 1;
    if (soundcontrol < 0)
        soundcontrol = 0;

    switch (soundcontrol)
    {
        case 0:
        if (BUTTON(gamefunc_Turn_Left))
        {
            musiclevel--;
            if (musiclevel < 0)
            {
                musiclevel = 0;

                StatusMessage(10, "Music %d", musiclevel);
            }
            else
            {
                StatusMessage(10, "Music %d", musiclevel);
                SND_Mixer(1, musiclevel);
            }
            break;
        }
        else if (BUTTON(gamefunc_Turn_Right))
        {
            musiclevel++;
            if (musiclevel > 16)
            {
                musiclevel = 16;
                StatusMessage(10, "Music %d", musiclevel);
            }
            else
            {
                StatusMessage(10, "Music %d", musiclevel);
                SND_Mixer(1, musiclevel);
            }
            break;
        }

        else
        {
            StatusMessage(10, "Music %d", musiclevel);
        }
        break;

        case 1:
        if (BUTTON(gamefunc_Turn_Left))
        {
            digilevel--;
            if (digilevel < 0)
            {
                digilevel = 0;
                StatusMessage(10, "Sounds %d", mousyspeed);
            }
            else
            {
                StatusMessage(10, "Sounds %d", mousyspeed);
                SND_Mixer(0, digilevel);
                SND_Sound(S_LOUDCHAINWALK);
            }
            break;
        }
        else if (BUTTON(gamefunc_Turn_Right))
        {
            digilevel++;
            if (digilevel > 16)
            {
                digilevel = 16;
                StatusMessage(10, "Sounds %d", mousyspeed);
            }
            else
            {
                StatusMessage(10, "Sounds %d", mousyspeed);
                SND_Mixer(0, digilevel);
                SND_Sound(S_LOUDCHAINWALK);
            }
            break;
        }

        else
        {
            StatusMessage(10, "Sounds %d", mousyspeed);
        }
        break;
    }
}


code:
#ifndef __whinp_h__
#define __whinp_h__

#include "player.h"

extern int32_t lockclock;
extern int32_t angvel, svel, vel;

void keytimerstuff();
void processinput(Player *plr);

#endif


code:
#ifndef __menu_h__
#define __menu_h__

#include <stdint.h>

void screenfx();
void initpaletteshifts();
int menuscreen();
void startredflash(int damage);
void fancyfontscreen(int x, int y, short tilenum, char* string);
void startwhiteflash(int bonus);
void fancyfont(int x, int y, short tilenum, char* string, char pal);
void startblueflash(int bluetime);
void startgreenflash(int greentime);
void startredflash(int damage);
void startnewgame();
void loadsave();
void thedifficulty();
void help();
void quit();
void loadgame();
void savegame();
void loadplayerstuff();
int savedgamedat(int gn);
void savegametext(int select);
int savedgamename(int gn);
void updatepaletteshifts();
void itemtoscreen(int x, int y, short tilenum, int8_t shade, char pal);

#endif


code:
//Be careful when changing this file - it is parsed by Editart and Build.
#define TILES000_START 0
#define SLIME 5
#define DRYWATER 22
#define DRYSLIME 33
#define COOLLAVA 58
#define ANILAVA 61
#define LAVA1 62
#define WOODPLANK 68
#define SKY 75
#define LAVA 79
#define LAVA2 80
#define HEALTHWATER 86
#define WATER 90
#define COOLLAVA2 103
#define SPLASHAROO 160
#define LASTSPLASHAROO 166
#define SKY2 167
#define SKY3 168
#define SKY4 169
#define SKY5 170
#define SKY6 171
#define SKY7 172
#define SKY8 179
#define SKY9 180
#define SKY10 181
#define TILES001_START 247
#define GIFTBOX 260
#define OPENCHEST 263
#define BARREL 269
#define SPACEPLANK 271
#define PENTADOOR1 280
#define PENTADOOR2 281
#define ROPEDOOR 282
#define TELEPAD 291
#define BULLET 294
#define PINE 308
#define FIREBALL 314
#define SWINGDOOR 324
#define SWINGDOOR2 325
#define SWINGDOOR3 326
#define BAT 330
#define FBARREL 340
#define FBARRELFALL 341
#define FSHATTERBARREL 342
#define LFIRE 345
#define SFIRE 353
#define BOULDER 361
#define PULLCHAIN1 370
#define PULLCHAIN2 371
#define PULLCHAIN3 372
#define SAPHIRERING 373
#define TALLSWING 385
#define TALLSWING2 386
#define SPARKFX 390
#define VASEA 392
#define VASEB 393
#define VASEC 394
#define HEALTHPOTION 401
#define FIREWALKPOTION 402
#define STRENGTHPOTION 403
#define INVISIBLEPOTION 404
#define ARMORPOTION 406
#define LAVASPLASH 423
#define LASTLAVASPLASH 437
#define WATERSPLASH 438
#define LASTWATERSPLASH 446
#define GLOW 451
#define SPIKEBLADE 461
#define SPIKE 462
#define SPIKEPOLE 463
#define HORIZSPIKEPOLE 464
#define HORIZSPIKE 465
#define HORIZSPIKEBLADE 466
#define GEARSSTART 467
#define GEARSEND 474
#define SWINGHOLE 476
#define SWINGGATE 477
#define SWINGGATE2 478
#define WOODGEARSSTART 483
#define WOODGEARSEND 486
#define SQUAREGRATE 490
#define COMPASS 499
#define MAINMENU 500
#define TITLEPIC 501
#define SAVEPIC 502
#define SAVENUMBERS 503
#define LOADPIC 504
#define AREYOUSURE 505
#define BLOODGORERED 506
#define DIFFICULTRED 507
#define BLOODGOREGREEN 508
#define DIFFICULTGREEN 509
#define HORNYSKULL1 510
#define HORNYSKULL2 511
#define HORNYSKULL3 512
#define HORNYSKULL4 513
#define HORNYBACK 514
#define LOADRED 515
#define SAVERED 516
#define LOADGREEN 517
#define SAVEGREEN 518
#define MENUSELECTIONS 519
#define NEWGAMEGREEN 520
#define LOADSAVEGREEN 521
#define BLOODGOREGREEN2 522
#define HELPGREEN 523
#define QUITGREEN 524
#define NOGORESHADOW 525
#define NOGORESOLID 526
#define GORESHADOW 527
#define GORESOLID 528
#define VDARK 529
#define MDARK 530
#define FLICKER 531
#define XDARK 532
#define BACKGROUND 536
#define WALLARROW 537
#define MUHANDS 538
#define MACEWITHSPIKES 540
#define MACE 541
#define KNIFE 542
#define SWORD1 543
#define SWORD2 544
#define STATUSBAR 545
#define ORB1 546
#define ORB2 547
#define FLASKBLUE 548
#define FLASKGREEN 549
#define FLASKOCHRE 550
#define FLASKRED 551
#define FLASKTAN 552
#define FLASKBLACK 553
#define BGZERO 554
#define BGONE 555
#define BGTWO 556
#define BGTHREE 557
#define BGFOUR 558
#define BGFIVE 559
#define BGSIX 560
#define BGSEVEN 561
#define BGEIGHT 562
#define BGNINE 563
#define SCOREFONT 564
#define MDONE 565
#define MDTWO 566
#define MDTHREE 567
#define MDFOUR 568
#define MDFIVE 569
#define MDSIX 570
#define MDSEVEN 571
#define MDEIGHT 572
#define MDNINE 573
#define SMFONT 574
#define SMONE 575
#define SMTWO 576
#define SMTHREE 577
#define SMFOUR 578
#define SMFIVE 579
#define SMSIX 580
#define SMSEVEN 581
#define SMEIGHT 582
#define SMNINE 583
#define SCOREBACKPIC 584
#define WEAPONBACKPIC 585
#define HEALTHBACKPIC 586
#define BOOKBACKPIC 587
#define ORBBACKPIC 588
#define POTIONBACKPIC 589
#define POTIONARROW 590
#define ORB3 595
#define ORB4 596
#define ORB5 597
#define ORB6 598
#define ORB7 599
#define ORB8 600
#define WAVES1_SND 607
#define WAVES2_SND 608
#define WIND1_SND 609
#define WIND2_SND 610
#define LAVA1_SND 611
#define LAVA2_SND 612
#define DIESPIKE 613
#define SPELLBOOKBLANK 618
#define SPELLBOOK2 627
#define SPELLBOOK3 636
#define CHUNK1 645
#define CHUNK2 646
#define CHUNK3 647
#define CHUNK4 648
#define PLASMA 649
#define ANNIHILATE 650
#define WILLOWEXPLO 653
#define EXPLOSION 656
#define FATSPANK 658
#define MONSTERBALL 660
#define ICECUBE 663
#define DISTORTIONBLAST 664
#define SPELLBOOKBACK 665
#define WEAPON3 666
#define ORB 674
#define SPAWN 675
#define ARMOR 676
#define POTION 677
#define WEAPON 678
#define COPYRIGHT 679
#define GYSER 680
#define DIAMONDRING 694
#define SHADOWAMULET 698
#define GLASSSKULL 706
#define AHNK 714
#define BLUESCEPTER 718
#define YELLOWSCEPTER 722
#define ADAMANTINERING 726
#define ONYXRING 730
#define TILES003_START 736
#define KOBOLD 737
#define KOBOLDATTACK 762
#define KOBOLDDIE 765
#define KOBOLDDEAD 770
#define KOBOLDTORCH 771
#define DEVILSTAND 772
#define DEVIL 777
#define DEVILATTACK 797
#define DEVILPAIN 809
#define DEVILDIE 814
#define DEVILDEAD 820
#define FATWITCH 821
#define FATWITCHATTACK 841
#define FATWITCHDIE 846
#define FATWITCHDEAD 852
#define SWAPCHILL 853
#define SWAMPSTAND 859
#define SWAMPTHING 860
#define SWAMPATTACK 862
#define FREDSTAND 866
#define FRED 871
#define FREDATTACK 891
#define FREDPAIN 906
#define FREDDIE 911
#define FREDDEAD 916
#define GOBLINSTAND 917
#define GOBLIN 922
#define GOBLINCHILL 942
#define GOBLINSURPRISE 946
#define GOBLINATTACK 952
#define GOBLINPAIN 963
#define GOBLINDIE 968
#define GOBLINDEAD 977
#define TILES004_START 980
#define MINOTAUR 981
#define MINOTAURATTACK 1001
#define MINOTAURPAIN 1012
#define MINOTAURDIE 1017
#define MINOTAURDEAD 1027
#define SKULLY 1028
#define SKULLYATTACK 1048
#define SKULLYDIE 1059
#define SKULLYDEAD 1069
#define SPIDER 1070
#define SPIDERDIE 1110
#define SPIDERDEAD 1117
#define JUDYSIT 1118
#define JUDYSTAND 1119
#define JUDY 1123
#define JUDYATTACK1 1143
#define JUDYATTACK2 1150
#define JUDYDIE 1159
#define JUDYDEAD 1163
#define DRAGONATTACK 1164
#define DRAGONDIE 1187
#define DRAGONDEAD 1200
#define DRAGON 1201
#define DRAGONATTACK2 1207
#define DOG 1213
#define TILES005_START 1221
#define KNIFEREADY 1249
#define KNIFEATTACK 1261
#define KNIFEDAMAGE 1267
#define RFIST 1268
#define THROWORB 1280
#define LASTTHROWORB 1290
#define SWORDATTACK 1291
#define MORNINGSTAR 1306
#define BOWREADY 1313
#define BOWREADYEND 1319
#define BOWWALK 1323
#define KNIFEATTACK2 1329
#define SWORDPULL 1333
#define SWORDATTACK2 1340
#define MORNINGATTACK2 1348
#define MUMEDUSA 1353
#define MUTWOHANDS 1363
#define BMUTWOHANDS 1378
#define GOBSWORDPULL 1390
#define GOBSWORDATTACK 1397
#define GOBSWORDATTACK2 1404
#define BIGAXEATTACK 1411
#define BIGAXEATTACK2 1421
#define BIGAXEDRAW 1429
#define HALBERDATTACK1 1438
#define HALBERDATTACK2 1443
#define HALBERDDRAW 1447
#define PIKEDRAW 1451
#define PIKEATTACK1 1457
#define EXCALATTACK1 1463
#define EXCALATTACK2 1473
#define EXCALDRAW 1481
#define BIGAXEDRAW9 1494
#define BIGAXEDRAW10 1495
#define GEARS2START 1498
#define GEARS2END 1502
#define HORNEDSKULL 1503
#define THEFONT 1511
#define CRYSTALSTAFF 1551
#define TORCH 1554
#define WEAPON1 1567
#define WEAPON1A 1571
#define AMULETOFTHEMIST 1575
#define WEAPON2 1583
#define CAM 1600
#define CRASH 1612
#define CRASH2 1618
#define BRASSKEY 1624
#define BLACKKEY 1632
#define GLASSKEY 1640
#define IVORYKEY 1648
#define NEWCHUNK 1664
#define BOING 1674
#define SWINGCRASH 1682
#define FLAME 1692
#define BOWLOFFIRE 1705
#define SPAWNFIREBALL 1718
#define SPELLBOOK4 1719
#define SPELLBOOK5 1727
#define SPELLBOOK6 1735
#define SPELLBOOK7 1744
#define SPELLBOOK8 1752
#define PENTAGRAM 1760
#define THEHORN 1768
#define BOW 1776
#define CREDIT1 1784
#define CREDIT2 1785
#define CREDIT3 1786
#define CREDIT4 1787
#define FLYING 1788
#define SWINGGATE3 1789
#define POTIONS 1790
#define SPELLS 1791
#define WALKING 1792
#define WEAPONS 1793
#define PLATEARMOR 1794
#define HELMET 1802
#define CHAINMAIL 1810
#define SCROLLSCARE 1818
#define SCROLLNUKE 1826
#define SCROLLFLY 1834
#define SCROLLFIREBALL 1842
#define SCROLLFREEZE 1850
#define SCROLLNIGHT 1858
#define SCROLLMAGIC 1866
#define SCROLLOPEN 1874
#define LEATHERARMOR 1882
#define QUIVER 1890
#define SWINGGATE4 1898
#define SWINGGATE5 1899
#define SMOKEFX 1904
#define STANDINTORCH 1908
#define ARROWS 1917
#define PLAYERLVL 1918
#define PENTADOOR3 1927
#define PENTADOOR4 1928
#define PENTADOOR5 1929
#define PENTADOOR6 1930
#define PENTADOOR7 1931
#define SCARY 1933
#define ORDER1 1937
#define ORDER2 1938
#define FISH 1945
#define LEVERUP 1947
#define LEVERDOWN 1949
#define SPAWNJAVLIN 1958
#define RAT 1962
#define LIGHTNING 1978
#define PIKE 1986
#define PIKES 1987
#define SVGAORDER2 1988
#define SVGAORDER1 1989
#define SVGAMENU2 1990
#define SVGAMENU 1991
#define GUARDIAN 1994
#define GUARDIANATTACK 2002
#define DART 2010
#define GASCLOUD 2021
#define WEAPON4 2027
#define WEAPON6 2039
#define WEAPON5 2047
#define WEAPON5B 2055
#define THROWHALBERD 2066
#define WEAPON7 2067
#define THROWPIKE 2071
#define EXPLO2 2096
#define SHATTERVASE 2124
#define SHATTERVASE2 2131
#define SHATTERVASE3 2138
#define HANGMAN 2145
#define SKELETON 2156
#define SKELETONATTACK 2181
#define SKELETONSKULL 2186
#define SKELETONUP 2191
#define SKELETONDIE 2192
#define SKELETONDEAD 2198
#define BONECHUNK1 2199
#define BONECHUNKEND 2208
#define PIKEATTACK2 2209
#define GOBWEAPON 2218
#define SFLASKBLUE 2240
#define SFLASKGREEN 2241
#define SFLASKOCHRE 2242
#define SFLASKRED 2243
#define SFLASKTAN 2244
#define SFLASKBLACK 2245
#define SHEALTHFONT 2246
#define SSCOREFONT 2256
#define SPOTIONFONT 2266
#define SSPELLBACK 2278
#define SPOTIONBACKPIC 2280
#define SPOTIONARROW 2281
#define SSPELLBOOKBLANK 2286
#define SSPELLBOOK2 2294
#define SSPELLBOOK3 2302
#define SSPELLBOOK4 2310
#define SSPELLBOOK5 2317
#define SSPELLBOOK6 2324
#define SSPELLBOOK7 2332
#define SSPELLBOOK8 2340
#define SARROWS 2348
#define SPLAYERLVL 2349
#define SSTATUSBAR 2358
#define SKEYBLANK 2359
#define SKEYBRASS 2360
#define SKEYBLACK 2361
#define SKEYGLASS 2362
#define SKEYIVORY 2363
#define SHEALTHBACK 2365
#define SSCOREBACKPIC 2368
#define SNDEFFECT 2369
#define SNDLOOP 2370
#define SNDLOOPOFF 2371
#define POISONICON 2373
#define NEWSTATUSBAR 2381
#define KEYBLANK 2382
#define KEYBRASS 2383
#define KEYBLACK 2384
#define KEYGLASS 2385
#define KEYIVORY 2386
#define BROWNCHUNKSTART 2387
#define BROWNCHUNKEND 2394
#define GREYCHUNKSTART 2395
#define GREYCHUNKEND 2402
#define REDCHUNKSTART 2403
#define REDCHUNKEND 2410
#define GREENCHUNKSTART 2411
#define GREENCHUNKEND 2418
#define TANCHUNKSTART 2419
#define TANCHUNKEND 2426
#define WILLOW 2427
#define SKULLYCHAR 2433
#define DEVILCHAR 2438
#define DRAGONCHAR 2443
#define FATWITCHCHAR 2448
#define GOBLINCHAR 2453
#define JUDYCHAR 2458
#define KOBOLDCHAR 2463
#define MINOTAURCHAR 2468
#define SKELETONCHAR 2473
#define GRONCHAR 2483
#define SPIKES 2488
#define HELPTHREE 2490
#define STHEFLAG 2491
#define HELPFIVE 2492
#define HELPSIX 2493
#define HELPSEVEN 2494
#define HELPEIGHT 2495
#define THEFLAG 2496
#define HELPTEN 2497
#define GUARDIANCHAR 2498
#define GRONHALATTACK 2504
#define GRONHALATTACKEND 2508
#define GROLHALREADY 2509
#define GRONHALREADYEND 2510
#define GRONHAL 2539
#define GRONHALPAIN 2559
#define GRONHALDIE 2560
#define GRONSWATTACK 2561
#define GRONSW 2581
#define GRONSWPAIN 2601
#define GRONSWDIE 2602
#define GRONMUATTACK 2603
#define GRONDIE 2633
#define GRONDEAD 2639
#define GRONMU 2640
#define GRONMUPAIN 2660
#define GRONMUDIE 2661
#define GRONSHIELD 2680
#define SHIELD 2684
#define BETAPAGE 2691
#define BLUEKNIGHTS 2692
#define BROWNKNIGHTS 2693
#define PURPLEKNIGHTS 2694
#define REDKNIGHTS 2695
#define FOURCASTLES 2696
#define ARENA 2697
#define STONEHENGE 2698
#define SVGAVICTORYA1 2699
#define SVGAVICTORYA2 2700
#define VICTORYA 2703
#define VICTORYB 2704
#define SVGAVICTORYB1 2705
#define SVGAVICTORYB2 2706
#define SVB1 2707
#define SVB2 2708
#define SVBR1 2709
#define SVBR2 2710
#define SVGAVICTORYC1 2712
#define SVGAVICTORYC2 2713
#define VICTORYC 2714
#define SVP1 2715
#define SVP2 2716
#define SVR1 2717
#define SVR2 2718