Updated VisualThinker render linkage

Now uses its own self-managed linked list with its level tracking the head pointer. Allows for VisualThinkers to be moved to any desired stat instead of being stuck in STAT_VISUALTHINKER.
This commit is contained in:
Boondorl 2025-01-13 12:15:00 -05:00 committed by Rachael Alexanderson
parent 61df13306f
commit c87dc5ccad
6 changed files with 42 additions and 28 deletions

View file

@ -427,11 +427,6 @@ public:
DThinker *CreateThinker(PClass *cls, int statnum = STAT_DEFAULT)
{
DThinker *thinker = static_cast<DThinker*>(cls->CreateNew());
if (thinker->IsKindOf(RUNTIME_CLASS(DVisualThinker)))
{
statnum = STAT_VISUALTHINKER;
}
assert(thinker->IsKindOf(RUNTIME_CLASS(DThinker)));
thinker->ObjectFlags |= OF_JustSpawned;
Thinkers.Link(thinker, statnum);
@ -708,6 +703,7 @@ public:
int ImpactDecalCount;
FDynamicLight *lights;
DVisualThinker* VisualThinkerHead = nullptr;
// links to global game objects
TArray<TObjPtr<AActor *>> CorpseQueue;

View file

@ -990,7 +990,8 @@ void FLevelLocals::Serialize(FSerializer &arc, bool hubload)
("scrolls", Scrolls)
("automap", automap)
("interpolator", interpolator)
("frozenstate", frozenstate);
("frozenstate", frozenstate)
("visualthinkerhead", VisualThinkerHead);
// Hub transitions must keep the current total time

View file

@ -810,11 +810,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DThinker, ChangeStatNum, ChangeStatNum)
PARAM_SELF_PROLOGUE(DThinker);
PARAM_INT(stat);
// do not allow ZScript to reposition thinkers in or out of particle ticking.
if (stat != STAT_VISUALTHINKER && !dynamic_cast<DVisualThinker*>(self))
{
ChangeStatNum(self, stat);
}
ChangeStatNum(self, stat);
return 0;
}

View file

@ -214,15 +214,13 @@ void P_FindParticleSubsectors (FLevelLocals *Level)
{
Level->subsectors[i].sprites.Clear();
}
// [MC] Not too happy about using an iterator for this but I can't think of another way to handle it.
// At least it's on its own statnum for maximum efficiency.
auto it = Level->GetThinkerIterator<DVisualThinker>(NAME_None, STAT_VISUALTHINKER);
DVisualThinker* sp;
while (sp = it.Next())
auto sp = Level->VisualThinkerHead;
while (sp != nullptr)
{
if (!sp->PT.subsector) sp->PT.subsector = Level->PointInRenderSubsector(sp->PT.Pos);
sp->PT.subsector->sprites.Push(sp);
sp = sp->GetNext();
}
// End VisualThinker hitching. Now onto the particles.
if (Level->ParticlesInSubsec.Size() < Level->subsectors.Size())
@ -1007,26 +1005,38 @@ void DVisualThinker::Construct()
cursector = nullptr;
PT.color = 0xffffff;
AnimatedTexture.SetNull();
}
DVisualThinker::DVisualThinker()
{
Construct();
_prev = _next = nullptr;
if (Level->VisualThinkerHead != nullptr)
{
Level->VisualThinkerHead->_prev = this;
_next = Level->VisualThinkerHead;
}
Level->VisualThinkerHead = this;
}
void DVisualThinker::OnDestroy()
{
if (_prev != nullptr)
_prev->_next = _next;
if (_next != nullptr)
_next->_prev = _prev;
if (Level->VisualThinkerHead == this)
Level->VisualThinkerHead = _next;
PT.alpha = 0.0; // stops all rendering.
Super::OnDestroy();
}
DVisualThinker* DVisualThinker::GetNext() const
{
return _next;
}
DVisualThinker* DVisualThinker::NewVisualThinker(FLevelLocals* Level, PClass* type)
{
if (type == nullptr)
return nullptr;
else if (type->bAbstract)
{
Printf("Attempt to spawn an instance of abstract VisualThinker class %s\n", type->TypeName.GetChars());
return nullptr;
}
else if (!type->IsDescendantOf(RUNTIME_CLASS(DVisualThinker)))
@ -1034,8 +1044,13 @@ DVisualThinker* DVisualThinker::NewVisualThinker(FLevelLocals* Level, PClass* ty
Printf("Attempt to spawn class not inherent to VisualThinker: %s\n", type->TypeName.GetChars());
return nullptr;
}
else if (type->bAbstract)
{
Printf("Attempt to spawn an instance of abstract VisualThinker class %s\n", type->TypeName.GetChars());
return nullptr;
}
DVisualThinker *zs = static_cast<DVisualThinker*>(Level->CreateThinker(type, STAT_VISUALTHINKER));
auto zs = static_cast<DVisualThinker*>(Level->CreateThinker(type, DVisualThinker::DEFAULT_STAT));
zs->Construct();
return zs;
}
@ -1284,7 +1299,7 @@ void DVisualThinker::Serialize(FSerializer& arc)
{
Super::Serialize(arc);
arc ("pos", PT.Pos)
arc("pos", PT.Pos)
("vel", PT.Vel)
("prev", Prev)
("scale", Scale)
@ -1300,7 +1315,9 @@ void DVisualThinker::Serialize(FSerializer& arc)
("lightlevel", LightLevel)
("animData", PT.animData)
("flags", PT.flags)
("visualThinkerFlags", flags);
("visualThinkerFlags", flags)
("next", _next)
("prev", _prev);
if(arc.isReading())
{

View file

@ -26,7 +26,11 @@ class DVisualThinker : public DThinker
{
DECLARE_CLASS(DVisualThinker, DThinker);
void UpdateSector(subsector_t * newSubsector);
DVisualThinker* _next, * _prev;
public:
static const int DEFAULT_STAT = STAT_VISUALTHINKER;
DVector3 Prev;
DVector2 Scale,
Offset;
@ -41,9 +45,9 @@ public:
// internal only variables
particle_t PT;
DVisualThinker();
void Construct();
void OnDestroy() override;
DVisualThinker* GetNext() const;
static DVisualThinker* NewVisualThinker(FLevelLocals* Level, PClass* type);
void SetTranslation(FName trname);

View file

@ -33,7 +33,7 @@ Class VisualThinker : Thinker native
native protected void UpdateSector(); // needs to be called if the thinker is set to a non-ticking statnum and the position is modified (or if Tick is overriden and doesn't call Super.Tick())
native protected void UpdateSpriteInfo(); // needs to be called every time the texture is updated if the thinker uses SPF_LOCAL_ANIM and is set to a non-ticking statnum (or if Tick is overriden and doesn't call Super.Tick())
static VisualThinker Spawn(Class<VisualThinker> type, TextureID tex, Vector3 pos, Vector3 vel, double alpha = 1.0, int flags = 0,
static VisualThinker Spawn(Class<VisualThinker> type, TextureID tex, Vector3 pos, Vector3 vel = (0,0,0), double alpha = 1.0, int flags = 0,
double roll = 0.0, Vector2 scale = (1,1), Vector2 offset = (0,0), int style = STYLE_Normal, TranslationID trans = 0, int VisualThinkerFlags = 0)
{
if (!Level) return null;