code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 "save.h"
#include "sound.h"
#include "status.h"
#include "init.h"
#include "menu.h"
#include <stdio.h>
#include <assert.h>
#include "exhumed.h"

const char kSaveSig[] = "EXSV";
const uint16_t kSaveVersion = 9999;

void testsave()
{
    LoadSave::SaveGame("exsave");
    StatusMessage(300, "Saved game");
}

void testload()
{
    LoadSave::LoadGame("exsave");
    StatusMessage(300, "Loaded game");
}

LoadSave LoadSave::head(123);

buildvfs_fd LoadSave::hSFile = buildvfs_fd_invalid;
buildvfs_fd LoadSave::hLFile = buildvfs_fd_invalid;

void LoadSave::Save()
{
    bail2dos("Pure virtual function called");
}

void LoadSave::Load()
{
    bail2dos("Pure virtual function called");
}

void LoadSave::Read(void* pData, int nSize)
{
    assert(hLFile != buildvfs_fd_invalid);

    int nActualRead = buildvfs_read(hLFile, pData, nSize);
    if (nActualRead != nSize)
        bail2dos("Error reading save file.");
  //  if (buildvfs_read(hLFile, pData, nSize) != nSize)
   //     bail2dos("Error reading save file.");
}

void LoadSave::Write(void* pData, int nSize)
{
    assert(hSFile != buildvfs_fd_invalid);

    if (buildvfs_write(hSFile, pData, nSize) != nSize)
        bail2dos("Error writing save file");
}

int savegame(int nSlot)
{
    char filename[BMAX_PATH];

    if (nSlot < 0 || nSlot >= 10) {
        return 0;
    }

    sprintf(filename, "save%1d.gam", nSlot);

    buildvfs_fd hFile = buildvfs_open_write(filename);
    if (hFile != -1)
    {
#if 1 // TODO
        buildvfs_write(hFile, &numsectors, sizeof(numsectors));
        buildvfs_write(hFile, sector, sizeof(SECTOR) * numsectors);
        buildvfs_write(hFile, &numwalls, sizeof(numwalls));
        buildvfs_write(hFile, wall, sizeof(WALL) * numwalls);
        buildvfs_write(hFile, sprite, sizeof(SPRITE) * kMaxSprites);
        buildvfs_write(hFile, headspritesect, sizeof(headspritesect));
        buildvfs_write(hFile, prevspritesect, sizeof(prevspritesect));
        buildvfs_write(hFile, nextspritesect, sizeof(nextspritesect));
        buildvfs_write(hFile, headspritestat, sizeof(headspritestat));
        buildvfs_write(hFile, prevspritestat, sizeof(prevspritestat));
        buildvfs_write(hFile, nextspritestat, sizeof(nextspritestat));
        buildvfs_write(hFile, startumost, sizeof(startumost));
        buildvfs_write(hFile, startdmost, sizeof(startdmost));
/*
        buildvfs_write(hFile, &brightness, 2);
        buildvfs_write(hFile, &visibility, 4);
        buildvfs_write(hFile, &parallaxtype, 1);
        buildvfs_write(hFile, &parallaxyoffs, 4);
        buildvfs_write(hFile, pskyoff, 512);
        buildvfs_write(hFile, &pskybits, 2);
        buildvfs_write(hFile, &inita, 2);
        buildvfs_write(hFile, &initsect, 2);
        buildvfs_write(hFile, &initx, 4);
        buildvfs_write(hFile, &inity, 4);
        buildvfs_write(hFile, &initz, 4);
        buildvfs_write(hFile, &levelnum, 2);
*/
#endif
        buildvfs_close(hFile);
    }

    return 1; // CHECKME
}

int loadgame(int nSlot)
{
    char filename[BMAX_PATH];

    if (nSlot < 0 || nSlot >= 10) {
        return 0;
    }

    sprintf(filename, "save%1d.gam", nSlot);

    buildvfs_fd hFile = buildvfs_open_read(filename);
    if (hFile != -1)
    {
#if 1 // TODO
        buildvfs_read(hFile, &numsectors, sizeof(numsectors));
        buildvfs_read(hFile, sector, sizeof(SECTOR) * numsectors);
        buildvfs_read(hFile, &numwalls, sizeof(numwalls));
        buildvfs_read(hFile, wall, sizeof(WALL) * numwalls);
        buildvfs_read(hFile, sprite, sizeof(SPRITE) * kMaxSprites);
        buildvfs_read(hFile, headspritesect, sizeof(headspritesect));
        buildvfs_read(hFile, prevspritesect, sizeof(prevspritesect));
        buildvfs_read(hFile, nextspritesect, sizeof(nextspritesect));
        buildvfs_read(hFile, headspritestat, sizeof(headspritestat));
        buildvfs_read(hFile, prevspritestat, sizeof(prevspritestat));
        buildvfs_read(hFile, nextspritestat, sizeof(nextspritestat));
        buildvfs_read(hFile, startumost, sizeof(startumost));
        buildvfs_read(hFile, startdmost, sizeof(startdmost));

        #if 0
        buildvfs_read(hFile, &brightness, 2);
        buildvfs_read(hFile, &visibility, 4);
        buildvfs_read(hFile, &parallaxtype, 1);
        buildvfs_read(hFile, &parallaxyoffs, 4);
        buildvfs_read(hFile, pskyoff, 512);
        buildvfs_read(hFile, &pskybits, 2);
        buildvfs_read(hFile, &inita, 2);
        buildvfs_read(hFile, &initsect, 2);
        buildvfs_read(hFile, &initx, 4);
        buildvfs_read(hFile, &inity, 4);
        buildvfs_read(hFile, &initz, 4);
        buildvfs_read(hFile, &levelnum, 2);

        lPlayerXVel = 0;
        lPlayerYVel = 0;
        nPlayerDAng = 0;
        #endif
#endif
        close(hFile);
    }

    return 1; // CHECKME
}

class MyLoadSave : public LoadSave
{
public:
    virtual void Load();
    virtual void Save();
};

void LoadSave::LoadGame(const char* pzFile)
{
    int nSlot = 3; // TEMP
    char filename[BMAX_PATH];

    sprintf(filename, "save%1d.gam", nSlot);

    hLFile = buildvfs_open_read(filename);
    if (hLFile == buildvfs_fd_invalid) {
        bail2dos("Error loading save file %s.", filename);
    }

    char signature[4];
    buildvfs_read(hLFile, &signature, sizeof(signature));
    if (memcmp(&signature, kSaveSig, 4) != 0) {
        bail2dos("Invalid save file signature");
    }

    uint16_t version;
    buildvfs_read(hLFile, &version, 2);
    if (version != kSaveVersion) {
        bail2dos("Invalid save file version");
    }

    StopAllSounds();
    memset(sprite, 0, sizeof(spritetype) * kMaxSprites);
    automapping = 1;

    ClearStatusMessage();

    LoadSave* rover = head.next;
    while (rover != &head)
    {
        rover->Load();
        rover = rover->next;
    }

    buildvfs_close(hLFile);
    hLFile = buildvfs_fd_invalid;

#ifdef USE_STRUCT_TRACKERS
    Bmemset(sectorchanged, 0, sizeof(sectorchanged));
    Bmemset(spritechanged, 0, sizeof(spritechanged));
    Bmemset(wallchanged, 0, sizeof(wallchanged));
#endif

#ifdef USE_OPENGL
    Polymost_prepare_loadboard();
#endif

#ifdef POLYMER
    if (videoGetRenderMode() == REND_POLYMER)
        polymer_loadboard();

    // this light pointer nulling needs to be outside the videoGetRenderMode check
    // because we might be loading the savegame using another renderer but
    // change to Polymer later
    /*
    for (int i = 0; i < kMaxSprites; i++)
    {
        gPolymerLight[i].lightptr = NULL;
        gPolymerLight[i].lightId = -1;
    }
    */
#endif
}

void LoadSave::SaveGame(const char* pzFile)
{
    int nSlot = 3; // TEMP
    char filename[BMAX_PATH];

    sprintf(filename, "save%1d.gam", nSlot);

    hSFile = buildvfs_open_write(filename);
    if (hSFile == buildvfs_fd_invalid) {
        bail2dos("Error creating save file");
    }

    buildvfs_write(hSFile, kSaveSig, 4);
    buildvfs_write(hSFile, &kSaveVersion, 2);

    LoadSave* rover = head.next;
    while (rover != &head)
    {
        rover->Save();
        rover = rover->next;
    }

    buildvfs_close(hSFile);
    hSFile = buildvfs_fd_invalid;
}

void MyLoadSave::Load()
{
    psky_t* pSky = tileSetupSky(0);
    int numsprites = 0;

    Read(&GameStats, sizeof(GameStats));

    Read(&numsectors, sizeof(numsectors));
    Read(&numwalls,   sizeof(numwalls));

    Read(sector, sizeof(sector[0]) * numsectors);
    Read(wall,   sizeof(wall[0])   * numwalls);
    Read(sprite, sizeof(sprite[0]) * kMaxSprites);

    Read(&parallaxtype, sizeof(parallaxtype));
    Read(&showinvisibility, sizeof(showinvisibility));
    Read(&pSky->horizfrac,  sizeof(pSky->horizfrac));
    Read(&pSky->yoffs,  sizeof(pSky->yoffs));
    Read(&pSky->yscale, sizeof(pSky->yscale));
    Read(&parallaxvisibility, sizeof(parallaxvisibility));
    Read(pSky->tileofs, sizeof(pSky->tileofs));
    Read(&pSky->lognumtiles, sizeof(pSky->lognumtiles));

    Read(headspritesect, sizeof(headspritesect));
    Read(headspritestat, sizeof(headspritestat));
    Read(prevspritesect, sizeof(prevspritesect));
    Read(prevspritestat, sizeof(prevspritestat));
    Read(nextspritesect, sizeof(nextspritesect));
    Read(nextspritestat, sizeof(nextspritestat));
    Read(show2dsector, sizeof(show2dsector));
    Read(show2dwall,   sizeof(show2dwall));
    Read(show2dsprite, sizeof(show2dsprite));
    Read(&automapping, sizeof(automapping));
    Read(gotpic,    sizeof(gotpic));
    Read(gotsector, sizeof(gotsector));

    Read(&initx, sizeof(initx));
    Read(&inity, sizeof(inity));
    Read(&initz, sizeof(initz));
    Read(&inita, sizeof(inita));
    Read(&initsect, sizeof(initsect));

    Read(nBodyGunSprite, sizeof(nBodyGunSprite));
    Read(&movefifoend, sizeof(movefifoend));
    Read(&movefifopos, sizeof(movefifopos));
    Read(&nCurBodyGunNum, sizeof(nCurBodyGunNum));

    Read(&nAmbientChannel, sizeof(nAmbientChannel));
    Read(&nStopSound,  sizeof(nStopSound));
    Read(&nStoneSound, sizeof(nStoneSound));
    Read(&nSwitchSound, sizeof(nSwitchSound));
    Read(&nElevSound, sizeof(nElevSound));
    Read(&nCreepyTimer, sizeof(nCreepyTimer));
    Read(&nCreaturesLeft, sizeof(nCreaturesLeft));

    Read(&nClockVal, sizeof(nClockVal));
    Read(&nRedTicks, sizeof(nRedTicks));
    Read(&nAlarmTicks,  sizeof(nAlarmTicks));
    Read(&nButtonColor, sizeof(nButtonColor));
    Read(&nEnergyChan,  sizeof(nEnergyChan));
    Read(&lCountDown,   sizeof(lCountDown));
    Read(&nEnergyTowers, sizeof(nEnergyTowers));
    Read(&moveframes, sizeof(moveframes));
    Read(&flash, sizeof(flash));
    //Read(&localclock, sizeof(localclock));
    Read(&totalmoves, sizeof(totalmoves));
    Read(&tclocks,  sizeof(tclocks));
    Read(&tclocks2, sizeof(tclocks2));
    Read(&ototalclock, sizeof(ototalclock));
    Read(&totalclock,  sizeof(totalclock));

    Read(&nSnakeCam,  sizeof(nSnakeCam));
    Read(&nBestLevel, sizeof(nBestLevel));
    Read(&levelnew,   sizeof(levelnew));
    Read(&levelnum,   sizeof(levelnum));

#ifdef YAX_ENABLE
    Read(&numyaxbunches, sizeof(numyaxbunches));
#endif
    psky_t skyInfo;
    Read(&skyInfo, sizeof(skyInfo));

    *tileSetupSky(0) = skyInfo;
}

