From 8f09121ad444adadaa66c923e2f774ac8fdcd0b3 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Thu, 1 Oct 2026 15:30:20 -0500 Subject: [PATCH] feat(entity): a character's components as the client makes them LWOCharacterComponent::Factory makes possession control, mount control, pet creator, level progression and forced movement, then the character, all with componentID -1; DLU now makes the same set in the same order (mount control and pet creator are new, empty). The ghost is added after the component walk, where the client adds it, for a character with physics. Nothing sent changes. Co-Authored-By: Claude Opus 5.5 --- dGame/Entity.cpp | 26 ++++++++++++++--------- dGame/dComponents/MountControlComponent.h | 18 ++++++++++++++++ dGame/dComponents/PetCreatorComponent.h | 18 ++++++++++++++++ 3 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 dGame/dComponents/MountControlComponent.h create mode 100644 dGame/dComponents/PetCreatorComponent.h diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 225642eb7..a2d2a1e30 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -95,6 +95,8 @@ #include "GateRushComponent.h" #include "FactionTriggerComponent.h" #include "RocketLandingComponent.h" +#include "MountControlComponent.h" +#include "PetCreatorComponent.h" #include "HFLightDirectionGadgetComponent.h" #include "SpawnerComponent.h" #include "PathfindingComponent.h" @@ -437,18 +439,17 @@ void Entity::Initialize() { case eReplicaComponentType::CHARACTER: { const auto characterID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER); if (characterID > 0 || m_Character) { - // Character Component always has a possessor, level, and forced movement components - AddComponent(characterID); - - // load in the xml for the level - AddComponent(characterID)->LoadFromSaveData(m_Character->GetXMLDoc()); - - AddComponent(characterID); + // LWOCharacterComponent::Factory (0x0104db40): possession control, mount control, pet creator, level + // progression and forced movement, then the character, all componentID -1 + AddComponent(-1); + AddComponent(-1); + AddComponent(-1); + AddComponent(-1)->LoadFromSaveData(m_Character->GetXMLDoc()); + AddComponent(-1); auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS; - AddComponent(characterID, m_Character, systemAddress)->LoadFromSaveData(m_Character->GetXMLDoc()); + AddComponent(-1, m_Character, systemAddress)->LoadFromSaveData(m_Character->GetXMLDoc()); - AddComponent(characterID)->LoadFromSaveData(m_Character->GetXMLDoc()); } break; } @@ -1044,8 +1045,13 @@ void Entity::Initialize() { } /** - * Added after the walk, as the client does (DoObjectComponentLoad): the trigger from the level config + * Added after the walk, as the client does (DoObjectComponentLoad): the ghost of a character (LWOGhostComponent's + * allocator 0x0101c010 makes one for characters with physics; level and plain spawned objects get none), then the + * trigger from the level config */ + if (m_Character && HasComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)) { + AddComponent(-1)->LoadFromSaveData(m_Character->GetXMLDoc()); + } const auto triggerInfo = GetVarAsString(u"trigger_id"); if (!triggerInfo.empty()) AddComponent(-1, triggerInfo); diff --git a/dGame/dComponents/MountControlComponent.h b/dGame/dComponents/MountControlComponent.h new file mode 100644 index 000000000..00cf101f2 --- /dev/null +++ b/dGame/dComponents/MountControlComponent.h @@ -0,0 +1,18 @@ +#ifndef MOUNTCONTROLCOMPONENT_H +#define MOUNTCONTROLCOMPONENT_H + +#include "Component.h" +#include "eReplicaComponentType.h" + +/** + * Mount control (111): the character's riding of mounts, as the client's LWOMountControlComponent: the client makes it (componentID -1) with every character + * (LWOCharacterComponent::Factory). Nothing of it is sent or done on the server yet. + */ +class MountControlComponent final : public Component { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOUNT_CONTROL; + + MountControlComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {} +}; + +#endif // MOUNTCONTROLCOMPONENT_H diff --git a/dGame/dComponents/PetCreatorComponent.h b/dGame/dComponents/PetCreatorComponent.h new file mode 100644 index 000000000..a6526b54c --- /dev/null +++ b/dGame/dComponents/PetCreatorComponent.h @@ -0,0 +1,18 @@ +#ifndef PETCREATORCOMPONENT_H +#define PETCREATORCOMPONENT_H + +#include "Component.h" +#include "eReplicaComponentType.h" + +/** + * Pet creator (37): the character's taming of pets, as the client's LWOPetCreatorComponent: the client makes it (componentID -1) with every character + * (LWOCharacterComponent::Factory). Nothing of it is sent or done on the server yet. + */ +class PetCreatorComponent final : public Component { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET_CREATOR; + + PetCreatorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {} +}; + +#endif // PETCREATORCOMPONENT_H