rework of the SEQ code.

Mostly redone by integrating Raze's changes into Blood-RE's version.
The BitReader was replaced with an independent implementation that's not integrated into the backend.
Most importantly this decouples the internal representation of SEQs from Blood's file format so a format redesign is doable.
This commit is contained in:
Christoph Oelckers 2023-10-11 19:20:23 +02:00
parent eb7d3c542e
commit 9eef6c42cb
17 changed files with 479 additions and 603 deletions

View file

@ -4,3 +4,4 @@
using FileSys::FileReader;
using FileSys::FileWriter;
using FileSys::BufferWriter;
using FileSys::BitReader;

View file

@ -362,6 +362,42 @@ public:
TArray<unsigned char>&& TakeBuffer() { return std::move(mBuffer); }
};
}
class BitReader
{
const uint8_t* buffer;
const size_t size; // counts in bits, not bytes
size_t bitofs;
public:
BitReader(const uint8_t* data, uint32_t _size) : buffer(data), size(_size * 8), bitofs(0) { }
~BitReader() = default;
uint32_t getBit()
{
if (bitofs > size) return ~0u;
// maintaining a byte offset costs more than doing this shift for every bit so skip it.
uint32_t ret = (buffer[bitofs >> 3] >> (bitofs & 7)) & 1;
bitofs++;
return ret;
}
uint32_t getBits(uint32_t n)
{
if (bitofs > size - n || n > 32) return ~0u;
uint32_t ret;
for (uint32_t i = 0; i < n; i++) ret |= getBit() << i;
return ret;
}
int32_t getBitsSigned(uint32_t n)
{
uint32_t notn = 32 - n;
return (((int)getBits(n)) << notn) >> notn;
}
void skipBits(uint32_t n)
{
bitofs += n;
}
};
}
#endif

View file

@ -39,6 +39,7 @@ void MarkVerticesForSector(int sector);
static constexpr double zmaptoworld = (1 / 256.); // this for necessary conversions to convert map data to floating point representation.
static constexpr double maptoworld = (1 / 16.); // this for necessary conversions to convert map data to floating point representation.
static constexpr double REPEAT_SCALE = (1 / 64.); // map's 'repeat' values use 2.6 fixed point.
static constexpr float REPEAT_SCALEF = (1 / 64.f); // map's 'repeat' values use 2.6 fixed point.
static constexpr double INV_REPEAT_SCALE = 64;
// These are refactoring markers that should be eliminated.
@ -170,7 +171,7 @@ enum ESpriteBits // names mostly from SW.
// Blood flags
CSTAT_SPRITE_BLOOD_BIT2 = 1024, // Both of these get set but not checked directly, so no idea what they mean...
CSTAT_SPRITE_BLOOD_BIT1 = 4096,
CSTAT_SPRITE_BLOOD_PUSHABLE = 4096,
CSTAT_SPRITE_MOVE_FORWARD = 8192,
CSTAT_SPRITE_MOVE_REVERSE = 16384,
CSTAT_SPRITE_MOVE_MASK = CSTAT_SPRITE_MOVE_FORWARD | CSTAT_SPRITE_MOVE_REVERSE,

View file