void MyLoadSave::Save()
{
    psky_t* pSky = tileSetupSky(0);

    Write(&GameStats, sizeof(GameStats));

    Write(&numsectors, sizeof(numsectors));
    Write(&numwalls,   sizeof(numwalls));

    Write(sector, sizeof(sector[0]) * numsectors);
    Write(wall,   sizeof(wall[0])   * numwalls);
    Write(sprite, sizeof(sprite[0]) * kMaxSprites);

    Write(&parallaxtype, sizeof(parallaxtype));
    Write(&showinvisibility, sizeof(showinvisibility));
    Write(&pSky->horizfrac,  sizeof(pSky->horizfrac));
    Write(&pSky->yoffs,  sizeof(pSky->yoffs));
    Write(&pSky->yscale, sizeof(pSky->yscale));
    Write(&parallaxvisibility, sizeof(parallaxvisibility));
    Write(pSky->tileofs, sizeof(pSky->tileofs));
    Write(&pSky->lognumtiles, sizeof(pSky->lognumtiles));

    Write(headspritesect, sizeof(headspritesect));
    Write(headspritestat, sizeof(headspritestat));
    Write(prevspritesect, sizeof(prevspritesect));
    Write(prevspritestat, sizeof(prevspritestat));
    Write(nextspritesect, sizeof(nextspritesect));
    Write(nextspritestat, sizeof(nextspritestat));
    Write(show2dsector, sizeof(show2dsector));
    Write(show2dwall, sizeof(show2dwall));
    Write(show2dsprite, sizeof(show2dsprite));
    Write(&automapping, sizeof(automapping));
    Write(gotpic,    sizeof(gotpic));
    Write(gotsector, sizeof(gotsector));

    Write(&initx, sizeof(initx));
    Write(&inity, sizeof(inity));
    Write(&initz, sizeof(initz));
    Write(&inita, sizeof(inita));
    Write(&initsect, sizeof(initsect));

    Write(nBodyGunSprite, sizeof(nBodyGunSprite));
    Write(&movefifoend, sizeof(movefifoend));
    Write(&movefifopos, sizeof(movefifopos));
    Write(&nCurBodyGunNum, sizeof(nCurBodyGunNum));

    Write(&nAmbientChannel, sizeof(nAmbientChannel));
    Write(&nStopSound,   sizeof(nStopSound));
    Write(&nStoneSound,  sizeof(nStoneSound));
    Write(&nSwitchSound, sizeof(nSwitchSound));
    Write(&nElevSound, sizeof(nElevSound));
    Write(&nCreepyTimer, sizeof(nCreepyTimer));
    Write(&nCreaturesLeft, sizeof(nCreaturesLeft));

    Write(&nClockVal, sizeof(nClockVal));
    Write(&nRedTicks, sizeof(nRedTicks));
    Write(&nAlarmTicks,  sizeof(nAlarmTicks));
    Write(&nButtonColor, sizeof(nButtonColor));
    Write(&nEnergyChan,  sizeof(nEnergyChan));
    Write(&lCountDown, sizeof(lCountDown));
    Write(&nEnergyTowers, sizeof(nEnergyTowers));
    Write(&moveframes, sizeof(moveframes));
    Write(&flash, sizeof(flash));
    //Write(&localclock, sizeof(localclock));
    Write(&totalmoves, sizeof(totalmoves));
    Write(&tclocks,  sizeof(tclocks));
    Write(&tclocks2, sizeof(tclocks2));
    Write(&ototalclock, sizeof(ototalclock));
    Write(&totalclock,  sizeof(totalclock));

    Write(&nSnakeCam,  sizeof(nSnakeCam));
    Write(&nBestLevel, sizeof(nBestLevel));
    Write(&levelnew,   sizeof(levelnew));
    Write(&levelnum,   sizeof(levelnum));

#ifdef YAX_ENABLE
    Write(&numyaxbunches, sizeof(numyaxbunches));
#endif
    psky_t skyInfo = *tileSetupSky(0);
    Write(&skyInfo, sizeof(skyInfo));
}

static MyLoadSave* myLoadSave;

/*
AiFunc aiFunctions[kFuncMax] = {
    FuncElev,
    FuncWallFace,
    FuncSlide,

    FuncCreatureChunk,
    FuncObject,
    FuncTrap,
    FuncEnergyBlock,
    FuncSpark,
};
*/

void LoadSaveSetup()
{
    void ObjectLoadSaveConstruct();
    void SwitchLoadSaveConstruct();
    void AnubisLoadSaveConstruct();
    void PlayerLoadSaveConstruct();
    void SpiderLoadSaveConstruct();
    void WaspLoadSaveConstruct();
    void RunListLoadSaveConstruct();
    void RatLoadSaveConstruct();
    void BubbleLoadSaveConstruct();
    void MummyLoadSaveConstruct();
    void LionLoadSaveConstruct();
    void FishLoadSaveConstruct();
    void LavaDudeLoadSaveConstruct();
    void GunLoadSaveConstruct();
    void StatusLoadSaveConstruct();
    void ViewLoadSaveConstruct();
    void AnimsLoadSaveConstruct();
    void BulletLoadSaveConstruct();
    void GrenadeLoadSaveConstruct();
    void RandomLoadSaveConstruct();
    void MoveLoadSaveConstruct();
    void ItemsLoadSaveConstruct();
    void RaLoadSaveConstruct();
    void RexLoadSaveConstruct();
    void RoachLoadSaveConstruct();
    void ScorpLoadSaveConstruct();
    void SetLoadSaveConstruct();
    void QueenLoadSaveConstruct();
    void SnakeLoadSaveConstruct();

    void LightingLoadSaveConstruct();

    myLoadSave = new MyLoadSave();

    ObjectLoadSaveConstruct();
    SwitchLoadSaveConstruct();
    MoveLoadSaveConstruct();
    LightingLoadSaveConstruct();

    RandomLoadSaveConstruct();
    AnimsLoadSaveConstruct();
    StatusLoadSaveConstruct();
    ViewLoadSaveConstruct();
    ItemsLoadSaveConstruct();

    PlayerLoadSaveConstruct();
    GunLoadSaveConstruct();
    BulletLoadSaveConstruct();
    GrenadeLoadSaveConstruct();

    AnubisLoadSaveConstruct();
    SpiderLoadSaveConstruct();
    WaspLoadSaveConstruct();
    RunListLoadSaveConstruct();
    RatLoadSaveConstruct();
    MummyLoadSaveConstruct();
    LionLoadSaveConstruct();
    FishLoadSaveConstruct();
    LavaDudeLoadSaveConstruct();
    RexLoadSaveConstruct();
    RoachLoadSaveConstruct();
    ScorpLoadSaveConstruct();
    SetLoadSaveConstruct();
    QueenLoadSaveConstruct();
    RaLoadSaveConstruct();
    SnakeLoadSaveConstruct();
    BubbleLoadSaveConstruct();
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 __save_h__
#define __save_h__

#include "engine.h"

class LoadSave
{
public:
    static LoadSave head;
    static buildvfs_fd hSFile;
    static buildvfs_fd hLFile;
    LoadSave* prev;
    LoadSave* next;
    LoadSave()
    {
        prev = head.prev;
        prev->next = this;
        next = &head;
        next->prev = this;
    }
    LoadSave(int dummy)
    {
        UNREFERENCED_PARAMETER(dummy);
        next = prev = this;
    }
    //~LoadSave() { }
    virtual void Save();
    virtual void Load();
    void Read(void*, int);
    void Write(void*, int);
    static void LoadGame(const char*);
    static void SaveGame(const char*);
};

void LoadSaveSetup();

int savegame(int nSlot);
int loadgame(int nSlot);

#endif

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 "scorp.h"
#include "engine.h"
#include "runlist.h"
#include "exhumed.h"
#include "move.h"
#include "sequence.h"
#include "sound.h"
#include "random.h"
#include "trigdat.h"
#include "bullet.h"
#include "spider.h"
#include "save.h"
#include <assert.h>

/*
    Selkis Boss AI code
*/

#define kMaxScorpions   5

struct Scorpion
{
    short nHealth;
    short nFrame;
    short nAction;
    short nSprite;
    short nTarget;
    short f;
    short g;
    int8_t h;
    int8_t i;
};

static actionSeq ActionSeq[] = {
    {0,  0},
    {8,  0},
    {29, 0},
    {19, 0},
    {45, 1},
    {46, 1},
    {47, 1},
    {48, 1},
    {50, 1},
    {53, 1}
};

short ScorpCount = -1;

Scorpion scorpion[kMaxScorpions];
short ScorpChan[kMaxScorpions];

void InitScorp()
{
    ScorpCount = kMaxScorpions;
}

int BuildScorp(short nSprite, int x, int y, int z, short nSector, short nAngle, int nChannel)
{
    ScorpCount--;

    short nScorp = ScorpCount;
    if (nScorp < 0) {
        return -1;
    }

    if (nSprite == -1)
    {
        nSprite = insertsprite(nSector, 122);
    }
    else
    {
        changespritestat(nSprite, 122);

        x = sprite[nSprite].x;
        y = sprite[nSprite].y;
        z = sector[sprite[nSprite].sectnum].floorz;
        nAngle = sprite[nSprite].ang;
    }

    assert(nSprite >= 0 && nSprite < kMaxSprites);

    sprite[nSprite].x = x;
    sprite[nSprite].y = y;
    sprite[nSprite].z = z;
    sprite[nSprite].cstat = 0x101;
    sprite[nSprite].clipdist = 70;
    sprite[nSprite].shade = -12;
    sprite[nSprite].xrepeat = 80;
    sprite[nSprite].yrepeat = 80;
    sprite[nSprite].picnum = 1;
    sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal;
    sprite[nSprite].xoffset = 0;
    sprite[nSprite].yoffset = 0;
    sprite[nSprite].ang = nAngle;
    sprite[nSprite].xvel = 0;
    sprite[nSprite].yvel = 0;
    sprite[nSprite].zvel = 0;
    sprite[nSprite].lotag = runlist_HeadRun() + 1;
    sprite[nSprite].extra = -1;
    sprite[nSprite].hitag = 0;

//	GrabTimeSlot(3);

    scorpion[nScorp].nHealth = 20000;
    scorpion[nScorp].nFrame = 0;
    scorpion[nScorp].nAction = 0;
    scorpion[nScorp].nSprite = nSprite;
    scorpion[nScorp].nTarget = -1;
    scorpion[nScorp].g = 0;
    scorpion[nScorp].i = 1;

    ScorpChan[nScorp] = nChannel;

    sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nScorp, kRunScorp);
    scorpion[nScorp].f = runlist_AddRunRec(NewRun, nScorp, kRunScorp);

    nCreaturesLeft++;

    return nScorp;
}

