raze/source/blood/src/loadsave.cpp
2020-09-20 16:20:06 +10:00

782 lines
19 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h" // Must come before everything else!
#include <stdio.h>
#include "build.h"
#include "compat.h"
#include "mmulti.h"
#include "common_game.h"
#include "ai.h"
#include "blood.h"
#include "globals.h"
#include "db.h"
#include "messages.h"
#include "loadsave.h"
#include "sectorfx.h"
#include "seq.h"
#include "sound.h"
#include "i_specialpaths.h"
#include "view.h"
#include "nnexts.h"
#include "savegamehelp.h"
#include "raze_music.h"
#include "mapinfo.h"
#include "gamestate.h"
#include "d_net.h"
#include "aistate.h"
#include "aiunicult.h"
BEGIN_BLD_NS
// All AI states for assigning an index.
static AISTATE* allAIStates[] =
{
nullptr,
&genIdle,
&genRecoil,
&batIdle,
&batFlyIdle,
&batChase,
&batPonder,
&batGoto,
&batBite,
&batRecoil,
&batSearch,
&batSwoop,
&batFly,
&batTurn,
&batHide,
&batDodgeUp,
&batDodgeUpRight,
&batDodgeUpLeft,
&batDodgeDown,
&batDodgeDownRight,
&batDodgeDownLeft,
&beastIdle,
&beastChase,
&beastDodge,
&beastGoto,
&beastSlash,
&beastStomp,
&beastSearch,
&beastRecoil,
&beastTeslaRecoil,
&beastSwimIdle,
&beastSwimChase,
&beastSwimDodge,
&beastSwimGoto,
&beastSwimSearch,
&beastSwimSlash,
&beastSwimRecoil,
&beastMorphToBeast,
&beastMorphFromCultist,
&beast138FB4,
&beast138FD0,
&beast138FEC,
&eelIdle,
&eelFlyIdle,
&eelChase,
&eelPonder,
&eelGoto,
&eelBite,
&eelRecoil,
&eelSearch,
&eelSwoop,
&eelFly,
&eelTurn,
&eelHide,
&eelDodgeUp,
&eelDodgeUpRight,
&eelDodgeUpLeft,
&eelDodgeDown,
&eelDodgeDownRight,
&eelDodgeDownLeft,
&cultistBurnIdle,
&cultistBurnChase,
&cultistBurnGoto,
&cultistBurnSearch,
&cultistBurnAttack,
&zombieABurnChase,
&zombieABurnGoto,
&zombieABurnSearch,
&zombieABurnAttack,
&zombieFBurnChase,
&zombieFBurnGoto,
&zombieFBurnSearch,
&zombieFBurnAttack,
&innocentBurnChase,
&innocentBurnGoto,
&innocentBurnSearch,
&innocentBurnAttack,
&beastBurnChase,
&beastBurnGoto,
&beastBurnSearch,
&beastBurnAttack,
&tinycalebBurnChase,
&tinycalebBurnGoto,
&tinycalebBurnSearch,
&tinycalebBurnAttack,
&genDudeBurnIdle,
&genDudeBurnChase,
&genDudeBurnGoto,
&genDudeBurnSearch,
&genDudeBurnAttack,
&tinycalebIdle,
&tinycalebChase,
&tinycalebDodge,
&tinycalebGoto,
&tinycalebAttack,
&tinycalebSearch,
&tinycalebRecoil,
&tinycalebTeslaRecoil,
&tinycalebSwimIdle,
&tinycalebSwimChase,
&tinycalebSwimDodge,
&tinycalebSwimGoto,
&tinycalebSwimSearch,
&tinycalebSwimAttack,
&tinycalebSwimRecoil,
&tinycaleb139660,
&tinycaleb13967C,
&tinycaleb139698,
&cerberusIdle,
&cerberusSearch,
&cerberusChase,
&cerberusRecoil,
&cerberusTeslaRecoil,
&cerberusGoto,
&cerberusBite,
&cerberusBurn,
&cerberus3Burn,
&cerberus2Idle,
&cerberus2Search,
&cerberus2Chase,
&cerberus2Recoil,
&cerberus2Goto,
&cerberus2Bite,
&cerberus2Burn,
&cerberus4Burn,
&cerberus139890,
&cerberus1398AC,
&cultistIdle,
&cultistProneIdle,
&fanaticProneIdle,
&cultistProneIdle3,
&cultistChase,
&fanaticChase,
&cultistDodge,
&cultistGoto,
&cultistProneChase,
&cultistProneDodge,
&cultistTThrow,
&cultistSThrow,
&cultistTsThrow,
&cultistDThrow,
&cultist139A78,
&cultist139A94,
&cultist139AB0,
&cultist139ACC,
&cultist139AE8,
&cultistSearch,
&cultistSFire,
&cultistTFire,
&cultistTsFire,
&cultistSProneFire,
&cultistTProneFire,
&cultistTsProneFire,
&cultistRecoil,
&cultistProneRecoil,
&cultistTeslaRecoil,
&cultistSwimIdle,
&cultistSwimChase,
&cultistSwimDodge,
&cultistSwimGoto,
&cultistSwimSearch,
&cultistSSwimFire,
&cultistTSwimFire,
&cultistTsSwimFire,
&cultistSwimRecoil,
&gargoyleFIdle,
&gargoyleStatueIdle,
&gargoyleFChase,
&gargoyleFGoto,
&gargoyleFSlash,
&gargoyleFThrow,
&gargoyleSThrow,
&gargoyleSBlast,
&gargoyleFRecoil,
&gargoyleFSearch,
&gargoyleFMorph2,
&gargoyleFMorph,
&gargoyleSMorph2,
&gargoyleSMorph,
&gargoyleSwoop,
&gargoyleFly,
&gargoyleTurn,
&gargoyleDodgeUp,
&gargoyleFDodgeUpRight,
&gargoyleFDodgeUpLeft,
&gargoyleDodgeDown,
&gargoyleFDodgeDownRight,
&gargoyleFDodgeDownLeft,
&statueFBreakSEQ,
&statueSBreakSEQ,
&ghostIdle,
&ghostChase,
&ghostGoto,
&ghostSlash,
&ghostThrow,
&ghostBlast,
&ghostRecoil,
&ghostTeslaRecoil,
&ghostSearch,
&ghostSwoop,
&ghostFly,
&ghostTurn,
&ghostDodgeUp,
&ghostDodgeUpRight,
&ghostDodgeUpLeft,
&ghostDodgeDown,
&ghostDodgeDownRight,
&ghostDodgeDownLeft,
&gillBeastIdle,
&gillBeastChase,
&gillBeastDodge,
&gillBeastGoto,
&gillBeastBite,
&gillBeastSearch,
&gillBeastRecoil,
&gillBeastSwimIdle,
&gillBeastSwimChase,
&gillBeastSwimDodge,
&gillBeastSwimGoto,
&gillBeastSwimSearch,
&gillBeastSwimBite,
&gillBeastSwimRecoil,
&gillBeast13A138,
&gillBeast13A154,
&gillBeast13A170,
&handIdle,
&hand13A3B4,
&handSearch,
&handChase,
&handRecoil,
&handGoto,
&handJump,
&houndIdle,
&houndSearch,
&houndChase,
&houndRecoil,
&houndTeslaRecoil,
&houndGoto,
&houndBite,
&houndBurn,
&innocentIdle,
&innocentSearch,
&innocentChase,
&innocentRecoil,
&innocentTeslaRecoil,
&innocentGoto,
&podIdle,
&pod13A600,
&podSearch,
&pod13A638,
&podRecoil,
&podChase,
&tentacleIdle,
&tentacle13A6A8,
&tentacle13A6C4,
&tentacle13A6E0,
&tentacle13A6FC,
&tentacle13A718,
&tentacleSearch,
&tentacle13A750,
&tentacleRecoil,
&tentacleChase,
&ratIdle,
&ratSearch,
&ratChase,
&ratDodge,
&ratRecoil,
&ratGoto,
&ratBite,
&spidIdle,
&spidChase,
&spidDodge,
&spidGoto,
&spidSearch,
&spidBite,
&spidJump,
&spid13A92C,
&tchernobogIdle,
&tchernobogSearch,
&tchernobogChase,
&tchernobogRecoil,
&tcherno13A9B8,
&tcherno13A9D4,
&tcherno13A9F0,
&tcherno13AA0C,
&tcherno13AA28,
&genDudeIdleL,
&genDudeIdleW,
&genDudeSearchL,
&genDudeSearchW,
&genDudeSearchShortL,
&genDudeSearchShortW,
&genDudeSearchNoWalkL,
&genDudeSearchNoWalkW,
&genDudeGotoL,
&genDudeGotoW,
&genDudeDodgeL,
&genDudeDodgeD,
&genDudeDodgeW,
&genDudeDodgeShortL,
&genDudeDodgeShortD,
&genDudeDodgeShortW,
&genDudeDodgeShorterL,
&genDudeDodgeShorterD,
&genDudeDodgeShorterW,
&genDudeChaseL,
&genDudeChaseD,
&genDudeChaseW,
&genDudeChaseNoWalkL,
&genDudeChaseNoWalkD,
&genDudeChaseNoWalkW,
&genDudeFireL,
&genDudeFireD,
&genDudeFireW,
&genDudeRecoilL,
&genDudeRecoilD,
&genDudeRecoilW,
&genDudeRecoilTesla,
&genDudeThrow,
&genDudeThrow2,
&genDudePunch,
&zombieAIdle,
&zombieAChase,
&zombieAPonder,
&zombieAGoto,
&zombieAHack,
&zombieASearch,
&zombieARecoil,
&zombieATeslaRecoil,
&zombieARecoil2,
&zombieAStand,
&zombieEIdle,
&zombieEUp2,
&zombieEUp,
&zombie2Idle,
&zombie2Search,
&zombieSIdle,
&zombie13AC2C,
&zombieFIdle,
&zombieFChase,
&zombieFGoto,
&zombieFDodge,
&zombieFHack,
&zombieFPuke,
&zombieFThrow,
&zombieFSearch,
&zombieFRecoil,
&zombieFTeslaRecoil,
};
void IndexAIState(AISTATE*& state)
{
int i = 0;
auto savestate = state;
for (auto cstate : allAIStates)
{
if (state == cstate)
{
state = (AISTATE*)(intptr_t)i;
return;
}
i++;
}
state = nullptr;
}
void UnindexAIState(AISTATE*& state)
{
auto index = intptr_t(state);
if (index >= 0 && index < countof(allAIStates))
{
state = allAIStates[index];
}
else
{
state = nullptr;
}
}
unsigned int dword_27AA38 = 0;
unsigned int dword_27AA3C = 0;
unsigned int dword_27AA40 = 0;
FileWriter *LoadSave::hSFile = NULL;
FileReader LoadSave::hLFile;
TDeletingArray<LoadSave*> LoadSave::loadSaves;
void sub_76FD4(void)
{
}
void LoadSave::Save(void)
{
ThrowError("Pure virtual function called");
}
void LoadSave::Load(void)
{
ThrowError("Pure virtual function called");
}
void LoadSave::Read(void *pData, int nSize)
{
dword_27AA38 += nSize;
dassert(hLFile.isOpen());
if (hLFile.Read(pData, nSize) != nSize)
ThrowError("Error reading save file.");
}
void LoadSave::Write(void *pData, int nSize)
{
dword_27AA38 += nSize;
dword_27AA3C += nSize;
dassert(hSFile != NULL);
if (hSFile->Write(pData, nSize) != (size_t)nSize)
ThrowError("File error #%d writing save file.", errno);
}
bool GameInterface::LoadGame(FSaveGameNode* node)
{
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
if (gamestate != GS_LEVEL)
{
memset(xsprite, 0, sizeof(xsprite));
}
LoadSave::hLFile = ReadSavegameChunk("snapshot.bld");
if (!LoadSave::hLFile.isOpen())
return false;
for (auto rover : LoadSave::loadSaves)
{
rover->Load();
}
LoadSave::hLFile.Close();
FinishSavegameRead();
InitSectorFX();
viewInitializePrediction();
PreloadCache();
if (!gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
sfxSetReverb(0);
ambInit();
for (int i = 0; i < gNetPlayers; i++)
playerSetRace(&gPlayer[i], gPlayer[i].lifeMode);
viewSetErrorMessage("");
Net_ClearFifo();
paused = 0;
#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
Mus_ResumeSaved();
PROFILE* pProfile = &gProfile[myconnectindex];
strcpy(pProfile->name, playername);
pProfile->skill = gSkill;
return true;
}
bool GameInterface::SaveGame(FSaveGameNode* node)
{
LoadSave::hSFile = WriteSavegameChunk("snapshot.bld");
try
{
dword_27AA38 = 0;
dword_27AA40 = 0;
for (auto rover : LoadSave::loadSaves)
{
rover->Save();
if (dword_27AA38 > dword_27AA40)
dword_27AA40 = dword_27AA38;
dword_27AA38 = 0;
}
}
catch (CRecoverableError & err)
{
// Let's not abort for write errors.
Printf(TEXTCOLOR_RED "%s\n", err.what());
return false;
}
LoadSave::hSFile = NULL;
return 1;
}
class MyLoadSave : public LoadSave
{
public:
virtual void Load(void);
virtual void Save(void);
};
void MyLoadSave::Load(void)
{
psky_t *pSky = tileSetupSky(DEFAULTPSKY);
int id;
Read(&id, sizeof(id));
if (id != 0x5653424e/*'VSBN'*/)
ThrowError("Old saved game found");
short version;
Read(&version, sizeof(version));
if (version != BYTEVERSION)
ThrowError("Incompatible version of saved game found!");
Read(&gGameOptions, sizeof(gGameOptions));
int nNumSprites;
Read(&nNumSprites, sizeof(nNumSprites));
Read(qsector_filler, sizeof(qsector_filler[0])*numsectors);
Read(qsprite_filler, sizeof(qsprite_filler[0])*kMaxSprites);
Read(&pSky->horizfrac, sizeof(pSky->horizfrac));
Read(&pSky->yoffs, sizeof(pSky->yoffs));
Read(&pSky->yscale, sizeof(pSky->yscale));
Read(&gVisibility, sizeof(gVisibility));
Read(pSky->tileofs, sizeof(pSky->tileofs));
Read(&pSky->lognumtiles, sizeof(pSky->lognumtiles));
Read(gotpic, sizeof(gotpic));
Read(gotsector, sizeof(gotsector));
Read(&gFrameClock, sizeof(gFrameClock));
Read(&gFrameCount, sizeof(gFrameCount));
Read(&paused, sizeof(paused));
Read(baseWall, sizeof(baseWall[0])*numwalls);
Read(baseSprite, sizeof(baseSprite[0])*nNumSprites);
Read(baseFloor, sizeof(baseFloor[0])*numsectors);
Read(baseCeil, sizeof(baseCeil[0])*numsectors);
Read(velFloor, sizeof(velFloor[0])*numsectors);
Read(velCeil, sizeof(velCeil[0])*numsectors);
Read(&gHitInfo, sizeof(gHitInfo));
Read(&byte_1A76C6, sizeof(byte_1A76C6));
Read(&byte_1A76C8, sizeof(byte_1A76C8));
Read(&byte_1A76C7, sizeof(byte_1A76C7));
Read(&byte_19AE44, sizeof(byte_19AE44));
Read(gStatCount, sizeof(gStatCount));
Read(nextXSprite, sizeof(nextXSprite));
Read(nextXWall, sizeof(nextXWall));
Read(nextXSector, sizeof(nextXSector));
memset(xsprite, 0, sizeof(xsprite));
for (int nSprite = 0; nSprite < kMaxSprites; nSprite++)
{
if (sprite[nSprite].statnum < kMaxStatus)
{
int nXSprite = sprite[nSprite].extra;
if (nXSprite > 0)
{
Read(&xsprite[nXSprite], sizeof(XSPRITE));
UnindexAIState(xsprite[nXSprite].aiState);
}
}
}
memset(xwall, 0, sizeof(xwall));
for (int nWall = 0; nWall < numwalls; nWall++)
{
int nXWall = wall[nWall].extra;
if (nXWall > 0)
Read(&xwall[nXWall], sizeof(XWALL));
}
memset(xsector, 0, sizeof(xsector));
for (int nSector = 0; nSector < numsectors; nSector++)
{
int nXSector = sector[nSector].extra;
if (nXSector > 0)
Read(&xsector[nXSector], sizeof(XSECTOR));
}
Read(xvel, nNumSprites*sizeof(xvel[0]));
Read(yvel, nNumSprites*sizeof(yvel[0]));
Read(zvel, nNumSprites*sizeof(zvel[0]));
Read(&gMapRev, sizeof(gMapRev));
Read(&gSongId, sizeof(gSkyCount));
Read(&gFogMode, sizeof(gFogMode));
#ifdef NOONE_EXTENSIONS
Read(&gModernMap, sizeof(gModernMap));
#endif
psky_t *skyInfo = tileSetupSky(DEFAULTPSKY);
Read(skyInfo, sizeof(*skyInfo));
skyInfo->combinedtile = -1;
cheatReset();
}
void MyLoadSave::Save(void)
{
psky_t *pSky = tileSetupSky(0);
int nNumSprites = 0;
int id = 0x5653424e/*'VSBN'*/;
Write(&id, sizeof(id));
short version = BYTEVERSION;
Write(&version, sizeof(version));
for (int nSprite = 0; nSprite < kMaxSprites; nSprite++)
{
if (sprite[nSprite].statnum < kMaxStatus && nSprite > nNumSprites)
nNumSprites = nSprite;
}
//nNumSprites += 2;
nNumSprites++;
Write(&gGameOptions, sizeof(gGameOptions));
Write(&nNumSprites, sizeof(nNumSprites));
Write(qsector_filler, sizeof(qsector_filler[0])*numsectors);
Write(qsprite_filler, sizeof(qsprite_filler[0])*kMaxSprites);
Write(&pSky->horizfrac, sizeof(pSky->horizfrac));
Write(&pSky->yoffs, sizeof(pSky->yoffs));
Write(&pSky->yscale, sizeof(pSky->yscale));
Write(&gVisibility, sizeof(gVisibility));
Write(pSky->tileofs, sizeof(pSky->tileofs));
Write(&pSky->lognumtiles, sizeof(pSky->lognumtiles));
Write(gotpic, sizeof(gotpic));
Write(gotsector, sizeof(gotsector));
Write(&gFrameClock, sizeof(gFrameClock));
Write(&gFrameCount, sizeof(gFrameCount));
Write(&paused, sizeof(paused));
Write(baseWall, sizeof(baseWall[0])*numwalls);
Write(baseSprite, sizeof(baseSprite[0])*nNumSprites);
Write(baseFloor, sizeof(baseFloor[0])*numsectors);
Write(baseCeil, sizeof(baseCeil[0])*numsectors);
Write(velFloor, sizeof(velFloor[0])*numsectors);
Write(velCeil, sizeof(velCeil[0])*numsectors);
Write(&gHitInfo, sizeof(gHitInfo));
Write(&byte_1A76C6, sizeof(byte_1A76C6));
Write(&byte_1A76C8, sizeof(byte_1A76C8));
Write(&byte_1A76C7, sizeof(byte_1A76C7));
Write(&byte_19AE44, sizeof(byte_19AE44));
Write(gStatCount, sizeof(gStatCount));
Write(nextXSprite, sizeof(nextXSprite));
Write(nextXWall, sizeof(nextXWall));
Write(nextXSector, sizeof(nextXSector));
for (int nSprite = 0; nSprite < kMaxSprites; nSprite++)
{
if (sprite[nSprite].statnum < kMaxStatus)
{
int nXSprite = sprite[nSprite].extra;
if (nXSprite > 0)
{
IndexAIState(xsprite[nXSprite].aiState);
Write(&xsprite[nXSprite], sizeof(XSPRITE));
UnindexAIState(xsprite[nXSprite].aiState);
}
}
}
for (int nWall = 0; nWall < numwalls; nWall++)
{
int nXWall = wall[nWall].extra;
if (nXWall > 0)
Write(&xwall[nXWall], sizeof(XWALL));
}
for (int nSector = 0; nSector < numsectors; nSector++)
{
int nXSector = sector[nSector].extra;
if (nXSector > 0)
Write(&xsector[nXSector], sizeof(XSECTOR));
}
Write(xvel, nNumSprites*sizeof(xvel[0]));
Write(yvel, nNumSprites*sizeof(yvel[0]));
Write(zvel, nNumSprites*sizeof(zvel[0]));
Write(&gMapRev, sizeof(gMapRev));
Write(&gSongId, sizeof(gSkyCount));
Write(&gFogMode, sizeof(gFogMode));
#ifdef NOONE_EXTENSIONS
Write(&gModernMap, sizeof(gModernMap));
#endif
psky_t *skyInfo = tileSetupSky(DEFAULTPSKY);
Write(skyInfo, sizeof(*skyInfo));
}
static MyLoadSave *myLoadSave;
void ActorLoadSaveConstruct(void);
void AILoadSaveConstruct(void);
void EndGameLoadSaveConstruct(void);
void EventQLoadSaveConstruct(void);
void LevelsLoadSaveConstruct(void);
void MessagesLoadSaveConstruct(void);
void MirrorLoadSaveConstruct(void);
void PlayerLoadSaveConstruct(void);
void SeqLoadSaveConstruct(void);
void TriggersLoadSaveConstruct(void);
void ViewLoadSaveConstruct(void);
void WarpLoadSaveConstruct(void);
void WeaponLoadSaveConstruct(void);
#ifdef NOONE_EXTENSIONS
void NNLoadSaveConstruct(void);
#endif
void LoadSaveSetup(void)
{
myLoadSave = new MyLoadSave();
ActorLoadSaveConstruct();
AILoadSaveConstruct();
EndGameLoadSaveConstruct();
EventQLoadSaveConstruct();
LevelsLoadSaveConstruct();
MessagesLoadSaveConstruct();
MirrorLoadSaveConstruct();
PlayerLoadSaveConstruct();
SeqLoadSaveConstruct();
TriggersLoadSaveConstruct();
ViewLoadSaveConstruct();
WarpLoadSaveConstruct();
WeaponLoadSaveConstruct();
#ifdef NOONE_EXTENSIONS
NNLoadSaveConstruct();
#endif
}
END_BLD_NS