feat: debug information (#1915)

This commit is contained in:
David Markowitz
2025-10-19 05:22:45 -07:00
committed by GitHub
parent f3a5f60d81
commit 002aa896d8
11 changed files with 253 additions and 4 deletions

View File

@@ -374,6 +374,21 @@ public:
return value->Insert<AmfType>("value", std::make_unique<AmfType>());
}
AMFArrayValue& PushDebug(const NiPoint3& point) {
PushDebug<AMFDoubleValue>("X") = point.x;
PushDebug<AMFDoubleValue>("Y") = point.y;
PushDebug<AMFDoubleValue>("Z") = point.z;
return *this;
}
AMFArrayValue& PushDebug(const NiQuaternion& rot) {
PushDebug<AMFDoubleValue>("W") = rot.w;
PushDebug<AMFDoubleValue>("X") = rot.x;
PushDebug<AMFDoubleValue>("Y") = rot.y;
PushDebug<AMFDoubleValue>("Z") = rot.z;
return *this;
}
private:
/**
* The associative portion. These values are key'd with strings to an AMFValue.

View File

@@ -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<GetObjectReportInfo>(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<GameMessages::GetObjectReportInfo&>(msg);
auto& cmptType = reportMsg.info->PushDebug("Base Combat AI");
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
auto& targetInfo = cmptType.PushDebug("Current Target Info");
targetInfo.PushDebug<AMFStringValue>("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<AMFDoubleValue>("Combat Round Time") = m_CombatRoundLength;
// roundInfo.PushDebug<AMFDoubleValue>("Minimum Time") = m_MinRoundLength;
// roundInfo.PushDebug<AMFDoubleValue>("Maximum Time") = m_MaxRoundLength;
// roundInfo.PushDebug<AMFDoubleValue>("Selected Time") = m_SelectedTime;
// roundInfo.PushDebug<AMFDoubleValue>("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<AMFStringValue>("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<AMFDoubleValue>("X") = m_StartPosition.x;
tetherPoint.PushDebug<AMFDoubleValue>("Y") = m_StartPosition.y;
tetherPoint.PushDebug<AMFDoubleValue>("Z") = m_StartPosition.z;
cmptType.PushDebug<AMFDoubleValue>("Hard Tether Radius") = m_HardTetherRadius;
cmptType.PushDebug<AMFDoubleValue>("Soft Tether Radius") = m_SoftTetherRadius;
cmptType.PushDebug<AMFDoubleValue>("Aggro Radius") = m_AggroRadius;
cmptType.PushDebug<AMFDoubleValue>("Tether Speed") = m_TetherSpeed;
cmptType.PushDebug<AMFDoubleValue>("Aggro Speed") = m_TetherSpeed;
// cmptType.PushDebug<AMFDoubleValue>("Specified Min Range") = m_SpecificMinRange;
// cmptType.PushDebug<AMFDoubleValue>("Specified Max Range") = m_SpecificMaxRange;
auto& threats = cmptType.PushDebug("Target Threats");
for (const auto& [id, threat] : m_ThreatEntries) {
threats.PushDebug<AMFDoubleValue>(std::to_string(id)) = threat;
}
auto& ignoredThreats = cmptType.PushDebug("Temp Ignored Threats");
for (const auto& [id, threat] : m_ThreatEntries) {
ignoredThreats.PushDebug<AMFDoubleValue>(std::to_string(id) + " - Time") = threat;
}
return true;
}

View File

@@ -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

View File

@@ -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<NiPoint3>(
GeneralUtils::SplitString(m_Parent->GetVarAsString(u"bouncer_destination"), '\x1f'))
.value_or(NiPoint3Constant::ZERO);
m_Speed = GeneralUtils::TryParse<float>(m_Parent->GetVarAsString(u"bouncer_speed")).value_or(-1.0f);
m_UsesHighArc = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"bouncer_uses_high_arc")).value_or(false);
m_LockControls = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"lock_controls")).value_or(false);
m_IgnoreCollision = !GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"ignore_collision")).value_or(true);
m_StickLanding = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"stickLanding")).value_or(false);
m_UsesGroupName = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"uses_group_name")).value_or(false);
m_GroupName = m_Parent->GetVarAsString(u"grp_name");
m_MinNumTargets = GeneralUtils::TryParse<int32_t>(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<GetObjectReportInfo>(this, &BouncerComponent::MsgGetObjectReportInfo);
}
}
BouncerComponent::~BouncerComponent() {
@@ -94,3 +112,54 @@ void BouncerComponent::LookupPetSwitch() {
});
}
}
bool BouncerComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
auto& cmptType = reportMsg.info->PushDebug("Bouncer");
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
auto& destPos = cmptType.PushDebug("Destination Position");
if (m_Destination != NiPoint3Constant::ZERO) {
destPos.PushDebug(m_Destination);
} else {
destPos.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has no target position, is likely missing config data");
}
if (m_Speed == -1.0f) {
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has no speed value, is likely missing config data");
} else {
cmptType.PushDebug<AMFDoubleValue>("Bounce Speed") = m_Speed;
}
cmptType.PushDebug<AMFStringValue>("Bounce trajectory arc") = m_UsesHighArc ? "High Arc" : "Low Arc";
cmptType.PushDebug<AMFBoolValue>("Collision Enabled") = m_IgnoreCollision;
cmptType.PushDebug<AMFBoolValue>("Stick Landing") = m_StickLanding;
cmptType.PushDebug<AMFBoolValue>("Locks character's controls") = m_LockControls;
if (!m_CinematicPath.empty()) cmptType.PushDebug<AMFStringValue>("Cinematic Camera Path (plays during bounce)") = m_CinematicPath;
auto* switchComponent = m_Parent->GetComponent<SwitchComponent>();
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<AMFBoolValue>("Uses a group name for interactions") = m_UsesGroupName;
if (!m_UsesGroupName) {
if (m_MinNumTargets > 1) {
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has a required number of objects to activate, but no group for interactions.");
}
if (!m_GroupName.empty()) {
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Has a group name for interactions , but is marked to not use that name.");
}
} else {
if (m_GroupName.empty()) {
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Set to use a group name for inter actions, but no group name is assigned");
}
cmptType.PushDebug<AMFIntValue>("Number of interactions to activate bouncer") = m_MinNumTargets;
}
return true;
}

View File

@@ -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

View File

@@ -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<GetObjectReportInfo>(this, &CollectibleComponent::MsgGetObjectReportInfo);
}
void CollectibleComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
outBitStream.Write(GetCollectibleId());
}
bool CollectibleComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
auto& cmptType = reportMsg.info->PushDebug("Collectible");
auto collectibleID = static_cast<uint32_t>(m_CollectibleId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
cmptType.PushDebug<AMFIntValue>("Collectible ID") = GetCollectibleId();
cmptType.PushDebug<AMFIntValue>("Mission Tracking ID (for save data)") = collectibleID;
auto* localCharEntity = Game::entityManager->GetEntity(reportMsg.clientID);
bool collected = false;
if (localCharEntity) {
auto* missionComponent = localCharEntity->GetComponent<MissionComponent>();
if (m_CollectibleId != 0) {
collected = missionComponent->HasCollectible(collectibleID);
}
}
cmptType.PushDebug<AMFBoolValue>("Has been collected") = collected;
return true;
}

View File

@@ -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;
};

View File

@@ -1122,8 +1122,8 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
stats.PushDebug<AMFIntValue>("Imagination") = m_iImagination;
stats.PushDebug<AMFDoubleValue>("Maximum Imagination") = m_fMaxImagination;
stats.PushDebug<AMFIntValue>("Damage Absorption Points") = m_DamageToAbsorb;
destroyableInfo.PushDebug<AMFBoolValue>("Is GM Immune") = m_IsGMImmune;
destroyableInfo.PushDebug<AMFBoolValue>("Is Shielded") = m_IsShielded;
stats.PushDebug<AMFBoolValue>("Is GM Immune") = m_IsGMImmune;
stats.PushDebug<AMFBoolValue>("Is Shielded") = m_IsShielded;
destroyableInfo.PushDebug<AMFIntValue>("Attacks To Block") = m_AttacksToBlock;
destroyableInfo.PushDebug<AMFIntValue>("Damage Reduction") = m_DamageReduction;
std::stringstream factionsStream;
@@ -1140,7 +1140,7 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
destroyableInfo.PushDebug<AMFStringValue>("Enemy Factions") = factionsStream.str();
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashable") = m_IsSmashable;
destroyableInfo.PushDebug<AMFBoolValue>("Is A Smashable") = m_IsSmashable;
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashed") = m_IsSmashed;
destroyableInfo.PushDebug<AMFBoolValue>("Is Module Assembly") = m_IsModuleAssembly;
destroyableInfo.PushDebug<AMFDoubleValue>("Explode Factor") = m_ExplodeFactor;

View File

@@ -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<GameMessages::GetObjectReportInfo>(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<GameMessages::GetObjectReportInfo&>(msg);
auto& cmptType = reportMsg.info->PushDebug("Ghost");
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
cmptType.PushDebug<AMFBoolValue>("Is GM Invis") = false;
return true;
}

View File

@@ -39,6 +39,8 @@ public:
void GhostEntity(const LWOOBJID id);
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
private:
NiPoint3 m_GhostReferencePoint;

View File

@@ -67,6 +67,10 @@ public:
*/
static SwitchComponent* GetClosestSwitch(NiPoint3 position);
const std::vector<int32_t>& GetFactionsToRespondTo() const {
return m_FactionsToRespondTo;
}
private:
/**
* A list of all pet switches.