void FuncScorp(int a, int nDamage, int nRun)
{
    short nScorp = RunData[nRun].nVal;
    assert(nScorp >= 0 && nScorp < kMaxScorpions);

    short nSprite = scorpion[nScorp].nSprite;
    short nAction = scorpion[nScorp].nAction;

    bool bVal = false;

    short nTarget = -1;

    int nMessage = a & kMessageMask;

    switch (nMessage)
    {
        default:
        {
            DebugOut("unknown msg %d for Scorp\n", nMessage);
            return;
        }

        case 0x90000:
        {
            seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqScorp] + ActionSeq[nAction].a, scorpion[nScorp].nFrame, ActionSeq[nAction].b);
            return;
        }

        case 0xA0000:
        {
            nDamage = runlist_CheckRadialDamage(nSprite);
            if (!nDamage) {
                return;
            }
            // else fall through to case 0x80000
            fallthrough__;
        }

        case 0x80000:
        {
            if (scorpion[nScorp].nHealth <= 0) {
                return;
            }

            scorpion[nScorp].nHealth -= nDamage;

            if (scorpion[nScorp].nHealth <= 0)
            {
                scorpion[nScorp].nHealth = 0;
                scorpion[nScorp].nAction = 4;
                scorpion[nScorp].nFrame = 0;
                scorpion[nScorp].g = 10;

                sprite[nSprite].xvel = 0;
                sprite[nSprite].yvel = 0;
                sprite[nSprite].zvel = 0;
                sprite[nSprite].cstat &= 0xFEFE;

                nCreaturesLeft--;
                return;
            }
            else
            {
                nTarget = a & 0xFFFF;

                if (nTarget >= 0)
                {
                    if (sprite[nSprite].statnum == 100 || (sprite[nSprite].statnum < 199 && !RandomSize(5)))
                    {
                        scorpion[nScorp].nTarget = nTarget;
                    }
                }

                if (!RandomSize(5))
                {
                    scorpion[nScorp].nAction = RandomSize(2) + 4;
                    scorpion[nScorp].nFrame = 0;
                    return;
                }

                if (RandomSize(2)) {
                    return;
                }

                D3PlayFX(StaticSound[kSoundScorpionICU], nSprite);

                goto FS_Pink_A;
            }
        }

        case 0x20000:
        {
            if (scorpion[nScorp].nHealth) {
                Gravity(nSprite);
            }

            int nSeq = SeqOffsets[kSeqScorp] + ActionSeq[nAction].a;

            sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, scorpion[nScorp].nFrame);
            seq_MoveSequence(nSprite, nSeq, scorpion[nScorp].nFrame);

            scorpion[nScorp].nFrame++;

            if (scorpion[nScorp].nFrame >= SeqSize[nSeq])
            {
                scorpion[nScorp].nFrame = 0;
                bVal = true;
            }

            int nFlag = FrameFlag[SeqBase[nSeq] + scorpion[nScorp].nFrame];
            nTarget = scorpion[nScorp].nTarget;

            switch (nAction)
            {
                default:
                    return;

                case 0:
                {
                    if (scorpion[nScorp].g > 0)
                    {
                        scorpion[nScorp].g--;
                        return;
                    }

                    if ((nScorp & 0x1F) == (totalmoves & 0x1F))
                    {
                        if (nTarget < 0)
                        {
                            nTarget = FindPlayer(nSprite, 500);

                            if (nTarget >= 0)
                            {
                                D3PlayFX(StaticSound[kSoundScorpionICU], nSprite);

                                scorpion[nScorp].nFrame = 0;
                                sprite[nSprite].xvel = Cos(sprite[nSprite].ang);
                                sprite[nSprite].yvel = Sin(sprite[nSprite].ang);

                                scorpion[nScorp].nAction = 1;
                                scorpion[nScorp].nTarget = nTarget;
                            }
                        }
                    }

                    return;
                }

                case 1:
                {
                    scorpion[nScorp].i--;

                    if (scorpion[nScorp].i <= 0)
                    {
                        scorpion[nScorp].i = RandomSize(5);
                        // GOTO FS_Pink_A:
                        goto FS_Pink_A;
                    }
                    else
                    {
                        int nMov = MoveCreatureWithCaution(nSprite);
                        if ((nMov & 0xC000) == 0xC000)
                        {
                            if (nTarget == (nMov & 0x3FFF))
                            {
                                int nAngle = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y);
                                if (AngleDiff(sprite[nSprite].ang, nAngle) < 64)
                                {
                                    scorpion[nScorp].nAction = 2;
                                    scorpion[nScorp].nFrame = 0;
                                }

                                goto FS_Red;
                            }
                            else
                            {
                                goto FS_Pink_A;
                            }
                        }
                        else if ((nMov & 0xC000) == 0x8000)
                        {
                            goto FS_Pink_A;
                        }
                        else
                        {
                            goto FS_Pink_B;
                        }
                    }
                }

                case 2:
                {
                    if (nTarget == -1)
                    {
                        scorpion[nScorp].nAction = 0;
                        scorpion[nScorp].g = 5;
                    }
                    else
                    {
                        if (PlotCourseToSprite(nSprite, nTarget) >= 768)
                        {
                            scorpion[nScorp].nAction = 1;
                        }
                        else if (nFlag & 0x80)
                        {
                            runlist_DamageEnemy(nTarget, nSprite, 7);
                        }
                    }

                    goto FS_Red;
                }

                case 3:
                {
                    if (bVal)
                    {
                        scorpion[nScorp].h--;
                        if (scorpion[nScorp].h <= 0)
                        {
                            scorpion[nScorp].nAction = 1;

                            sprite[nSprite].xvel = Cos(sprite[nSprite].ang);
                            sprite[nSprite].yvel = Sin(sprite[nSprite].ang);

                            scorpion[nScorp].nFrame = 0;
                            return;
                        }
                    }

                    if (!(nFlag & 0x80)) {
                        return;
                    }

                    int nBullet = BuildBullet(nSprite, 16, 0, 0, -1, sprite[nSprite].ang, nTarget + 10000, 1) & 0xFFFF;
                    int nBulletSprite = GetBulletSprite(nBullet);

                    if (nBulletSprite > -1)
                    {
                        PlotCourseToSprite(nBulletSprite, nTarget);
                    }

                    return;
                }

                case 4:
                case 5:
                case 6:
                case 7:
                {
                    if (!bVal) {
                        return;
                    }

                    if (scorpion[nScorp].nHealth > 0)
                    {
                        scorpion[nScorp].nAction = 1;
                        scorpion[nScorp].nFrame = 0;
                        scorpion[nScorp].g = 0;
                        return;
                    }

                    scorpion[nScorp].g--;
                    if (scorpion[nScorp].g <= 0)
                    {
                        scorpion[nScorp].nAction = 8;
                    }
                    else
                    {
                        scorpion[nScorp].nAction = RandomBit() + 6;
                    }

                    return;
                }

                case 8:
                {
                    if (bVal)
                    {
                        scorpion[nScorp].nAction++; // set to 9
                        scorpion[nScorp].nFrame = 0;

                        runlist_ChangeChannel(ScorpChan[nScorp], 1);
                        return;
                    }

                    int nSpider = BuildSpider(-1, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, sprite[nSprite].ang);
                    if (nSpider != -1)
                    {
                        int nSpiderSprite = GetSpiderSprite(nSpider);

                        sprite[nSpiderSprite].ang = RandomSize(11);

                        int nVel = RandomSize(5) + 1;

                        sprite[nSpiderSprite].xvel = (Cos(sprite[nSpiderSprite].ang) >> 8) * nVel;
                        sprite[nSpiderSprite].yvel = (Sin(sprite[nSpiderSprite].ang) >> 8) * nVel;
                        sprite[nSpiderSprite].zvel = (-(RandomSize(5) + 3)) << 8;
                    }

                    return;
                }

                case 9:
                {
                    sprite[nSprite].cstat &= 0xFEFE;

                    if (bVal)
                    {
                        runlist_SubRunRec(scorpion[nScorp].f);
                        runlist_DoSubRunRec(sprite[nSprite].owner);
                        runlist_FreeRun(sprite[nSprite].lotag - 1);

                        mydeletesprite(nSprite);
                    }

                    return;
                }
            }

            break;
        }
    }

FS_Pink_A:
    PlotCourseToSprite(nSprite, nTarget);
    sprite[nSprite].ang += RandomSize(7) - 63;
    sprite[nSprite].ang &= kAngleMask;

    sprite[nSprite].xvel = Cos(sprite[nSprite].ang);
    sprite[nSprite].yvel = Sin(sprite[nSprite].ang);

FS_Pink_B:
    if (scorpion[nScorp].g)
    {
        scorpion[nScorp].g--;
    }
    else
    {
        scorpion[nScorp].g = 45;

        if (cansee(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - GetSpriteHeight(nSprite), sprite[nSprite].sectnum,
            sprite[nTarget].x, sprite[nTarget].y, sprite[nTarget].z - GetSpriteHeight(nTarget), sprite[nTarget].sectnum))
        {
            sprite[nSprite].xvel = 0;
            sprite[nSprite].yvel = 0;
            sprite[nSprite].ang = GetMyAngle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y);

            scorpion[nScorp].h = RandomSize(2) + RandomSize(3);

            if (!scorpion[nScorp].h) {
                scorpion[nScorp].g = RandomSize(5);
            }
            else
            {
                scorpion[nScorp].nAction = 3;
                scorpion[nScorp].nFrame = 0;
            }
        }
    }

FS_Red:
    if (!nAction || nTarget == -1) {
        return;
    }

    if (!(sprite[nTarget].cstat & 0x101))
    {
        scorpion[nScorp].nAction = 0;
        scorpion[nScorp].nFrame = 0;
        scorpion[nScorp].g = 30;
        scorpion[nScorp].nTarget = -1;

        sprite[nSprite].xvel = 0;
        sprite[nSprite].yvel = 0;
    }
}

class ScorpLoadSave : public LoadSave
{
public:
    virtual void Load();
    virtual void Save();
};

void ScorpLoadSave::Load()
{
    Read(&ScorpCount, sizeof(ScorpCount));
    Read(&scorpion[ScorpCount], sizeof(Scorpion) * (kMaxScorpions - ScorpCount));
    Read(ScorpChan, sizeof(ScorpChan));
}

void ScorpLoadSave::Save()
{
    Write(&ScorpCount, sizeof(ScorpCount));
    Write(&scorpion[ScorpCount], sizeof(Scorpion) * (kMaxScorpions - ScorpCount));
    Write(ScorpChan, sizeof(ScorpChan));
}

static ScorpLoadSave* myLoadSave;

