- moved xsprite into DBloodActor and save all relevant parts of DBloodActor.

This completes Blood's transition.
This commit is contained in:
Christoph Oelckers 2021-09-05 10:09:59 +02:00
parent 766d87106a
commit 6705e8e51e
10 changed files with 58 additions and 214 deletions

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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!

View file

@ -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<MAXSPRITES>&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.
}

View file

@ -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<MAXSPRITES> 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<MAXSPRITES>& 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);

View file

@ -38,9 +38,6 @@ BEGIN_BLD_NS
void validateLinks();
FixedBitArray<MAXSPRITES> 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);

View file

@ -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)

View file

@ -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();
}

View file

@ -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();