diff --git a/source/core/version.h b/source/core/version.h index 557dbbed2..bbb3bde6f 100644 --- a/source/core/version.h +++ b/source/core/version.h @@ -70,15 +70,14 @@ const char *GetVersionString(); #define SAVESIG_PS GAMENAME ".Exhumed" #define MINSAVEVER_DN3D 10 -#define MINSAVEVER_BLD 11 +#define MINSAVEVER_BLD 12 #define MINSAVEVER_SW 13 #define MINSAVEVER_PS 15 #define SAVEVER_DN3D 12 -#define SAVEVER_BLD 11 +#define SAVEVER_BLD 12 #define SAVEVER_SW 13 #define SAVEVER_PS 15 -#define OLD_SAVEGAME 1 // this is to keep writing out the old format in Blood, even when data has been refactored. #define NETGAMEVERSION 1 diff --git a/source/games/blood/src/actor.cpp b/source/games/blood/src/actor.cpp index 84a2a7c34..558cb0f2c 100644 --- a/source/games/blood/src/actor.cpp +++ b/source/games/blood/src/actor.cpp @@ -6356,21 +6356,7 @@ void actProcessSprites(void) DBloodActor* actSpawnSprite(int nSector, int x, int y, int z, int nStat, bool setextra) { - DBloodActor* actor; - int nSprite = InsertSprite(nSector, nStat); - if (nSprite >= 0) - { - sprite[nSprite].extra = -1; - actor = &bloodActors[nSprite]; - } - else - { - BloodStatIterator it(kStatPurge); - actor = it.Next(); - assert(actor != nullptr); - ChangeActorSect(actor, nSector); - actPostSprite(actor, nStat); - } + DBloodActor* actor = InsertSprite(nSector, nStat); vec3_t pos = { x, y, z }; setActorPos(actor, &pos); @@ -6395,18 +6381,7 @@ DBloodActor* actSpawnSprite(int nSector, int x, int y, int z, int nStat, bool se DBloodActor* actSpawnSprite(DBloodActor* source, int nStat) { auto pSource = &source->s(); - int nSprite = InsertSprite(pSource->sectnum, nStat); - DBloodActor* actor; - if (nSprite < 0) - { - BloodStatIterator it(kStatPurge); - actor = it.Next(); - assert(actor); - assert(validSectorIndex(pSource->sectnum)); - ChangeActorSect(actor, pSource->sectnum); - actPostSprite(actor, nStat); - } - else actor = &bloodActors[nSprite]; + DBloodActor* actor = InsertSprite(pSource->sectnum, nStat); spritetype* pSprite = &actor->s(); pSprite->x = pSource->x; diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index 6c588fa4c..24e987230 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -110,7 +110,6 @@ void StartLevel(MapRecord* level, bool newgame) gHealthTemp[i] = gPlayer[i].actor->x().health; } } - memset(xsprite, 0, sizeof(xsprite)); //drawLoadingScreen(); dbLoadMap(currentLevel->fileName, (int*)&startpos.x, (int*)&startpos.y, (int*)&startpos.z, &startang, &startsectnum, nullptr); SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name); diff --git a/source/games/blood/src/bloodactor.h b/source/games/blood/src/bloodactor.h index e6ba531cc..fd4fbdd40 100644 --- a/source/games/blood/src/bloodactor.h +++ b/source/games/blood/src/bloodactor.h @@ -112,18 +112,20 @@ class DBloodActor public: int dudeSlope; + int xvel, yvel, zvel; + bool hasx; + XSPRITE xsprite; SPRITEHIT hit; DUDEEXTRA dudeExtra; SPRITEMASS spriteMass; GENDUDEEXTRA genDudeExtra; DBloodActor* prevmarker; // needed by the nnext marker code. This originally hijacked targetX in XSPRITE POINT3D basePoint; - int xvel, yvel, zvel; + ConditionElement condition[2]; + bool explosionhackflag; // this originally hijacked the target field which is not safe when working with pointers. // transient data (not written to savegame) int cumulDamage; - ConditionElement condition[2]; - bool explosionhackflag; // this originally hijacked the target field which is not safe when working with pointers. bool interpolated; DBloodActor() :index(int(this - base())) {} @@ -138,18 +140,18 @@ public: genDudeExtra = {}; prevmarker = nullptr; basePoint = {}; + xsprite = {}; + hasx = false; interpolated = false; xvel = yvel = zvel = 0; explosionhackflag = false; interpolated = false; } - bool hasX() { return sprite[index].extra > 0; } - void addX() - { - if (s().extra == -1) dbInsertXSprite(s().index); - } + bool hasX() { return hasx; } + void addX() { hasx = true; } + spritetype& s() { return sprite[index]; } - XSPRITE& x() { return xsprite[sprite[index].extra]; } // calling this does not validate the xsprite! + XSPRITE& x() { return xsprite; } // calling this does not validate the xsprite! int GetIndex() { return s().time; } // For error printing only! This is only identical with the sprite index for items spawned at map start. int GetSpriteIndex() { return index; } // this is only here to mark places that need changing later! diff --git a/source/games/blood/src/db.cpp b/source/games/blood/src/db.cpp index 62debe633..f3a11804a 100644 --- a/source/games/blood/src/db.cpp +++ b/source/games/blood/src/db.cpp @@ -44,11 +44,9 @@ DBloodActor bloodActors[kMaxSprites]; bool gModernMap = false; unsigned short gStatCount[kMaxStatus + 1]; -XSPRITE xsprite[kMaxXSprites]; XSECTOR xsector[kMaxXSectors]; XWALL xwall[kMaxXWalls]; -unsigned short nextXSprite[kMaxXSprites]; int XWallsUsed, XSectorsUsed; @@ -174,13 +172,14 @@ void qinitspritelists(void) // Replace Numsprites = 0; } -int InsertSprite(int nSector, int nStat) +DBloodActor* InsertSprite(int nSector, int nStat) { int nSprite = headspritestat[kMaxStatus]; assert(nSprite < kMaxSprites); if (nSprite < 0) { - return nSprite; + I_Error("Out of sprites!"); // we cannot deal with this - and most of the calling code never checks... + return nullptr; } RemoveSpriteStat(nSprite); DBloodActor* actor = &bloodActors[nSprite]; @@ -193,13 +192,12 @@ int InsertSprite(int nSector, int nStat) pSprite->clipdist = 32; pSprite->xrepeat = pSprite->yrepeat = 64; actor->SetOwner(nullptr); - pSprite->extra = -1; pSprite->index = nSprite; Numsprites++; sprite[nSprite].time = leveltimer++; - return nSprite; + return actor; } int DeleteSprite(int nSprite) @@ -207,10 +205,6 @@ int DeleteSprite(int nSprite) FVector3 pos = GetSoundPos(&sprite[nSprite].pos); soundEngine->RelinkSound(SOURCE_Actor, &sprite[nSprite], nullptr, &pos); - if (sprite[nSprite].extra > 0) - { - InsertFree(nextXSprite, sprite[nSprite].extra); - } assert(sprite[nSprite].statnum >= 0 && sprite[nSprite].statnum < kMaxStatus); RemoveSpriteStat(nSprite); assert(validSectorIndex(sprite[nSprite].sectnum)); @@ -259,39 +253,6 @@ void InitFreeList(unsigned short *pList, int nCount) pList[0] = nCount - 1; } -void InitFreeList(unsigned short* pList, int nCount, FixedBitArray&used) -{ - int lastfree = 0; - for (int i = 1; i < nCount; i++) - { - if (!used[i]) - { - pList[i] = lastfree; - lastfree = i; - } - } - pList[0] = lastfree; -} - -void InsertFree(unsigned short *pList, int nIndex) -{ - pList[nIndex] = pList[0]; - pList[0] = nIndex; -} - -unsigned short dbInsertXSprite(int nSprite) -{ - int nXSprite = nextXSprite[0]; - nextXSprite[0] = nextXSprite[nXSprite]; - if (nXSprite == 0) - { - I_Error("Out of free XSprites"); - } - memset(&xsprite[nXSprite], 0, sizeof(XSPRITE)); - bloodActors[nSprite].hit = {}; - sprite[nSprite].extra = nXSprite; - return nXSprite; -} unsigned short dbInsertXWall(int nWall) { @@ -321,7 +282,6 @@ unsigned short dbInsertXSector(int nSector) void dbInit(void) { - InitFreeList(nextXSprite, kMaxXSprites); XWallsUsed = XSectorsUsed = 1; // 0 is not usable because it's the default for 'extra' and some code actually uses it to clobber the contents in here. :( for (int i = 1; i < kMaxXWalls; i++) { @@ -488,8 +448,6 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, int gModernMap = false; #endif - memset(xsprite, 0, sizeof(xsprite)); - #ifdef NOONE_EXTENSIONS for (auto& ctrl : gPlayerCtrl) ctrl.qavScene.initiator = nullptr; #endif @@ -830,7 +788,9 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, int { RemoveSpriteStat(i); spritetypedisk load; - spritetype *pSprite = &sprite[i]; + auto actor = &bloodActors[i]; + actor->Clear(); + spritetype *pSprite = &actor->s(); fr.Read(&load, sizeof(spritetypedisk)); // load into an intermediate buffer so that spritetype is no longer bound by file formats. if (encrypted) // What were these people thinking? :( { @@ -871,9 +831,8 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, int if (pSprite->extra > 0) { char pBuffer[nXSpriteSize]; - int nXSprite = dbInsertXSprite(i); - XSPRITE *pXSprite = &xsprite[nXSprite]; - memset(pXSprite, 0, sizeof(XSPRITE)); + actor->addX(); + XSPRITE *pXSprite = &actor->x(); int nCount; if (!encrypted) { @@ -963,7 +922,6 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, int { sprite[i].cstat &= ~0x30; } - bloodActors[i].Clear(); } unsigned int nCRC = fr.ReadUInt32(); @@ -1064,3 +1022,4 @@ void qloadboard(const char* filename, char flags, vec3_t* dapos, int16_t* daang, Blood::dbLoadMap(filename, &dapos->x, &dapos->y, &dapos->z, daang, dacursectnum, NULL); Blood::dbInit(); // clean up immediately. } + \ No newline at end of file diff --git a/source/games/blood/src/db.h b/source/games/blood/src/db.h index bd0f7ea53..e7e60270f 100644 --- a/source/games/blood/src/db.h +++ b/source/games/blood/src/db.h @@ -293,13 +293,9 @@ extern unsigned short gStatCount[kMaxStatus + 1];; extern bool drawtile2048, encrypted; extern MAPHEADER2 byte_19AE44; -extern XSPRITE xsprite[kMaxXSprites]; extern XSECTOR xsector[kMaxXSectors]; extern XWALL xwall[kMaxXWalls]; -extern FixedBitArray activeXSprites; - - extern uint8_t qsector_filler[kMaxSectors]; extern int gVisibility; @@ -308,7 +304,6 @@ extern const char *gItemText[]; extern const char *gAmmoText[]; extern const char *gWeaponText[]; -extern unsigned short nextXSprite[kMaxXSprites]; extern int XWallsUsed, XSectorsUsed; static inline int GetWallType(int nWall) @@ -348,15 +343,12 @@ void RemoveSpriteSect(int nSprite); void InsertSpriteStat(int nSprite, int nStat); void RemoveSpriteStat(int nSprite); void qinitspritelists(void); -int InsertSprite(int nSector, int nStat); +DBloodActor* InsertSprite(int nSector, int nStat); int DeleteSprite(int nSprite); int ChangeSpriteSect(int nSprite, int nSector); int qchangespritesect(short nSprite, short nSector); int ChangeSpriteStat(int nSprite, int nStatus); void InitFreeList(unsigned short *pList, int nCount); -void InitFreeList(unsigned short* pList, int nCount, FixedBitArray& activeXSprites); -void InsertFree(unsigned short *pList, int nIndex); -unsigned short dbInsertXSprite(int nSprite); unsigned short dbInsertXWall(int nWall); unsigned short dbInsertXSector(int nSector); void dbInit(void); diff --git a/source/games/blood/src/loadsave.cpp b/source/games/blood/src/loadsave.cpp index c16b092cc..c36db1e52 100644 --- a/source/games/blood/src/loadsave.cpp +++ b/source/games/blood/src/loadsave.cpp @@ -38,9 +38,6 @@ BEGIN_BLD_NS void validateLinks(); - -FixedBitArray activeXSprites; - // All AI states for assigning an index. static AISTATE* allAIStates[] = { @@ -448,26 +445,31 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, DUDEEXTRA& w, DUDE if (arc.BeginObject(keyname)) { -#ifdef OLD_SAVEGAME // Note: birthCounter/thinkTime are a union and share the same value (this is used for savefile backwards compatibility - see correct implementation below) - arc("time", w.time, &empty) - ("recoil", w.teslaHit, &empty2) - ("prio", w.prio, &empty) - ("x1", w.stats.birthCounter, &empty) - ("x2", w.stats.thinkTime, &empty) - ("x3", w.stats.active, &empty2) - .EndObject(); -#else arc("time", w.time, &empty) ("teslaHit", w.teslaHit, &empty2) ("prio", w.prio, &empty) ("thinkTime", w.stats.thinkTime, &empty) - ("active", w.stats.active, &empty2) -#endif + ("active", w.stats.active, &empty2); } return arc; } +FSerializer& Serialize(FSerializer& arc, const char* keyname, ConditionElement& w, ConditionElement* def) +{ + int empty = 0; + DBloodActor* empty2 = nullptr; + if (arc.isReading()) w = {}; + + if (arc.BeginObject(keyname)) + { + arc("type", w.type, &empty) + ("index", w.index, &empty) + ("actor", w.actor, &empty2) + .EndObject(); + } + return arc; +} FSerializer& Serialize(FSerializer& arc, const char* keyname, DBloodActor& w, DBloodActor* def) { @@ -480,37 +482,37 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, DBloodActor& w, DB if (arc.BeginObject(keyname)) { -#ifndef OLD_SAVEGAME arc("xvel", w.xvel, def->xvel) ("yvel", w.yvel, def->yvel) - ("zvel", w.zvel, def->zvel); -#endif + ("zvel", w.zvel, def->zvel) + ("hasx", w.hasx, def->hasx); // The rest is only relevant if the actor has an xsprite. if (w.hasX()) { - arc("dudeslope", w.dudeSlope, def->dudeSlope) + arc ("xsprite", w.xsprite, def->xsprite) + ("dudeslope", w.dudeSlope, def->dudeSlope) ("dudeextra", w.dudeExtra, def->dudeExtra) ("explosionflag", w.explosionhackflag, def->explosionhackflag) - ("spritehit", w.hit, def->hit); -#ifndef OLD_SAVEGAME - arc("basepoint", w.basePoint, def->basePoint); -#endif + ("spritehit", w.hit, def->hit) + ("basepoint", w.basePoint, def->basePoint); +#ifdef NOONE_EXTENSIONS if (gModernMap) { - arc("spritemass", w.spriteMass, def->spriteMass); // no treatment for old savegames. If this gets lost it is not critical -#ifndef OLD_SAVEGAME - ("prevmarker", w.prevmarker, def->prevmarker); + arc("spritemass", w.spriteMass, def->spriteMass) // no treatment for old savegames. If this gets lost it is not critical + ("prevmarker", w.prevmarker, def->prevmarker) + .Array("conditions", w.condition, def->condition, 2); + // GenDudeExtra only contains valid info for kDudeModernCustom and kDudeModernCustomBurning so only save when needed as these are not small. if (w.s().type == kDudeModernCustom || w.s().time == kDudeModernCustomBurning) { arc("gendudeextra", w.genDudeExtra); } + } #endif } - } arc.EndObject(); } return arc; @@ -725,31 +727,8 @@ void SerializeState(FSerializer& arc) .Array("xwall", xwall, XWallsUsed) // todo .Array("xsector", xsector, XSectorsUsed) - .SparseArray("actors", bloodActors, kMaxSprites, activeSprites) - .SparseArray("xsprite", xsprite, kMaxXSprites, activeXSprites); + .SparseArray("actors", bloodActors, kMaxSprites, activeSprites); -#ifdef OLD_SAVEGAME - POINT3D baseSprite[kMaxSprites]; - int xvel[kMaxSprites], yvel[kMaxSprites], zvel[kMaxSprites]; - for (int i = 0; i < kMaxSprites; i++) - { - baseSprite[i] = bloodActors[i].basePoint; - xvel[i] = bloodActors[i].xvel; - yvel[i] = bloodActors[i].yvel; - zvel[i] = bloodActors[i].zvel; - } - arc.SparseArray("basesprite", baseSprite, kMaxSprites, activeSprites) - .SparseArray("xvel", xvel, kMaxSprites, activeSprites) - .SparseArray("yvel", yvel, kMaxSprites, activeSprites) - .SparseArray("zvel", zvel, kMaxSprites, activeSprites); - if (arc.isReading()) for (int i = 0; i < kMaxSprites; i++) if (activeSprites[i]) - { - bloodActors[i].basePoint = baseSprite[i]; - bloodActors[i].xvel = xvel[i]; - bloodActors[i].yvel = yvel[i]; - bloodActors[i].zvel = zvel[i]; - } -#endif arc.EndObject(); } } @@ -770,26 +749,15 @@ void GameInterface::SerializeGameState(FSerializer& arc) { if (arc.isWriting()) { - activeXSprites.Zero(); - for (int i = 0; i < kMaxSprites; i++) - { - if (activeSprites[i] && sprite[i].extra > 0) activeXSprites.Set(sprite[i].extra); } - } else { sndKillAllSounds(); sfxKillAllSounds(); ambKillAll(); seqKillAll(); - if (gamestate != GS_LEVEL) - { - memset(xsprite, 0, sizeof(xsprite)); } - } - arc.SerializeMemory("activexsprites", activeXSprites.Storage(), activeXSprites.StorageSize()); SerializeState(arc); - InitFreeList(nextXSprite, kMaxXSprites, activeXSprites); SerializeActor(arc); SerializePlayers(arc); SerializeEvents(arc); diff --git a/source/games/blood/src/nnexts.cpp b/source/games/blood/src/nnexts.cpp index a06ec0676..6e79efebf 100644 --- a/source/games/blood/src/nnexts.cpp +++ b/source/games/blood/src/nnexts.cpp @@ -3608,8 +3608,7 @@ void useSeqSpawnerGen(DBloodActor* sourceactor, int objType, int index, DBloodAc { if (pXSource->data3 > 0) { - int nSpawned = InsertSprite(pSprite->sectnum, kStatDecoration); - auto spawned = &bloodActors[nSpawned]; + auto spawned = InsertSprite(pSprite->sectnum, kStatDecoration); auto pSpawned = &spawned->s(); int top, bottom; GetActorExtents(spawned, &top, &bottom); pSpawned->x = pSprite->x; @@ -9239,36 +9238,6 @@ void SerializeNNExts(FSerializer& arc) { if (arc.BeginObject("nnexts")) { -#ifdef OLD_SAVEGAME - // the GenDudeArray only contains valid info for kDudeModernCustom and kDudeModernCustomBurning so only save the relevant entries as these are not small. - bool foundsome = false; - for (int i = 0; i < kMaxSprites; i++) - { - if (activeSprites[i] && (sprite[i].type == kDudeModernCustom || sprite[i].type == kDudeModernCustomBurning)) - { - if (!foundsome) arc.BeginArray("gendudeextra"); - foundsome = true; - arc(nullptr, bloodActors[i].genDudeExtra); - } - } - if (foundsome) arc.EndArray(); - - // In compatibility mode write this out as a sparse array sorted by xsprite index. - SPRITEMASS gSpriteMass[kMaxSprites]; - for (int i = 0; i < kMaxSprites; i++) - { - int x = sprite[i].extra; - if (x <= 0) continue; - gSpriteMass[x] = bloodActors[i].spriteMass; - } - arc.SparseArray("spritemass", gSpriteMass, kMaxSprites, activeXSprites); - for (int i = 0; i < kMaxSprites; i++) - { - int x = sprite[i].extra; - if (x <= 0) continue; - if (activeXSprites[x]) bloodActors[i].spriteMass = gSpriteMass[x]; - } -#endif arc ("proxyspritescount", gProxySpritesCount) .Array("proxyspriteslist", gProxySpritesList, gProxySpritesCount) ("sightspritescount", gSightSpritesCount) diff --git a/source/games/blood/src/seq.cpp b/source/games/blood/src/seq.cpp index 7e23e8281..9bbaf0fc1 100644 --- a/source/games/blood/src/seq.cpp +++ b/source/games/blood/src/seq.cpp @@ -761,14 +761,9 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, SEQINST& w, SEQINS ("callback", w.callback) ("seqid", w.nSeqID) ("timecounter", w.timeCounter) - ("frameindex", w.frameIndex); -#ifdef OLD_SAVEGAME - if (w.type == SS_SPRITE) arc("index", w.actor); - else arc("index", w.seqindex); -#else - arc("index", w.seqindex) + ("frameindex", w.frameIndex) + ("index", w.seqindex) ("actor", w.actor); -#endif arc.EndObject(); } diff --git a/source/games/blood/src/triggers.cpp b/source/games/blood/src/triggers.cpp index 80fee85b3..520f9f814 100644 --- a/source/games/blood/src/triggers.cpp +++ b/source/games/blood/src/triggers.cpp @@ -2292,20 +2292,6 @@ void SerializeTriggers(FSerializer& arc) { if (arc.BeginObject("triggers")) { -#ifdef OLD_SAVEGAME - if (arc.BeginArray("basepath")) - { - int nul = 0; - for (int i = 0; i < numsectors; i++) - { - if (sector[i].extra > 0) - arc(nullptr, xsector[sector[i].extra].basePath); - else - arc(nullptr, nul); - } - arc.EndArray(); - } -#endif arc("busycount", gBusyCount) .Array("busy", gBusy, gBusyCount) .EndObject();