From 7a4927fdf5b9fdf85407c1441f461be50ffa18f4 Mon Sep 17 00:00:00 2001 From: Boondorl Date: Tue, 28 Jan 2025 09:15:56 -0500 Subject: [PATCH] Added TransferredOwner virtual Allows for proper cleaning up of behaviors when moving between Actors. Important for player respawn handling and morphing. --- src/playsim/actor.h | 4 ++++ src/playsim/p_mobj.cpp | 34 +++++++++++++++++++++++---- wadsrc/static/zscript/actors/actor.zs | 1 + 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index cbc2781de..3339adeb9 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -1453,6 +1453,10 @@ public: auto b = Behaviors.CheckKey(type); return b != nullptr ? b->Get() : nullptr; } + bool IsValidBehavior(const DBehavior& b) const + { + return !(b.ObjectFlags & OF_EuthanizeMe) && b.Owner.ForceGet() == this; + } DBehavior* AddBehavior(PClass& type); bool RemoveBehavior(FName type); void TickBehaviors(); diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index c6ebe85e4..d040f3de0 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -541,7 +541,7 @@ DBehavior* AActor::AddBehavior(PClass& type) VMValue params[] = { b }; VMCall(func, params, 1, nullptr, 0); - if (b->ObjectFlags & OF_EuthanizeMe) + if (!IsValidBehavior(*b)) { RemoveBehavior(type.TypeName); return nullptr; @@ -555,7 +555,7 @@ DBehavior* AActor::AddBehavior(PClass& type) VMValue params[] = { b }; VMCall(func, params, 1, nullptr, 0); - if (b->ObjectFlags & OF_EuthanizeMe) + if (!IsValidBehavior(*b)) { RemoveBehavior(type.TypeName); return nullptr; @@ -599,7 +599,7 @@ void AActor::TickBehaviors() for (auto& b : toTick) { - if ((b->ObjectFlags & OF_EuthanizeMe) || b->Owner != this) + if (!IsValidBehavior(*b)) { toRemove.Push(b->GetClass()->TypeName); continue; @@ -610,7 +610,7 @@ void AActor::TickBehaviors() VMValue params[] = { b }; VMCall(func, params, 1, nullptr, 0); - if (b->ObjectFlags & OF_EuthanizeMe) + if (!IsValidBehavior(*b)) toRemove.Push(b->GetClass()->TypeName); } } @@ -645,12 +645,16 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, FindBehavior, FindBehavior) void AActor::MoveBehaviors(AActor& from) { + if (&from == this) + return; + // Clean these up properly before transferring. ClearBehaviors(); Behaviors.TransferFrom(from.Behaviors); TArray toRemove = {}; + TArray toTransfer = {}; // Clean up any empty behaviors that remained as well while // changing the owner. @@ -671,6 +675,26 @@ void AActor::MoveBehaviors(AActor& from) b->Level->RemoveActorBehavior(*b); Level->AddActorBehavior(*b); } + + toTransfer.Push(b); + } + + for (auto& b : toTransfer) + { + if (!IsValidBehavior(*b)) + { + toRemove.Push(b->GetClass()->TypeName); + continue; + } + + IFOVERRIDENVIRTUALPTRNAME(b, NAME_Behavior, TransferredOwner) + { + VMValue params[] = { b, &from }; + VMCall(func, params, 2, nullptr, 0); + + if (!IsValidBehavior(*b)) + toRemove.Push(b->GetClass()->TypeName); + } } for (auto& type : toRemove) @@ -5990,6 +6014,8 @@ AActor *FLevelLocals::SpawnPlayer (FPlayerStart *mthing, int playernum, int flag const auto heldWeap = state == PST_REBORN && (dmflags3 & DF3_REMEMBER_LAST_WEAP) ? p->ReadyWeapon : nullptr; if (state == PST_REBORN || state == PST_ENTER) { + if (state == PST_REBORN && oldactor != nullptr) + p->mo->MoveBehaviors(*oldactor); PlayerReborn (playernum); } else if (oldactor != NULL && oldactor->player == p && !(flags & SPF_TEMPPLAYER)) diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 1471dec61..56fa7ad10 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -80,6 +80,7 @@ class Behavior native play abstract virtual void Initialize() {} virtual void Reinitialize() {} + virtual void TransferredOwner(Actor oldOwner) {} virtual void Tick() {} }