diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 9764ebb39..d34f2190e 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -762,7 +762,7 @@ public: DVisualThinker* VisualThinkerHead = nullptr; // links to global game objects - TArray ActorBehaviors; + TArray ActorBehaviors, ClientSideActorBehaviors; TArray> CorpseQueue; TObjPtr FraggleScriptThinker = MakeObjPtr(nullptr); TObjPtr ACSThinker = MakeObjPtr(nullptr); @@ -780,7 +780,10 @@ public: if (b.Level == nullptr) { b.Level = this; - ActorBehaviors.Push(&b); + if (b.IsClientside()) + ClientSideActorBehaviors.Push(&b); + else + ActorBehaviors.Push(&b); } } @@ -789,7 +792,10 @@ public: if (b.Level == this) { b.Level = nullptr; - ActorBehaviors.Delete(ActorBehaviors.Find(&b)); + if (b.IsClientside()) + ClientSideActorBehaviors.Delete(ClientSideActorBehaviors.Find(&b)); + else + ActorBehaviors.Delete(ActorBehaviors.Find(&b)); } } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index b948d4734..b698b3eec 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -387,6 +387,7 @@ void FLevelLocals::ClearLevelData(bool fullgc) levelMesh = nullptr; VisualThinkerHead = nullptr; ActorBehaviors.Clear(); + ClientSideActorBehaviors.Clear(); if (screen) screen->SetAABBTree(nullptr); } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 0b01f2cfe..e93190c8f 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -539,6 +539,8 @@ DBehavior* AActor::AddBehavior(PClass& type) return nullptr; b->Owner = this; + b->ObjectFlags |= (ObjectFlags & (OF_ClientSide | OF_Transient)); + Behaviors[type.TypeName] = b; Level->AddActorBehavior(*b); IFOVERRIDENVIRTUALPTRNAME(b, NAME_Behavior, Initialize) @@ -653,6 +655,9 @@ void AActor::MoveBehaviors(AActor& from) if (&from == this) return; + if (IsClientside() != from.IsClientside()) + I_Error("Cannot move Behaviors between client-side and world Actors"); + // Clean these up properly before transferring. ClearBehaviors(); diff --git a/src/scripting/vmiterators.cpp b/src/scripting/vmiterators.cpp index 730f09226..f8cac10d2 100644 --- a/src/scripting/vmiterators.cpp +++ b/src/scripting/vmiterators.cpp @@ -446,9 +446,10 @@ public: Reinit(); } - DBehaviorIterator(const FLevelLocals& level, PClass* type, PClass* ownerType) + DBehaviorIterator(const FLevelLocals& level, PClass* type, PClass* ownerType, bool clientSide) { - for (auto& b : level.ActorBehaviors) + auto& list = clientSide ? level.ClientSideActorBehaviors : level.ActorBehaviors; + for (auto& b : list) { if (ownerType != nullptr && !b->Owner->IsKindOf(ownerType)) continue; @@ -498,7 +499,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DBehaviorIterator, CreateFrom, CreateBehaviorItFro static DBehaviorIterator* CreateBehaviorIt(PClass* type, PClass* ownerType) { - return Create(*primaryLevel, type, ownerType); + return Create(*primaryLevel, type, ownerType, false); } DEFINE_ACTION_FUNCTION_NATIVE(DBehaviorIterator, Create, CreateBehaviorIt) @@ -509,6 +510,19 @@ DEFINE_ACTION_FUNCTION_NATIVE(DBehaviorIterator, Create, CreateBehaviorIt) ACTION_RETURN_OBJECT(CreateBehaviorIt(type, ownerType)); } +static DBehaviorIterator* CreateClientSideBehaviorIt(PClass* type, PClass* ownerType) +{ + return Create(*primaryLevel, type, ownerType, true); +} + +DEFINE_ACTION_FUNCTION_NATIVE(DBehaviorIterator, CreateClientSide, CreateClientSideBehaviorIt) +{ + PARAM_PROLOGUE; + PARAM_CLASS(type, DBehavior); + PARAM_CLASS(ownerType, AActor); + ACTION_RETURN_OBJECT(CreateClientSideBehaviorIt(type, ownerType)); +} + static DBehavior* NextBehavior(DBehaviorIterator* self) { return self->Next(); diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 15bd68236..123fe91be 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -88,6 +88,7 @@ class BehaviorIterator native abstract final version("4.15.1") { native static BehaviorIterator CreateFrom(Actor mobj, class type = null); native static BehaviorIterator Create(class type = null, class ownerType = null); + native static BehaviorIterator CreateClientSide(class type = null, class ownerType = null); native Behavior Next(); native void Reinit();