Clean up behaviors properly when moving levels

Also adds support for foreach loops with the behavior iterator.
This commit is contained in:
Boondorl 2025-01-25 10:52:54 -05:00 committed by Ricardo Luís Vaz Silva
parent ad87477650
commit 1e05bb2a55
6 changed files with 55 additions and 2 deletions

View file

@ -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)
{

View file

@ -453,6 +453,7 @@ xx(Playermenu)
// more stuff
xx(Behavior)
xx(BehaviorIterator)
xx(ColorSet)
xx(NeverSwitchOnPickup)
xx(MoveBob)

View file

@ -380,6 +380,7 @@ void FLevelLocals::ClearLevelData(bool fullgc)
aabbTree = nullptr;
levelMesh = nullptr;
VisualThinkerHead = nullptr;
ActorBehaviors.Clear();
if (screen)
screen->SetAABBTree(nullptr);
}

View file

@ -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;

View file

@ -716,6 +716,44 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearBehaviors, ClearBehaviors)
return 0;
}
void AActor::UnlinkBehaviorsFromLevel()
{
TArray<FName> toRemove = {};
TMap<FName, DBehavior*>::Iterator it = { Behaviors };
TMap<FName, DBehavior*>::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<FName> toRemove = {};
TMap<FName, DBehavior*>::Iterator it = { Behaviors };
TMap<FName, DBehavior*>::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

View file

@ -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;