@ -894,7 +894,7 @@ static void actInitDudes()
if (!act->IsPlayerActor())
{
act->clipdist = act->fClipDist();
act->spr.cstat |= CSTAT_SPRITE_BLOOD_BIT1 | CSTAT_SPRITE_BLOCK_ALL;
act->spr.cstat |= CSTAT_SPRITE_BLOOD_PUSHABLE | CSTAT_SPRITE_BLOCK_ALL;
act->vel.Zero();
if ((currentLevel->featureflags & kFeatureEnemyAttacks) && act->xspr.sysData2 > 0)
@ -4138,7 +4138,7 @@ DBloodActor* actSpawnDude(DBloodActor* source, int nType, double dist)
spawned->spr.Angles.Yaw = angle;
SetActor(spawned, pos);
spawned->spr.cstat |= CSTAT_SPRITE_BLOCK_ALL | CSTAT_SPRITE_BLOOD_BIT1;
spawned->spr.cstat |= CSTAT_SPRITE_BLOCK_ALL | CSTAT_SPRITE_BLOOD_PUSHABLE;
spawned->clipdist = spawned->fClipDist();
spawned->xspr.health = spawned->startHealth() << 4;
spawned->xspr.respawn = 1;

View file

@ -689,15 +689,15 @@ void viewProcessSprites(tspriteArray& tsprites, const DVector3& cPos, DAngle cA,
{
pTSprite->shade = ClipRange(pTSprite->shade - 16 - QRandom(8), -128, 127);
}
if (pTSprite->flags & 256)
if (pTSprite->flags & kHitagSmoke)
{
viewAddEffect(tsprites, nTSprite, kViewEffectSmokeHigh);
}
if (pTSprite->flags & 1024)
if (pTSprite->flags & kHitagFlipX)
{
pTSprite->cstat |= CSTAT_SPRITE_XFLIP;
}
if (pTSprite->flags & 2048)
if (pTSprite->flags & kHitagFlipY)
{
pTSprite->cstat |= CSTAT_SPRITE_YFLIP;
}

View file

@ -37,7 +37,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "actor.h"
#include "ai.h"
#include "aiunicult.h"
#include "callback.h"
#include "db.h"
#include "endgame.h"
#include "eventq.h"
@ -318,10 +317,6 @@ inline bool DemoRecordStatus(void)
return false;
}
inline bool VanillaMode()
{
return false;
}
void sndPlaySpecialMusicOrNothing(int nMusic);
struct GameInterface : public ::GameInterface

View file

@ -283,7 +283,7 @@ void Respawn(DBloodActor* actor) // 9
if (actor->IsDudeActor())
{
actor->spr.pos = actor->basePoint;
actor->spr.cstat |= CSTAT_SPRITE_BLOOD_BIT1 | CSTAT_SPRITE_BLOCK_ALL;
actor->spr.cstat |= CSTAT_SPRITE_BLOOD_PUSHABLE | CSTAT_SPRITE_BLOCK_ALL;
#ifdef NOONE_EXTENSIONS
if (!gModernMap || actor->xspr.sysData2 <= 0) actor->xspr.health = actor->startHealth() << 4;
else actor->xspr.health = ClipRange(actor->xspr.sysData2 << 4, 1, 65535);
@ -493,6 +493,7 @@ void fxTeslaAlt(DBloodActor* actor) // 15
static const int tommySleeveSnd[] = { 608, 609, 611 }; // unused?
static const int sawedOffSleeveSnd[] = { 610, 612 };
void sleeveStopBouncing(DBloodActor* pSprite);
void fxBouncingSleeve(DBloodActor* actor) // 16
{

View file

@ -1,28 +0,0 @@
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
#pragma once
BEGIN_BLD_NS
void sleeveStopBouncing(DBloodActor* pSprite);
END_BLD_NS

View file

@ -27,12 +27,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "misc.h"
#include "printf.h"
#include "v_text.h"
#include "seqcb.h"
#include "coreactor.h"
#include "vectors.h"
BEGIN_BLD_NS
inline bool VanillaMode()
{
return false;
}
void QuitGame(void);
enum
@ -434,16 +438,23 @@ enum {
enum
{
// sprite physics attributes
kPhysMove = 0x0001, // affected by movement physics
kPhysGravity = 0x0002, // affected by gravity
kPhysFalling = 0x0004, // currently in z-motion
// sprite attributes
kHitagAutoAim = 0x0008,
kHitagRespawn = 0x0010,
kHitagFree = 0x0020,
kHitagSmoke = 0x0100,
// sprite physics attributes
kPhysMove = 0x0001, // affected by movement physics
kPhysGravity = 0x0002, // affected by gravity
kPhysFalling = 0x0004, // currently in z-motion
// why are these needed? They seem redunant.
kHitagFlipX = 1024,
kHitagFlipY = 2048,
kAng5 = 28,
kAng15 = 85,
@ -660,52 +671,4 @@ public:
}
};
class BitReader {
public:
int nBitPos;
int nSize;
uint8_t* pBuffer;
BitReader(uint8_t* _pBuffer, int _nSize, int _nBitPos) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = _nBitPos; nSize -= nBitPos >> 3; }
BitReader(uint8_t* _pBuffer, int _nSize) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = 0; }
int readBit()
{
if (nSize <= 0)
I_Error("Buffer overflow in BitReader");
int bit = ((*pBuffer) >> nBitPos) & 1;
if (++nBitPos >= 8)
{
nBitPos = 0;
pBuffer++;
nSize--;
}
return bit;
}
void skipBits(int nBits)
{
nBitPos += nBits;
pBuffer += nBitPos >> 3;
nSize -= nBitPos >> 3;
nBitPos &= 7;
if ((nSize == 0 && nBitPos > 0) || nSize < 0)
I_Error("Buffer overflow in BitReader");
}
unsigned int readUnsigned(int nBits)
{
unsigned int n = 0;
assert(nBits <= 32);
for (int i = 0; i < nBits; i++)
n += readBit() << i;
return n;
}
int readSigned(int nBits)
{
assert(nBits <= 32);
int n = (int)readUnsigned(nBits);
n <<= 32 - nBits;
n >>= 32 - nBits;
return n;
}
};
END_BLD_NS

View file

@ -314,83 +314,83 @@ void dbLoadMap(const char* pPath, DVector3& pos, short* pAngle, sectortype** cur
assert(nCount <= nXSectorSize);
fr.Read(pBuffer, nCount);
BitReader bitReader(pBuffer, nCount);
/*pXSector->reference =*/ bitReader.readSigned(14);
pXSector->state = bitReader.readUnsigned(1);
pXSector->busy = bitReader.readUnsigned(17);
pXSector->data = bitReader.readUnsigned(16);
pXSector->txID = bitReader.readUnsigned(10);
pXSector->busyWaveA = bitReader.readUnsigned(3);
pXSector->busyWaveB = bitReader.readUnsigned(3);
pXSector->rxID = bitReader.readUnsigned(10);
pXSector->command = bitReader.readUnsigned(8);
pXSector->triggerOn = bitReader.readUnsigned(1);
pXSector->triggerOff = bitReader.readUnsigned(1);
pXSector->busyTimeA = bitReader.readUnsigned(12);
pXSector->waitTimeA = bitReader.readUnsigned(12);
pXSector->restState = bitReader.readUnsigned(1);
pXSector->interruptable = bitReader.readUnsigned(1);
pXSector->amplitude = bitReader.readSigned(8);
pXSector->freq = bitReader.readUnsigned(8);
pXSector->reTriggerA = bitReader.readUnsigned(1);
pXSector->reTriggerB = bitReader.readUnsigned(1);
pXSector->phase = bitReader.readUnsigned(8);
pXSector->wave = bitReader.readUnsigned(4);
pXSector->shadeAlways = bitReader.readUnsigned(1);
pXSector->shadeFloor = bitReader.readUnsigned(1);
pXSector->shadeCeiling = bitReader.readUnsigned(1);
pXSector->shadeWalls = bitReader.readUnsigned(1);
pXSector->shade = bitReader.readSigned(8);
pXSector->panAlways = bitReader.readUnsigned(1);
pXSector->panFloor = bitReader.readUnsigned(1);
pXSector->panCeiling = bitReader.readUnsigned(1);
pXSector->Drag = bitReader.readUnsigned(1);
pXSector->Underwater = bitReader.readUnsigned(1);
pXSector->Depth = bitReader.readUnsigned(3);
pXSector->panVel = bitReader.readUnsigned(8);
pXSector->panAngle = mapangle(bitReader.readUnsigned(11));
pXSector->pauseMotion = bitReader.readUnsigned(1);
pXSector->decoupled = bitReader.readUnsigned(1);
pXSector->triggerOnce = bitReader.readUnsigned(1);
pXSector->isTriggered = bitReader.readUnsigned(1);
pXSector->Key = bitReader.readUnsigned(3);
pXSector->Push = bitReader.readUnsigned(1);
pXSector->Vector = bitReader.readUnsigned(1);
pXSector->Reserved = bitReader.readUnsigned(1);
pXSector->Enter = bitReader.readUnsigned(1);
pXSector->Exit = bitReader.readUnsigned(1);
pXSector->Wallpush = bitReader.readUnsigned(1);
pXSector->color = bitReader.readUnsigned(1);
/*pXSector->unused2 =*/ bitReader.readUnsigned(1);
pXSector->busyTimeB = bitReader.readUnsigned(12);
pXSector->waitTimeB = bitReader.readUnsigned(12);
pXSector->stopOn = bitReader.readUnsigned(1);
pXSector->stopOff = bitReader.readUnsigned(1);
pXSector->ceilpal = bitReader.readUnsigned(4);
pXSector->offCeilZ = bitReader.readSigned(32) * zmaptoworld;
pXSector->onCeilZ = bitReader.readSigned(32) * zmaptoworld;
pXSector->offFloorZ = bitReader.readSigned(32) * zmaptoworld;
pXSector->onFloorZ = bitReader.readSigned(32) * zmaptoworld;
/*pXSector->marker0 =*/ bitReader.readUnsigned(16);
/*pXSector->marker1 =*/ bitReader.readUnsigned(16);
pXSector->Crush = bitReader.readUnsigned(1);
pSector->ceilingxpan_ += bitReader.readUnsigned(8) / 256.f;
pSector->ceilingypan_ += bitReader.readUnsigned(8) / 256.f;
pSector->floorxpan_ += bitReader.readUnsigned(8) / 256.f;
pXSector->damageType = bitReader.readUnsigned(3);
pXSector->floorpal = bitReader.readUnsigned(4);
pSector->floorypan_ += bitReader.readUnsigned(8) / 256.f;
pXSector->locked = bitReader.readUnsigned(1);
pXSector->windVel = bitReader.readUnsigned(10);
pXSector->windAng = mapangle(bitReader.readUnsigned(11));
pXSector->windAlways = bitReader.readUnsigned(1);
pXSector->dudeLockout = bitReader.readUnsigned(1);
pXSector->bobTheta = bitReader.readUnsigned(11);
pXSector->bobZRange = bitReader.readUnsigned(5);
pXSector->bobSpeed = bitReader.readSigned(12);
pXSector->bobAlways = bitReader.readUnsigned(1);
pXSector->bobFloor = bitReader.readUnsigned(1);
pXSector->bobCeiling = bitReader.readUnsigned(1);
pXSector->bobRotate = bitReader.readUnsigned(1);
/*pXSector->reference =*/ bitReader.getBitsSigned(14);
pXSector->state = bitReader.getBits(1);
pXSector->busy = bitReader.getBits(17);
pXSector->data = bitReader.getBits(16);
pXSector->txID = bitReader.getBits(10);
pXSector->busyWaveA = bitReader.getBits(3);
pXSector->busyWaveB = bitReader.getBits(3);
pXSector->rxID = bitReader.getBits(10);
pXSector->command = bitReader.getBits(8);
pXSector->triggerOn = bitReader.getBits(1);
pXSector->triggerOff = bitReader.getBits(1);
pXSector->busyTimeA = bitReader.getBits(12);
pXSector->waitTimeA = bitReader.getBits(12);
pXSector->restState = bitReader.getBits(1);
pXSector->interruptable = bitReader.getBits(1);
pXSector->amplitude = bitReader.getBitsSigned(8);
pXSector->freq = bitReader.getBits(8);
pXSector->reTriggerA = bitReader.getBits(1);
pXSector->reTriggerB = bitReader.getBits(1);
pXSector->phase = bitReader.getBits(8);
pXSector->wave = bitReader.getBits(4);
pXSector->shadeAlways = bitReader.getBits(1);
pXSector->shadeFloor = bitReader.getBits(1);
pXSector->shadeCeiling = bitReader.getBits(1);
pXSector->shadeWalls = bitReader.getBits(1);
pXSector->shade = bitReader.getBitsSigned(8);
pXSector->panAlways = bitReader.getBits(1);
pXSector->panFloor = bitReader.getBits(1);
pXSector->panCeiling = bitReader.getBits(1);
pXSector->Drag = bitReader.getBits(1);
pXSector->Underwater = bitReader.getBits(1);
pXSector->Depth = bitReader.getBits(3);
pXSector->panVel = bitReader.getBits(8);
pXSector->panAngle = mapangle(bitReader.getBits(11));
pXSector->pauseMotion = bitReader.getBits(1);
pXSector->decoupled = bitReader.getBits(1);
pXSector->triggerOnce = bitReader.getBits(1);
pXSector->isTriggered = bitReader.getBits(1);
pXSector->Key = bitReader.getBits(3);
pXSector->Push = bitReader.getBits(1);
pXSector->Vector = bitReader.getBits(1);
pXSector->Reserved = bitReader.getBits(1);
pXSector->Enter = bitReader.getBits(1);
pXSector->Exit = bitReader.getBits(1);
pXSector->Wallpush = bitReader.getBits(1);
pXSector->color = bitReader.getBits(1);
/*pXSector->unused2 =*/ bitReader.getBits(1);
pXSector->busyTimeB = bitReader.getBits(12);
pXSector->waitTimeB = bitReader.getBits(12);
pXSector->stopOn = bitReader.getBits(1);
pXSector->stopOff = bitReader.getBits(1);
pXSector->ceilpal = bitReader.getBits(4);
pXSector->offCeilZ = bitReader.getBitsSigned(32) * zmaptoworld;
pXSector->onCeilZ = bitReader.getBitsSigned(32) * zmaptoworld;
pXSector->offFloorZ = bitReader.getBitsSigned(32) * zmaptoworld;
pXSector->onFloorZ = bitReader.getBitsSigned(32) * zmaptoworld;
/*pXSector->marker0 =*/ bitReader.getBits(16);
/*pXSector->marker1 =*/ bitReader.getBits(16);
pXSector->Crush = bitReader.getBits(1);
pSector->ceilingxpan_ += bitReader.getBits(8) / 256.f;
pSector->ceilingypan_ += bitReader.getBits(8) / 256.f;
pSector->floorxpan_ += bitReader.getBits(8) / 256.f;
pXSector->damageType = bitReader.getBits(3);
pXSector->floorpal = bitReader.getBits(4);
pSector->floorypan_ += bitReader.getBits(8) / 256.f;
pXSector->locked = bitReader.getBits(1);
pXSector->windVel = bitReader.getBits(10);
pXSector->windAng = mapangle(bitReader.getBits(11));
pXSector->windAlways = bitReader.getBits(1);
pXSector->dudeLockout = bitReader.getBits(1);
pXSector->bobTheta = bitReader.getBits(11);
pXSector->bobZRange = bitReader.getBits(5);
pXSector->bobSpeed = bitReader.getBitsSigned(12);
pXSector->bobAlways = bitReader.getBits(1);
pXSector->bobFloor = bitReader.getBits(1);
pXSector->bobCeiling = bitReader.getBits(1);
pXSector->bobRotate = bitReader.getBits(1);
pXSector->busy = IntToFixed(pXSector->state);
}
@ -441,37 +441,37 @@ void dbLoadMap(const char* pPath, DVector3& pos, short* pAngle, sectortype** cur
assert(nCount <= nXWallSize);
fr.Read(pBuffer, nCount);
BitReader bitReader(pBuffer, nCount);
/*pXWall->reference =*/ bitReader.readSigned(14);
pXWall->state = bitReader.readUnsigned(1);
pXWall->busy = bitReader.readUnsigned(17);
pXWall->data = bitReader.readSigned(16);
pXWall->txID = bitReader.readUnsigned(10);
bitReader.readUnsigned(6);
pXWall->rxID = bitReader.readUnsigned(10);
pXWall->command = bitReader.readUnsigned(8);
pXWall->triggerOn = bitReader.readUnsigned(1);
pXWall->triggerOff = bitReader.readUnsigned(1);
pXWall->busyTime = bitReader.readUnsigned(12);
pXWall->waitTime = bitReader.readUnsigned(12);
pXWall->restState = bitReader.readUnsigned(1);
pXWall->interruptable = bitReader.readUnsigned(1);
pXWall->panAlways = bitReader.readUnsigned(1);
pXWall->panVel.X = bitReader.readSigned(8);
pXWall->panVel.Y = bitReader.readSigned(8);
pXWall->decoupled = bitReader.readUnsigned(1);
pXWall->triggerOnce = bitReader.readUnsigned(1);
pXWall->isTriggered = bitReader.readUnsigned(1);
pXWall->key = bitReader.readUnsigned(3);
pXWall->triggerPush = bitReader.readUnsigned(1);
pXWall->triggerVector = bitReader.readUnsigned(1);
pXWall->triggerTouch = bitReader.readUnsigned(1);
bitReader.readUnsigned(2);
pWall->xpan_ += bitReader.readUnsigned(8) / 256.f;
pWall->ypan_ += bitReader.readUnsigned(8) / 256.f;
pXWall->locked = bitReader.readUnsigned(1);
pXWall->dudeLockout = bitReader.readUnsigned(1);
bitReader.readUnsigned(4);
bitReader.readUnsigned(32);
/*pXWall->reference =*/ bitReader.getBitsSigned(14);
pXWall->state = bitReader.getBits(1);
pXWall->busy = bitReader.getBits(17);
pXWall->data = bitReader.getBitsSigned(16);
pXWall->txID = bitReader.getBits(10);
bitReader.getBits(6);
pXWall->rxID = bitReader.getBits(10);
pXWall->command = bitReader.getBits(8);
pXWall->triggerOn = bitReader.getBits(1);
pXWall->triggerOff = bitReader.getBits(1);
pXWall->busyTime = bitReader.getBits(12);
pXWall->waitTime = bitReader.getBits(12);
pXWall->restState = bitReader.getBits(1);
pXWall->interruptable = bitReader.getBits(1);
pXWall->panAlways = bitReader.getBits(1);
pXWall->panVel.X = bitReader.getBitsSigned(8);
pXWall->panVel.Y = bitReader.getBitsSigned(8);
pXWall->decoupled = bitReader.getBits(1);
pXWall->triggerOnce = bitReader.getBits(1);
pXWall->isTriggered = bitReader.getBits(1);
pXWall->key = bitReader.getBits(3);
pXWall->triggerPush = bitReader.getBits(1);
pXWall->triggerVector = bitReader.getBits(1);
pXWall->triggerTouch = bitReader.getBits(1);
bitReader.getBits(2);
pWall->xpan_ += bitReader.getBits(8) / 256.f;
pWall->ypan_ += bitReader.getBits(8) / 256.f;
pXWall->locked = bitReader.getBits(1);
pXWall->dudeLockout = bitReader.getBits(1);
bitReader.getBits(4);
bitReader.getBits(32);
pXWall->busy = IntToFixed(pXWall->state);
}
@ -529,66 +529,66 @@ void dbLoadMap(const char* pPath, DVector3& pos, short* pAngle, sectortype** cur
assert(nCount <= nXSpriteSize);
fr.Read(pBuffer, nCount);
BitReader bitReader(pBuffer, nCount);
/*pXSprite->reference =*/ bitReader.readSigned(14);
pXSprite->state = bitReader.readUnsigned(1);
pXSprite->busy = bitReader.readUnsigned(17);
pXSprite->txID = bitReader.readUnsigned(10);
pXSprite->rxID = bitReader.readUnsigned(10);
pXSprite->command = bitReader.readUnsigned(8);
pXSprite->triggerOn = bitReader.readUnsigned(1);
pXSprite->triggerOff = bitReader.readUnsigned(1);
pXSprite->wave = bitReader.readUnsigned(2);
pXSprite->busyTime = bitReader.readUnsigned(12);
pXSprite->waitTime = bitReader.readUnsigned(12);
pXSprite->restState = bitReader.readUnsigned(1);
pXSprite->Interrutable = bitReader.readUnsigned(1);
pXSprite->modernFlags = bitReader.readUnsigned(2);
pXSprite->respawnPending = bitReader.readUnsigned(2);
pXSprite->patrolstate = bitReader.readUnsigned(1);
pXSprite->lT = bitReader.readUnsigned(1);
pXSprite->dropMsg = bitReader.readUnsigned(8);
pXSprite->Decoupled = bitReader.readUnsigned(1);
pXSprite->triggerOnce = bitReader.readUnsigned(1);
pXSprite->isTriggered = bitReader.readUnsigned(1);
pXSprite->key = bitReader.readUnsigned(3);
pXSprite->Push = bitReader.readUnsigned(1);
pXSprite->Vector = bitReader.readUnsigned(1);
pXSprite->Impact = bitReader.readUnsigned(1);
pXSprite->Pickup = bitReader.readUnsigned(1);
pXSprite->Touch = bitReader.readUnsigned(1);
pXSprite->Sight = bitReader.readUnsigned(1);
pXSprite->Proximity = bitReader.readUnsigned(1);
pXSprite->sightstuff = bitReader.readUnsigned(2);
pXSprite->lSkill = bitReader.readUnsigned(5);
pXSprite->lS = bitReader.readUnsigned(1);
pXSprite->lB = bitReader.readUnsigned(1);
pXSprite->lC = bitReader.readUnsigned(1);
pXSprite->DudeLockout = bitReader.readUnsigned(1);
pXSprite->data1 = bitReader.readSigned(16);
pXSprite->data2 = bitReader.readSigned(16);
pXSprite->data3 = bitReader.readSigned(16);
pXSprite->goalAng = mapangle(bitReader.readUnsigned(11));
pXSprite->dodgeDir = bitReader.readSigned(2);
pXSprite->locked = bitReader.readUnsigned(1);
pXSprite->medium = bitReader.readUnsigned(2);
pXSprite->respawn = bitReader.readUnsigned(2);
pXSprite->data4 = bitReader.readUnsigned(16);
pXSprite->patrolturndelay = bitReader.readUnsigned(6);
pXSprite->lockMsg = bitReader.readUnsigned(8);
pXSprite->health = bitReader.readUnsigned(12);
pXSprite->dudeDeaf = bitReader.readUnsigned(1);
pXSprite->dudeAmbush = bitReader.readUnsigned(1);
pXSprite->dudeGuard = bitReader.readUnsigned(1);
pXSprite->dudeFlag4 = bitReader.readUnsigned(1);
/*pXSprite->target_i = */ bitReader.readSigned(16);
int tx = bitReader.readSigned(32);
int ty = bitReader.readSigned(32);
int tz = bitReader.readSigned(32);
/*pXSprite->reference =*/ bitReader.getBitsSigned(14);
pXSprite->state = bitReader.getBits(1);
pXSprite->busy = bitReader.getBits(17);
pXSprite->txID = bitReader.getBits(10);
pXSprite->rxID = bitReader.getBits(10);
pXSprite->command = bitReader.getBits(8);
pXSprite->triggerOn = bitReader.getBits(1);
pXSprite->triggerOff = bitReader.getBits(1);
pXSprite->wave = bitReader.getBits(2);
pXSprite->busyTime = bitReader.getBits(12);
pXSprite->waitTime = bitReader.getBits(12);
pXSprite->restState = bitReader.getBits(1);
pXSprite->Interrutable = bitReader.getBits(1);
pXSprite->modernFlags = bitReader.getBits(2);
pXSprite->respawnPending = bitReader.getBits(2);
pXSprite->patrolstate = bitReader.getBits(1);
pXSprite->lT = bitReader.getBits(1);
pXSprite->dropMsg = bitReader.getBits(8);
pXSprite->Decoupled = bitReader.getBits(1);
pXSprite->triggerOnce = bitReader.getBits(1);
pXSprite->isTriggered = bitReader.getBits(1);
pXSprite->key = bitReader.getBits(3);
pXSprite->Push = bitReader.getBits(1);
pXSprite->Vector = bitReader.getBits(1);
pXSprite->Impact = bitReader.getBits(1);
pXSprite->Pickup = bitReader.getBits(1);
pXSprite->Touch = bitReader.getBits(1);
pXSprite->Sight = bitReader.getBits(1);
pXSprite->Proximity = bitReader.getBits(1);
pXSprite->sightstuff = bitReader.getBits(2);
pXSprite->lSkill = bitReader.getBits(5);
pXSprite->lS = bitReader.getBits(1);
pXSprite->lB = bitReader.getBits(1);
pXSprite->lC = bitReader.getBits(1);
pXSprite->DudeLockout = bitReader.getBits(1);
pXSprite->data1 = bitReader.getBitsSigned(16);
pXSprite->data2 = bitReader.getBitsSigned(16);
pXSprite->data3 = bitReader.getBitsSigned(16);
pXSprite->goalAng = mapangle(bitReader.getBits(11));
pXSprite->dodgeDir = bitReader.getBitsSigned(2);
pXSprite->locked = bitReader.getBits(1);
pXSprite->medium = bitReader.getBits(2);
pXSprite->respawn = bitReader.getBits(2);
pXSprite->data4 = bitReader.getBits(16);
pXSprite->patrolturndelay = bitReader.getBits(6);
pXSprite->lockMsg = bitReader.getBits(8);
pXSprite->health = bitReader.getBits(12);
pXSprite->dudeDeaf = bitReader.getBits(1);
pXSprite->dudeAmbush = bitReader.getBits(1);
pXSprite->dudeGuard = bitReader.getBits(1);
pXSprite->dudeFlag4 = bitReader.getBits(1);
/*pXSprite->target_i = */ bitReader.getBitsSigned(16);
int tx = bitReader.getBitsSigned(32);
int ty = bitReader.getBitsSigned(32);
int tz = bitReader.getBitsSigned(32);
pXSprite->TargetPos = {tx * maptoworld, ty * maptoworld, tz * zmaptoworld };
pXSprite->burnTime = bitReader.readUnsigned(16);
/*pXSprite->burnSource =*/ bitReader.readSigned(16);
pXSprite->height = bitReader.readUnsigned(16);
pXSprite->stateTimer = bitReader.readUnsigned(16);
pXSprite->burnTime = bitReader.getBits(16);
/*pXSprite->burnSource =*/ bitReader.getBitsSigned(16);
pXSprite->height = bitReader.getBits(16);
pXSprite->stateTimer = bitReader.getBits(16);
pXSprite->aiState = NULL;
bitReader.skipBits(32);
pXSprite->busy = IntToFixed(pXSprite->state);

View file

@ -28,17 +28,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_BLD_NS
enum
{
kAttrMove = 0x0001, // is affected by movement physics
kAttrGravity = 0x0002, // is affected by gravity
kAttrFalling = 0x0004, // in z motion
kAttrAiming = 0x0008,
kAttrRespawn = 0x0010,
kAttrFree = 0x0020,
kAttrSmoke = 0x0100, // receives tsprite smoke/steam
};
#pragma pack(push, 1)

View file

@ -21,7 +21,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once
#include "callback.h"
BEGIN_BLD_NS

View file

@ -1,61 +1,51 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
Copyright (C) 2020 - Christoph Oelckers
* Copyright (C) 2018, 2022 nukeykt
* Copyright (C) 2020-2023 Christoph Oelckers
*
* This file is part of Raze
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*
*/
This file is part of Raze
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.
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"
#include "filesystem.h"
#include "tarray.h"
#include "files.h"
#include "build.h"
#include "blood.h"
#include "files.h"
#include "eventq.h"
#include "callback.h"
BEGIN_BLD_NS
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
FMemArena seqcache; // also used by QAVs.
static TMap<int64_t, Seq*> sequences;
void Seq::Precache(int palette)
{
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(seqGetTexture(&frames[i]), -1, palette);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void seqPrecacheId(FName name, int id, int palette)
{
auto pSeq = getSequence(name, id);
if (pSeq) pSeq->Precache(palette);
if (auto pSeq = getSequence(name, id))
{
for (const auto& frame : pSeq->frames)
tilePrecacheTile(frame.texture, -1, palette);
}
}
//---------------------------------------------------------------------------
@ -64,12 +54,10 @@ void seqPrecacheId(FName name, int id, int palette)
//
//---------------------------------------------------------------------------
void UpdateCeiling(sectortype* pSector, SEQFRAME* pFrame)
template<class T, class U> void SetFlag(T& flagvar, U bit, bool condition)
{
pSector->setceilingtexture(seqGetTexture(pFrame));
pSector->ceilingshade = pFrame->shade;
if (pFrame->palette)
pSector->ceilingpal = pFrame->palette;
if (condition) flagvar |= bit;
else flagvar &= ~bit;
}
//---------------------------------------------------------------------------
@ -78,12 +66,49 @@ void UpdateCeiling(sectortype* pSector, SEQFRAME* pFrame)
//
//---------------------------------------------------------------------------
void UpdateFloor(sectortype* pSector, SEQFRAME* pFrame)
void UpdateSprite(DBloodActor* actor, SeqFrame* pFrame)
{
pSector->setfloortexture(seqGetTexture(pFrame));
pSector->floorshade = pFrame->shade;
if (pFrame->palette)
pSector->floorpal = pFrame->palette;
assert(actor->hasX());
if (actor->spr.flags & kPhysGravity)
{
auto atex = TexMan.GetGameTexture(actor->spr.spritetexture());
auto stex = TexMan.GetGameTexture(pFrame->texture);
if (atex->GetDisplayHeight() != stex->GetDisplayHeight() || atex->GetDisplayTopOffset() != stex->GetDisplayTopOffset()
|| (pFrame->scale.Y && pFrame->scale.Y != actor->spr.scale.Y))
actor->spr.flags |= kPhysFalling;
}
actor->spr.setspritetexture(pFrame->texture);
if (pFrame->palette) actor->spr.pal = pFrame->palette;
actor->spr.shade = pFrame->shade;
double scale = VanillaMode() ? 0 : actor->xspr.scale / 256.; // SEQ size scaling (nnext feature)
if (pFrame->scale.X)
{
double s = pFrame->scale.X;
if (scale) s = clamp(s * scale, 0., 4.); // Q: Do we need the clamp to 4 here?
actor->spr.scale.X = s;
}
if (pFrame->scale.Y)
{
double s = pFrame->scale.Y * REPEAT_SCALE;
if (scale) s = clamp(s * scale, 0., 4.);
actor->spr.scale.Y = s;
}
SetFlag(actor->spr.cstat, CSTAT_SPRITE_TRANSLUCENT, pFrame->transparent);
SetFlag(actor->spr.cstat, CSTAT_SPRITE_TRANS_FLIP, pFrame->transparent2);
SetFlag(actor->spr.cstat, CSTAT_SPRITE_BLOCK, pFrame->blockable);
SetFlag(actor->spr.cstat, CSTAT_SPRITE_BLOCK_HITSCAN, pFrame->hittable);
SetFlag(actor->spr.cstat, CSTAT_SPRITE_INVISIBLE, pFrame->invisible);
SetFlag(actor->spr.cstat, CSTAT_SPRITE_BLOOD_PUSHABLE, pFrame->pushable);
SetFlag(actor->spr.flags, kHitagSmoke, pFrame->smoke);
SetFlag(actor->spr.flags, kHitagAutoAim, pFrame->aiming);
SetFlag(actor->spr.flags, kHitagFlipX, pFrame->flipx);
SetFlag(actor->spr.flags, kHitagFlipY, pFrame->flipy);
}
//---------------------------------------------------------------------------
@ -92,28 +117,16 @@ void UpdateFloor(sectortype* pSector, SEQFRAME* pFrame)
//
//---------------------------------------------------------------------------
void UpdateWall(walltype* pWall, SEQFRAME* pFrame)
void UpdateWall(walltype* pWall, SeqFrame* pFrame)
{
assert(pWall->hasX());
pWall->setwalltexture(seqGetTexture(pFrame));
if (pFrame->palette)
pWall->pal = pFrame->palette;
if (pFrame->transparent)
pWall->cstat |= CSTAT_WALL_TRANSLUCENT;
else
pWall->cstat &= ~CSTAT_WALL_TRANSLUCENT;
if (pFrame->transparent2)
pWall->cstat |= CSTAT_WALL_TRANS_FLIP;
else
pWall->cstat &= ~CSTAT_WALL_TRANS_FLIP;
if (pFrame->blockable)
pWall->cstat |= CSTAT_WALL_BLOCK;
else
pWall->cstat &= ~CSTAT_WALL_BLOCK;
if (pFrame->hittable)
pWall->cstat |= CSTAT_WALL_BLOCK_HITSCAN;
else
pWall->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN;
pWall->setwalltexture(pFrame->texture);
if (pFrame->palette) pWall->pal = pFrame->palette;
SetFlag(pWall->cstat, CSTAT_WALL_TRANSLUCENT, pFrame->transparent);
SetFlag(pWall->cstat, CSTAT_WALL_TRANS_FLIP, pFrame->transparent2);
SetFlag(pWall->cstat, CSTAT_WALL_BLOCK, pFrame->blockable);
SetFlag(pWall->cstat, CSTAT_WALL_BLOCK_HITSCAN, pFrame->hittable);
}
//---------------------------------------------------------------------------
@ -122,135 +135,50 @@ void UpdateWall(walltype* pWall, SEQFRAME* pFrame)
//
//---------------------------------------------------------------------------
void UpdateMasked(walltype* pWall, SEQFRAME* pFrame)
void UpdateFloor(sectortype* pSector, SeqFrame* pFrame)
{
pSector->setfloortexture(pFrame->texture);
pSector->floorshade = pFrame->shade;
if (pFrame->palette) pSector->floorpal = pFrame->palette;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateCeiling(sectortype* pSector, SeqFrame* pFrame)
{
pSector->setceilingtexture(pFrame->texture);
pSector->ceilingshade = pFrame->shade;
if (pFrame->palette) pSector->ceilingpal = pFrame->palette;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateMasked(walltype* pWall, SeqFrame* pFrame)
{
assert(pWall->hasX());
walltype* pWallNext = pWall->nextWall();
auto texid = seqGetTexture(pFrame);
auto texid = pFrame->texture;
pWall->setovertexture(texid);
pWallNext->setovertexture(texid);
if (pFrame->palette)
pWall->pal = pWallNext->pal = pFrame->palette;
if (pFrame->transparent)
{
pWall->cstat |= CSTAT_WALL_TRANSLUCENT;
pWallNext->cstat |= CSTAT_WALL_TRANSLUCENT;
}
else
{
pWall->cstat &= ~CSTAT_WALL_TRANSLUCENT;
pWallNext->cstat &= ~CSTAT_WALL_TRANSLUCENT;
}
if (pFrame->transparent2)
{
pWall->cstat |= CSTAT_WALL_TRANS_FLIP;
pWallNext->cstat |= CSTAT_WALL_TRANS_FLIP;
}
else
{
pWall->cstat &= ~CSTAT_WALL_TRANS_FLIP;
pWallNext->cstat &= ~CSTAT_WALL_TRANS_FLIP;
}
if (pFrame->blockable)
{
pWall->cstat |= CSTAT_WALL_BLOCK;
pWallNext->cstat |= CSTAT_WALL_BLOCK;
}
else
{
pWall->cstat &= ~CSTAT_WALL_BLOCK;
pWallNext->cstat &= ~CSTAT_WALL_BLOCK;
}
if (pFrame->hittable)
{
pWall->cstat |= CSTAT_WALL_BLOCK_HITSCAN;
pWallNext->cstat |= CSTAT_WALL_BLOCK_HITSCAN;
}
else
{
pWall->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN;
pWallNext->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void UpdateSprite(DBloodActor* actor, SEQFRAME* pFrame)
{
assert(actor->hasX());
if (actor->spr.flags & 2)
{
auto atex = TexMan.GetGameTexture(actor->spr.spritetexture());
auto stex = TexMan.GetGameTexture(seqGetTexture(pFrame));
if (atex->GetDisplayHeight() != stex->GetDisplayHeight() || atex->GetDisplayTopOffset() != stex->GetDisplayTopOffset()
|| (pFrame->scaley && pFrame->scaley != int(actor->spr.scale.Y * INV_REPEAT_SCALE)))
actor->spr.flags |= 4;
}
actor->spr.setspritetexture(seqGetTexture(pFrame));
if (pFrame->palette)
actor->spr.pal = pFrame->palette;
actor->spr.shade = pFrame->shade;
int scale = actor->xspr.scale; // SEQ size scaling
if (pFrame->scalex)
{
int s;
if (scale) s = ClipRange(MulScale(pFrame->scalex, scale, 8), 0, 255);
else s = pFrame->scalex;
actor->spr.scale.X = (s * REPEAT_SCALE);
}
if (pFrame->scaley) {
int s;
if (scale) s = ClipRange(MulScale(pFrame->scaley, scale, 8), 0, 255);
else s = pFrame->scaley;
actor->spr.scale.Y = (s * REPEAT_SCALE);
}
if (pFrame->transparent)
actor->spr.cstat |= CSTAT_SPRITE_TRANSLUCENT;
else
actor->spr.cstat &= ~CSTAT_SPRITE_TRANSLUCENT;
if (pFrame->transparent2)
actor->spr.cstat |= CSTAT_SPRITE_TRANS_FLIP;
else
actor->spr.cstat &= ~CSTAT_SPRITE_TRANS_FLIP;
if (pFrame->blockable)
actor->spr.cstat |= CSTAT_SPRITE_BLOCK;
else
actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK;
if (pFrame->hittable)
actor->spr.cstat |= CSTAT_SPRITE_BLOCK_HITSCAN;
else
actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN;
if (pFrame->invisible)
actor->spr.cstat |= CSTAT_SPRITE_INVISIBLE;
else
actor->spr.cstat &= ~CSTAT_SPRITE_INVISIBLE;
if (pFrame->pushable)
actor->spr.cstat |= CSTAT_SPRITE_BLOOD_BIT1;
else
actor->spr.cstat &= ~CSTAT_SPRITE_BLOOD_BIT1;
if (pFrame->smoke)
actor->spr.flags |= 256;
else
actor->spr.flags &= ~256;
if (pFrame->aiming)
actor->spr.flags |= 8;
else
actor->spr.flags &= ~8;
if (pFrame->flipx)
actor->spr.flags |= 1024;
else
actor->spr.flags &= ~1024;
if (pFrame->flipy)
actor->spr.flags |= 2048;
else
actor->spr.flags &= ~2048;
SetFlag(pWall->cstat, CSTAT_WALL_TRANSLUCENT, pFrame->transparent);
SetFlag(pWallNext->cstat, CSTAT_WALL_TRANSLUCENT, pFrame->transparent);
SetFlag(pWall->cstat, CSTAT_WALL_TRANS_FLIP, pFrame->transparent2);
SetFlag(pWallNext->cstat, CSTAT_WALL_TRANS_FLIP, pFrame->transparent2);
SetFlag(pWall->cstat, CSTAT_WALL_BLOCK, pFrame->blockable);
SetFlag(pWallNext->cstat, CSTAT_WALL_BLOCK, pFrame->blockable);
SetFlag(pWall->cstat, CSTAT_WALL_BLOCK_HITSCAN, pFrame->hittable);
SetFlag(pWallNext->cstat, CSTAT_WALL_BLOCK_HITSCAN, pFrame->hittable);
}
//---------------------------------------------------------------------------
@ -261,7 +189,7 @@ void UpdateSprite(DBloodActor* actor, SEQFRAME* pFrame)
void SEQINST::Update()
{
assert(frameIndex < pSequence->nFrames);
assert(frameIndex < pSequence->frames.Size());
switch (type)
{
case SS_WALL:
@ -282,50 +210,22 @@ void SEQINST::Update()
auto actor = target.actor();
if (!actor) break;
UpdateSprite(actor, &pSequence->frames[frameIndex]);
if (pSequence->frames[frameIndex].playsound) {
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(actor, sound, -1, 0);
}
// by NoOne: add surfaceSound trigger feature
if (!VanillaMode() && pSequence->frames[frameIndex].surfaceSound && actor->vel.Z == 0 && actor->vel.X != 0) {
sfxPlay3DSound(actor, pSequence->getSound(frameIndex), -1, 0);
// NBlood's surfaceSound trigger feature - needs to be reviewed because this never checks if the actor touches the floor!
if (!VanillaMode() && pSequence->frames[frameIndex].surfaceSound && actor->vel.Z == 0 && actor->vel.X != 0)
{
if (actor->sector()->upperLink) break; // don't play surface sound for stacked sectors
int surf = GetExtInfo(actor->sector()->floortexture).surftype;
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 surf = tilesurface(actor->sector()->floortexture);
if (surf <= kSurfNone || surf >= kSurfMax) break;
int sndId = surfSfxMove[surf][Random(2)];
int sndId = 800 + surf * 2 + Random(2);
auto snd = soundEngine->FindSoundByResID(sndId);
if (snd.isvalid())
{
auto udata = soundEngine->GetSfx(snd);
int relVol = udata ? udata->UserVal : 80;
sfxPlay3DSoundVolume(actor, sndId, -1, 0, 0, (surfSfxMove[surf][2] != relVol) ? relVol : surfSfxMove[surf][3]);
sfxPlay3DSoundVolume(actor, snd, -1, 0, 0, (relVol != 80) ? relVol : 25); // some weird shit logic here with the volume...
}
}
break;
@ -336,10 +236,9 @@ void SEQINST::Update()
break;
}
// all seq callbacks are for sprites, but there's no sanity checks here that what gets passed is meant to be for a sprite...
if (pSequence->frames[frameIndex].trigger && callback != nullptr)
// all seq callbacks are for sprites, so skip for other types.
if (type == SS_SPRITE && pSequence->frames[frameIndex].trigger && callback != nullptr)
{
assert(type == SS_SPRITE);
if (target.isActor()) callActorFunction(callback, target.actor());
}
@ -468,45 +367,77 @@ void seqKill(DBloodActor* actor)
//
//---------------------------------------------------------------------------
static void ByteSwapSEQ(Seq* pSeq)
static Seq* ReadSEQ(int index)
{
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++)
auto fr = fileSystem.OpenFileReader(index);
if (!fr.isOpen())
{
SEQFRAME* pFrame = &pSeq->frames[i];
BitReader bitReader((uint8_t*)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.scalex = bitReader.readUnsigned(8);
swapFrame.scaley = 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;
Printf("%s: unable to open", fileSystem.GetFileFullName(index));
return nullptr;
}
char header[4];
fr.Read(header, 4);
if (memcmp(&header[0], "SEQ\x1a", 4) != 0)
{
Printf("%s: Invalid sequence", fileSystem.GetFileFullName(index));
return nullptr;
}
int version = fr.ReadInt16();
if ((version & 0xff00) != 0x300)
{
Printf("%s: Obsolete sequence version", fileSystem.GetFileFullName(index));
return nullptr;
}
unsigned int frames = fr.ReadUInt16();
int ticks = fr.ReadUInt16();
int soundid = fr.ReadUInt16();
int flags = fr.ReadInt32();
// allocate both buffers off our memory arena.
auto seqdata = (Seq*)seqcache.Alloc(sizeof(Seq));
seqdata->frames.Set((SeqFrame*)seqcache.Alloc(sizeof(SeqFrame) * frames), frames);
seqdata->flags = flags;
seqdata->ticksPerFrame = ticks;
seqdata->soundResId = soundid;
seqdata->soundId = soundEngine->FindSoundByResID(soundid);
for (auto& frame : seqdata->frames)
{
uint8_t framebuf[8];
fr.Read(framebuf, 8);
BitReader bitReader(framebuf, sizeof(framebuf));
int tile = bitReader.getBits(12);
frame.transparent = bitReader.getBit();
frame.transparent2 = bitReader.getBit();
frame.blockable = bitReader.getBit();
frame.hittable = bitReader.getBit();
frame.scale.X = bitReader.getBits(8) * REPEAT_SCALEF;
frame.scale.Y = bitReader.getBits(8) * REPEAT_SCALEF;
frame.shade = bitReader.getBitsSigned(8);
frame.palette = bitReader.getBits(5);
frame.trigger = bitReader.getBit();
frame.smoke = bitReader.getBit();
frame.aiming = bitReader.getBit();
frame.pushable = bitReader.getBit();
frame.playsound = bitReader.getBit();
frame.invisible = bitReader.getBit();
frame.flipx = bitReader.getBit();
frame.flipy = bitReader.getBit();
tile += bitReader.getBits(4) << 12;;
frame.soundRange = bitReader.getBits(4);
frame.surfaceSound = bitReader.getBit();
frame.texture = tileGetTextureID(tile);
}
return seqdata;
}
FMemArena seqcache;
static TMap<int64_t, Seq*> sequences;
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
Seq* getSequence(FName res_name, int res_id)
{
int64_t key = ((int64_t)res_name.GetIndex() << 32) | res_id;
@ -529,14 +460,13 @@ Seq* getSequence(FName res_name, int res_id)
if (index < 0)
{
if (res_name != NAME_None) Printf("%s: sequence not found", res_name.GetChars());
sequences.Insert(key, nullptr); // even store null entries to avoid repeated lookup for them.
return nullptr;
}
auto fr = fileSystem.OpenFileReader(index);
auto seqdata = (Seq*)seqcache.Alloc(fr.GetLength());
fr.Read(seqdata, fr.GetLength());
auto seqdata = ReadSEQ(index);
sequences.Insert(key, seqdata);
ByteSwapSEQ(seqdata);
return seqdata;
}
@ -564,11 +494,11 @@ void seqSpawn_(FName name, int nSeqID, int type, const EventObject& eob, VMFunct
return;
}
pInst->pSequence = pSequence;
pInst->nSeqID = nSeqID;
pInst->pSequence = pSequence;
pInst->nName = name;
pInst->callback = callback;
pInst->timeCounter = (short)pSequence->ticksPerFrame;
pInst->timeCounter = pSequence->ticksPerFrame;
pInst->frameIndex = 0;
pInst->type = type;
pInst->target = eob;
@ -672,7 +602,7 @@ void seqProcess(int nTicks)
Seq* pSeq = pInst->pSequence;
auto target = pInst->target;
assert(pInst->frameIndex < pSeq->nFrames);
assert(pInst->frameIndex < pSeq->frames.Size());
pInst->timeCounter -= nTicks;
while (pInst->timeCounter < 0)
@ -680,7 +610,7 @@ void seqProcess(int nTicks)
pInst->timeCounter += pSeq->ticksPerFrame;
++pInst->frameIndex;
if (pInst->frameIndex == pSeq->nFrames)
if (pInst->frameIndex == pSeq->frames.Size())
{
if (!pSeq->isLooping())
{
@ -693,7 +623,7 @@ void seqProcess(int nTicks)
if (actor)
{
evKillActor(actor);
if ((actor->spr.hitag & kAttrRespawn) != 0 && actor->WasDudeActor())
if ((actor->spr.hitag & kHitagRespawn) != 0 && actor->WasDudeActor())
evPostActor(actor, gGameOptions.nMonsterRespawnTime, AF(Respawn));
else DeleteSprite(actor); // safe to not use actPostSprite here
}

View file

@ -1,65 +1,56 @@
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
* Copyright (C) 2018, 2022 nukeykt
* Copyright (C) 2020-2023 Christoph Oelckers
*
* This file is part of Raze
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*
*/
#pragma once
#include "misc.h"
#include "common_game.h"
BEGIN_BLD_NS
struct SEQFRAME
struct SeqFrame
{
unsigned int tile : 12;
FVector2 scale;
FTextureID texture;
uint8_t palette;
int8_t shade;
int8_t soundRange;
unsigned int transparent : 1;
unsigned int transparent2 : 1;
unsigned int blockable : 1;
unsigned int hittable : 1;
unsigned int scalex : 8;
unsigned int scaley : 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 invisible : 1;
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;
unsigned int surfaceSound : 1; // (by NoOne) trigger surface sound when moving / touching
};
struct Seq {
char signature[4];
int16_t version;
int16_t nFrames;
int16_t ticksPerFrame;
int16_t soundId;
struct Seq
{
TArrayView<SeqFrame> frames;
int ticksPerFrame;
FSoundID soundId;
int soundResId; // still needed for the soundRange feature
int flags;
SEQFRAME frames[1];
void Precache(int palette);
bool isLooping()
{
@ -70,6 +61,15 @@ struct Seq {
{
return (flags & 2) != 0;
}
FSoundID getSound(int frame)
{
if (!frames[frame].playsound) return NO_SOUND;
int range = frames[frame].soundRange;
if (VanillaMode() || range <= 0) return soundId;
return soundEngine->FindSoundByResID(soundResId + Random(max(2, range)));
}
};
class DBloodActor;
@ -77,21 +77,16 @@ struct SEQINST
{
Seq* pSequence;
EventObject target;
int type;
VMFunction* callback;
int type;
int nSeqID; // only one of these two may be set
FName nName;
VMFunction* callback;
int16_t timeCounter;
uint8_t frameIndex;
uint16_t frameIndex;
void Update();
};
inline FTextureID seqGetTexture(SEQFRAME* pFrame)
{
return tileGetTextureID(pFrame->tile + (pFrame->tile2 << 12));
}
void seqPrecacheId(FName name, int id, int palette);
inline void seqPrecacheId(int id, int palette)

View file

@ -1,6 +0,0 @@
#pragma once
BEGIN_BLD_NS
END_BLD_NS

View file

@ -2395,7 +2395,7 @@ void trInit(TArray<DBloodActor*>& actors)
break;
}
if (actor->xspr.Vector) actor->spr.cstat |= CSTAT_SPRITE_BLOCK_HITSCAN;
if (actor->xspr.Push) actor->spr.cstat |= CSTAT_SPRITE_BLOOD_BIT1;
if (actor->xspr.Push) actor->spr.cstat |= CSTAT_SPRITE_BLOOD_PUSHABLE;
}
}

View file

@ -1997,7 +1997,7 @@ void AltFireLifeLeech(int, DBloodPlayer* pPlayer)
auto missile = playerFireThing(pPlayer, 0, -4730 / 65536., kThingDroppedLifeLeech, 1.6);
if (missile)
{
missile->spr.cstat |= CSTAT_SPRITE_BLOOD_BIT1;
missile->spr.cstat |= CSTAT_SPRITE_BLOOD_PUSHABLE;
missile->xspr.Push = 1;
missile->xspr.Proximity = 1;
missile->xspr.DudeLockout = 1;