From 1e05bb2a553b5279723da38fc0f37ccbcd52309e Mon Sep 17 00:00:00 2001 From: Boondorl Date: Sat, 25 Jan 2025 10:52:54 -0500 Subject: [PATCH] Clean up behaviors properly when moving levels Also adds support for foreach loops with the behavior iterator. --- src/g_level.cpp | 4 +++ src/namedef_custom.h | 1 + src/p_setup.cpp | 1 + src/playsim/actor.h | 3 ++ src/playsim/p_mobj.cpp | 38 ++++++++++++++++++++++++++ src/scripting/backend/codegen_doom.cpp | 10 +++++-- 6 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index a1759cdce..9fe3c9699 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1607,6 +1607,7 @@ void FLevelLocals::StartTravel () if (Players[i]->health > 0) { pawn->UnlinkFromWorld (nullptr); + pawn->UnlinkBehaviorsFromLevel(); int tid = pawn->tid; // Save TID pawn->SetTID(0); pawn->tid = tid; // Restore TID (but no longer linked into the hash chain) @@ -1617,6 +1618,7 @@ void FLevelLocals::StartTravel () { inv->ChangeStatNum (STAT_TRAVELLING); inv->UnlinkFromWorld (nullptr); + inv->UnlinkBehaviorsFromLevel(); inv->DeleteAttachedLights(); } } @@ -1720,6 +1722,7 @@ int FLevelLocals::FinishTravel () pawndup->Destroy(); } pawn->LinkToWorld (nullptr); + pawn->LinkBehaviorsToLevel(); pawn->ClearInterpolation(); pawn->ClearFOVInterpolation(); const int tid = pawn->tid; // Save TID (actor isn't linked into the hash chain yet) @@ -1733,6 +1736,7 @@ int FLevelLocals::FinishTravel () inv->ChangeStatNum (STAT_INVENTORY); inv->LinkToWorld (nullptr); P_FindFloorCeiling(inv, FFCF_ONLYSPAWNPOS); + inv->LinkBehaviorsToLevel(); IFVIRTUALPTRNAME(inv, NAME_Inventory, Travelled) { diff --git a/src/namedef_custom.h b/src/namedef_custom.h index af61b6499..c2925bb83 100644 --- a/src/namedef_custom.h +++ b/src/namedef_custom.h @@ -453,6 +453,7 @@ xx(Playermenu) // more stuff xx(Behavior) +xx(BehaviorIterator) xx(ColorSet) xx(NeverSwitchOnPickup) xx(MoveBob) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 2775e259e..85b592bb3 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -380,6 +380,7 @@ void FLevelLocals::ClearLevelData(bool fullgc) aabbTree = nullptr; levelMesh = nullptr; VisualThinkerHead = nullptr; + ActorBehaviors.Clear(); if (screen) screen->SetAABBTree(nullptr); } diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 7d1bb8740..db069bc32 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -1458,6 +1458,9 @@ public: void TickBehaviors(); void MoveBehaviors(AActor& from); void ClearBehaviors(); + // Internal only, mostly for traveling. + void UnlinkBehaviorsFromLevel(); + void LinkBehaviorsToLevel(); bool HasSpecialDeathStates () const; diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 6677e4fa4..5c7979c7c 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -716,6 +716,44 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearBehaviors, ClearBehaviors) return 0; } +void AActor::UnlinkBehaviorsFromLevel() +{ + TArray toRemove = {}; + + TMap::Iterator it = { Behaviors }; + TMap::Pair* pair = nullptr; + while (it.NextPair(pair)) + { + auto b = pair->Value; + if (b == nullptr || (b->ObjectFlags & OF_EuthanizeMe)) + toRemove.Push(pair->Key); + else + b->Level->RemoveActorBehavior(*b); + } + + for (auto& type : toRemove) + RemoveBehavior(*PClass::FindClass(type)); +} + +void AActor::LinkBehaviorsToLevel() +{ + TArray toRemove = {}; + + TMap::Iterator it = { Behaviors }; + TMap::Pair* pair = nullptr; + while (it.NextPair(pair)) + { + auto b = pair->Value; + if (b == nullptr || (b->ObjectFlags & OF_EuthanizeMe)) + toRemove.Push(pair->Key); + else + Level->AddActorBehavior(*b); + } + + for (auto& type : toRemove) + RemoveBehavior(*PClass::FindClass(type)); +} + //========================================================================== // // AActor::InStateSequence diff --git a/src/scripting/backend/codegen_doom.cpp b/src/scripting/backend/codegen_doom.cpp index 9feab0f9c..8ca5437e3 100644 --- a/src/scripting/backend/codegen_doom.cpp +++ b/src/scripting/backend/codegen_doom.cpp @@ -1034,6 +1034,10 @@ FxExpression *FxCastForEachLoop::Resolve(FCompileContext &ctx) { fieldName = "Thinker"; } + else if (itType->TypeName == NAME_BehaviorIterator) + { + fieldName = "Behavior"; + } else { ScriptPosition.Message(MSG_ERROR, "foreach(Type var : it ) - 'it' must be an actor or thinker iterator, but is a %s",Expr->ValueType->DescriptiveName()); @@ -1218,6 +1222,7 @@ bool IsGameSpecificForEachLoop(FxForEachLoop * loop) || ((PObjectPointer*)vt)->PointedClass()->TypeName == NAME_BlockThingsIterator || ((PObjectPointer*)vt)->PointedClass()->TypeName == NAME_ActorIterator || ((PObjectPointer*)vt)->PointedClass()->TypeName == NAME_ThinkerIterator + || ((PObjectPointer*)vt)->PointedClass()->TypeName == NAME_BehaviorIterator )); } @@ -1232,7 +1237,7 @@ FxExpression * ResolveGameSpecificForEachLoop(FxForEachLoop * loop) delete loop; return blockIt; } - else if(cname == NAME_ActorIterator || cname == NAME_ThinkerIterator) + else if(cname == NAME_ActorIterator || cname == NAME_ThinkerIterator || cname == NAME_BehaviorIterator) { auto castIt = new FxCastForEachLoop(NAME_None, loop->loopVarName, loop->Array, loop->Code, loop->ScriptPosition); loop->Array = loop->Code = nullptr; @@ -1324,13 +1329,14 @@ bool IsGameSpecificTypedForEachLoop(FxTypedForEachLoop * loop) return (vt->isObjectPointer() && ( ((PObjectPointer*)vt)->PointedClass()->TypeName == NAME_ActorIterator || ((PObjectPointer*)vt)->PointedClass()->TypeName == NAME_ThinkerIterator + || ((PObjectPointer*)vt)->PointedClass()->TypeName == NAME_BehaviorIterator )); } FxExpression * ResolveGameSpecificTypedForEachLoop(FxTypedForEachLoop * loop) { assert(loop->Expr->ValueType->isObjectPointer()); - assert(((PObjectPointer*)loop->Expr->ValueType)->PointedClass()->TypeName == NAME_ActorIterator || ((PObjectPointer*)loop->Expr->ValueType)->PointedClass()->TypeName == NAME_ThinkerIterator); + assert(((PObjectPointer*)loop->Expr->ValueType)->PointedClass()->TypeName == NAME_ActorIterator || ((PObjectPointer*)loop->Expr->ValueType)->PointedClass()->TypeName == NAME_ThinkerIterator || ((PObjectPointer*)loop->Expr->ValueType)->PointedClass()->TypeName == NAME_BehaviorIterator); FxExpression * castIt = new FxCastForEachLoop(loop->className, loop->varName, loop->Expr, loop->Code, loop->ScriptPosition); loop->Expr = loop->Code = nullptr;