#include "NtFactionSpyServer.h" #include "Character.h" #include "ProximityMonitorComponent.h" #include "InventoryComponent.h" #include "GameMessages.h" #include "MissionComponent.h" #include "eMissionState.h" #include "eReplicaComponentType.h" #include "eCinematicEvent.h" #include "ePlayerFlag.h" void NtFactionSpyServer::OnStartup(Entity* self) { SetVariables(self); // Set the proximity to sense later auto* proximityMonitor = self->GetComponent(); if (proximityMonitor == nullptr) { proximityMonitor = self->AddComponent(-1, -1); } proximityMonitor->SetProximityRadius(self->GetVar(m_SpyProximityVariable), m_ProximityName); } void NtFactionSpyServer::SetVariables(Entity* self) { self->SetVar(m_SpyProximityVariable, 0.0f); self->SetVar(m_SpyDataVariable, {}); self->SetVar>(m_SpyDialogueTableVariable, {}); // If there's an alternating conversation, indices should be provided using the conversationID variables self->SetVar>(m_SpyCinematicObjectsVariable, { self->GetObjectID() }); } void NtFactionSpyServer::OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) { if (name == m_ProximityName && status == "ENTER" && IsSpy(self, entering)) { auto cinematic = self->GetVar(m_SpyCinematicVariable); if (!cinematic.empty()) { // Save the root of this cinematic so we can identify updates later auto cinematicSplit = GeneralUtils::SplitString(cinematic, u'_'); if (!cinematicSplit.empty()) { self->SetVar(m_CinematicRootVariable, cinematicSplit.at(0)); } GameMessages::SendPlayCinematic(entering->GetObjectID(), cinematic, entering->GetSystemAddress(), true, true, true); } } } bool NtFactionSpyServer::IsSpy(Entity* self, Entity* possibleSpy) { auto spyData = self->GetVar(m_SpyDataVariable); if (!spyData.missionID || !spyData.flagID || !spyData.itemID) return false; auto* missionComponent = possibleSpy->GetComponent(); auto* inventoryComponent = possibleSpy->GetComponent(); auto* character = possibleSpy->GetCharacter(); // A player is a spy if they have the spy mission, have the spy equipment equipped and don't have the spy flag set yet return missionComponent != nullptr && missionComponent->GetMissionState(spyData.missionID) == eMissionState::ACTIVE && inventoryComponent != nullptr && inventoryComponent->IsEquipped(spyData.itemID) && character != nullptr && !character->GetPlayerFlag(spyData.flagID); } void NtFactionSpyServer::OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName, float_t pathTime, float_t totalTime, int32_t waypoint) { const auto& cinematicRoot = self->GetVar(m_CinematicRootVariable); auto pathNameCopy = std::u16string(pathName); // Mutable copy auto pathSplit = GeneralUtils::SplitString(pathNameCopy, u'_'); // Make sure we have a path of type _ if (pathSplit.size() >= 2) { auto pathRoot = pathSplit.at(0); auto pathIndex = std::stoi(GeneralUtils::UTF16ToWTF8(pathSplit.at(1))) - 1; const auto& dialogueTable = self->GetVar>(m_SpyDialogueTableVariable); // Make sure we're listening to the root we're interested in if (pathRoot == cinematicRoot) { if (event == eCinematicEvent::STARTED && pathIndex >= 0 && pathIndex < dialogueTable.size()) { // If the cinematic started, show part of the conversation GameMessages::SendNotifyClientObject(self->GetObjectID(), m_SpyDialogueNotification, 0, 0, ParamObjectForConversationID(self, dialogueTable.at(pathIndex).conversationID), dialogueTable.at(pathIndex).token, sender->GetSystemAddress()); } else if (event == eCinematicEvent::ENDED && pathIndex >= dialogueTable.size() - 1) { auto spyData = self->GetVar(m_SpyDataVariable); auto* character = sender->GetCharacter(); if (character != nullptr) { character->SetPlayerFlag(spyData.flagID, true); } } } } } LWOOBJID NtFactionSpyServer::ParamObjectForConversationID(Entity* self, uint32_t conversationID) { auto paramObjects = self->GetVar>(m_SpyCinematicObjectsVariable); auto index = conversationID >= paramObjects.size() ? paramObjects.size() - 1 : conversationID; return paramObjects.at(index); }