void ScorpLoadSaveConstruct()
{
    myLoadSave = new ScorpLoadSave();
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 __scorp_h__
#define __scorp_h__

#include "compat.h"
#include "aistuff.h"

/*
    Selkis Boss AI code
*/

void InitScorp();
int BuildScorp(short nSprite, int x, int y, int z, short nSector, short nAngle, int nChannel);
void FuncScorp(int, int, int);

#endif


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 "typedefs.h"
#include "sequence.h"
#include "engine.h"
#include "exhumed.h"
#include "sound.h"
#include "player.h"
#include "trigdat.h"
#include "move.h"
#include "view.h"
#include "init.h"
#include "light.h"
#include "baselayer.h"
#ifndef __WATCOMC__
#include <cstring>
#include <cstdio> // for printf
#else
#include <string.h>
#include <stdio.h>
#endif

// TEMP
#include <assert.h>

#define kMaxSequences	4096
#define kMaxSEQFiles	78
#define kMaxSEQFrames	18000
#define kMaxSEQChunks	21000

short sequences = 0;
short frames = 0;
short chunks = 0;
short nPilotLightFrame;
short nPilotLightCount;

short nPilotLightBase;
short laststatustile;

short nShadowWidth = 1;
short nFlameHeight = 1;

short SeqBase[kMaxSequences];
short SeqSize[kMaxSequences];
short SeqFlag[kMaxSequences];

short FrameSound[kMaxSEQFrames];
short FrameSize[kMaxSEQFrames];
short FrameBase[kMaxSEQFrames];
short FrameFlag[kMaxSEQFrames];

short ChunkYpos[kMaxSEQChunks];
short ChunkXpos[kMaxSEQChunks];
short ChunkPict[kMaxSEQChunks];
short ChunkFlag[kMaxSEQChunks];

const char *SeqNames[kMaxSEQFiles] =
{
  "rothands",
  "sword",
  "pistol",
  "m_60",
  "flamer", // 4
  "grenade",
  "cobra",
  "bonesaw",
  "scramble",
  "glove",
  "mummy", // 10
  "skull",
  "poof",
  "kapow",
  "fireball",
  "bubble",
  "spider", // 16
  "anubis",
  "anuball",
  "fish",
  "snakehed", // 20?
  "snakbody",
  "wasp",
  "cobrapow",
  "scorp",
  "joe", // 25
  "status",
  "dead",
  "deadex",
  "anupoof",
  "skulpoof", // 30
  "bullet",
  "shadow",
  "grenroll",
  "grenboom",
  "splash",
  "grenpow",
  "skulstrt",
  "firepoof",
  "bloodhit",
  "lion", // 40
  "items",
  "lavag", // 42
  "lsplash",
  "lavashot",
  "smokebal",
  "firepot",
  "rex",
  "set", // 48
  "queen",
  "roach", // 50
  "hawk",
  "setghost",
  "setgblow",
  "bizztail",
  "bizzpoof",
  "queenegg",
  "roacshot",
  "backgrnd",
  "screens", // 59
  "arrow",
  "fonts",
  "drips",
  "firetrap",
  "magic2",
  "creepy",
  "slider", // 66
  "ravolt",
  "eyehit",
  "font2", // 69
  "seebubbl",
  "blood",
  "drum",
  "poof2",
  "deadbrn",
  "grenbubb",
  "rochfire",
  "rat"
};

short SeqOffsets[kMaxSEQFiles];

int seq_ReadSequence(const char *seqName)
{
    int i;
    char buffer[BMAX_PATH];
    buffer[0] = '\0';

    strcat(buffer, seqName);
    strcat(buffer, ".seq");

    int hFile = kopen4loadfrommod(buffer, 0);
    if (hFile == -1)
    {
        initprintf("Unable to open '%s'!\n", buffer);
        kclose(hFile);
        return 0;
    }

    short tag;
    kread(hFile, &tag, sizeof(tag));
#if B_BIG_ENDIAN == 1
    tag = B_LITTLE16(tag);
#endif
    if (tag < 'HI' || tag > 'HI' && tag != 'SD')
    {
        initprintf("Unsupported sequence version!\n");
        kclose(hFile);
        return 0;
    }

    short centerx, centery; // TODO - are global vars?
    short nSeqs;
    kread(hFile, &centerx, sizeof(centerx));
    kread(hFile, &centery, sizeof(centery));
    kread(hFile, &nSeqs, sizeof(nSeqs));
#if B_BIG_ENDIAN == 1
    centerx = B_LITTLE16(centerx);
    centery = B_LITTLE16(centery);
    nSeqs = B_LITTLE16(nSeqs);
#endif

    if (nSeqs <= 0 || sequences + nSeqs >= kMaxSequences)
    {
        if (nSeqs < 0)
        {
            initprintf("Invalid sequence count!\n");
            kclose(hFile);
            return 0;
        }
        else {
            bail2dos("Not enough sequences available!  Increase array!\n");
        }
    }

    kread(hFile, &SeqBase[sequences], nSeqs * sizeof(SeqBase[0]));
    kread(hFile, &SeqSize[sequences], nSeqs * sizeof(SeqSize[0]));
    kread(hFile, &SeqFlag[sequences], nSeqs * sizeof(SeqFlag[0]));

    for (i = 0; i < nSeqs; i++)
    {
#if B_BIG_ENDIAN == 1
        SeqBase[sequences + i] = B_LITTLE16(SeqBase[sequences + i]);
        SeqSize[sequences + i] = B_LITTLE16(SeqSize[sequences + i]);
        SeqFlag[sequences + i] = B_LITTLE16(SeqFlag[sequences + i]);
#endif
        SeqBase[sequences + i] += frames;
    }

    short vdi = frames;

    int16_t nFrames;
    kread(hFile, &nFrames, sizeof(nFrames));
#if B_BIG_ENDIAN == 1
    nFrames = B_LITTLE16(nFrames);
#endif

    if (nFrames <= 0 || frames + nFrames >= kMaxSEQFrames)
    {
        if (nFrames < 0 )
        {
            initprintf("Invalid frame count!\n");
            kclose(hFile);
            return 0;
        }
        else {
            bail2dos("Not enough frames available!  Increase FRAMEMAX!\n");
        }
    }

    kread(hFile, &FrameBase[frames], nFrames * sizeof(FrameBase[0]));
    kread(hFile, &FrameSize[frames], nFrames * sizeof(FrameSize[0]));
    kread(hFile, &FrameFlag[frames], nFrames * sizeof(FrameFlag[0]));
    memset(&FrameSound[frames], -1,  nFrames * sizeof(FrameSound[0]));

    for (i = 0; i < nFrames; i++)
    {
#if B_BIG_ENDIAN == 1
        FrameBase[frames + i] = B_LITTLE16(FrameBase[frames + i]);
        FrameSize[frames + i] = B_LITTLE16(FrameSize[frames + i]);
        FrameFlag[frames + i] = B_LITTLE16(FrameFlag[frames + i]);
#endif
        FrameBase[frames + i] += chunks;
    }

    int16_t nChunks;
    kread(hFile, &nChunks, sizeof(nChunks));
#if B_BIG_ENDIAN == 1
    nChunks = B_LITTLE16(nChunks);
#endif

    if (nChunks < 0 || chunks + nChunks >= kMaxSEQChunks)
    {
        if (nChunks < 0 )
        {
            initprintf("Invalid chunk count!\n");
            kclose(hFile);
            return 0;
        }
        else {
            bail2dos("Not enough chunks available!  Increase CHUNKMAX!\n");
        }
    }

    kread(hFile, &ChunkXpos[chunks], nChunks * sizeof(ChunkXpos[0]));
    kread(hFile, &ChunkYpos[chunks], nChunks * sizeof(ChunkYpos[0]));
    kread(hFile, &ChunkPict[chunks], nChunks * sizeof(ChunkPict[0]));
    kread(hFile, &ChunkFlag[chunks], nChunks * sizeof(ChunkFlag[0]));

    for (i = 0; i < nChunks; i++)
    {
#if B_BIG_ENDIAN == 1
        ChunkXpos[chunks + i] = B_LITTLE16(ChunkXpos[chunks + i]);
        ChunkYpos[chunks + i] = B_LITTLE16(ChunkYpos[chunks + i]);
        ChunkPict[chunks + i] = B_LITTLE16(ChunkPict[chunks + i]);
        ChunkFlag[chunks + i] = B_LITTLE16(ChunkFlag[chunks + i]);
#endif
        ChunkXpos[chunks + i] -= centerx;
        ChunkYpos[chunks + i] -= centery;

        short nTile = ChunkPict[chunks + i];
        if (waloff[nTile] == 0)
        {
            tileLoad(nTile);
        }
    }

    sequences += nSeqs;
    FrameBase[frames + nFrames] = chunks + nChunks;
    frames += nFrames;
    SeqBase[sequences] = frames;
    chunks += nChunks;

    if (tag == 'SD')
    {
        short var_20;
        kread(hFile, &var_20, sizeof(var_20));
#if B_BIG_ENDIAN == 1
        var_20 = B_LITTLE16(var_20);
#endif

        for (i = 0; i < var_20; i++)
        {
            kread(hFile, &buffer[i * 10], 8);
        }

        short var_24;
        kread(hFile, &var_24, sizeof(var_24));
#if B_BIG_ENDIAN == 1
        var_24 = B_LITTLE16(var_24);
#endif

        for (i = 0; i < var_24; i++)
        {
            short var_28, var_2C;
            kread(hFile, &var_28, sizeof(var_28));
            kread(hFile, &var_2C, sizeof(var_2C));
#if B_BIG_ENDIAN == 1
            var_28 = B_LITTLE16(var_28);
            var_2C = B_LITTLE16(var_2C);
#endif

            int hSound = LoadSound(&buffer[(var_2C&0x1FF)*10]);

            FrameSound[vdi + var_28] = hSound | (var_2C & 0xFE00);
        }
    }

    kclose(hFile);
    return nSeqs;
}

int seq_GetFirstSeqPicnum(int nSeq)
{
    int i = SeqOffsets[nSeq];
    i = SeqBase[i];
    i = FrameBase[i];
    i = ChunkPict[i];

    return i;
}

void seq_LoadSequences()
{
    int i;

    for (i = 0; i < kMaxSEQFiles; i++)
    {
        SeqOffsets[i] = sequences;

        if (seq_ReadSequence(SeqNames[i]) == 0) {
            initprintf("Error loading '%s'\n", SeqNames[i]);
        }
    }

    nShadowPic = seq_GetFirstSeqPicnum(kSeqShadow);
    nShadowWidth = tilesiz[nShadowPic].x;

    nFlameHeight = tilesiz[seq_GetFirstSeqPicnum(kSeqFirePoof)].y;

    nBackgroundPic = seq_GetFirstSeqPicnum(kSeqBackgrnd);

    nPilotLightBase  = SeqBase[SeqOffsets[kSeqFlamer] + 3];
    nPilotLightCount = SeqSize[SeqOffsets[kSeqFlamer] + 3];
    nPilotLightFrame = 0;

    nFontFirstChar = seq_GetFirstSeqPicnum(kSeqFont2);

    short nSize = SeqSize[SeqOffsets[kSeqFont2]];

    for (i = 0; i < nSize; i++)
    {
        picanm[nFontFirstChar + i].xofs = 0;
        picanm[nFontFirstChar + i].yofs = 0;
    }
}

void seq_DrawStatusSequence(short nSequence, uint16_t edx, short ebx)
{
    edx += SeqBase[nSequence];

    short nFrameBase = FrameBase[edx];
    int16_t nFrameSize = FrameSize[edx];

    int const nPal = RemapPLU(kPalNormal);

    while (1)
    {
        nFrameSize--;
        if (nFrameSize < 0)
            break;

        uint8_t nStat = 1; // (thex, they) is middle

        laststatusx = ChunkXpos[nFrameBase] + 160;
        laststatusy = ChunkYpos[nFrameBase] + 100 + ebx;

        short chunkFlag = ChunkFlag[nFrameBase];

        if (chunkFlag & 1) {
            nStat = 0x9; // (thex, they) is middle, and x-flipped
        }

        if (chunkFlag & 2) {
            nStat |= 0x10; // y-flipped
        }

        laststatustile = ChunkPict[nFrameBase];

        if (bHiRes) {
            nStat |= 0x2; // scale and clip to viewing window
        }

        overwritesprite(laststatusx, laststatusy, laststatustile, 0, nStat, nPal);
        nFrameBase++;
    }
}

short seq_GetFrameFlag(short val, short nFrame)
{
    return FrameFlag[SeqBase[val] + nFrame];
}

void seq_DrawPilotLightSeq(int xOffset, int yOffset)
{
    short nSect = nPlayerViewSect[nLocalPlayer];

    if (!(SectFlag[nSect] & kSectUnderwater))
    {
        short nFrame = nPilotLightBase + nPilotLightFrame;
        short nFrameBase = FrameBase[nFrame];
        short nFrameSize = FrameSize[nFrame];

        while (1)
        {
            nFrameSize--;
            if (nFrameSize < 0)
                return;

            short nTile = ChunkPict[nFrameBase];
            int x = ChunkXpos[nFrameBase] + (160 + xOffset);
            int y = ChunkYpos[nFrameBase] + (100 + yOffset);

            rotatesprite(x << 16, y << 16, 0x10000, (-2 * fix16_to_int(nPlayerDAng)) & kAngleMask, nTile, -127, 1, 2, windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y);
            nFrameBase++;
        }
    }
}

/*
    6 parameters

    arg0 - shade?

*/

int seq_DrawGunSequence(int nSeqOffset, short dx, int xOffs, int yOffs, int nShade, int nPal)
{
    short nFrame = SeqBase[nSeqOffset] + dx;
    short nFrameBase = FrameBase[nFrame];
    short nFrameSize = FrameSize[nFrame];
    short frameFlag = FrameFlag[nFrame];

    while (1)
    {
        nFrameSize--;
        if (nFrameSize < 0)
            break;

        uint8_t nStat = 3;
        int x = ChunkXpos[nFrameBase] + 160;
        int y = ChunkYpos[nFrameBase] + 100;

        if (ChunkFlag[nFrameBase] & 1) {
            nStat = 11;
        }

        if (ChunkFlag[nFrameBase] & 2) {
            nStat |= 0x10;
        }

        short nTile = ChunkPict[nFrameBase];

        if (frameFlag & 4) {
            nShade = -100;
        }

        if (nPlayerInvisible[nLocalPlayer]) {
            nStat |= 0x4;
        }

        overwritesprite(x + xOffs, y + yOffs, nTile, nShade, nStat, nPal);
        nFrameBase++;
    }

    return frameFlag;
}

int seq_GetFrameSound(int val, int edx)
{
    return FrameSound[SeqBase[val] + edx];
}

void seq_MoveSequence(short nSprite, short nSeq, short bx)
{
    assert(nSeq >= 0); // TEMP

    int nSound = FrameSound[SeqBase[nSeq] + bx];
    if (nSound == -1) {
        return;
    }

    if (nSprite > -1) {
        D3PlayFX(nSound, nSprite);
    }
    else {
        PlayLocalSound(nSound, 0);
    }
}

int seq_GetSeqPicnum2(short nSeq, short nFrame)
{
    short nBase = FrameBase[SeqBase[nSeq] + nFrame];
    return ChunkPict[nBase];
}

int seq_GetSeqPicnum(short nSeq, short edx, short ebx)
{
    edx += SeqOffsets[nSeq];
    ebx += SeqBase[edx];
    short c = FrameBase[ebx];

    return ChunkPict[c];
}

int seq_PlotArrowSequence(short nSprite, short nSeq, int nVal)
{
    tspriteptr_t pTSprite = &tsprite[nSprite];
    int nAngle = GetMyAngle(nCamerax - pTSprite->x, nCameray - pTSprite->y);

    int nSeqOffset = ((((pTSprite->ang + 512) - nAngle) + 128) & kAngleMask) >> 8;

    short nFrame = SeqBase[nSeqOffset + nSeq] + nVal;

    short nFrameBase = FrameBase[nFrame];
    short nFrameSize = FrameSize[nFrame];

    uint8_t nShade = pTSprite->shade;
    short nStat = pTSprite->cstat;

    nStat |= 0x80;

    if (nSeqOffset & 3) {
        nStat |= 0x18;
    }
    else {
        nStat &= 0x0E7;
    }

    if (FrameFlag[nFrame] & 4) {
        nShade -= 100;
    }

    pTSprite->cstat = nStat;
    pTSprite->shade = nShade;
    pTSprite->statnum = nFrameSize;

    if (ChunkFlag[nFrameBase] & 1)
    {
        pTSprite->xoffset = ChunkXpos[nFrameBase];
        pTSprite->cstat |= 4;
    }
    else
    {
        pTSprite->xoffset = -ChunkXpos[nFrameBase];
    }

    pTSprite->yoffset = -ChunkYpos[nFrameBase];
    pTSprite->picnum = ChunkPict[nFrameBase];

    return ChunkPict[nFrameBase];
}

int seq_PlotSequence(short nSprite, short edx, short nFrame, short ecx)
{
    tspriteptr_t pTSprite = &tsprite[nSprite];
    int nAngle = GetMyAngle(nCamerax - pTSprite->x, nCameray - pTSprite->y);

    int val;

    if (ecx & 1)
    {
        val = 0;
    }
    else
    {
        val = (((pTSprite->ang - nAngle) + 128) & kAngleMask) >> 8;
    }

    int eax = SeqBase[edx] + nFrame;
    int edi = SeqBase[edx + val] + nFrame;

    short nBase = FrameBase[edi];
    short nSize = FrameSize[edi];

    int8_t shade = pTSprite->shade;

    if (FrameFlag[eax] & 4)
    {
        shade -= 100;
    }

    short nPict = ChunkPict[nBase];

    if (ecx & 0x100)
    {
        edx = -3;
    }
    else
    {
        edx = 100;
    }

    int esi = nSize + 1;
    esi += edx;

    int var_14 = edx + 1;
    short nOwner = pTSprite->owner;

    while (1)
    {
        esi--;
        nSize--;

        if (esi < var_14) {
            break;
        }

        tspriteptr_t tsp = &tsprite[spritesortcnt++];
        tsp->x       = pTSprite->x;
        tsp->y       = pTSprite->y;
        tsp->z       = pTSprite->z;
        tsp->shade   = shade;
        tsp->pal     = pTSprite->pal;
        tsp->xrepeat = pTSprite->xrepeat;
        tsp->yrepeat = pTSprite->yrepeat;
        tsp->ang     = pTSprite->ang;
        tsp->owner   = pTSprite->owner;
        tsp->sectnum = pTSprite->sectnum;
        tsp->cstat   = pTSprite->cstat |= 0x80;
        tsp->statnum = esi;

        if (ChunkFlag[nBase] & 1)
        {
            tsp->xoffset = ChunkXpos[nBase];
            tsp->cstat |= 4; // x-flipped
        }
        else
        {
            tsp->xoffset = -ChunkXpos[nBase];
        }

        tsp->yoffset = -ChunkYpos[nBase];
        tsp->picnum = ChunkPict[nBase];

        nBase++;
    }

    if (!(pTSprite->cstat & 0x101) || (sprite[nOwner].statnum == 100 && nNetPlayerCount))
    {
        pTSprite->owner = -1;
    }
    else
    {
        short nSector = pTSprite->sectnum;
        int nFloorZ = sector[nSector].floorz;

        if (nFloorZ <= eyelevel[nLocalPlayer] + initz) {
            pTSprite->owner = -1;
        }
        else
        {
            pTSprite->picnum = nShadowPic;

            int edx = ((tilesiz[nPict].x << 5) / nShadowWidth) - ((nFloorZ - pTSprite->z) >> 10);
            if (edx < 1) {
                edx = 1;
            }

            pTSprite->cstat = 0x22; // transluscence, floor sprite
            pTSprite->z = videoGetRenderMode() >= REND_POLYMOST ? nFloorZ : nFloorZ + 1;
            pTSprite->yrepeat = (uint8_t)edx;
            pTSprite->xrepeat = (uint8_t)edx;
            pTSprite->statnum = -3;
            pTSprite->pal = 0;
        }
    }

    return nPict;
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 __sequence_h__
#define __sequence_h__

#include "compat.h"

enum {
    kSeqRothands = 0,
    kSeqSword,
    kSeqPistol,
    kSeqM60,
    kSeqFlamer,
    kSeqGrenade,
    kSeqCobra,
    kSeqBoneSaw,
    kSeqScramble,
    kSeqGlove,
    kSeqMummy,
    kSeqSkull,
    kSeqPoof,
    kSeqKapow,
    kSeqFireball,
    kSeqBubble,
    kSeqSpider,
    kSeqAnubis,
    kSeqAnuBall,
    kSeqFish,
    kSeqSnakehed,
    kSeqSnakBody,
    kSeqWasp,
    kSeqCobraPow,
    kSeqScorp,
    kSeqJoe, // player pic
    kSeqStatus,
    kSeqDead,
    kSeqDeadEx,
    kSeqAnuPoof,
    kSeqSkulPoof,
    kSeqBullet,
    kSeqShadow,
    kSeqGrenRoll,
    kSeqGrenBoom,
    kSeqSplash,
    kSeqGrenPow,
    kSeqSkulSrt,
    kSeqFirePoof,
    kSeqBloodHit,
    kSeqLion,
    kSeqItems,
    kSeqLavag,
    kSeqLsplash,
    kSeqLavaShot,
    kSeqSmokeBal,
    kSeqFirePot,
    kSeqRex,
    kSeqSet,
    kSeqQueen,
    kSeqRoach,
    kSeqHawk,
    kSeqSetGhost,
    kSeqSetGBlow,
    kSeqBizzTail,
    kSeqBizzPoof,
    kSeqQueenEgg,
    kSeqRoacShot,
    kSeqBackgrnd,
    kSeqScreens,
    kSeqArrow,
    kSeqFonts,
    kSeqDrips,
    kSeqFireTrap,
    kSeqMagic2,
    kSeqCreepy,
    kSeqSlider,
    kSeqRavolt, // 67
    kSeqEyeHit,
    kSeqFont2,
    kSeqSeeBubbl,
    kSeqBlood,
    kSeqDrum,
    kSeqPoof2,
    kSeqDeadBrn, // 74
    kSeqGrenBubb,
    kSeqRochfire,
    kSeqRat
};

struct actionSeq
{
    short a;
    short b;
};

extern short frames;

extern short SeqBase[];
extern short SeqSize[];
extern short SeqOffsets[];

extern short FrameFlag[];

extern short nShadowWidth;
extern short nFlameHeight;

extern short nPilotLightFrame;
extern short nPilotLightCount;

extern short laststatustile;

extern int laststatusx;
extern int laststatusy;

void seq_LoadSequences();
int seq_GetFrameSound(int val, int edx);
void seq_MoveSequence(short nSprite, short nSeq, short bx);
int seq_GetSeqPicnum2(short nSeq, short nFrame);
int seq_GetSeqPicnum(short nSeq, short edx, short ebx);
void seq_DrawStatusSequence(short nSequence, uint16_t edx, short ebx);

int seq_DrawGunSequence(int nSeqOffset, short dx, int xOffs, int yOffs, int nShade, int nPal);
short seq_GetFrameFlag(short val, short nFrame);
int seq_PlotSequence(short nSprite, short edx, short nFrame, short ecx);
int seq_PlotArrowSequence(short nSprite, short nSeq, int nVal);
void seq_DrawPilotLightSeq(int xOffset, int yOffset);

#endif


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 "typedefs.h"
#include "serial.h"

short bSendBye = kFalse;

void UpdateSerialInputs()
{

}

void ClearSerialInbuf()
{

}

void HangUp()
{

}

void UnInitSerial()
{

}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 __serial_h__
#define __serial_h__

extern short bSendBye;

void UpdateSerialInputs();
void ClearSerialInbuf();
void HangUp();
void UnInitSerial();

#endif


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 "set.h"
#include "engine.h"
#include "exhumed.h"
#include "runlist.h"
#include "sequence.h"
#include "random.h"
#include "move.h"
#include "trigdat.h"
#include "bullet.h"
#include "set.h"
#include "save.h"
#include <assert.h>

#define kMaxSets    10

static actionSeq ActionSeq[] = {
    {0,  0},
    {77, 1},
    {78, 1},
    {0,  0},
    {9,  0},
    {63, 0},
    {45, 0},
    {18, 0},
    {27, 0},
    {36, 0},
    {72, 1},
    {74, 1}
};

struct Set
{
    short nHealth;
    short nFrame;
    short nAction;
    short nSprite;
    short nTarget;
    short field_A;
    short field_C;
    short field_D;
    short field_E;
};

short SetCount = 0;

Set SetList[kMaxSets];
short SetChan[kMaxSets];

void InitSets()
{
    SetCount = kMaxSets;
}

int BuildSet(short nSprite, int x, int y, int z, short nSector, short nAngle, int nChannel)
{
    SetCount--;

    short nSet = SetCount;
    if (nSet < 0) {
        return -1;
    }

    if (nSprite == -1)
    {
        nSprite = insertsprite(nSector, 120);
    }
    else
    {
        changespritestat(nSprite, 120);
        x = sprite[nSprite].x;
        y = sprite[nSprite].y;
        z = sector[sprite[nSprite].sectnum].floorz;
        nAngle = sprite[nSprite].ang;
    }

    assert(nSprite >= 0 && nSprite < kMaxSprites);

    sprite[nSprite].x = x;
    sprite[nSprite].y = y;
    sprite[nSprite].z = z;
    sprite[nSprite].cstat = 0x101;
    sprite[nSprite].shade = -12;
    sprite[nSprite].clipdist = 110;
    sprite[nSprite].xvel = 0;
    sprite[nSprite].yvel = 0;
    sprite[nSprite].zvel = 0;
    sprite[nSprite].xrepeat = 87;
    sprite[nSprite].yrepeat = 96;
    sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal;
    sprite[nSprite].xoffset = 0;
    sprite[nSprite].yoffset = 0;
    sprite[nSprite].ang = nAngle;
    sprite[nSprite].picnum = 1;
    sprite[nSprite].hitag = 0;
    sprite[nSprite].lotag = runlist_HeadRun() + 1;
    sprite[nSprite].extra = -1;

    //	GrabTimeSlot(3);

    SetList[nSet].nAction = 1;
    SetList[nSet].nHealth = 8000;
    SetList[nSet].nSprite = nSprite;
    SetList[nSet].nFrame  = 0;
    SetList[nSet].nTarget = -1;
    SetList[nSet].field_A = 90;
    SetList[nSet].field_C = 0;
    SetList[nSet].field_D = 0;

    SetChan[nSet] = nChannel;

    sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nSet, kRunSet);

    // this isn't stored anywhere.
    runlist_AddRunRec(NewRun, nSet, kRunSet);

    nCreaturesLeft++;

    return nSet;
}

int BuildSoul(int nSet)
{
    int nSetSprite = SetList[nSet].nSprite;
    int nSprite = insertsprite(sprite[nSetSprite].sectnum, 0);

    assert(nSprite >= 0 && nSprite < kMaxSprites);

    sprite[nSprite].cstat = 0x8000;
    sprite[nSprite].shade = -127;
    sprite[nSprite].xrepeat = 1;
    sprite[nSprite].yrepeat = 1;
    sprite[nSprite].pal = 0;
    sprite[nSprite].clipdist = 5;
    sprite[nSprite].xoffset = 0;
    sprite[nSprite].yoffset = 0;
    sprite[nSprite].picnum = seq_GetSeqPicnum(kSeqSet, 75, 0);
    sprite[nSprite].ang = RandomSize(11);
    sprite[nSprite].xvel = 0;
    sprite[nSprite].yvel = 0;
    sprite[nSprite].zvel = (-256) - RandomSize(10);
    sprite[nSprite].x = sprite[nSetSprite].x;
    sprite[nSprite].y = sprite[nSetSprite].y;

    short nSector = sprite[nSprite].sectnum;
    sprite[nSprite].z = (RandomSize(8) << 8) + 8192 + sector[nSector].ceilingz - GetSpriteHeight(nSprite);

    sprite[nSprite].hitag = nSet;
    sprite[nSprite].lotag = runlist_HeadRun() + 1;
    sprite[nSprite].extra = 0;

//	GrabTimeSlot(3);

    sprite[nSprite].owner = runlist_AddRunRec(NewRun, nSprite, kRunSoul);

    return nSprite | 0x230000; // TODO: What should this return, ideally/usefully?
}

void FuncSoul(int a, int UNUSED(b), int nRun)
{
    short nSprite = RunData[nRun].nVal;

    int nMessage = a & kMessageMask;

    switch (nMessage)
    {
        case 0x20000:
        {
            seq_MoveSequence(nSprite, SeqOffsets[kSeqSet] + 75, 0);

            if (sprite[nSprite].xrepeat < 32)
            {
                sprite[nSprite].xrepeat++;
                sprite[nSprite].yrepeat++;
            }

            sprite[nSprite].extra += (nSprite & 0x0F) + 5;
            sprite[nSprite].extra &= kAngleMask;

            int nVel = (Cos(sprite[nSprite].extra) >> 7);

            if (movesprite(nSprite, Cos(sprite[nSprite].ang) * nVel, Sin(sprite[nSprite].ang) * nVel, sprite[nSprite].zvel, 5120, 0, CLIPMASK0) & 0x10000)
            {
                int nSet = sprite[nSprite].hitag;
                int nSetSprite = SetList[nSet].nSprite;

                sprite[nSprite].cstat = 0;
                sprite[nSprite].yrepeat = 1;
                sprite[nSprite].xrepeat = 1;
                sprite[nSprite].x = sprite[nSetSprite].x;
                sprite[nSprite].y = sprite[nSetSprite].y;
                sprite[nSprite].z = sprite[nSetSprite].z - (GetSpriteHeight(nSetSprite) >> 1);
                mychangespritesect(nSprite, sprite[nSetSprite].sectnum);
                return;
            }
        }

        case 0x80000:
        case 0xA0000:
        case 0x90000:
            return;

        default:
            DebugOut("unknown msg %d for Soul\n", nMessage);
    }
}

void FuncSet(int a, int nDamage, int nRun)
{
    short nSet = RunData[nRun].nVal;
    assert(nSet >= 0 && nSet < kMaxSets);

    short nSprite = SetList[nSet].nSprite;
    short nAction = SetList[nSet].nAction;

    bool bVal = false;

    int nMessage = a & kMessageMask;

    switch (nMessage)
    {
        default:
        {
            DebugOut("unknown msg %d for Set\n", nMessage);
            return;
        }

        case 0xA0000:
        {
            if (nAction == 5)
            {
                nDamage = runlist_CheckRadialDamage(nSprite);
                // fall through to case 0x80000
            }
            fallthrough__;
        }

        case 0x80000:
        {
            if (nDamage && SetList[nSet].nHealth > 0)
            {
                if (nAction != 1)
                {
                    SetList[nSet].nHealth -= nDamage;
                }

                if (SetList[nSet].nHealth <= 0)
                {
                    sprite[nSprite].xvel = 0;
                    sprite[nSprite].yvel = 0;
                    sprite[nSprite].zvel = 0;
                    sprite[nSprite].cstat &= 0xFEFE;

                    SetList[nSet].nHealth = 0;

                    nCreaturesLeft--;

                    if (nAction < 10)
                    {
                        SetList[nSet].nFrame  = 0;
                        SetList[nSet].nAction = 10;
                    }
                }
                else if (nAction == 1)
                {
                    SetList[nSet].nAction = 2;
                    SetList[nSet].nFrame  = 0;
                }
            }
            return;
        }

        case 0x90000:
        {
            seq_PlotSequence(a, SeqOffsets[kSeqSet] + ActionSeq[nAction].a, SetList[nSet].nFrame, ActionSeq[nAction].b);
            return;
        }

        case 0x20000:
        {
            Gravity(nSprite);

            short nSeq = SeqOffsets[kSeqSet] + ActionSeq[SetList[nSet].nAction].a;
            sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, SetList[nSet].nFrame);
            seq_MoveSequence(nSprite, nSeq, SetList[nSet].nFrame);

            if (nAction == 3)
            {
                if (SetList[nSet].field_D) {
                    SetList[nSet].nFrame++;
                }
            }

            SetList[nSet].nFrame++;
            if (SetList[nSet].nFrame >= SeqSize[nSeq])
            {
                SetList[nSet].nFrame = 0;
                bVal = true;
            }

            short nFlag = FrameFlag[SeqBase[nSeq] + SetList[nSet].nFrame];
            short nTarget = SetList[nSet].nTarget;

            if (nTarget > -1 && nAction < 10)
            {
                if (!(sprite[nTarget].cstat & 0x101))
                {
                    SetList[nSet].nTarget = -1;
                    SetList[nSet].nAction = 0;
                    SetList[nSet].nFrame  = 0;
                    nTarget = -1;
                }
            }

            int nMov = MoveCreature(nSprite);

            pushmove_old(&sprite[nSprite].x, &sprite[nSprite].y, &sprite[nSprite].z, &sprite[nSprite].sectnum, sprite[nSprite].clipdist << 2, 5120, -5120, CLIPMASK0);

            if (sprite[nSprite].zvel > 4000)
            {
                if (nMov & 0x20000)
                {
                    SetQuake(nSprite, 100);
                }
            }

            switch (nAction)
            {
                default:
                    return;

                case 0:
                {
                    if ((nSet & 0x1F) == (totalmoves & 0x1F))
                    {
                        if (nTarget < 0)
                        {
                            nTarget = FindPlayer(nSprite, 1000);
                        }

                        if (nTarget >= 0)
                        {
                            SetList[nSet].nAction = 3;
                            SetList[nSet].nFrame  = 0;
                            SetList[nSet].nTarget = nTarget;

                            sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 1;
                            sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1;
                        }
                    }

                    return;
                }

                case 1:
                {
                    if (FindPlayer(nSprite, 1000) >= 0)
                    {
                        SetList[nSet].field_A--;
                        if (SetList[nSet].field_A <= 0)
                        {
                            SetList[nSet].nAction = 2;
                            SetList[nSet].nFrame  = 0;
                        }
                    }

                    return;
                }

                case 2:
                {
                    if (bVal)
                    {
                        SetList[nSet].nAction = 7;
                        SetList[nSet].field_C = 0;
                        SetList[nSet].nFrame  = 0;

                        sprite[nSprite].xvel = 0;
                        sprite[nSprite].yvel = 0;

                        SetList[nSet].nTarget = FindPlayer(nSprite, 1000);
                    }
                    return;
                }

                case 3:
                {
                    if (nTarget != -1)
                    {
                        if ((nFlag & 0x10) && (nMov & 0x20000))
                        {
                            SetQuake(nSprite, 100);
                        }

                        int nCourse = PlotCourseToSprite(nSprite, nTarget);

                        if ((nSet & 0x1F) == (totalmoves & 0x1F))
                        {
                            int nRand = RandomSize(3);

                            switch (nRand)
                            {
                                case 0:
                                case 2:
                                {
                                    SetList[nSet].field_C = 0;
                                    SetList[nSet].nAction = 7;
                                    SetList[nSet].nFrame  = 0;
                                    sprite[nSprite].xvel = 0;
                                    sprite[nSprite].yvel = 0;
                                    return;
                                }
                                case 1:
                                {
                                    PlotCourseToSprite(nSprite, nTarget);

                                    SetList[nSet].nAction = 6;
                                    SetList[nSet].nFrame  = 0;
                                    SetList[nSet].field_E = 5;
                                    sprite[nSprite].xvel = 0;
                                    sprite[nSprite].yvel = 0;
                                    return;
                                }
                                default:
                                {
                                    if (nCourse <= 100)
                                    {
                                        SetList[nSet].field_D = 0;
                                    }
                                    else
                                    {
                                        SetList[nSet].field_D = 1;
                                    }
                                    break;
                                }
                            }
                        }

                        // loc_338E2
                        int nAngle = sprite[nSprite].ang & 0xFFF8;
                        sprite[nSprite].xvel = Cos(nAngle) >> 1;
                        sprite[nSprite].yvel = Sin(nAngle) >> 1;

                        if (SetList[nSet].field_D)
                        {
                            sprite[nSprite].xvel *= 2;
                            sprite[nSprite].yvel *= 2;
                        }

                        if ((nMov & 0xC000) == 0x8000)
                        {
                            short nWall = nMov & 0x3FFF;
                            short nSector = wall[nWall].nextsector;

                            if (nSector >= 0)
                            {
                                if ((sprite[nSprite].z - sector[nSector].floorz) < 55000)
                                {
                                    if (sprite[nSprite].z > sector[nSector].ceilingz)
                                    {
                                        SetList[nSet].field_C = 1;
                                        SetList[nSet].nAction = 7;
                                        SetList[nSet].nFrame  = 0;
                                        sprite[nSprite].xvel = 0;
                                        sprite[nSprite].yvel = 0;
                                        return;
                                    }
                                }
                            }

                            sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask;
                            sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 1;
                            sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1;
                            break;
                        }
                        else if ((nMov & 0xC000) == 0xC000)
                        {
                            if (nTarget == (nMov & 0x3FFF))
                            {
                                int nAng = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y);
                                if (AngleDiff(sprite[nSprite].ang, nAng) < 64)
                                {
                                    SetList[nSet].nAction = 4;
                                    SetList[nSet].nFrame  = 0;
                                }
                                break;
                            }
                            else
                            {
                                SetList[nSet].field_C = 1;
                                SetList[nSet].nAction = 7;
                                SetList[nSet].nFrame  = 0;
                                sprite[nSprite].xvel = 0;
                                sprite[nSprite].yvel = 0;
                                return;
                            }
                        }

                        break;
                    }
                    else
                    {
                        SetList[nSet].nAction = 0;
                        SetList[nSet].nFrame  = 0;
                        return;
                    }
                }

                case 4:
                {
                    if (nTarget == -1)
                    {
                        SetList[nSet].nAction = 0;
                        SetList[nSet].field_A = 50;
                    }
                    else
                    {
                        if (PlotCourseToSprite(nSprite, nTarget) >= 768)
                        {
                            SetList[nSet].nAction = 3;
                        }
                        else if (nFlag & 0x80)
                        {
                            runlist_DamageEnemy(nTarget, nSprite, 5);
                        }
                    }

                    break;
                }

                case 5:
                {
                    if (bVal)
                    {
                        SetList[nSet].nAction = 0;
                        SetList[nSet].field_A = 15;
                    }
                    return;
                }

                case 6:
                {
                    if (nFlag & 0x80)
                    {
                        int nBullet = BuildBullet(nSprite, 11, 0, 0, -1, sprite[nSprite].ang, nTarget + 10000, 1);
                        SetBulletEnemy(nBullet, nTarget);

                        SetList[nSet].field_E--;
                        if (SetList[nSet].field_E <= 0 || !RandomBit())
                        {
                            SetList[nSet].nAction = 0;
                            SetList[nSet].nFrame  = 0;
                        }
                    }
                    return;
                }

                case 7:
                {
                    if (bVal)
                    {
                        if (SetList[nSet].field_C)
                        {
                            sprite[nSprite].zvel = -10000;
                        }
                        else
                        {
                            sprite[nSprite].zvel = -(PlotCourseToSprite(nSprite, nTarget));
                        }

                        SetList[nSet].nAction = 8;
                        SetList[nSet].nFrame  = 0;

                        sprite[nSprite].xvel = Cos(sprite[nSprite].ang);
                        sprite[nSprite].yvel = Sin(sprite[nSprite].ang);
                    }
                    return;
                }

                case 8:
                {
                    if (bVal)
                    {
                        SetList[nSet].nFrame = SeqSize[nSeq] - 1;
                    }

                    if (nMov & 0x20000)
                    {
                        SetQuake(nSprite, 200);
                        SetList[nSet].nAction = 9;
                        SetList[nSet].nFrame  = 0;
                    }
                    return;
                }

                case 9:
                {
                    sprite[nSprite].xvel >>= 1;
                    sprite[nSprite].yvel >>= 1;

                    if (bVal)
                    {
                        sprite[nSprite].xvel = 0;
                        sprite[nSprite].yvel = 0;

                        PlotCourseToSprite(nSprite, nTarget);

                        SetList[nSet].nAction = 6;
                        SetList[nSet].nFrame  = 0;
                        SetList[nSet].field_E = 5;

                        sprite[nSprite].xvel = 0;
                        sprite[nSprite].yvel = 0;
                    }
                    return;
                }

                case 10:
                {
                    if (nFlag & 0x80)
                    {
                        sprite[nSprite].z -= GetSpriteHeight(nSprite);
                        BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqSet, 76, 0));
                        sprite[nSprite].z += GetSpriteHeight(nSprite);
                    }

                    if (bVal)
                    {
                        SetList[nSet].nAction = 11;
                        SetList[nSet].nFrame  = 0;

                        runlist_ChangeChannel(SetChan[nSet], 1);

                        for (int i = 0; i < 20; i++)
                        {
                            BuildSoul(nSet);
                        }
                    }
                    return;
                }

                case 11:
                {
                    sprite[nSprite].cstat &= 0xFEFE;
                    return;
                }
            }

            // loc_33AE3: ?
            if (nAction)
            {
                if (nTarget != -1)
                {
                    if (!(sprite[nTarget].cstat & 0x101))
                    {
                        SetList[nSet].nAction = 0;
                        SetList[nSet].nFrame  = 0;
                        SetList[nSet].field_A = 100;
                        SetList[nSet].nTarget = -1;
                        sprite[nSprite].xvel = 0;
                        sprite[nSprite].yvel = 0;
                    }
                }
            }

            return;
        }
    }
}

