refactor(component): controllable physics places its object itself

Where an object starts (where the level put it) moves into
ControllablePhysicsComponent::LoadConfigData, and where a character comes
into the world (its save, then its respawn point, the target scene or the
zone's spawn when it came from elsewhere) into LoadFromSaveData, out of
Entity::Initialize. Same placement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-10-01 16:05:04 -05:00
parent 4ea1762e0a
commit b24661710b
3 changed files with 49 additions and 33 deletions

View File

@@ -341,40 +341,9 @@ void Entity::Initialize() {
case eReplicaComponentType::CONTROLLABLE_PHYSICS: {
const auto controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
if (controllablePhysicsComponentID > 0) {
// The component places it: where the level put an object, where a character's save and the zone put it
auto* controllablePhysics = AddComponent<ControllablePhysicsComponent>(controllablePhysicsComponentID);
if (m_Character) {
controllablePhysics->LoadFromSaveData(m_Character->GetXMLDoc());
const auto mapID = Game::server->GetZoneID();
const auto& targetSceneName = m_Character->GetTargetScene();
//If we came from another zone, put us in the starting loc
// Exception for Moon Base as you tend to spawn on the roof.
// second exception if we have a specified targetScene since that would only be possible in a test map
if (m_Character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603 || !targetSceneName.empty()) {
NiPoint3 pos;
NiQuaternion rot = QuatUtils::IDENTITY;
auto* targetScene = Game::entityManager->GetSpawnPointEntity(targetSceneName);
if (m_Character->HasBeenToWorld(mapID) && targetSceneName.empty()) {
pos = m_Character->GetRespawnPoint(mapID);
rot = Game::zoneManager->GetZone()->GetSpawnRot();
} else if (targetScene != nullptr) {
pos = targetScene->GetPosition();
rot = targetScene->GetRotation();
} else {
pos = Game::zoneManager->GetZone()->GetSpawnPos();
rot = Game::zoneManager->GetZone()->GetSpawnRot();
}
controllablePhysics->SetPosition(pos);
controllablePhysics->SetRotation(rot);
}
} else {
controllablePhysics->SetPosition(m_DefaultPosition);
controllablePhysics->SetRotation(m_DefaultRotation);
}
if (m_Character) controllablePhysics->LoadFromSaveData(m_Character->GetXMLDoc());
}
break;
}

View File

@@ -13,6 +13,8 @@
#include "EntityManager.h"
#include "Character.h"
#include "dZoneManager.h"
#include "dServer.h"
#include "Zone.h"
#include "LevelProgressionComponent.h"
#include "eStateChangeType.h"
#include "StringifiedEnum.h"
@@ -55,6 +57,12 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, const
void ControllablePhysicsComponent::LoadConfigData() {
PhysicsComponent::LoadConfigData();
// An object that isn't a character starts where the level put it (a character's place comes with its save)
if (!m_Parent->GetCharacter()) {
SetPosition(m_Parent->GetDefaultPosition());
SetRotation(m_Parent->GetDefaultRotation());
}
if (m_Parent->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
return;
@@ -171,7 +179,42 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
}
}
// A character's save, then where it comes into this world
void ControllablePhysicsComponent::LoadFromSaveData(const tinyxml2::XMLDocument& doc) {
ReadSaveData(doc);
PlaceCharacter();
}
// If it came from another zone it starts at its respawn point, the target scene or the zone's spawn (Moon Base always:
// the saved place tends to be on the roof; a target scene too: only possible in a test map)
void ControllablePhysicsComponent::PlaceCharacter() {
auto* const character = m_Parent->GetCharacter();
if (!character) return;
const auto mapID = Game::server->GetZoneID();
const auto& targetSceneName = character->GetTargetScene();
if (character->GetZoneID() == Game::server->GetZoneID() && mapID != 1603 && targetSceneName.empty()) return;
NiPoint3 pos;
NiQuaternion rot = QuatUtils::IDENTITY;
auto* targetScene = Game::entityManager->GetSpawnPointEntity(targetSceneName);
if (character->HasBeenToWorld(mapID) && targetSceneName.empty()) {
pos = character->GetRespawnPoint(mapID);
rot = Game::zoneManager->GetZone()->GetSpawnRot();
} else if (targetScene != nullptr) {
pos = targetScene->GetPosition();
rot = targetScene->GetRotation();
} else {
pos = Game::zoneManager->GetZone()->GetSpawnPos();
rot = Game::zoneManager->GetZone()->GetSpawnRot();
}
SetPosition(pos);
SetRotation(rot);
}
void ControllablePhysicsComponent::ReadSaveData(const tinyxml2::XMLDocument& doc) {
auto* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) {
LOG("Failed to find char tag!");

View File

@@ -285,6 +285,10 @@ public:
const bool GetImmuneToStunUseItem() { return m_ImmuneToStunUseItemCount > 0;};
private:
// The save's physics state (LoadFromSaveData), then where the character comes into the world
void ReadSaveData(const tinyxml2::XMLDocument& doc);
void PlaceCharacter();
bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo);
/**