raze/source/core/coreactor.h

675 lines
15 KiB
C
Raw Normal View History

2021-11-25 20:42:49 +00:00
#pragma once
#include <stdint.h>
#include "maptypes.h"
#include "build.h"
#include "actorinfo.h"
#include "interphelpers.h"
enum
{
MAXSTATUS = 1024
};
2021-11-25 20:42:49 +00:00
struct FWallSpriteDesc
{
walltype* wall;
float offsetOnWall;
};
// This is for quick determination of walls a wall sprite is attached to.
struct FWallSpriteInfo
{
tspritetype base; // this is needed for checking if something has changed.
TArray<FWallSpriteDesc> connections[2]; // two lists - for front and back
};
class DCoreActor : public DObject
2021-11-25 20:42:49 +00:00
{
DECLARE_CLASS(DCoreActor, DObject)
HAS_OBJECT_POINTERS
2021-11-25 20:42:49 +00:00
// common part of the game actors
public:
// These two are needed because we cannot rely on the ones in the sprites for unlinking.
int link_stat;
sectortype* link_sector;
DCoreActor* prevStat, * nextStat;
DCoreActor* prevSect, * nextSect;
FWallSpriteInfo* wallspriteinfo; // this is render data but needs to be attached to the actor so it can be found.
spritetype spr;
spriteext_t sprext;
spritesmooth_t spsmooth;
2022-02-03 21:06:09 +00:00
DVector3 opos;
int time;
DAngle oang;
2022-09-03 07:41:02 +00:00
DVector3 vel;
2022-02-13 10:38:21 +00:00
int16_t spritesetindex;
2021-12-30 17:21:02 +00:00
DCoreActor() = default;
virtual ~DCoreActor() = default;
DCoreActor(const DCoreActor& other) = delete; // we also do not want to allow copies.
DCoreActor& operator=(const DCoreActor& other) = delete;
virtual void Serialize(FSerializer& arc);
virtual void BeginPlay() {}
void OnDestroy() override;
size_t PropagateMark() override;
int GetOffsetAndHeight(int& height);
bool exists() const
{
2021-12-23 22:48:39 +00:00
return (unsigned)spr.statnum < MAXSTATUS;
}
int GetIndex() const
2021-11-25 20:42:49 +00:00
{
2021-12-22 20:06:31 +00:00
// This is only identical with the sprite index for items spawned at map start.
return time;
}
2022-01-31 18:20:53 +00:00
const vec3_t int_pos() const
{
return { int(spr.pos.X * worldtoint), int(spr.pos.Y * worldtoint), int(spr.pos.Z * zworldtoint) };
}
void set_int_z(int z)
{
spr.pos.Z = z * zinttoworld;
}
void add_int_z(int z)
{
spr.pos.Z += z * zinttoworld;
}
void add_int_pos(const vec3_t& add)
{
spr.pos += { add.X* inttoworld, add.Y* inttoworld, add.Z* zinttoworld };
}
2022-08-16 22:50:50 +00:00
constexpr int int_ang() const
2022-08-16 21:15:16 +00:00
{
return spr.angle.Buildang();
2022-08-16 21:15:16 +00:00
}
void set_int_ang(int a)
{
spr.angle = DAngle::fromBuild(a);
2022-08-16 21:15:16 +00:00
}
2022-08-16 21:25:11 +00:00
void add_int_ang(int a)
{
spr.angle += DAngle::fromBuild(a);
2022-08-16 21:25:11 +00:00
}
2022-08-16 21:15:16 +00:00
2022-08-16 21:27:44 +00:00
void norm_ang()
{
spr.angle = spr.angle.Normalized360();
2022-08-16 21:27:44 +00:00
}
2022-08-31 21:49:56 +00:00
int int_zvel() const
{
2022-09-03 07:41:02 +00:00
return vel.Z * zworldtoint;
}
2022-08-31 21:57:15 +00:00
void set_int_zvel(int v)
{
2022-09-03 07:41:02 +00:00
vel.Z = v * zinttoworld;
2022-08-31 21:57:15 +00:00
}
2022-08-31 22:30:16 +00:00
void add_int_zvel(int v)
{
2022-09-03 07:41:02 +00:00
vel.Z += v * zinttoworld;
2022-08-31 22:30:16 +00:00
}
// Note: Both Duke and SW use Q12.4 for this, Exhumed doesn't seem to treat horizontal velocity with a fixed factor.
int int_xvel() const
{
2022-09-03 07:41:02 +00:00
return vel.X * worldtoint;
}
void set_int_xvel(int v)
{
2022-09-03 07:41:02 +00:00
vel.X = v * inttoworld;
}
void add_int_xvel(int v)
{
2022-09-03 07:41:02 +00:00
vel.X += v * inttoworld;
}
2022-09-01 15:19:28 +00:00
// Only used this way by Exhumed.
int int_yvel() const
{
2022-09-03 07:41:02 +00:00
return vel.Y * worldtoint;
2022-09-01 15:19:28 +00:00
}
void set_int_yvel(int v)
{
2022-09-03 07:41:02 +00:00
vel.Y = v * inttoworld;
2022-09-01 15:19:28 +00:00
}
void add_int_yvel(int v)
{
2022-09-03 07:41:02 +00:00
vel.Y += v * inttoworld;
2022-09-01 15:19:28 +00:00
}
2022-09-01 18:36:55 +00:00
vec3_t int_vel() const
{
2022-09-03 07:41:02 +00:00
return vec3_t(FloatToFixed(vel.X), FloatToFixed(vel.Y), FloatToFixed(vel.Z));
2022-09-01 18:36:55 +00:00
}
void set_int_bvel_x(int x)
{
2022-09-03 07:41:02 +00:00
vel .X = FixedToFloat(x);
2022-09-01 18:36:55 +00:00
}
void set_int_bvel_y(int x)
{
2022-09-03 07:41:02 +00:00
vel .Y = FixedToFloat(x);
2022-09-01 18:36:55 +00:00
}
void set_int_bvel_z(int x)
{
2022-09-03 07:41:02 +00:00
vel .Z = FixedToFloat(x);
2022-09-01 18:36:55 +00:00
}
void add_int_bvel_x(int x)
{
2022-09-03 07:41:02 +00:00
vel .X += FixedToFloat(x);
2022-09-01 18:36:55 +00:00
}
void add_int_bvel_y(int x)
{
2022-09-03 07:41:02 +00:00
vel .Y += FixedToFloat(x);
2022-09-01 18:36:55 +00:00
}
2022-09-01 19:08:05 +00:00
void add_int_bvel_z(int x)
2022-09-01 18:56:12 +00:00
{
2022-09-03 07:41:02 +00:00
vel .Z += FixedToFloat(x);
2022-09-01 18:56:12 +00:00
}
void ZeroVelocityXY()
2022-09-01 18:36:55 +00:00
{
2022-09-03 07:41:02 +00:00
vel .X = vel .Y = 0;
2022-09-01 18:36:55 +00:00
}
void ZeroVelocity()
{
2022-09-03 07:41:02 +00:00
vel = { 0,0,0 };
}
// Same as above but with inverted y and z axes to match the renderer's coordinate system.
2022-02-03 21:06:09 +00:00
double interpolatedx(double const smoothratio, int const scale = 16)
{
return interpolatedvaluef(opos.X, spr.pos.X, smoothratio, scale);
}
double interpolatedy(double const smoothratio, int const scale = 16)
{
return interpolatedvaluef(opos.Y, spr.pos.Y, smoothratio, scale);
}
double interpolatedz(double const smoothratio, int const scale = 16)
{
return interpolatedvaluef(opos.Z, spr.pos.Z, smoothratio, scale);
2022-02-03 21:06:09 +00:00
}
DVector3 interpolatedvec3(double const smoothratio)
2022-02-03 21:06:09 +00:00
{
return ::interpolatedvec3(opos, spr.pos, smoothratio);
2022-02-03 21:06:09 +00:00
}
int32_t __interpolatedx(double const smoothratio, int const scale = 16)
{
2022-02-03 21:06:09 +00:00
return interpolatedx(smoothratio, scale) * worldtoint;
}
int32_t __interpolatedy(double const smoothratio, int const scale = 16)
{
2022-02-03 21:06:09 +00:00
return interpolatedy(smoothratio, scale) * worldtoint;
}
int32_t __interpolatedz(double const smoothratio, int const scale = 16)
{
2022-02-03 21:06:09 +00:00
return interpolatedz(smoothratio, scale) * zworldtoint;
}
DAngle interpolatedangle(double const smoothratio)
{
return ::interpolatedangle(oang, spr.angle, smoothratio);
}
void backupz()
{
2022-02-03 21:06:09 +00:00
opos.Z = spr.pos.Z;
}
void backupvec2()
{
opos.XY() = spr.pos.XY();
}
void backuppos()
{
2022-02-03 21:06:09 +00:00
opos = spr.pos;
}
void backupang()
{
oang = spr.angle;
}
void backuploc()
{
backuppos();
backupang();
}
sectortype* sector() const
2021-11-25 20:42:49 +00:00
{
return spr.sectp;
2021-11-25 20:42:49 +00:00
}
bool insector() const
{
return spr.sectp != nullptr;
}
2021-11-25 20:42:49 +00:00
void setsector(sectortype* sect)
{
// place for asserts.
spr.sectp = sect;
}
2021-12-30 15:39:43 +00:00
int sectno() const
{
return spr.sectp ? ::sector.IndexOf(spr.sectp) : -1;
}
auto spriteset() const
{
return static_cast<PClassActor*>(GetClass())->ActorInfo()->SpriteSet;
}
2021-12-30 15:39:43 +00:00
2021-11-25 20:42:49 +00:00
};
// holds pointers to the game-side actors.
extern TArray<sectortype> sector;
extern TArray<walltype> wall;
// Masking these into the object index to keep it in 16 bit was probably the single most dumbest and pointless thing Build ever did.
// Gonna be fun to globally replace these to finally lift the limit this imposes on map size.
// Names taken from DukeGDX
enum EHitBits
{
kHitNone = 0,
kHitTypeMask = 0xC000,
kHitTypeMaskSW = 0x1C000, // SW has one more relevant bit
kHitIndexMask = 0x3FFF,
kHitSector = 0x4000,
kHitWall = 0x8000,
kHitSprite = 0xC000,
kHitVoid = 0x10000, // SW only
2021-11-25 20:42:49 +00:00
};
// This serves as input/output for all functions dealing with collisions, hits, etc.
// Not all utilities use all variables.
struct HitInfoBase
{
2022-08-17 16:53:45 +00:00
DVector3 hitpos;
2021-11-25 20:42:49 +00:00
sectortype* hitSector;
walltype* hitWall;
DCoreActor* hitActor;
2021-11-25 20:42:49 +00:00
void clearObj()
{
hitSector = nullptr;
hitWall = nullptr;
hitActor = nullptr;
}
const vec3_t int_hitpos() const
{
2022-08-17 16:53:45 +00:00
return { int(hitpos.X * worldtoint), int(hitpos.Y * worldtoint), int(hitpos.Z * zworldtoint), };
}
void set_int_hitpos_xy(int x, int y)
{
hitpos.X = x * inttoworld;
hitpos.Y = y * inttoworld;
}
};
template<class T>
struct THitInfo : public HitInfoBase
{
T* actor() const { return static_cast<T*>(hitActor); }
};
struct CollisionBase
{
int type;
2021-11-26 13:26:03 +00:00
int exbits; // extended game-side info (only used by Exhumed)
union
{
// can only have one at a time
sectortype* hitSector;
walltype* hitWall;
DCoreActor* hitActor;
};
2021-11-25 20:42:49 +00:00
void invalidate()
{
type = -1; // something invalid that's not a valid hit type.
hitSector = nullptr;
2021-11-25 20:42:49 +00:00
}
int setNone()
{
*this = {};
return kHitNone;
}
int setSector(int num)
{
*this = {};
type = kHitSector;
hitSector = &sector[num];
return kHitSector;
}
int setSector(sectortype* num)
{
*this = {};
type = kHitSector;
hitSector = num;
return kHitSector;
}
2021-11-25 20:42:49 +00:00
int setWall(int num)
{
*this = {};
type = kHitWall;
hitWall = &wall[num];
return kHitWall;
}
int setWall(walltype* num)
{
*this = {};
type = kHitWall;
hitWall = num;
return kHitWall;
}
int setSprite(DCoreActor* num)
2021-11-25 20:42:49 +00:00
{
*this = {};
type = kHitSprite;
hitActor = num;
return kHitSprite;
}
int setVoid()
{
hitSector = nullptr;
2021-11-25 20:42:49 +00:00
type = kHitVoid;
return kHitVoid;
2021-11-25 20:42:49 +00:00
}
};
template<class T>
struct TCollision : public CollisionBase
{
T* actor() const
{
return static_cast<T*>(hitActor);
}
// normally collision data is short lived, this is only needed in some very rare circumstances.
T* safeActor()
{
return static_cast<T*>(GC::ReadBarrier(hitActor));
}
auto operator=(const CollisionBase& other)
{
*(CollisionBase*)this = other;
return *this;
}
};
struct ActorStatList
{
DCoreActor* firstEntry, * lastEntry;
};
extern ActorStatList statList[MAXSTATUS];
template<class TActor>
class TStatIterator
{
DCoreActor* next;
public:
TStatIterator(int stat)
{
next = statList[stat].firstEntry;
}
void Reset(int stat)
{
next = statList[stat].firstEntry;
}
TActor* Next()
{
auto n = next;
if (next) next = next->nextStat;
return static_cast<TActor*>(n);
}
TActor* Peek()
{
return static_cast<TActor*>(next);
}
};
template<class TActor>
class TSectIterator
{
DCoreActor* next;
public:
//[[deprecated]]
TSectIterator(int stat)
{
next = sector[stat].firstEntry;
}
TSectIterator(sectortype* stat)
{
next = stat->firstEntry;
}
//[[deprecated]]
void Reset(int stat)
{
next = sector[stat].firstEntry;
}
void Reset(sectortype* stat)
{
next = stat->firstEntry;
}
TActor* Next()
{
auto n = next;
if (next) next = next->nextSect;
return static_cast<TActor*>(n);
}
TActor* Peek()
{
return static_cast<TActor*>(next);
}
};
// An iterator to iterate over all sprites.
template<class TActor>
class TSpriteIterator
{
TStatIterator<TActor> it;
int stat = 0;
public:
TSpriteIterator() : it(0) {}
TActor* Next()
{
while (stat < MAXSTATUS)
{
auto ac = it.Next();
if (ac) return ac;
stat++;
if (stat < MAXSTATUS) it.Reset(stat);
}
return nullptr;
}
void Reset()
{
stat = 0;
it.Reset(0);
}
};
using CoreSectIterator = TSectIterator<DCoreActor>;
DCoreActor* InsertActor(PClass* type, sectortype* sector, int stat, bool forcetail = false);
void ChangeActorSect(DCoreActor* actor, sectortype* sector, bool forcetail = false);
int ChangeActorStat(DCoreActor* actor, int nStatus, bool forcetail = false);
void InitSpriteLists();
void SetActorZ(DCoreActor* actor, const DVector3& newpos);
void SetActor(DCoreActor* actor, const DVector3& newpos);
inline int clipmove(vec3_t& pos, sectortype** const sect, int xvect, int yvect,
int const walldist, int const ceildist, int const flordist, unsigned const cliptype, CollisionBase& result, int clipmoveboxtracenum = 3)
{
int sectno = *sect ? sector.IndexOf(*sect) : -1;
result = clipmove_(&pos, &sectno, xvect, yvect, walldist, ceildist, flordist, cliptype, clipmoveboxtracenum);
*sect = sectno == -1 ? nullptr : &sector[sectno];
return result.type;
}
2021-11-26 17:59:28 +00:00
inline int clipmove(DVector3& pos, sectortype** const sect, int xvect, int yvect,
int const walldist, int const ceildist, int const flordist, unsigned const cliptype, CollisionBase& result, int clipmoveboxtracenum = 3)
{
auto vect = vec3_t(pos.X * worldtoint, pos.Y * worldtoint, pos.Z * zworldtoint);
int res = clipmove(vect, sect, xvect, yvect, walldist, ceildist, flordist, cliptype, result, clipmoveboxtracenum);
pos = { vect.X * inttoworld, vect.Y * inttoworld, vect.Z * zinttoworld };
return res;
}
inline int clipmove(DVector3& pos, sectortype** const sect, int xvect, int yvect,
int const walldist, double const ceildist, double const flordist, unsigned const cliptype, CollisionBase& result, int clipmoveboxtracenum = 3)
{
auto vect = vec3_t(pos.X * worldtoint, pos.Y * worldtoint, pos.Z * zworldtoint);
int res = clipmove(vect, sect, xvect, yvect, walldist, int(ceildist * zworldtoint), int(flordist * zworldtoint), cliptype, result, clipmoveboxtracenum);
pos = { vect.X * inttoworld, vect.Y * inttoworld, vect.Z * zinttoworld };
return res;
}
// this one should be the final version everything needs to migrate to
inline int clipmove(DVector3& pos, sectortype** const sect, const DVector2& mvec,
double const walldist, double const ceildist, double const flordist, unsigned const cliptype, CollisionBase& result, int clipmoveboxtracenum = 3)
{
auto vect = vec3_t(pos.X * worldtoint, pos.Y * worldtoint, pos.Z * zworldtoint);
int res = clipmove(vect, sect, int(mvec.X * worldtoint), int(mvec.Y * worldtoint), int(walldist * worldtoint), int(ceildist * zworldtoint), int(flordist * zworldtoint), cliptype, result, clipmoveboxtracenum);
pos = { vect.X * inttoworld, vect.Y * inttoworld, vect.Z * zinttoworld };
return res;
}
inline int pushmove(vec3_t* const vect, sectortype** const sect, int32_t const walldist, int32_t const ceildist, int32_t const flordist,
uint32_t const cliptype, bool clear = true)
{
int sectno = *sect ? sector.IndexOf(*sect) : -1;
int res = pushmove_(vect, &sectno, walldist, ceildist, flordist, cliptype, clear);
*sect = sectno == -1 ? nullptr : &sector[sectno];
return res;
}
inline int pushmove(DVector3& pos, sectortype** const sect, int32_t const walldist, int32_t const ceildist, int32_t const flordist,
uint32_t const cliptype, bool clear = true)
{
auto vect = vec3_t(pos.X * worldtoint, pos.Y * worldtoint, pos.Z * zworldtoint);
int sectno = *sect ? sector.IndexOf(*sect) : -1;
int res = pushmove_(&vect, &sectno, walldist, ceildist, flordist, cliptype, clear);
pos = { vect.X * inttoworld, vect.Y * inttoworld, vect.Z * zinttoworld };
*sect = sectno == -1 ? nullptr : &sector[sectno];
return res;
}
inline int pushmove(DVector3& pos, sectortype** const sect, int32_t const walldist, double const ceildist, double const flordist,
uint32_t const cliptype, bool clear = true)
{
auto vect = vec3_t(pos.X * worldtoint, pos.Y * worldtoint, pos.Z * zworldtoint);
int sectno = *sect ? sector.IndexOf(*sect) : -1;
int res = pushmove_(&vect, &sectno, walldist, int(ceildist * zworldtoint), int(flordist * zworldtoint), cliptype, clear);
pos = { vect.X * inttoworld, vect.Y * inttoworld, vect.Z * zinttoworld };
*sect = sectno == -1 ? nullptr : &sector[sectno];
return res;
}
#if 0
inline int pushmove(DVector3& pos, sectortype** const sect, double const walldist, double const ceildist, double const flordist,
uint32_t const cliptype, bool clear = true)
{
auto vect = vec3_t(pos.X * worldtoint, pos.Y * worldtoint, pos.Z * zworldtoint);
int sectno = *sect ? sector.IndexOf(*sect) : -1;
int res = pushmove_(&vect, &sectno, walldist * worldtoint, ceildist * worldtoint, flordist * worldtoint, cliptype, clear);
pos = { vect.X * inttoworld, vect.Y * inttoworld, vect.Z * zinttoworld };
*sect = sectno == -1 ? nullptr : &sector[sectno];
return res;
}
#endif
inline int pushmove(DCoreActor* actor, sectortype** const sect, int32_t const walldist, int32_t const ceildist, int32_t const flordist,
uint32_t const cliptype, bool clear = true)
{
return pushmove(actor->spr.pos, sect, walldist, ceildist, flordist, cliptype, clear);
}
tspritetype* renderAddTsprite(tspriteArray& tsprites, DCoreActor* actor);
inline PClassActor* PClass::FindActor(FName name)
{
auto cls = FindClass(name);
return cls && cls->IsDescendantOf(RUNTIME_CLASS(DCoreActor)) ? static_cast<PClassActor*>(cls) : nullptr;
}
2022-01-23 09:54:56 +00:00
inline DCoreActor* GetDefaultByType(const PClass* type)
{
return (DCoreActor*)(type->Defaults);
}