class SetLoadSave : public LoadSave
{
public:
    virtual void Load();
    virtual void Save();
};

void SetLoadSave::Load()
{
    Read(&SetCount, sizeof(SetCount));
    Read(&SetList[SetCount], sizeof(Set) * (kMaxSets - SetCount));
    Read(SetChan, sizeof(SetChan));
}

void SetLoadSave::Save()
{
    Write(&SetCount, sizeof(SetCount));
    Write(&SetList[SetCount], sizeof(Set) * (kMaxSets - SetCount));
    Write(SetChan, sizeof(SetChan));
}

static SetLoadSave* myLoadSave;

void SetLoadSaveConstruct()
{
    myLoadSave = new SetLoadSave();
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 __set_h__
#define __set_h__

#include "aistuff.h"

void InitSets();
int BuildSet(short nSprite, int x, int y, int z, short nSector, short nAngle, int nChannel);
void FuncSoul(int, int, int);
void FuncSet(int, int, int);

#endif


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 "engine.h"
#include "exhumed.h"
#include "snake.h"
#include "status.h"
#include "player.h"
#include "runlist.h"
#include "sequence.h"
#include "bullet.h"
#include "input.h"
#include "anims.h"
#include "lighting.h"
#include "sound.h"
#include "move.h"
#include "trigdat.h"
#include "gun.h"
#include "save.h"
#include <string.h>
#include <assert.h>

#define kMaxSnakes  50

int nSnakeCount = 0;
int nSnakesFree;

short SnakeFree[kMaxSnakes];
short nPlayerSnake[kMaxPlayers];

Snake SnakeList[kMaxSnakes];
short nSnakePlayer[kMaxSnakes];

void InitSnakes()
{
    nSnakeCount = 0;

    for (int i = 0; i < kMaxSnakes; i++) {
        SnakeFree[i] = i;
    }

    nSnakesFree = kMaxSnakes;
    memset(nPlayerSnake, 0, sizeof(nPlayerSnake));
}

short GrabSnake()
{
    nSnakesFree--;
    return SnakeFree[nSnakesFree];
}

void DestroySnake(int nSnake)
{
    short nRun = SnakeList[nSnake].nRun;
    runlist_SubRunRec(nRun);

    for (int i = 0; i < kSnakeSprites; i++)
    {
        short nSprite = SnakeList[nSnake].nSprites[i];

        runlist_DoSubRunRec(sprite[nSprite].lotag - 1);
        runlist_DoSubRunRec(sprite[nSprite].owner);

        mydeletesprite(nSprite);
    }

    SnakeFree[nSnakesFree] = nSnake;
    nSnakesFree++;

    if (nSnake == nSnakeCam)
    {
        nSnakeCam = -1;
        if (!bFullScreen) {
            RefreshStatus();
        }
    }
}

void ExplodeSnakeSprite(int nSprite, short nPlayer)
{
    short nDamage = BulletInfo[kWeaponStaff].nDamage;

    if (nPlayerDouble[nPlayer] > 0) {
        nDamage *= 2;
    }

    // take a copy of this, to revert after call to runlist_RadialDamageEnemy()
    short nOwner = sprite[nSprite].owner;
    sprite[nSprite].owner = PlayerList[nPlayer].nSprite;

    runlist_RadialDamageEnemy(nSprite, nDamage, BulletInfo[kWeaponStaff].nRadius);

    sprite[nSprite].owner = nOwner;

    BuildAnim(-1, 23, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, 40, 4);

    AddFlash(sprite[nSprite].sectnum, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, 128);

    StopSpriteSound(nSprite);
}

int BuildSnake(short nPlayer, short zVal)
{
    if (!nSnakesFree)
        return -1;

    int nSnake = -1;

    zVal -= 1280;

    short nPlayerSprite = PlayerList[nPlayer].nSprite;
    short nViewSect = nPlayerViewSect[nPlayer];
    short nPic = seq_GetSeqPicnum(kSeqSnakBody, 0, 0);

    int x = sprite[nPlayerSprite].x;
    int y = sprite[nPlayerSprite].y;
    int z = (sprite[nPlayerSprite].z + zVal) - 2560;
    short nAngle = sprite[nPlayerSprite].ang;

    short hitsect, hitsprite;
    int hitx, hity, hitz;

    short nSprite;

    vec3_t pos = { x, y, z };
    hitdata_t hitData;
    hitscan(&pos, sprite[nPlayerSprite].sectnum, Cos(nAngle), Sin(nAngle), 0, &hitData, CLIPMASK1);

    hitx = hitData.x;
    hity = hitData.y;
    hitz = hitData.z;
    hitsect = hitData.sect;
    hitsprite = hitData.sprite;

    uint32_t xDiff = klabs(hitx - x);
    uint32_t yDiff = klabs(hity - y);

    uint32_t sqrtNum = xDiff * xDiff + yDiff * yDiff;

    if (sqrtNum > INT_MAX)
    {
        OSD_Printf("%s %d: overflow\n", EDUKE32_FUNCTION, __LINE__);
        sqrtNum = INT_MAX;
    }

    int nSqrt = ksqrt(sqrtNum);

    if (nSqrt < (sintable[512] >> 4))
    {
        BackUpBullet(&hitx, &hity, nAngle);
        nSprite = insertsprite(hitsect, 202);
        sprite[nSprite].x = hitx;
        sprite[nSprite].y = hity;
        sprite[nSprite].z = hitz;

        ExplodeSnakeSprite(nSprite, nPlayer);
        mydeletesprite(nSprite);
        return -1;
    }
    else
    {
        short nTarget;

        if (hitsprite >= 0 && sprite[hitsprite].statnum >= 90 && sprite[hitsprite].statnum <= 199) {
            nTarget = hitsprite;
        }
        else {
            nTarget = sPlayerInput[nPlayer].nTarget;
        }

        nSnake = GrabSnake();

//		GrabTimeSlot(3);

        short var_24;

        for (int i = 0; i < kSnakeSprites; i++)
        {
            nSprite = insertsprite(nViewSect, 202);
            assert(nSprite >= 0 && nSprite < kMaxSprites);

            sprite[nSprite].owner = nPlayerSprite;
            sprite[nSprite].picnum = nPic;

            if (i == 0)
            {
                sprite[nSprite].x = sprite[nPlayerSprite].x;
                sprite[nSprite].y = sprite[nPlayerSprite].y;
                sprite[nSprite].z = sprite[nPlayerSprite].z + zVal;
                sprite[nSprite].xrepeat = 32;
                sprite[nSprite].yrepeat = 32;
                nViewSect = sprite[nSprite].sectnum;
                var_24 = nSprite;
            }
            else
            {
                sprite[nSprite].x = sprite[var_24].x;
                sprite[nSprite].y = sprite[var_24].y;
                sprite[nSprite].z = sprite[var_24].z;
                sprite[nSprite].xrepeat = 40 - 3*i;
                sprite[nSprite].yrepeat = 40 - 3*i;
            }

            sprite[nSprite].clipdist = 10;
            sprite[nSprite].cstat = 0;
            sprite[nSprite].shade = -64;
            sprite[nSprite].pal = 0;
            sprite[nSprite].xoffset = 0;
            sprite[nSprite].yoffset = 0;
            sprite[nSprite].ang = sprite[nPlayerSprite].ang;
            sprite[nSprite].xvel = 0;
            sprite[nSprite].yvel = 0;
            sprite[nSprite].zvel = 0;
            sprite[nSprite].hitag = 0;
            sprite[nSprite].extra = -1;
            sprite[nSprite].lotag = runlist_HeadRun() + 1;

            SnakeList[nSnake].nSprites[i] = nSprite;

            sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, ((nSnake << 8) | i), kRunSnake);
        }

        SnakeList[nSnake].nRun = runlist_AddRunRec(NewRun, nSnake, kRunSnake);
        SnakeList[nSnake].c[1] = 2;
        SnakeList[nSnake].c[5] = 5;
        SnakeList[nSnake].c[2] = 4;
        SnakeList[nSnake].c[3] = 6;
        SnakeList[nSnake].c[4] = 7;
        SnakeList[nSnake].c[6] = 6;
        SnakeList[nSnake].c[7] = 7;
        SnakeList[nSnake].nEnemy = nTarget;
        SnakeList[nSnake].sC = 1200;
        SnakeList[nSnake].sE = 0;
        nSnakePlayer[nSnake] = nPlayer;
        nPlayerSnake[nPlayer] = nSnake;

        if (bSnakeCam)
        {
            if (nSnakeCam < 0) {
                nSnakeCam = nSnake;
            }
        }

        D3PlayFX(StaticSound[kSoundCobraSprite], var_24);
    }

    return nSnake;
}

