diff --git a/source/blood/src/actor.cpp b/source/blood/src/actor.cpp index aa9540c66..763ef42ac 100644 --- a/source/blood/src/actor.cpp +++ b/source/blood/src/actor.cpp @@ -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; } diff --git a/source/blood/src/aiunicult.cpp b/source/blood/src/aiunicult.cpp index 60abee088..98d1b39e5 100644 --- a/source/blood/src/aiunicult.cpp +++ b/source/blood/src/aiunicult.cpp @@ -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; } diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp index de3aef3cb..1a4aac992 100644 --- a/source/blood/src/loadsave.cpp +++ b/source/blood/src/loadsave.cpp @@ -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); } diff --git a/source/blood/src/seq.cpp b/source/blood/src/seq.cpp index 523e581a5..5664e6d64 100644 --- a/source/blood/src/seq.cpp +++ b/source/blood/src/seq.cpp @@ -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 +#include "ns.h" +#include "filesystem.h" +#include "tarray.h" + #include "build.h" #include "common_game.h" @@ -38,683 +41,679 @@ 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 static void (*seqClientCallback[])(int, int) = { - FireballSeqCallback, - sub_38938, - NapalmSeqCallback, - sub_3888C, - TreeToGibCallback, - DudeToGibCallback1, - DudeToGibCallback2, - batBiteSeqCallback, - SlashSeqCallback, - StompSeqCallback, - eelBiteSeqCallback, - BurnSeqCallback, - SeqAttackCallback, - cerberusBiteSeqCallback, - cerberusBurnSeqCallback, - cerberusBurnSeqCallback2, - TommySeqCallback, - TeslaSeqCallback, - ShotSeqCallback, - cultThrowSeqCallback, - sub_68170, - sub_68230, - SlashFSeqCallback, - ThrowFSeqCallback, - ThrowSSeqCallback, - BlastSSeqCallback, - ghostSlashSeqCallback, - ghostThrowSeqCallback, - ghostBlastSeqCallback, - GillBiteSeqCallback, - HandJumpSeqCallback, - houndBiteSeqCallback, - houndBurnSeqCallback, - sub_6FFA0, - sub_70284, - sub_6FF08, - sub_6FF54, - ratBiteSeqCallback, - SpidBiteSeqCallback, - SpidJumpSeqCallback, - sub_71370, - sub_71BD4, - sub_720AC, - sub_71A90, - genDudeAttack1, - punchCallback, - ThrowCallback1, - ThrowCallback2, - HackSeqCallback, - StandSeqCallback, - zombfHackSeqCallback, - PukeSeqCallback, - ThrowSeqCallback, - PlayerSurvive, - PlayerKneelsOver, - FireballTrapSeqCallback, - MGunFireSeqCallback, - MGunOpenSeqCallback, + FireballSeqCallback, + sub_38938, + NapalmSeqCallback, + sub_3888C, + TreeToGibCallback, + DudeToGibCallback1, + DudeToGibCallback2, + batBiteSeqCallback, + SlashSeqCallback, + StompSeqCallback, + eelBiteSeqCallback, + BurnSeqCallback, + SeqAttackCallback, + cerberusBiteSeqCallback, + cerberusBurnSeqCallback, + cerberusBurnSeqCallback2, + TommySeqCallback, + TeslaSeqCallback, + ShotSeqCallback, + cultThrowSeqCallback, + sub_68170, + sub_68230, + SlashFSeqCallback, + ThrowFSeqCallback, + ThrowSSeqCallback, + BlastSSeqCallback, + ghostSlashSeqCallback, + ghostThrowSeqCallback, + ghostBlastSeqCallback, + GillBiteSeqCallback, + HandJumpSeqCallback, + houndBiteSeqCallback, + houndBurnSeqCallback, + sub_6FFA0, + sub_70284, + sub_6FF08, + sub_6FF54, + ratBiteSeqCallback, + SpidBiteSeqCallback, + SpidJumpSeqCallback, + sub_71370, + sub_71BD4, + sub_720AC, + sub_71A90, + genDudeAttack1, + punchCallback, + ThrowCallback1, + ThrowCallback2, + HackSeqCallback, + StandSeqCallback, + zombfHackSeqCallback, + PukeSeqCallback, + ThrowSeqCallback, + PlayerSurvive, + PlayerKneelsOver, + FireballTrapSeqCallback, + MGunFireSeqCallback, + MGunOpenSeqCallback, }; -enum -{ - kMaxSeqClients = 256, - kMaxSequences = 1024 -}; -static ACTIVE activeList[kMaxSequences]; -static int seqActiveCount = 0; +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- void Seq::Preload(void) { - if (memcmp(signature, "SEQ\x1a", 4) != 0) - I_Error("Invalid sequence"); - if ((version & 0xff00) != 0x300) - I_Error("Obsolete sequence version"); - for (int i = 0; i < nFrames; i++) - tilePreloadTile(seqGetTile(&frames[i])); + if (memcmp(signature, "SEQ\x1a", 4) != 0) + I_Error("Invalid sequence"); + if ((version & 0xff00) != 0x300) + I_Error("Obsolete sequence version"); + for (int i = 0; i < nFrames; i++) + 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"); - if ((version & 0xff00) != 0x300) - I_Error("Obsolete sequence version"); - for (int i = 0; i < nFrames; i++) - tilePrecacheTile(seqGetTile(&frames[i]), -1, hits); + if (memcmp(signature, "SEQ\x1a", 4) != 0) + I_Error("Invalid sequence"); + if ((version & 0xff00) != 0x300) + I_Error("Obsolete sequence version"); + for (int i = 0; i < nFrames; i++) + 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); + 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 = §or[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(nXWall > 0 && nXWall < kMaxXWalls); - int nWall = xwall[nXWall].reference; - assert(nWall >= 0 && nWall < kMaxWalls); - 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) - pWall->cstat |= 128; - else - pWall->cstat &= ~128; - if (pFrame->at1_5) - pWall->cstat |= 512; - else - pWall->cstat &= ~512; - if (pFrame->at1_6) - pWall->cstat |= 1; - else - pWall->cstat &= ~1; - if (pFrame->at1_7) - pWall->cstat |= 64; - else - pWall->cstat &= ~64; + assert(nXSector > 0 && nXSector < kMaxXSectors); + int nSector = xsector[nXSector].reference; + assert(nSector >= 0 && nSector < kMaxSectors); + sectortype* pSector = §or[nSector]; + assert(pSector->extra == nXSector); + pSector->floorpicnum = seqGetTile(pFrame); + pSector->floorshade = pFrame->shade; + if (pFrame->palette) + pSector->floorpal = pFrame->palette; } -void UpdateMasked(int nXWall, SEQFRAME *pFrame) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +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]; - assert(pWall->extra == nXWall); - assert(pWall->nextwall >= 0); - 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) - { - pWall->cstat |= 128; - pWallNext->cstat |= 128; - } - else - { - pWall->cstat &= ~128; - pWallNext->cstat &= ~128; - } - if (pFrame->at1_5) - { - pWall->cstat |= 512; - pWallNext->cstat |= 512; - } - else - { - pWall->cstat &= ~512; - pWallNext->cstat &= ~512; - } - if (pFrame->at1_6) - { - pWall->cstat |= 1; - pWallNext->cstat |= 1; - } - else - { - pWall->cstat &= ~1; - pWallNext->cstat &= ~1; - } - if (pFrame->at1_7) - { - pWall->cstat |= 64; - pWallNext->cstat |= 64; - } - else - { - pWall->cstat &= ~64; - pWallNext->cstat &= ~64; - } + assert(nXWall > 0 && nXWall < kMaxXWalls); + int nWall = xwall[nXWall].reference; + assert(nWall >= 0 && nWall < kMaxWalls); + walltype* pWall = &wall[nWall]; + assert(pWall->extra == nXWall); + pWall->picnum = seqGetTile(pFrame); + if (pFrame->palette) + pWall->pal = pFrame->palette; + if (pFrame->transparent) + pWall->cstat |= 128; + else + pWall->cstat &= ~128; + if (pFrame->transparent2) + pWall->cstat |= 512; + else + pWall->cstat &= ~512; + if (pFrame->blockable) + pWall->cstat |= 1; + else + pWall->cstat &= ~1; + if (pFrame->hittable) + pWall->cstat |= 64; + else + pWall->cstat &= ~64; } -void UpdateFloor(int nXSector, SEQFRAME *pFrame) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void UpdateMasked(int nXWall, SEQFRAME* pFrame) { - assert(nXSector > 0 && nXSector < kMaxXSectors); - int nSector = xsector[nXSector].reference; - assert(nSector >= 0 && nSector < kMaxSectors); - sectortype *pSector = §or[nSector]; - assert(pSector->extra == nXSector); - pSector->floorpicnum = seqGetTile(pFrame); - pSector->floorshade = pFrame->at4_0; - if (pFrame->at5_0) - pSector->floorpal = pFrame->at5_0; + assert(nXWall > 0 && nXWall < kMaxXWalls); + int nWall = xwall[nXWall].reference; + assert(nWall >= 0 && nWall < kMaxWalls); + walltype* pWall = &wall[nWall]; + assert(pWall->extra == nXWall); + assert(pWall->nextwall >= 0); + walltype* pWallNext = &wall[pWall->nextwall]; + pWall->overpicnum = pWallNext->overpicnum = seqGetTile(pFrame); + if (pFrame->palette) + pWall->pal = pWallNext->pal = pFrame->palette; + if (pFrame->transparent) + { + pWall->cstat |= 128; + pWallNext->cstat |= 128; + } + else + { + pWall->cstat &= ~128; + pWallNext->cstat &= ~128; + } + if (pFrame->transparent2) + { + pWall->cstat |= 512; + pWallNext->cstat |= 512; + } + else + { + pWall->cstat &= ~512; + pWallNext->cstat &= ~512; + } + if (pFrame->blockable) + { + pWall->cstat |= 1; + pWallNext->cstat |= 1; + } + else + { + pWall->cstat &= ~1; + pWallNext->cstat &= ~1; + } + if (pFrame->hittable) + { + pWall->cstat |= 64; + pWallNext->cstat |= 64; + } + else + { + pWall->cstat &= ~64; + pWallNext->cstat &= ~64; + } } -void UpdateCeiling(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 = §or[nSector]; - assert(pSector->extra == nXSector); - pSector->ceilingpicnum = seqGetTile(pFrame); - pSector->ceilingshade = pFrame->at4_0; - if (pFrame->at5_0) - pSector->ceilingpal = 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 SEQINST::Update(ACTIVE *pActive) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void SEQINST::Update() { - assert(frameIndex < pSequence->nFrames); - switch (pActive->type) - { - case 0: - UpdateWall(pActive->xindex, &pSequence->frames[frameIndex]); - break; - case 1: - UpdateCeiling(pActive->xindex , &pSequence->frames[frameIndex]); - break; - case 2: - UpdateFloor(pActive->xindex, &pSequence->frames[frameIndex]); - break; - case 3: - { + assert(frameIndex < pSequence->nFrames); + switch (type) + { + case 0: + UpdateWall(index, &pSequence->frames[frameIndex]); + break; + case 1: + UpdateCeiling(index, &pSequence->frames[frameIndex]); + break; + case 2: + UpdateFloor(index, &pSequence->frames[frameIndex]); + break; + case 3: + { - UpdateSprite(pActive->xindex, &pSequence->frames[frameIndex]); - if (pSequence->frames[frameIndex].at6_1) { - - int sound = pSequence->ata; - - // 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); - } + UpdateSprite(index, &pSequence->frames[frameIndex]); + if (pSequence->frames[frameIndex].playsound) { - - // by NoOne: add surfaceSound trigger feature - spritetype* pSprite = &sprite[xsprite[pActive->xindex].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 - int surf = tileGetSurfType(pSprite->sectnum + 0x4000); if (!surf) break; - static int surfSfxMove[15][4] = { - /* {snd1, snd2, gameVolume, myVolume} */ - {800,801,80,25}, - {802,803,80,25}, - {804,805,80,25}, - {806,807,80,25}, - {808,809,80,25}, - {810,811,80,25}, - {812,813,80,25}, - {814,815,80,25}, - {816,817,80,25}, - {818,819,80,25}, - {820,821,80,25}, - {822,823,80,25}, - {824,825,80,25}, - {826,827,80,25}, - {828,829,80,25}, - }; + int sound = pSequence->soundId; - int sndId = surfSfxMove[surf][Random(2)]; - auto snd = soundEngine->FindSoundByResID(sndId); - if (snd > 0) - { - auto udata = soundEngine->GetUserData(snd); - int relVol = udata ? udata[2] : 255; - sfxPlay3DSoundCP(pSprite, sndId, -1, 0, 0, (surfSfxMove[surf][2] != relVol) ? relVol : surfSfxMove[surf][3]); - } - } - break; - } - case 4: - UpdateMasked(pActive->xindex, &pSequence->frames[frameIndex]); - break; - } - if (pSequence->frames[frameIndex].at5_5 && atc != -1) - seqClientCallback[atc](pActive->type, pActive->xindex); + // 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[index].reference], sound, -1, 0); + } + + + // by NoOne: add surfaceSound trigger feature + 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 + int surf = tileGetSurfType(pSprite->sectnum + 0x4000); if (!surf) break; + static int surfSfxMove[15][4] = { + /* {snd1, snd2, gameVolume, myVolume} */ + {800,801,80,25}, + {802,803,80,25}, + {804,805,80,25}, + {806,807,80,25}, + {808,809,80,25}, + {810,811,80,25}, + {812,813,80,25}, + {814,815,80,25}, + {816,817,80,25}, + {818,819,80,25}, + {820,821,80,25}, + {822,823,80,25}, + {824,825,80,25}, + {826,827,80,25}, + {828,829,80,25}, + }; + + int sndId = surfSfxMove[surf][Random(2)]; + auto snd = soundEngine->FindSoundByResID(sndId); + if (snd > 0) + { + auto udata = soundEngine->GetUserData(snd); + int relVol = udata ? udata[2] : 255; + sfxPlay3DSoundCP(pSprite, sndId, -1, 0, 0, (surfSfxMove[surf][2] != relVol) ? relVol : surfSfxMove[surf][3]); + } + } + break; + } + case 4: + UpdateMasked(index, &pSequence->frames[frameIndex]); + break; + } + if (pSequence->frames[frameIndex].trigger && callback != -1) + seqClientCallback[callback](type, index); } -SEQINST * GetInstance(int a1, int a2) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +struct ActiveList { - switch (a1) - { - 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; - } - return NULL; -} + TArray list; -void UnlockInstance(SEQINST *pInst) -{ - assert(pInst != NULL); - assert(pInst->pSequence != NULL); - pInst->pSequence = NULL; - pInst->at13 = 0; -} + 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 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); + void remove(int num) + { + list[num] = list.Last(); + list.Pop(); + } - int i = seqActiveCount; - if (pInst->at13) - { - if (pSeq == pInst->pSequence) - 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]); -} + SEQINST* getNew() + { + list.Reserve(1); + return &list.Last(); + } -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); -} + SEQINST* get(int type, int index) + { + for (auto& n : list) + { + if (n.type == type && n.index == index) return &n; + } + return nullptr; + } -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 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; + } + } + } }; -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 - 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); - 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.tile2 = bitReader.readUnsigned(4); - swapFrame.soundRange = bitReader.readUnsigned(4); - swapFrame.surfaceSound = bitReader.readBit(); - swapFrame.reserved = bitReader.readUnsigned(2); - *pFrame = swapFrame; - } +#if 1//B_BIG_ENDIAN == 1 + pSeq->version = LittleShort(pSeq->version); + pSeq->nFrames = LittleShort(pSeq->nFrames); + 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.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(); + swapFrame.reserved = bitReader.readUnsigned(2); + *pFrame = swapFrame; + } #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 sequences; Seq* getSequence(int res_id) { - auto p = sequences.CheckKey(res_id); - if (p != nullptr) return *p; + auto p = sequences.CheckKey(res_id); + if (p != nullptr) return *p; - int index = fileSystem.FindResource(res_id, "SEQ"); - if (index < 0) - { - return nullptr; - } - auto fr = fileSystem.OpenFileReader(index); - auto seqdata = (Seq*)seqcache.Alloc(fr.GetLength()); - fr.Read(seqdata, fr.GetLength()); - sequences.Insert(res_id, seqdata); - ByteSwapSEQ(seqdata); - return seqdata; + int index = fileSystem.FindResource(res_id, "SEQ"); + if (index < 0) + { + return nullptr; + } + auto fr = fileSystem.OpenFileReader(index); + auto seqdata = (Seq*)seqcache.Alloc(fr.GetLength()); + fr.Read(seqdata, fr.GetLength()); + sequences.Insert(res_id, seqdata); + ByteSwapSEQ(seqdata); + 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 diff --git a/source/blood/src/seq.h b/source/blood/src/seq.h index 7195fa51f..538f460e9 100644 --- a/source/blood/src/seq.h +++ b/source/blood/src/seq.h @@ -26,68 +26,80 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS -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 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 - unsigned reserved : 2; +struct SEQFRAME +{ + unsigned int tile : 12; + 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 + unsigned reserved : 2; }; struct Seq { - char signature[4]; - short version; - short nFrames; // at6 - short at8; - short ata; - int atc; - SEQFRAME frames[1]; - void Preload(void); - void Precache(HitList &); + char signature[4]; + short version; + short nFrames; + short ticksPerFrame; + short soundId; + int flags; + SEQFRAME frames[1]; + void Preload(void); + void Precache(HitList&); + + bool isLooping() + { + return (flags & 1) != 0; + } + + bool isRemovable() + { + return (flags & 2) != 0; + } }; struct ACTIVE { - unsigned char type; - unsigned short xindex; + unsigned char type; + unsigned short xindex; }; struct SEQINST { - Seq *pSequence; - int at8; - int atc; - short at10; - unsigned char frameIndex; - char at13; - void Update(ACTIVE *pActive); + Seq* pSequence; + int index, type; + + int nSeqID; + int callback; + short timeCounter; + unsigned char frameIndex; + 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);