From c87dc5ccadf0e84b8845dd6079cb512a2f4332b1 Mon Sep 17 00:00:00 2001 From: Boondorl Date: Mon, 13 Jan 2025 12:15:00 -0500 Subject: [PATCH] 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. --- src/g_levellocals.h | 6 +--- src/p_saveg.cpp | 3 +- src/playsim/dthinker.cpp | 6 +--- src/playsim/p_effect.cpp | 47 ++++++++++++++++++-------- src/playsim/p_visualthinker.h | 6 +++- wadsrc/static/zscript/visualthinker.zs | 2 +- 6 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 5a2b37ca267..30602d4d07a 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -427,11 +427,6 @@ struct FLevelLocals DThinker *CreateThinker(PClass *cls, int statnum = STAT_DEFAULT) { DThinker *thinker = static_cast(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 @@ struct FLevelLocals int ImpactDecalCount; FDynamicLight *lights; + DVisualThinker* VisualThinkerHead = nullptr; // links to global game objects TArray> CorpseQueue; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 3a267c81627..ce26ff39c7a 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -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 diff --git a/src/playsim/dthinker.cpp b/src/playsim/dthinker.cpp index e8438475209..51cf3a67036 100644 --- a/src/playsim/dthinker.cpp +++ b/src/playsim/dthinker.cpp @@ -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(self)) - { - ChangeStatNum(self, stat); - } + ChangeStatNum(self, stat); return 0; } diff --git a/src/playsim/p_effect.cpp b/src/playsim/p_effect.cpp index a2d42f7c57b..af8514698bb 100644 --- a/src/playsim/p_effect.cpp +++ b/src/playsim/p_effect.cpp @@ -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(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(Level->CreateThinker(type, STAT_VISUALTHINKER)); + auto zs = static_cast(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()) { diff --git a/src/playsim/p_visualthinker.h b/src/playsim/p_visualthinker.h index d4b1e262dd9..9fb5eae65c3 100644 --- a/src/playsim/p_visualthinker.h +++ b/src/playsim/p_visualthinker.h @@ -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 @@ class DVisualThinker : public DThinker // 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); diff --git a/wadsrc/static/zscript/visualthinker.zs b/wadsrc/static/zscript/visualthinker.zs index 45b32865cb2..2b6d9a8428b 100644 --- a/wadsrc/static/zscript/visualthinker.zs +++ b/wadsrc/static/zscript/visualthinker.zs @@ -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 type, TextureID tex, Vector3 pos, Vector3 vel, double alpha = 1.0, int flags = 0, + static VisualThinker Spawn(Class 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;