int FindSnakeEnemy(short nSnake)
{
    short nPlayer = nSnakePlayer[nSnake];
    short nPlayerSprite = PlayerList[nPlayer].nSprite;

    short nSprite = SnakeList[nSnake].nSprites[0]; // CHECKME

    short nAngle = sprite[nSprite].ang;
    short nSector = sprite[nSprite].sectnum;

    int esi = 2048;

    int nEnemy = -1;

    for (int i = headspritesect[nSector]; i >= 0; i = nextspritesect[i])
    {
        if (sprite[i].statnum >= 90 && sprite[i].statnum < 150 && (sprite[i].cstat & 0x101))
        {
            if (i != nPlayerSprite && !(sprite[i].cstat & 0x8000))
            {
                int nAngle2 = (nAngle - GetAngleToSprite(nSprite, i)) & kAngleMask;
                if (nAngle2 < esi)
                {
                    nEnemy = i;
                    esi = nAngle2;
                }
            }
        }
    }

    if (nEnemy != -1)
    {
        SnakeList[nSnake].nEnemy = nEnemy;
    }
    else
    {
        SnakeList[nSnake].nEnemy--;
        if (SnakeList[nSnake].nEnemy < -25)
        {
            nEnemy = nPlayerSprite;
            SnakeList[nSnake].nEnemy = nPlayerSprite;
        }
    }

    return nEnemy;
}

