From 002aa896d8d1b810eeaf000ec0931fedd0771beb Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 19 Oct 2025 05:22:45 -0700 Subject: [PATCH] feat: debug information (#1915) --- dCommon/Amf3.h | 15 +++++ dGame/dComponents/BaseCombatAIComponent.cpp | 75 +++++++++++++++++++++ dGame/dComponents/BaseCombatAIComponent.h | 2 + dGame/dComponents/BouncerComponent.cpp | 69 +++++++++++++++++++ dGame/dComponents/BouncerComponent.h | 32 +++++++++ dGame/dComponents/CollectibleComponent.cpp | 34 ++++++++++ dGame/dComponents/CollectibleComponent.h | 4 +- dGame/dComponents/DestroyableComponent.cpp | 6 +- dGame/dComponents/GhostComponent.cpp | 14 ++++ dGame/dComponents/GhostComponent.h | 2 + dGame/dComponents/SwitchComponent.h | 4 ++ 11 files changed, 253 insertions(+), 4 deletions(-) diff --git a/dCommon/Amf3.h b/dCommon/Amf3.h index 9a34ad59..174ad814 100644 --- a/dCommon/Amf3.h +++ b/dCommon/Amf3.h @@ -374,6 +374,21 @@ public: return value->Insert("value", std::make_unique()); } + AMFArrayValue& PushDebug(const NiPoint3& point) { + PushDebug("X") = point.x; + PushDebug("Y") = point.y; + PushDebug("Z") = point.z; + return *this; + } + + AMFArrayValue& PushDebug(const NiQuaternion& rot) { + PushDebug("W") = rot.w; + PushDebug("X") = rot.x; + PushDebug("Y") = rot.y; + PushDebug("Z") = rot.z; + return *this; + } + private: /** * The associative portion. These values are key'd with strings to an AMFValue. diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index d264801f..73118e36 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -27,8 +27,13 @@ #include "CDComponentsRegistryTable.h" #include "CDPhysicsComponentTable.h" #include "dNavMesh.h" +#include "Amf3.h" BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) { + { + using namespace GameMessages; + RegisterMsg(this, &BaseCombatAIComponent::MsgGetObjectReportInfo); + } m_Target = LWOOBJID_EMPTY; m_DirtyStateOrTarget = true; m_State = AiState::spawn; @@ -839,3 +844,73 @@ void BaseCombatAIComponent::IgnoreThreat(const LWOOBJID threat, const float valu SetThreat(threat, 0.0f); m_Target = LWOOBJID_EMPTY; } + +bool BaseCombatAIComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) { + using enum AiState; + auto& reportMsg = static_cast(msg); + auto& cmptType = reportMsg.info->PushDebug("Base Combat AI"); + cmptType.PushDebug("Component ID") = GetComponentID(); + auto& targetInfo = cmptType.PushDebug("Current Target Info"); + targetInfo.PushDebug("Current Target ID") = std::to_string(m_Target); + // if (m_Target != LWOOBJID_EMPTY) { + // LWOGameMessages::ObjGetName nameMsg(m_CurrentTarget); + // SEND_GAMEOBJ_MSG(nameMsg); + // if (!nameMsg.msg.name.empty()) targetInfo.PushDebug("Name") = nameMsg.msg.name; + // } + + auto& roundInfo = cmptType.PushDebug("Round Info"); + // roundInfo.PushDebug("Combat Round Time") = m_CombatRoundLength; + // roundInfo.PushDebug("Minimum Time") = m_MinRoundLength; + // roundInfo.PushDebug("Maximum Time") = m_MaxRoundLength; + // roundInfo.PushDebug("Selected Time") = m_SelectedTime; + // roundInfo.PushDebug("Combat Start Delay") = m_CombatStartDelay; + std::string curState; + switch (m_State) { + case idle: curState = "Idling"; break; + case aggro: curState = "Aggroed"; break; + case tether: curState = "Returning to Tether"; break; + case spawn: curState = "Spawn"; break; + case dead: curState = "Dead"; break; + default: curState = "Unknown or Undefined"; break; + } + cmptType.PushDebug("Current Combat State") = curState; + + //switch (m_CombatBehaviorType) { + // case 0: curState = "Passive"; break; + // case 1: curState = "Aggressive"; break; + // case 2: curState = "Passive (Turret)"; break; + // case 3: curState = "Aggressive (Turret)"; break; + // default: curState = "Unknown or Undefined"; break; + //} + //cmptType.PushDebug("Current Combat Behavior State") = curState; + + //switch (m_CombatRole) { + // case 0: curState = "Melee"; break; + // case 1: curState = "Ranged"; break; + // case 2: curState = "Support"; break; + // default: curState = "Unknown or Undefined"; break; + //} + //cmptType.PushDebug("Current Combat Role") = curState; + + auto& tetherPoint = cmptType.PushDebug("Tether Point"); + tetherPoint.PushDebug("X") = m_StartPosition.x; + tetherPoint.PushDebug("Y") = m_StartPosition.y; + tetherPoint.PushDebug("Z") = m_StartPosition.z; + cmptType.PushDebug("Hard Tether Radius") = m_HardTetherRadius; + cmptType.PushDebug("Soft Tether Radius") = m_SoftTetherRadius; + cmptType.PushDebug("Aggro Radius") = m_AggroRadius; + cmptType.PushDebug("Tether Speed") = m_TetherSpeed; + cmptType.PushDebug("Aggro Speed") = m_TetherSpeed; + // cmptType.PushDebug("Specified Min Range") = m_SpecificMinRange; + // cmptType.PushDebug("Specified Max Range") = m_SpecificMaxRange; + auto& threats = cmptType.PushDebug("Target Threats"); + for (const auto& [id, threat] : m_ThreatEntries) { + threats.PushDebug(std::to_string(id)) = threat; + } + + auto& ignoredThreats = cmptType.PushDebug("Temp Ignored Threats"); + for (const auto& [id, threat] : m_ThreatEntries) { + ignoredThreats.PushDebug(std::to_string(id) + " - Time") = threat; + } + return true; +} diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index 164b2ef5..009a96d2 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -234,6 +234,8 @@ public: // Ignore a threat for a certain amount of time void IgnoreThreat(const LWOOBJID target, const float time); + bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg); + private: /** * Returns the current target or the target that currently is the largest threat to this entity diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index a7c7f1a8..3c535e43 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -8,15 +8,33 @@ #include "GameMessages.h" #include "BitStream.h" #include "eTriggerEventType.h" +#include "Amf3.h" BouncerComponent::BouncerComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) { m_PetEnabled = false; m_PetBouncerEnabled = false; m_PetSwitchLoaded = false; + m_Destination = GeneralUtils::TryParse( + GeneralUtils::SplitString(m_Parent->GetVarAsString(u"bouncer_destination"), '\x1f')) + .value_or(NiPoint3Constant::ZERO); + m_Speed = GeneralUtils::TryParse(m_Parent->GetVarAsString(u"bouncer_speed")).value_or(-1.0f); + m_UsesHighArc = GeneralUtils::TryParse(m_Parent->GetVarAsString(u"bouncer_uses_high_arc")).value_or(false); + m_LockControls = GeneralUtils::TryParse(m_Parent->GetVarAsString(u"lock_controls")).value_or(false); + m_IgnoreCollision = !GeneralUtils::TryParse(m_Parent->GetVarAsString(u"ignore_collision")).value_or(true); + m_StickLanding = GeneralUtils::TryParse(m_Parent->GetVarAsString(u"stickLanding")).value_or(false); + m_UsesGroupName = GeneralUtils::TryParse(m_Parent->GetVarAsString(u"uses_group_name")).value_or(false); + m_GroupName = m_Parent->GetVarAsString(u"grp_name"); + m_MinNumTargets = GeneralUtils::TryParse(m_Parent->GetVarAsString(u"num_targets_to_activate")).value_or(1); + m_CinematicPath = m_Parent->GetVarAsString(u"attached_cinematic_path"); if (parent->GetLOT() == 7625) { LookupPetSwitch(); } + + { + using namespace GameMessages; + RegisterMsg(this, &BouncerComponent::MsgGetObjectReportInfo); + } } BouncerComponent::~BouncerComponent() { @@ -94,3 +112,54 @@ void BouncerComponent::LookupPetSwitch() { }); } } + +bool BouncerComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) { + auto& reportMsg = static_cast(msg); + auto& cmptType = reportMsg.info->PushDebug("Bouncer"); + cmptType.PushDebug("Component ID") = GetComponentID(); + auto& destPos = cmptType.PushDebug("Destination Position"); + if (m_Destination != NiPoint3Constant::ZERO) { + destPos.PushDebug(m_Destination); + } else { + destPos.PushDebug("WARNING: Bouncer has no target position, is likely missing config data"); + } + + + if (m_Speed == -1.0f) { + cmptType.PushDebug("WARNING: Bouncer has no speed value, is likely missing config data"); + } else { + cmptType.PushDebug("Bounce Speed") = m_Speed; + } + cmptType.PushDebug("Bounce trajectory arc") = m_UsesHighArc ? "High Arc" : "Low Arc"; + cmptType.PushDebug("Collision Enabled") = m_IgnoreCollision; + cmptType.PushDebug("Stick Landing") = m_StickLanding; + cmptType.PushDebug("Locks character's controls") = m_LockControls; + if (!m_CinematicPath.empty()) cmptType.PushDebug("Cinematic Camera Path (plays during bounce)") = m_CinematicPath; + + auto* switchComponent = m_Parent->GetComponent(); + auto& respondsToFactions = cmptType.PushDebug("Responds to Factions"); + if (!switchComponent || switchComponent->GetFactionsToRespondTo().empty()) respondsToFactions.PushDebug("Faction 1"); + else { + for (const auto faction : switchComponent->GetFactionsToRespondTo()) { + respondsToFactions.PushDebug(("Faction " + std::to_string(faction))); + } + } + + cmptType.PushDebug("Uses a group name for interactions") = m_UsesGroupName; + if (!m_UsesGroupName) { + if (m_MinNumTargets > 1) { + cmptType.PushDebug("WARNING: Bouncer has a required number of objects to activate, but no group for interactions."); + } + + if (!m_GroupName.empty()) { + cmptType.PushDebug("WARNING: Has a group name for interactions , but is marked to not use that name."); + } + } else { + if (m_GroupName.empty()) { + cmptType.PushDebug("WARNING: Set to use a group name for inter actions, but no group name is assigned"); + } + cmptType.PushDebug("Number of interactions to activate bouncer") = m_MinNumTargets; + } + + return true; +} diff --git a/dGame/dComponents/BouncerComponent.h b/dGame/dComponents/BouncerComponent.h index 53ba26fa..b3221e12 100644 --- a/dGame/dComponents/BouncerComponent.h +++ b/dGame/dComponents/BouncerComponent.h @@ -51,6 +51,8 @@ public: */ void LookupPetSwitch(); + bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg); + private: /** * Whether this bouncer needs to be activated by a pet @@ -66,6 +68,36 @@ private: * Whether the pet switch for this bouncer has been located */ bool m_PetSwitchLoaded; + + // The bouncer destination + NiPoint3 m_Destination; + + // The speed at which the player is bounced + float m_Speed{}; + + // Whether to use a high arc for the bounce trajectory + bool m_UsesHighArc{}; + + // Lock controls when bouncing + bool m_LockControls{}; + + // Ignore collision when bouncing + bool m_IgnoreCollision{}; + + // Stick the landing afterwards or let the player slide + bool m_StickLanding{}; + + // Whether or not there is a group name + bool m_UsesGroupName{}; + + // The group name for targets + std::string m_GroupName{}; + + // The number of targets to activate the bouncer + int32_t m_MinNumTargets{}; + + // The cinematic path to play during the bounce + std::string m_CinematicPath{}; }; #endif // BOUNCERCOMPONENT_H diff --git a/dGame/dComponents/CollectibleComponent.cpp b/dGame/dComponents/CollectibleComponent.cpp index f6ba25b2..fce32e93 100644 --- a/dGame/dComponents/CollectibleComponent.cpp +++ b/dGame/dComponents/CollectibleComponent.cpp @@ -1,5 +1,39 @@ #include "CollectibleComponent.h" +#include "MissionComponent.h" +#include "dServer.h" +#include "Amf3.h" + +CollectibleComponent::CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId) : + Component(parentEntity, componentID), m_CollectibleId(collectibleId) { + using namespace GameMessages; + RegisterMsg(this, &CollectibleComponent::MsgGetObjectReportInfo); +} + void CollectibleComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) { outBitStream.Write(GetCollectibleId()); } + +bool CollectibleComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) { + auto& reportMsg = static_cast(msg); + auto& cmptType = reportMsg.info->PushDebug("Collectible"); + auto collectibleID = static_cast(m_CollectibleId) + static_cast(Game::server->GetZoneID() << 8); + + cmptType.PushDebug("Component ID") = GetComponentID(); + + cmptType.PushDebug("Collectible ID") = GetCollectibleId(); + cmptType.PushDebug("Mission Tracking ID (for save data)") = collectibleID; + + auto* localCharEntity = Game::entityManager->GetEntity(reportMsg.clientID); + bool collected = false; + if (localCharEntity) { + auto* missionComponent = localCharEntity->GetComponent(); + + if (m_CollectibleId != 0) { + collected = missionComponent->HasCollectible(collectibleID); + } + } + + cmptType.PushDebug("Has been collected") = collected; + return true; +} diff --git a/dGame/dComponents/CollectibleComponent.h b/dGame/dComponents/CollectibleComponent.h index d9356112..ba1a3f28 100644 --- a/dGame/dComponents/CollectibleComponent.h +++ b/dGame/dComponents/CollectibleComponent.h @@ -7,10 +7,12 @@ class CollectibleComponent final : public Component { public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE; - CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId) : Component(parentEntity, componentID), m_CollectibleId(collectibleId) {} + CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId); int16_t GetCollectibleId() const { return m_CollectibleId; } void Serialize(RakNet::BitStream& outBitStream, bool isConstruction) override; + + bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg); private: int16_t m_CollectibleId = 0; }; diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 0658757c..47aa0c90 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -1122,8 +1122,8 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) { stats.PushDebug("Imagination") = m_iImagination; stats.PushDebug("Maximum Imagination") = m_fMaxImagination; stats.PushDebug("Damage Absorption Points") = m_DamageToAbsorb; - destroyableInfo.PushDebug("Is GM Immune") = m_IsGMImmune; - destroyableInfo.PushDebug("Is Shielded") = m_IsShielded; + stats.PushDebug("Is GM Immune") = m_IsGMImmune; + stats.PushDebug("Is Shielded") = m_IsShielded; destroyableInfo.PushDebug("Attacks To Block") = m_AttacksToBlock; destroyableInfo.PushDebug("Damage Reduction") = m_DamageReduction; std::stringstream factionsStream; @@ -1140,7 +1140,7 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) { destroyableInfo.PushDebug("Enemy Factions") = factionsStream.str(); - destroyableInfo.PushDebug("Is Smashable") = m_IsSmashable; + destroyableInfo.PushDebug("Is A Smashable") = m_IsSmashable; destroyableInfo.PushDebug("Is Smashed") = m_IsSmashed; destroyableInfo.PushDebug("Is Module Assembly") = m_IsModuleAssembly; destroyableInfo.PushDebug("Explode Factor") = m_ExplodeFactor; diff --git a/dGame/dComponents/GhostComponent.cpp b/dGame/dComponents/GhostComponent.cpp index 4755a1f9..d86de72b 100644 --- a/dGame/dComponents/GhostComponent.cpp +++ b/dGame/dComponents/GhostComponent.cpp @@ -1,9 +1,14 @@ #include "GhostComponent.h" +#include "Amf3.h" +#include "GameMessages.h" + GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) { m_GhostReferencePoint = NiPoint3Constant::ZERO; m_GhostOverridePoint = NiPoint3Constant::ZERO; m_GhostOverride = false; + + RegisterMsg(this, &GhostComponent::MsgGetObjectReportInfo); } GhostComponent::~GhostComponent() { @@ -55,3 +60,12 @@ bool GhostComponent::IsObserved(LWOOBJID id) { void GhostComponent::GhostEntity(LWOOBJID id) { m_ObservedEntities.erase(id); } + +bool GhostComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) { + auto& reportMsg = static_cast(msg); + auto& cmptType = reportMsg.info->PushDebug("Ghost"); + cmptType.PushDebug("Component ID") = GetComponentID(); + cmptType.PushDebug("Is GM Invis") = false; + + return true; +} diff --git a/dGame/dComponents/GhostComponent.h b/dGame/dComponents/GhostComponent.h index edf05c13..75ed3c9d 100644 --- a/dGame/dComponents/GhostComponent.h +++ b/dGame/dComponents/GhostComponent.h @@ -39,6 +39,8 @@ public: void GhostEntity(const LWOOBJID id); + bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg); + private: NiPoint3 m_GhostReferencePoint; diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index ecbdeb73..755d134a 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -67,6 +67,10 @@ public: */ static SwitchComponent* GetClosestSwitch(NiPoint3 position); + const std::vector& GetFactionsToRespondTo() const { + return m_FactionsToRespondTo; + } + private: /** * A list of all pet switches.