mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-15 15:11:41 +00:00
666 lines
20 KiB
C++
666 lines
20 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
|
Copyright (C) 2019 Nuke.YKT
|
|
|
|
This file is part of NBlood.
|
|
|
|
NBlood is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License version 2
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
#include "ns.h" // Must come before everything else!
|
|
|
|
#include <string.h>
|
|
#include "build.h"
|
|
#include "common_game.h"
|
|
|
|
#include "blood.h"
|
|
#include "db.h"
|
|
#include "eventq.h"
|
|
#include "globals.h"
|
|
#include "levels.h"
|
|
#include "loadsave.h"
|
|
#include "sound.h"
|
|
#include "seq.h"
|
|
#include "gameutil.h"
|
|
#include "actor.h"
|
|
#include "view.h"
|
|
#include "raze_sound.h"
|
|
|
|
BEGIN_BLD_NS
|
|
|
|
#define kMaxClients 256
|
|
#define kMaxSequences 1024
|
|
|
|
static ACTIVE activeList[kMaxSequences];
|
|
static int activeCount = 0;
|
|
static int nClients = 0;
|
|
static void(*clientCallback[kMaxClients])(int, int);
|
|
|
|
int seqRegisterClient(void(*pClient)(int, int))
|
|
{
|
|
dassert(nClients < kMaxClients);
|
|
clientCallback[nClients] = pClient;
|
|
return nClients++;
|
|
}
|
|
|
|
void Seq::Preload(void)
|
|
{
|
|
if (memcmp(signature, "SEQ\x1a", 4) != 0)
|
|
ThrowError("Invalid sequence");
|
|
if ((version & 0xff00) != 0x300)
|
|
ThrowError("Obsolete sequence version");
|
|
for (int i = 0; i < nFrames; i++)
|
|
tilePreloadTile(seqGetTile(&frames[i]));
|
|
}
|
|
|
|
void Seq::Precache(HitList &hits)
|
|
{
|
|
if (memcmp(signature, "SEQ\x1a", 4) != 0)
|
|
ThrowError("Invalid sequence");
|
|
if ((version & 0xff00) != 0x300)
|
|
ThrowError("Obsolete sequence version");
|
|
for (int i = 0; i < nFrames; i++)
|
|
tilePrecacheTile(seqGetTile(&frames[i]), -1, 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)
|
|
{
|
|
dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
|
|
int nSprite = xsprite[nXSprite].reference;
|
|
dassert(nSprite >= 0 && nSprite < kMaxSprites);
|
|
spritetype *pSprite = &sprite[nSprite];
|
|
dassert(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;
|
|
}
|
|
|
|
void UpdateWall(int nXWall, SEQFRAME *pFrame)
|
|
{
|
|
dassert(nXWall > 0 && nXWall < kMaxXWalls);
|
|
int nWall = xwall[nXWall].reference;
|
|
dassert(nWall >= 0 && nWall < kMaxWalls);
|
|
walltype *pWall = &wall[nWall];
|
|
dassert(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;
|
|
}
|
|
|
|
void UpdateMasked(int nXWall, SEQFRAME *pFrame)
|
|
{
|
|
dassert(nXWall > 0 && nXWall < kMaxXWalls);
|
|
int nWall = xwall[nXWall].reference;
|
|
dassert(nWall >= 0 && nWall < kMaxWalls);
|
|
walltype *pWall = &wall[nWall];
|
|
dassert(pWall->extra == nXWall);
|
|
dassert(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;
|
|
}
|
|
}
|
|
|
|
void UpdateFloor(int nXSector, SEQFRAME *pFrame)
|
|
{
|
|
dassert(nXSector > 0 && nXSector < kMaxXSectors);
|
|
int nSector = xsector[nXSector].reference;
|
|
dassert(nSector >= 0 && nSector < kMaxSectors);
|
|
sectortype *pSector = §or[nSector];
|
|
dassert(pSector->extra == nXSector);
|
|
pSector->floorpicnum = seqGetTile(pFrame);
|
|
pSector->floorshade = pFrame->at4_0;
|
|
if (pFrame->at5_0)
|
|
pSector->floorpal = pFrame->at5_0;
|
|
}
|
|
|
|
void UpdateCeiling(int nXSector, SEQFRAME *pFrame)
|
|
{
|
|
dassert(nXSector > 0 && nXSector < kMaxXSectors);
|
|
int nSector = xsector[nXSector].reference;
|
|
dassert(nSector >= 0 && nSector < kMaxSectors);
|
|
sectortype *pSector = §or[nSector];
|
|
dassert(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)
|
|
{
|
|
dassert(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:
|
|
{
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
// 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 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)
|
|
clientCallback[atc](pActive->type, pActive->xindex);
|
|
}
|
|
|
|
SEQINST * GetInstance(int a1, int a2)
|
|
{
|
|
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;
|
|
}
|
|
|
|
void UnlockInstance(SEQINST *pInst)
|
|
{
|
|
dassert(pInst != NULL);
|
|
dassert(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)
|
|
ThrowError("Missing sequence #%d", a1);
|
|
|
|
int i = activeCount;
|
|
if (pInst->at13)
|
|
{
|
|
if (pSeq == pInst->pSequence)
|
|
return;
|
|
UnlockInstance(pInst);
|
|
for (i = 0; i < activeCount; i++)
|
|
{
|
|
if (activeList[i].type == a2 && activeList[i].xindex == a3)
|
|
break;
|
|
}
|
|
dassert(i < activeCount);
|
|
}
|
|
if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0)
|
|
ThrowError("Invalid sequence %d", a1);
|
|
if ((pSeq->version & 0xff00) != 0x300)
|
|
ThrowError("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 == activeCount)
|
|
{
|
|
dassert(activeCount < kMaxSequences);
|
|
activeList[activeCount].type = a2;
|
|
activeList[activeCount].xindex = a3;
|
|
activeCount++;
|
|
}
|
|
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 < activeCount; i++)
|
|
{
|
|
if (activeList[i].type == a1 && activeList[i].xindex == a2)
|
|
break;
|
|
}
|
|
dassert(i < activeCount);
|
|
activeCount--;
|
|
activeList[i] = activeList[activeCount];
|
|
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]);
|
|
}
|
|
activeCount = 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 < activeCount; i++)
|
|
{
|
|
SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex);
|
|
Seq *pSeq = pInst->pSequence;
|
|
dassert(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;
|
|
dassert(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;
|
|
dassert(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[--activeCount];
|
|
break;
|
|
}
|
|
}
|
|
pInst->Update(&activeList[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
class SeqLoadSave : public LoadSave {
|
|
virtual void Load(void);
|
|
virtual void Save(void);
|
|
};
|
|
|
|
void SeqLoadSave::Load(void)
|
|
{
|
|
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(&activeCount, sizeof(activeCount));
|
|
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 < activeCount; i++)
|
|
{
|
|
SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex);
|
|
if (pInst->at13)
|
|
{
|
|
int nSeq = pInst->at8;
|
|
auto pSeq = getSequence(nSeq);
|
|
if (!pSeq) {
|
|
ThrowError("Missing sequence #%d", nSeq);
|
|
continue;
|
|
}
|
|
if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0)
|
|
ThrowError("Invalid sequence %d", nSeq);
|
|
if ((pSeq->version & 0xff00) != 0x300)
|
|
ThrowError("Sequence %d is obsolete version", nSeq);
|
|
pInst->pSequence = pSeq;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SeqLoadSave::Save(void)
|
|
{
|
|
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(&activeCount, sizeof(activeCount));
|
|
}
|
|
|
|
static SeqLoadSave *myLoadSave;
|
|
|
|
void SeqLoadSaveConstruct(void)
|
|
{
|
|
myLoadSave = new SeqLoadSave();
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
#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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
END_BLD_NS
|