diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index ef991136e..601e6ba63 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -283,6 +283,12 @@ Entity::ObjectLoadState Entity::DoObjectPreLoad() { // physics from the model case, which takes the registry's simple physics id state.hasModelBehaviors = !GetVar(u"inInventory") && HasVar(u"modelType") && HasVar(u"modelBehaviors"); + // ApplyMovementAiIfNeeded (0x01031350): an object with a script (its registry's, or a custom script in the config, + // ApplyScriptComponentIfHasScript) or an attached path gets movement AI. Not a spawner object: the client never + // loads one, the server keeps thousands + const bool hasScript = state.GetComponentID(eReplicaComponentType::SCRIPT, -1) > -1 || HasVar(u"custom_script_server") || HasVar(u"custom_script_client"); + state.addsMovementAI = m_TemplateID != 176 && (hasScript || HasVar(u"attached_path")); + // ObjectLoader2::ApplyComponentWhitelist (0x0103e300): the config's componentWhitelist keeps only these registry // components (the lists ObjectLoader2's constructor builds; property models use 1) static constexpr std::array, 5> COMPONENT_WHITELISTS{ { @@ -464,6 +470,9 @@ void Entity::DoObjectComponentLoad(ObjectLoadState& state) { }; AddComponent(-1, moveInfo); + } else if (state.addsMovementAI) { + // ApplyMovementAiIfNeeded: a scripted object or one with an attached path has one (id 0: no row) + AddComponent(0, MovementAIInfo{}); } break; } diff --git a/dGame/Entity.h b/dGame/Entity.h index 1bb612572..6e312bd8e 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -406,6 +406,8 @@ private: const std::initializer_list* whitelist = nullptr; bool hasModelBehaviors = false; bool markedAsPhantom = false; + // ApplyMovementAiIfNeeded: movement AI for a scripted object or one with an attached path + bool addsMovementAI = false; // A model with behaviors' physics id, which the physics cases leave to the model case int32_t modelPhysicsComponentID = -1; diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index da5536558..4af8b3989 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -14,6 +14,7 @@ #include "CDClientManager.h" #include "Game.h" #include "dZoneManager.h" +#include "Zone.h" #include "CDComponentsRegistryTable.h" #include "QuickBuildComponent.h" @@ -97,7 +98,15 @@ void MovementAIComponent::LoadConfigData() { if (m_Info.wanderRadius > 8.0f) m_Info.wanderRadius = 8.0f; if (m_Info.wanderSpeed > 0.5f) m_Info.wanderSpeed *= 0.5f; - SetPath(m_Parent->GetVarAsString(u"attached_path")); + // A movement AI the object has only because it is scripted or has a path (id 0) follows only a movement path: a + // moving platform's path belongs to its moving platform + const auto pathName = m_Parent->GetVarAsString(u"attached_path"); + if (m_ComponentID == 0) { + if (pathName.empty()) return; + const auto* const path = Game::zoneManager->GetZone()->GetPath(pathName); + if (!path || path->pathType != PathType::Movement) return; + } + SetPath(pathName); } void MovementAIComponent::SetPath(const std::string pathName) {