void FuncSnake(int a, int UNUSED(nDamage), int nRun)
{
    int nMessage = a & kMessageMask;

    switch (nMessage)
    {
        case 0x20000:
        {
            short nSnake = RunData[nRun].nVal;
            assert(nSnake >= 0 && nSnake < kMaxSnakes);

            short nSprite = SnakeList[nSnake].nSprites[0];

            seq_MoveSequence(nSprite, SeqOffsets[kSeqSnakehed], 0);

            short nEnemySprite = SnakeList[nSnake].nEnemy;

            int nMov;
            int zVal;

            if (nEnemySprite < 0)
            {
SEARCH_ENEMY:
                nMov = movesprite(nSprite,
                    600 * Cos(sprite[nSprite].ang),
                    600 * Sin(sprite[nSprite].ang),
                    Sin(SnakeList[nSnake].sE) >> 5,
                    0, 0, CLIPMASK1);

                FindSnakeEnemy(nSnake);

                zVal = 0;
            }
            else
            {
                if (!(sprite[nEnemySprite].cstat&0x101))
                {
                    SnakeList[nSnake].nEnemy = -1;
                    goto SEARCH_ENEMY;
                }

                zVal = sprite[nSprite].z;

                nMov = AngleChase(nSprite, nEnemySprite, 1200, SnakeList[nSnake].sE, 32);

                zVal = sprite[nSprite].z - zVal;
            }

            if (nMov)
            {
                short nPlayer = nSnakePlayer[nSnake];
                ExplodeSnakeSprite(SnakeList[nSnake].nSprites[0], nPlayer);

                nPlayerSnake[nPlayer] = -1;
                nSnakePlayer[nSnake] = -1;

                DestroySnake(nSnake);
            }
            else
            {
                short nAngle = sprite[nSprite].ang;
                int var_30 = -(64 * Cos(nAngle));
                int var_34 = -(64 * Sin(nAngle));

                int var_20 = SnakeList[nSnake].sE;

                SnakeList[nSnake].sE = (SnakeList[nSnake].sE + 64) & 0x7FF;

                int var_28 = (nAngle + 512) & kAngleMask;
                short nSector = sprite[nSprite].sectnum;

                int x = sprite[nSprite].x;
                int y = sprite[nSprite].y;
                int z = sprite[nSprite].z;

                for (int i = 7; i > 0; i--)
                {
                    int nSprite2 = SnakeList[nSnake].nSprites[i];

                    sprite[nSprite2].ang = nAngle;
                    sprite[nSprite2].x = x;
                    sprite[nSprite2].y = y;
                    sprite[nSprite2].z = z;

                    mychangespritesect(nSprite2, nSector);

                    int eax = (Sin(var_20) * SnakeList[nSnake].c[i]) >> 9;

                    movesprite(nSprite2, var_30 + var_30 * i + eax * Cos(var_28), var_30 + var_34 * i + eax * Sin(var_28),
                        -zVal*(i-1), 0, 0, CLIPMASK1);

                    var_20 = (var_20 + 128) & kAngleMask;
                }
            }
            break;
        }

        case 0x90000:
        {
            short nSnake = RunData[nRun].nVal;
            short nSprite = a & 0xFFFF;

            if ((nSnake & 0xFF) == 0) {
                seq_PlotSequence(nSprite, SeqOffsets[kSeqSnakehed], 0, 0);
            }
            else {
                seq_PlotSequence(nSprite, SeqOffsets[kSeqSnakBody], 0, 0);
            }

            tsprite[nSprite].owner = -1;
            break;
        }

        case 0xA0000:
        {
            break;
        }

        default:
        {
            DebugOut("unknown msg %x for bullet\n", nMessage);
            break;
        }
    }
}

