diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 5a2b37ca2..30602d4d0 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -427,11 +427,6 @@ public: 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 @@ public: 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 3a267c816..ce26ff39c 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 e84384752..51cf3a670 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 a2d42f7c5..af8514698 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 d4b1e262d..9fb5eae65 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 @@ 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); diff --git a/wadsrc/static/zscript/visualthinker.zs b/wadsrc/static/zscript/visualthinker.zs index 45b32865c..2b6d9a842 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;