feat: proper gminvs with ghosting (#1920)

* feat: proper gminvis ghosting

* address feedback

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
This commit is contained in:
Aaron Kimbrell
2025-11-15 18:43:33 -06:00
committed by GitHub
parent 2fb16420f3
commit 11d44ffb98
9 changed files with 116 additions and 49 deletions

View File

@@ -1,4 +1,9 @@
#include "GhostComponent.h"
#include "PlayerManager.h"
#include "Character.h"
#include "ControllablePhysicsComponent.h"
#include "UserManager.h"
#include "User.h"
#include "Amf3.h"
#include "GameMessages.h"
@@ -7,7 +12,9 @@ GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Comp
m_GhostReferencePoint = NiPoint3Constant::ZERO;
m_GhostOverridePoint = NiPoint3Constant::ZERO;
m_GhostOverride = false;
RegisterMsg<GameMessages::ToggleGMInvis>(this, &GhostComponent::OnToggleGMInvis);
RegisterMsg<GameMessages::GetGMInvis>(this, &GhostComponent::OnGetGMInvis);
RegisterMsg<GameMessages::GetObjectReportInfo>(this, &GhostComponent::MsgGetObjectReportInfo);
}
@@ -22,6 +29,25 @@ GhostComponent::~GhostComponent() {
}
}
void GhostComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
auto* objElement = doc.FirstChildElement("obj");
if (!objElement) return;
auto* ghstElement = objElement->FirstChildElement("ghst");
if (!ghstElement) return;
m_IsGMInvisible = ghstElement->BoolAttribute("i");
}
void GhostComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
auto* objElement = doc.FirstChildElement("obj");
if (!objElement) return;
auto* ghstElement = objElement->FirstChildElement("ghst");
if (ghstElement) objElement->DeleteChild(ghstElement);
// Only save if GM invisible
if (!m_IsGMInvisible) return;
ghstElement = objElement->InsertNewChildElement("ghst");
if (ghstElement) ghstElement->SetAttribute("i", m_IsGMInvisible);
}
void GhostComponent::SetGhostReferencePoint(const NiPoint3& value) {
m_GhostReferencePoint = value;
}
@@ -61,6 +87,40 @@ void GhostComponent::GhostEntity(LWOOBJID id) {
m_ObservedEntities.erase(id);
}
bool GhostComponent::OnToggleGMInvis(GameMessages::GameMsg& msg) {
auto& gmInvisMsg = static_cast<GameMessages::ToggleGMInvis&>(msg);
gmInvisMsg.bStateOut = !m_IsGMInvisible;
m_IsGMInvisible = !m_IsGMInvisible;
LOG_DEBUG("GM Invisibility toggled to: %s", m_IsGMInvisible ? "true" : "false");
gmInvisMsg.Send(UNASSIGNED_SYSTEM_ADDRESS);
auto* thisUser = UserManager::Instance()->GetUser(m_Parent->GetSystemAddress());
for (const auto& player : PlayerManager::GetAllPlayers()) {
if (!player || player->GetObjectID() == m_Parent->GetObjectID()) continue;
auto* toUser = UserManager::Instance()->GetUser(player->GetSystemAddress());
if (m_IsGMInvisible) {
if (toUser->GetMaxGMLevel() < thisUser->GetMaxGMLevel()) {
Game::entityManager->DestructEntity(m_Parent, player->GetSystemAddress());
}
} else {
if (toUser->GetMaxGMLevel() >= thisUser->GetMaxGMLevel()) {
Game::entityManager->ConstructEntity(m_Parent, player->GetSystemAddress());
auto* controllableComp = m_Parent->GetComponent<ControllablePhysicsComponent>();
controllableComp->SetDirtyPosition(true);
}
}
}
Game::entityManager->SerializeEntity(m_Parent);
return true;
}
bool GhostComponent::OnGetGMInvis(GameMessages::GameMsg& msg) {
LOG_DEBUG("GM Invisibility requested: %s", m_IsGMInvisible ? "true" : "false");
auto& gmInvisMsg = static_cast<GameMessages::GetGMInvis&>(msg);
gmInvisMsg.bGMInvis = m_IsGMInvisible;
return gmInvisMsg.bGMInvis;
}
bool GhostComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
auto& cmptType = reportMsg.info->PushDebug("Ghost");