class SnakeLoadSave : public LoadSave
{
public:
    virtual void Load();
    virtual void Save();
};

void SnakeLoadSave::Load()
{
    Read(&nSnakeCount, sizeof(nSnakeCount));
    Read(&nSnakesFree, sizeof(nSnakesFree));
    Read(SnakeFree, sizeof(SnakeFree));
    Read(nPlayerSnake, sizeof(nPlayerSnake));
    Read(SnakeList, sizeof(SnakeList));
    Read(nSnakePlayer, sizeof(nSnakePlayer));
}

void SnakeLoadSave::Save()
{
    Write(&nSnakeCount, sizeof(nSnakeCount));
    Write(&nSnakesFree, sizeof(nSnakesFree));
    Write(SnakeFree, sizeof(SnakeFree));
    Write(nPlayerSnake, sizeof(nPlayerSnake));
    Write(SnakeList, sizeof(SnakeList));
    Write(nSnakePlayer, sizeof(nSnakePlayer));
}

static SnakeLoadSave* myLoadSave;

void SnakeLoadSaveConstruct()
{
    myLoadSave = new SnakeLoadSave();
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed 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 __snake_h__
#define __snake_h__

#include "aistuff.h"

#define kSnakeSprites	8 // or rename to kSnakeParts?

// 32bytes
struct Snake
{
    short nEnemy;	 // nRun
    short nSprites[kSnakeSprites];

    short sC;
    short nRun;

    // array?
    char c[8];
    /*
    char c1;
    char c2;
    char c3;
    char c4;
    char c5;
    char c6;
    char c7;
    char c8;
    */

    short sE;
};

extern Snake SnakeList[];

void InitSnakes();
short GrabSnake();
int BuildSnake(short nPlayer, short zVal);
void FuncSnake(int, int, int);

#endif