- Blood: cleanup and simplification of SEQ code.

* removed all the large instance arrays. None of this is really necessary, all we need is a single dynamic array with only the active sequences that can expand as needed.
* added JSON serialization. With the above change this part now becomes trivial.
* renamed all 'atxx' variables in this code.
This commit is contained in:
Christoph Oelckers 2020-11-21 18:12:51 +01:00
parent 4aa06ff702
commit 8bfc6d98d4
5 changed files with 685 additions and 673 deletions

View file

@ -2488,10 +2488,10 @@ void actInit(bool bSaveLoad) {
break;
case kThingBloodChunks: {
SEQINST *pInst = GetInstance(3, pSprite->extra);
if (pInst && pInst->at13) {
auto seq = getSequence(pInst->at8);
if (pInst) {
auto seq = getSequence(pInst->nSeqID);
if (!seq) break;
seqSpawn(pInst->at8, 3, pSprite->extra);
seqSpawn(pInst->nSeqID, 3, pSprite->extra);
}
break;
}

View file

@ -1460,9 +1460,9 @@ int getDispersionModifier(spritetype* pSprite, int minDisp, int maxDisp)
int disp = 1;
if (pSeq != nullptr)
{
int nFrames = pSeq->nFrames; int ticks = pSeq->at8; int shots = 0;
int nFrames = pSeq->nFrames; int ticks = pSeq->ticksPerFrame; int shots = 0;
for (int i = 0; i <= pSeq->nFrames; i++) {
if (pSeq->frames[i].at5_5) shots++;
if (pSeq->frames[i].trigger) shots++;
}
disp = (((shots * 1000) / nFrames) / ticks) * 20;
@ -2040,7 +2040,7 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
{
pExtra->forcePunch = true; // required for those who don't have fire trigger in punch seq and for default animation
for (int i = 0; i < pSeq->nFrames; i++) {
if (!pSeq->frames[i].at5_5) continue;
if (!pSeq->frames[i].trigger) continue;
pExtra->forcePunch = false;
break;
}

View file

@ -476,14 +476,6 @@ void LoadSave::Write(const void *pData, int nSize)
bool GameInterface::LoadGame()
{
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
if (gamestate != GS_LEVEL)
{
memset(xsprite, 0, sizeof(xsprite));
}
LoadSave::hLFile = ReadSavegameChunk("snapshot.bld");
if (!LoadSave::hLFile.isOpen())
return false;
@ -735,7 +727,6 @@ void LevelsLoadSaveConstruct(void);
void MessagesLoadSaveConstruct(void);
void MirrorLoadSaveConstruct(void);
void PlayerLoadSaveConstruct(void);
void SeqLoadSaveConstruct(void);
void TriggersLoadSaveConstruct(void);
void ViewLoadSaveConstruct(void);
void WarpLoadSaveConstruct(void);
@ -754,7 +745,6 @@ void LoadSaveSetup(void)
MessagesLoadSaveConstruct();
MirrorLoadSaveConstruct();
PlayerLoadSaveConstruct();
SeqLoadSaveConstruct();
TriggersLoadSaveConstruct();
ViewLoadSaveConstruct();
WarpLoadSaveConstruct();
@ -765,10 +755,21 @@ void LoadSaveSetup(void)
void SerializeEvents(FSerializer& arc);
void SerializeSequences(FSerializer& arc);
void GameInterface::SerializeGameState(FSerializer& arc)
{
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
if (gamestate != GS_LEVEL)
{
memset(xsprite, 0, sizeof(xsprite));
}
SerializeEvents(arc);
SerializeSequences(arc);
}

View file

@ -2,10 +2,11 @@
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
Copyright (C) 2020 - Christoph Oelckers
This file is part of NBlood.
This file is part of Raze
NBlood is free software; you can redistribute it and/or
Raze 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.
@ -20,9 +21,11 @@ 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 <string.h>
#include "ns.h"
#include "filesystem.h"
#include "tarray.h"
#include "build.h"
#include "common_game.h"
@ -38,7 +41,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "actor.h"
#include "view.h"
#include "raze_sound.h"
#include "seqcb.h"
#include "actor.h"
#include "seq.h"
#include "files.h"
BEGIN_BLD_NS
@ -103,14 +111,12 @@ static void (*seqClientCallback[])(int, int) = {
MGunOpenSeqCallback,
};
enum
{
kMaxSeqClients = 256,
kMaxSequences = 1024
};
static ACTIVE activeList[kMaxSequences];
static int seqActiveCount = 0;
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void Seq::Preload(void)
{
@ -122,7 +128,7 @@ void Seq::Preload(void)
tilePreloadTile(seqGetTile(&frames[i]));
}
void Seq::Precache(HitList &hits)
void Seq::Precache(HitList& hits)
{
if (memcmp(signature, "SEQ\x1a", 4) != 0)
I_Error("Invalid sequence");
@ -132,130 +138,103 @@ void Seq::Precache(HitList &hits)
tilePrecacheTile(seqGetTile(&frames[i]), -1, hits);
}
void seqPrecacheId(int id, HitList &hits)
void seqPrecacheId(int id, HitList& hits)
{
auto pSeq = getSequence(id);
if (pSeq) pSeq->Precache(hits);
}
SEQINST siWall[kMaxXWalls];
SEQINST siCeiling[kMaxXSectors];
SEQINST siFloor[kMaxXSectors];
SEQINST siSprite[kMaxXSprites];
SEQINST siMasked[kMaxXWalls];
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateSprite(int nXSprite, SEQFRAME *pFrame)
void UpdateCeiling(int nXSector, SEQFRAME* pFrame)
{
assert(nXSprite > 0 && nXSprite < kMaxXSprites);
int nSprite = xsprite[nXSprite].reference;
assert(nSprite >= 0 && nSprite < kMaxSprites);
spritetype *pSprite = &sprite[nSprite];
assert(pSprite->extra == nXSprite);
if (pSprite->flags & 2)
{
if (tilesiz[pSprite->picnum].y != tilesiz[seqGetTile(pFrame)].y || tileTopOffset(pSprite->picnum) != tileTopOffset(seqGetTile(pFrame))
|| (pFrame->at3_0 && pFrame->at3_0 != pSprite->yrepeat))
pSprite->flags |= 4;
}
pSprite->picnum = seqGetTile(pFrame);
if (pFrame->at5_0)
pSprite->pal = pFrame->at5_0;
pSprite->shade = pFrame->at4_0;
int scale = xsprite[nXSprite].scale; // SEQ size scaling
if (pFrame->at2_0) {
if (scale) pSprite->xrepeat = ClipRange(mulscale8(pFrame->at2_0, scale), 0, 255);
else pSprite->xrepeat = pFrame->at2_0;
}
if (pFrame->at3_0) {
if (scale) pSprite->yrepeat = ClipRange(mulscale8(pFrame->at3_0, scale), 0, 255);
else pSprite->yrepeat = pFrame->at3_0;
}
if (pFrame->at1_4)
pSprite->cstat |= 2;
else
pSprite->cstat &= ~2;
if (pFrame->at1_5)
pSprite->cstat |= 512;
else
pSprite->cstat &= ~512;
if (pFrame->at1_6)
pSprite->cstat |= 1;
else
pSprite->cstat &= ~1;
if (pFrame->at1_7)
pSprite->cstat |= 256;
else
pSprite->cstat &= ~256;
if (pFrame->at6_2)
pSprite->cstat |= 32768;
else
pSprite->cstat &= (unsigned short)~32768;
if (pFrame->at6_0)
pSprite->cstat |= 4096;
else
pSprite->cstat &= ~4096;
if (pFrame->at5_6)
pSprite->flags |= 256;
else
pSprite->flags &= ~256;
if (pFrame->at5_7)
pSprite->flags |= 8;
else
pSprite->flags &= ~8;
if (pFrame->at6_3)
pSprite->flags |= 1024;
else
pSprite->flags &= ~1024;
if (pFrame->at6_4)
pSprite->flags |= 2048;
else
pSprite->flags &= ~2048;
assert(nXSector > 0 && nXSector < kMaxXSectors);
int nSector = xsector[nXSector].reference;
assert(nSector >= 0 && nSector < kMaxSectors);
sectortype* pSector = &sector[nSector];
assert(pSector->extra == nXSector);
pSector->ceilingpicnum = seqGetTile(pFrame);
pSector->ceilingshade = pFrame->shade;
if (pFrame->palette)
pSector->ceilingpal = pFrame->palette;
}
void UpdateWall(int nXWall, SEQFRAME *pFrame)
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateFloor(int nXSector, SEQFRAME* pFrame)
{
assert(nXSector > 0 && nXSector < kMaxXSectors);
int nSector = xsector[nXSector].reference;
assert(nSector >= 0 && nSector < kMaxSectors);
sectortype* pSector = &sector[nSector];
assert(pSector->extra == nXSector);
pSector->floorpicnum = seqGetTile(pFrame);
pSector->floorshade = pFrame->shade;
if (pFrame->palette)
pSector->floorpal = pFrame->palette;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateWall(int nXWall, SEQFRAME* pFrame)
{
assert(nXWall > 0 && nXWall < kMaxXWalls);
int nWall = xwall[nXWall].reference;
assert(nWall >= 0 && nWall < kMaxWalls);
walltype *pWall = &wall[nWall];
walltype* pWall = &wall[nWall];
assert(pWall->extra == nXWall);
pWall->picnum = seqGetTile(pFrame);
if (pFrame->at5_0)
pWall->pal = pFrame->at5_0;
if (pFrame->at1_4)
if (pFrame->palette)
pWall->pal = pFrame->palette;
if (pFrame->transparent)
pWall->cstat |= 128;
else
pWall->cstat &= ~128;
if (pFrame->at1_5)
if (pFrame->transparent2)
pWall->cstat |= 512;
else
pWall->cstat &= ~512;
if (pFrame->at1_6)
if (pFrame->blockable)
pWall->cstat |= 1;
else
pWall->cstat &= ~1;
if (pFrame->at1_7)
if (pFrame->hittable)
pWall->cstat |= 64;
else
pWall->cstat &= ~64;
}
void UpdateMasked(int nXWall, SEQFRAME *pFrame)
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateMasked(int nXWall, SEQFRAME* pFrame)
{
assert(nXWall > 0 && nXWall < kMaxXWalls);
int nWall = xwall[nXWall].reference;
assert(nWall >= 0 && nWall < kMaxWalls);
walltype *pWall = &wall[nWall];
walltype* pWall = &wall[nWall];
assert(pWall->extra == nXWall);
assert(pWall->nextwall >= 0);
walltype *pWallNext = &wall[pWall->nextwall];
walltype* pWallNext = &wall[pWall->nextwall];
pWall->overpicnum = pWallNext->overpicnum = seqGetTile(pFrame);
if (pFrame->at5_0)
pWall->pal = pWallNext->pal = pFrame->at5_0;
if (pFrame->at1_4)
if (pFrame->palette)
pWall->pal = pWallNext->pal = pFrame->palette;
if (pFrame->transparent)
{
pWall->cstat |= 128;
pWallNext->cstat |= 128;
@ -265,7 +244,7 @@ void UpdateMasked(int nXWall, SEQFRAME *pFrame)
pWall->cstat &= ~128;
pWallNext->cstat &= ~128;
}
if (pFrame->at1_5)
if (pFrame->transparent2)
{
pWall->cstat |= 512;
pWallNext->cstat |= 512;
@ -275,7 +254,7 @@ void UpdateMasked(int nXWall, SEQFRAME *pFrame)
pWall->cstat &= ~512;
pWallNext->cstat &= ~512;
}
if (pFrame->at1_6)
if (pFrame->blockable)
{
pWall->cstat |= 1;
pWallNext->cstat |= 1;
@ -285,7 +264,7 @@ void UpdateMasked(int nXWall, SEQFRAME *pFrame)
pWall->cstat &= ~1;
pWallNext->cstat &= ~1;
}
if (pFrame->at1_7)
if (pFrame->hittable)
{
pWall->cstat |= 64;
pWallNext->cstat |= 64;
@ -297,64 +276,121 @@ void UpdateMasked(int nXWall, SEQFRAME *pFrame)
}
}
void UpdateFloor(int nXSector, SEQFRAME *pFrame)
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateSprite(int nXSprite, SEQFRAME* pFrame)
{
assert(nXSector > 0 && nXSector < kMaxXSectors);
int nSector = xsector[nXSector].reference;
assert(nSector >= 0 && nSector < kMaxSectors);
sectortype *pSector = &sector[nSector];
assert(pSector->extra == nXSector);
pSector->floorpicnum = seqGetTile(pFrame);
pSector->floorshade = pFrame->at4_0;
if (pFrame->at5_0)
pSector->floorpal = pFrame->at5_0;
assert(nXSprite > 0 && nXSprite < kMaxXSprites);
int nSprite = xsprite[nXSprite].reference;
assert(nSprite >= 0 && nSprite < kMaxSprites);
spritetype* pSprite = &sprite[nSprite];
assert(pSprite->extra == nXSprite);
if (pSprite->flags & 2)
{
if (tilesiz[pSprite->picnum].y != tilesiz[seqGetTile(pFrame)].y || tileTopOffset(pSprite->picnum) != tileTopOffset(seqGetTile(pFrame))
|| (pFrame->yrepeat && pFrame->yrepeat != pSprite->yrepeat))
pSprite->flags |= 4;
}
pSprite->picnum = seqGetTile(pFrame);
if (pFrame->palette)
pSprite->pal = pFrame->palette;
pSprite->shade = pFrame->shade;
int scale = xsprite[nXSprite].scale; // SEQ size scaling
if (pFrame->xrepeat) {
if (scale) pSprite->xrepeat = ClipRange(mulscale8(pFrame->xrepeat, scale), 0, 255);
else pSprite->xrepeat = pFrame->xrepeat;
}
if (pFrame->yrepeat) {
if (scale) pSprite->yrepeat = ClipRange(mulscale8(pFrame->yrepeat, scale), 0, 255);
else pSprite->yrepeat = pFrame->yrepeat;
}
if (pFrame->transparent)
pSprite->cstat |= 2;
else
pSprite->cstat &= ~2;
if (pFrame->transparent2)
pSprite->cstat |= 512;
else
pSprite->cstat &= ~512;
if (pFrame->blockable)
pSprite->cstat |= 1;
else
pSprite->cstat &= ~1;
if (pFrame->hittable)
pSprite->cstat |= 256;
else
pSprite->cstat &= ~256;
if (pFrame->invisible)
pSprite->cstat |= 32768;
else
pSprite->cstat &= (unsigned short)~32768;
if (pFrame->pushable)
pSprite->cstat |= 4096;
else
pSprite->cstat &= ~4096;
if (pFrame->smoke)
pSprite->flags |= 256;
else
pSprite->flags &= ~256;
if (pFrame->aiming)
pSprite->flags |= 8;
else
pSprite->flags &= ~8;
if (pFrame->flipx)
pSprite->flags |= 1024;
else
pSprite->flags &= ~1024;
if (pFrame->flipy)
pSprite->flags |= 2048;
else
pSprite->flags &= ~2048;
}
void UpdateCeiling(int nXSector, SEQFRAME *pFrame)
{
assert(nXSector > 0 && nXSector < kMaxXSectors);
int nSector = xsector[nXSector].reference;
assert(nSector >= 0 && nSector < kMaxSectors);
sectortype *pSector = &sector[nSector];
assert(pSector->extra == nXSector);
pSector->ceilingpicnum = seqGetTile(pFrame);
pSector->ceilingshade = pFrame->at4_0;
if (pFrame->at5_0)
pSector->ceilingpal = pFrame->at5_0;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void SEQINST::Update(ACTIVE *pActive)
void SEQINST::Update()
{
assert(frameIndex < pSequence->nFrames);
switch (pActive->type)
switch (type)
{
case 0:
UpdateWall(pActive->xindex, &pSequence->frames[frameIndex]);
UpdateWall(index, &pSequence->frames[frameIndex]);
break;
case 1:
UpdateCeiling(pActive->xindex , &pSequence->frames[frameIndex]);
UpdateCeiling(index, &pSequence->frames[frameIndex]);
break;
case 2:
UpdateFloor(pActive->xindex, &pSequence->frames[frameIndex]);
UpdateFloor(index, &pSequence->frames[frameIndex]);
break;
case 3:
{
UpdateSprite(pActive->xindex, &pSequence->frames[frameIndex]);
if (pSequence->frames[frameIndex].at6_1) {
UpdateSprite(index, &pSequence->frames[frameIndex]);
if (pSequence->frames[frameIndex].playsound) {
int sound = pSequence->ata;
int sound = pSequence->soundId;
// by NoOne: add random sound range feature
if (!VanillaMode() && pSequence->frames[frameIndex].soundRange > 0)
sound += Random(((pSequence->frames[frameIndex].soundRange == 1) ? 2 : pSequence->frames[frameIndex].soundRange));
sfxPlay3DSound(&sprite[xsprite[pActive->xindex].reference], sound, -1, 0);
sfxPlay3DSound(&sprite[xsprite[index].reference], sound, -1, 0);
}
// by NoOne: add surfaceSound trigger feature
spritetype* pSprite = &sprite[xsprite[pActive->xindex].reference];
spritetype* pSprite = &sprite[xsprite[index].reference];
if (!VanillaMode() && pSequence->frames[frameIndex].surfaceSound && zvel[pSprite->index] == 0 && xvel[pSprite->index] != 0) {
if (gUpperLink[pSprite->sectnum] >= 0) break; // don't play surface sound for stacked sectors
@ -390,300 +426,123 @@ void SEQINST::Update(ACTIVE *pActive)
break;
}
case 4:
UpdateMasked(pActive->xindex, &pSequence->frames[frameIndex]);
UpdateMasked(index, &pSequence->frames[frameIndex]);
break;
}
if (pSequence->frames[frameIndex].at5_5 && atc != -1)
seqClientCallback[atc](pActive->type, pActive->xindex);
if (pSequence->frames[frameIndex].trigger && callback != -1)
seqClientCallback[callback](type, index);
}
SEQINST * GetInstance(int a1, int a2)
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
struct ActiveList
{
switch (a1)
TArray<SEQINST> list;
void clear() { list.Clear(); }
int getSize() { return list.Size(); }
SEQINST* getInst(int num) { return &list[num]; }
int getIndex(int num) { return list[num].index; }
int getType(int num) { return list[num].type; }
void remove(int num)
{
case 0:
if (a2 > 0 && a2 < kMaxXWalls) return &siWall[a2];
break;
case 1:
if (a2 > 0 && a2 < kMaxXSectors) return &siCeiling[a2];
break;
case 2:
if (a2 > 0 && a2 < kMaxXSectors) return &siFloor[a2];
break;
case 3:
if (a2 > 0 && a2 < kMaxXSprites) return &siSprite[a2];
break;
case 4:
if (a2 > 0 && a2 < kMaxWalls) return &siMasked[a2];
break;
list[num] = list.Last();
list.Pop();
}
return NULL;
}
void UnlockInstance(SEQINST *pInst)
{
assert(pInst != NULL);
assert(pInst->pSequence != NULL);
pInst->pSequence = NULL;
pInst->at13 = 0;
}
void seqSpawn(int a1, int a2, int a3, int a4)
{
SEQINST *pInst = GetInstance(a2, a3);
if (!pInst) return;
auto pSeq = getSequence(a1);
if (!pSeq)
I_Error("Missing sequence #%d", a1);
int i = seqActiveCount;
if (pInst->at13)
SEQINST* getNew()
{
if (pSeq == pInst->pSequence)
list.Reserve(1);
return &list.Last();
}
SEQINST* get(int type, int index)
{
for (auto& n : list)
{
if (n.type == type && n.index == index) return &n;
}
return nullptr;
}
void remove(int type, int index)
{
for (unsigned i = 0; i < list.Size(); i++)
{
auto& n = list[i];
if (n.type == type && n.index == index)
{
remove(i);
return;
UnlockInstance(pInst);
for (i = 0; i < seqActiveCount; i++)
{
if (activeList[i].type == a2 && activeList[i].xindex == a3)
break;
}
assert(i < seqActiveCount);
}
if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0)
I_Error("Invalid sequence %d", a1);
if ((pSeq->version & 0xff00) != 0x300)
I_Error("Sequence %d is obsolete version", a1);
if ((pSeq->version & 0xff) == 0x00)
{
for (int i = 0; i < pSeq->nFrames; i++)
pSeq->frames[i].tile2 = 0;
}
pInst->at13 = 1;
pInst->pSequence = pSeq;
pInst->at8 = a1;
pInst->atc = a4;
pInst->at10 = pSeq->at8;
pInst->frameIndex = 0;
if (i == seqActiveCount)
{
assert(seqActiveCount < kMaxSequences);
activeList[seqActiveCount].type = a2;
activeList[seqActiveCount].xindex = a3;
seqActiveCount++;
}
pInst->Update(&activeList[i]);
}
void seqKill(int a1, int a2)
{
SEQINST *pInst = GetInstance(a1, a2);
if (!pInst || !pInst->at13)
return;
int i;
for (i = 0; i < seqActiveCount; i++)
{
if (activeList[i].type == a1 && activeList[i].xindex == a2)
break;
}
assert(i < seqActiveCount);
seqActiveCount--;
activeList[i] = activeList[seqActiveCount];
pInst->at13 = 0;
UnlockInstance(pInst);
}
void seqKillAll(void)
{
for (int i = 0; i < kMaxXWalls; i++)
{
if (siWall[i].at13)
UnlockInstance(&siWall[i]);
if (siMasked[i].at13)
UnlockInstance(&siMasked[i]);
}
for (int i = 0; i < kMaxXSectors; i++)
{
if (siCeiling[i].at13)
UnlockInstance(&siCeiling[i]);
if (siFloor[i].at13)
UnlockInstance(&siFloor[i]);
}
for (int i = 0; i < kMaxXSprites; i++)
{
if (siSprite[i].at13)
UnlockInstance(&siSprite[i]);
}
seqActiveCount = 0;
}
int seqGetStatus(int a1, int a2)
{
SEQINST *pInst = GetInstance(a1, a2);
if (pInst && pInst->at13)
return pInst->frameIndex;
return -1;
}
int seqGetID(int a1, int a2)
{
SEQINST *pInst = GetInstance(a1, a2);
if (pInst)
return pInst->at8;
return -1;
}
void seqProcess(int a1)
{
for (int i = 0; i < seqActiveCount; i++)
{
SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex);
Seq *pSeq = pInst->pSequence;
assert(pInst->frameIndex < pSeq->nFrames);
pInst->at10 -= a1;
while (pInst->at10 < 0)
{
pInst->at10 += pSeq->at8;
pInst->frameIndex++;
if (pInst->frameIndex == pSeq->nFrames)
{
if (pSeq->atc & 1)
pInst->frameIndex = 0;
else
{
UnlockInstance(pInst);
if (pSeq->atc & 2)
{
switch (activeList[i].type)
{
case 3:
{
int nXSprite = activeList[i].xindex;
int nSprite = xsprite[nXSprite].reference;
assert(nSprite >= 0 && nSprite < kMaxSprites);
evKill(nSprite, 3);
if ((sprite[nSprite].flags & kHitagRespawn) && sprite[nSprite].inittype >= kDudeBase && sprite[nSprite].inittype < kDudeMax)
evPost(nSprite, 3, gGameOptions.nMonsterRespawnTime, kCallbackRespawn);
else
DeleteSprite(nSprite);
break;
}
case 4:
{
int nXWall = activeList[i].xindex;
int nWall = xwall[nXWall].reference;
assert(nWall >= 0 && nWall < kMaxWalls);
wall[nWall].cstat &= ~(8 + 16 + 32);
if (wall[nWall].nextwall != -1)
wall[wall[nWall].nextwall].cstat &= ~(8 + 16 + 32);
break;
}
}
}
activeList[i--] = activeList[--seqActiveCount];
break;
}
}
pInst->Update(&activeList[i]);
}
}
}
class SeqLoadSave : public LoadSave {
virtual void Load(void);
virtual void Save(void);
};
void SeqLoadSave::Load(void)
static ActiveList activeList;
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
SEQINST* GetInstance(int type, int nXIndex)
{
Read(&siWall, sizeof(siWall));
Read(&siMasked, sizeof(siMasked));
Read(&siCeiling, sizeof(siCeiling));
Read(&siFloor, sizeof(siFloor));
Read(&siSprite, sizeof(siSprite));
Read(&activeList, sizeof(activeList));
Read(&seqActiveCount, sizeof(seqActiveCount));
for (int i = 0; i < kMaxXWalls; i++)
{
siWall[i].pSequence = NULL;
siMasked[i].pSequence = NULL;
}
for (int i = 0; i < kMaxXSectors; i++)
{
siCeiling[i].pSequence = NULL;
siFloor[i].pSequence = NULL;
}
for (int i = 0; i < kMaxXSprites; i++)
{
siSprite[i].pSequence = NULL;
}
for (int i = 0; i < seqActiveCount; i++)
{
SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex);
if (pInst->at13)
{
int nSeq = pInst->at8;
auto pSeq = getSequence(nSeq);
if (!pSeq) {
I_Error("Missing sequence #%d", nSeq);
continue;
}
if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0)
I_Error("Invalid sequence %d", nSeq);
if ((pSeq->version & 0xff00) != 0x300)
I_Error("Sequence %d is obsolete version", nSeq);
pInst->pSequence = pSeq;
}
}
return activeList.get(type, nXIndex);
}
void SeqLoadSave::Save(void)
void seqKill(int type, int nXIndex)
{
Write(&siWall, sizeof(siWall));
Write(&siMasked, sizeof(siMasked));
Write(&siCeiling, sizeof(siCeiling));
Write(&siFloor, sizeof(siFloor));
Write(&siSprite, sizeof(siSprite));
Write(&activeList, sizeof(activeList));
Write(&seqActiveCount, sizeof(seqActiveCount));
activeList.remove(type, nXIndex);
}
void SeqLoadSaveConstruct(void)
void seqKillAll()
{
new SeqLoadSave();
activeList.clear();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void ByteSwapSEQ(Seq* pSeq)
{
#if B_BIG_ENDIAN == 1
#if 1//B_BIG_ENDIAN == 1
pSeq->version = LittleShort(pSeq->version);
pSeq->nFrames = LittleShort(pSeq->nFrames);
pSeq->at8 = LittleShort(pSeq->at8);
pSeq->ata = LittleShort(pSeq->ata);
pSeq->atc = LittleLong(pSeq->atc);
pSeq->ticksPerFrame = LittleShort(pSeq->ticksPerFrame);
pSeq->soundId = LittleShort(pSeq->soundId);
pSeq->flags = LittleLong(pSeq->flags);
for (int i = 0; i < pSeq->nFrames; i++)
{
SEQFRAME* pFrame = &pSeq->frames[i];
BitReader bitReader((char*)pFrame, sizeof(SEQFRAME));
SEQFRAME swapFrame;
swapFrame.tile = bitReader.readUnsigned(12);
swapFrame.at1_4 = bitReader.readBit();
swapFrame.at1_5 = bitReader.readBit();
swapFrame.at1_6 = bitReader.readBit();
swapFrame.at1_7 = bitReader.readBit();
swapFrame.at2_0 = bitReader.readUnsigned(8);
swapFrame.at3_0 = bitReader.readUnsigned(8);
swapFrame.at4_0 = bitReader.readSigned(8);
swapFrame.at5_0 = bitReader.readUnsigned(5);
swapFrame.at5_5 = bitReader.readBit();
swapFrame.at5_6 = bitReader.readBit();
swapFrame.at5_7 = bitReader.readBit();
swapFrame.at6_0 = bitReader.readBit();
swapFrame.at6_1 = bitReader.readBit();
swapFrame.at6_2 = bitReader.readBit();
swapFrame.at6_3 = bitReader.readBit();
swapFrame.at6_4 = bitReader.readBit();
swapFrame.transparent = bitReader.readBit();
swapFrame.transparent2 = bitReader.readBit();
swapFrame.blockable = bitReader.readBit();
swapFrame.hittable = bitReader.readBit();
swapFrame.xrepeat = bitReader.readUnsigned(8);
swapFrame.yrepeat = bitReader.readUnsigned(8);
swapFrame.shade = bitReader.readSigned(8);
swapFrame.palette = bitReader.readUnsigned(5);
swapFrame.trigger = bitReader.readBit();
swapFrame.smoke = bitReader.readBit();
swapFrame.aiming = bitReader.readBit();
swapFrame.pushable = bitReader.readBit();
swapFrame.playsound = bitReader.readBit();
swapFrame.invisible = bitReader.readBit();
swapFrame.flipx = bitReader.readBit();
swapFrame.flipy = bitReader.readBit();
swapFrame.tile2 = bitReader.readUnsigned(4);
swapFrame.soundRange = bitReader.readUnsigned(4);
swapFrame.surfaceSound = bitReader.readBit();
@ -693,10 +552,6 @@ static void ByteSwapSEQ(Seq* pSeq)
#endif
}
// This is to eliminate a huge design issue in NBlood that was apparently copied verbatim from the DOS-Version.
// Sequences were cached in the resource and directly returned from there in writable form, with byte swapping directly performed in the cache on Big Endian systems.
// To avoid such unsafe operations this caches the read data separately in static memory that's guaranteed not to be touched by the file system.
FMemArena seqcache;
static TMap<int, Seq*> sequences;
Seq* getSequence(int res_id)
@ -717,4 +572,148 @@ Seq* getSequence(int res_id)
return seqdata;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void seqSpawn(int nSeqID, int type, int nXIndex, int callback)
{
Seq* pSequence = getSequence(nSeqID);
if (pSequence == nullptr) return;
SEQINST* pInst = activeList.get(type, nXIndex);
if (!pInst)
{
pInst = activeList.getNew();
}
else
{
// already playing this sequence?
if (pInst->nSeqID == nSeqID)
return;
}
pInst->pSequence = pSequence;
pInst->nSeqID = nSeqID;
pInst->callback = callback;
pInst->timeCounter = (short)pSequence->ticksPerFrame;
pInst->frameIndex = 0;
pInst->type = type;
pInst->index = nXIndex;
pInst->Update();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int seqGetStatus(int type, int nXIndex)
{
SEQINST* pInst = activeList.get(type, nXIndex);
if (pInst) return pInst->frameIndex;
return -1;
}
int seqGetID(int type, int nXIndex)
{
SEQINST* pInst = activeList.get(type, nXIndex);
if (pInst) return pInst->nSeqID;
return -1;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void seqProcess(int nTicks)
{
for (int i = 0; i < activeList.getSize(); i++)
{
SEQINST* pInst = activeList.getInst(i);
Seq* pSeq = pInst->pSequence;
int index = pInst->index;
assert(pInst->frameIndex < pSeq->nFrames);
pInst->timeCounter -= nTicks;
while (pInst->timeCounter < 0)
{
pInst->timeCounter += pSeq->ticksPerFrame;
++pInst->frameIndex;
if (pInst->frameIndex == pSeq->nFrames)
{
if (!pSeq->isLooping())
{
if (pSeq->isRemovable())
{
if (pInst->type == SS_SPRITE)
{
short nSprite = (short)xsprite[index].reference;
assert(nSprite >= 0 && nSprite < kMaxSprites);
evKill(nSprite, SS_SPRITE);
if ((sprite[nSprite].hitag & kAttrRespawn) != 0 && (sprite[nSprite].zvel >= kDudeBase && sprite[nSprite].zvel < kDudeMax))
evPost(nSprite, 3, gGameOptions.nMonsterRespawnTime, kCallbackRespawn);
else deletesprite(nSprite); // safe to not use actPostSprite here
}
if (pInst->type == SS_MASKED)
{
int nWall = xwall[index].reference;
assert(nWall >= 0 && nWall < kMaxWalls);
wall[nWall].cstat &= ~(8 + 16 + 32);
if (wall[nWall].nextwall != -1)
wall[wall[nWall].nextwall].cstat &= ~(8 + 16 + 32);
}
}
// remove it from the active list
activeList.remove(i--);
break;
}
else pInst->frameIndex = 0;
}
pInst->Update();
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
FSerializer& Serialize(FSerializer& arc, const char* keyname, SEQINST& w, SEQINST* def)
{
if (arc.BeginObject(keyname))
{
arc("index", w.index)
("type", w.type)
("callback", w.callback)
("seqid", w.nSeqID)
("timecounter", w.timeCounter)
("frameindex", w.frameIndex)
.EndObject();
}
if (arc.isReading())
{
w.pSequence = getSequence(w.nSeqID);
}
return arc;
}
void SerializeSequences(FSerializer& arc)
{
arc("sequences", activeList.list);
}
END_BLD_NS

View file

@ -26,24 +26,25 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_BLD_NS
struct SEQFRAME {
struct SEQFRAME
{
unsigned int tile : 12;
unsigned int at1_4 : 1; // transparent
unsigned int at1_5 : 1; // transparent
unsigned int at1_6 : 1; // blockable
unsigned int at1_7 : 1; // hittable
unsigned int at2_0 : 8; // xrepeat
unsigned int at3_0 : 8; // yrepeat
signed int at4_0 : 8; // shade
unsigned int at5_0 : 5; // palette
unsigned int at5_5 : 1; //
unsigned int at5_6 : 1; //
unsigned int at5_7 : 1; //
unsigned int at6_0 : 1; //
unsigned int at6_1 : 1; //
unsigned int at6_2 : 1; // invisible
unsigned int at6_3 : 1; //
unsigned int at6_4 : 1; //
unsigned int transparent : 1;
unsigned int transparent2 : 1;
unsigned int blockable : 1;
unsigned int hittable : 1;
unsigned int xrepeat : 8;
unsigned int yrepeat : 8;
signed int shade : 8;
unsigned int palette : 5;
unsigned int trigger : 1;
unsigned int smoke : 1;
unsigned int aiming : 1;
unsigned int pushable : 1;
unsigned int playsound : 1;
unsigned int invisible : 1;// invisible
unsigned int flipx : 1;
unsigned int flipy : 1;
unsigned int tile2 : 4;
unsigned soundRange : 4; // (by NoOne) random sound range relative to global SEQ sound
unsigned surfaceSound : 1; // (by NoOne) trigger surface sound when moving / touching
@ -53,13 +54,23 @@ struct SEQFRAME {
struct Seq {
char signature[4];
short version;
short nFrames; // at6
short at8;
short ata;
int atc;
short nFrames;
short ticksPerFrame;
short soundId;
int flags;
SEQFRAME frames[1];
void Preload(void);
void Precache(HitList &);
void Precache(HitList&);
bool isLooping()
{
return (flags & 1) != 0;
}
bool isRemovable()
{
return (flags & 2) != 0;
}
};
struct ACTIVE
@ -70,24 +81,25 @@ struct ACTIVE
struct SEQINST
{
Seq *pSequence;
int at8;
int atc;
short at10;
Seq* pSequence;
int index, type;
int nSeqID;
int callback;
short timeCounter;
unsigned char frameIndex;
char at13;
void Update(ACTIVE *pActive);
void Update();
};
inline int seqGetTile(SEQFRAME* pFrame)
{
return pFrame->tile+(pFrame->tile2<<12);
return pFrame->tile + (pFrame->tile2 << 12);
}
int seqRegisterClient(void(*pClient)(int, int));
void seqPrecacheId(int id, HitList &hits);
SEQINST * GetInstance(int a1, int a2);
void UnlockInstance(SEQINST *pInst);
void seqPrecacheId(int id, HitList& hits);
SEQINST* GetInstance(int a1, int a2);
void UnlockInstance(SEQINST* pInst);
void seqSpawn(int a1, int a2, int a3, int a4 = -1);
void seqKill(int a1, int a2);
void seqKillAll(void);