diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index d1e19cc8..250365b2 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -237,3 +237,7 @@ bool ModelComponent::TrySetVelocity(const NiPoint3& velocity) const { void ModelComponent::SetVelocity(const NiPoint3& velocity) const { m_Parent->SetVelocity(velocity); } + +void ModelComponent::OnChatMessageReceived(const std::string& sMessage) { + for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(sMessage); +} diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index 8d430d2e..b67a53d2 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -138,6 +138,8 @@ public: // Force sets the velocity to a value. void SetVelocity(const NiPoint3& velocity) const; + + void OnChatMessageReceived(const std::string& sMessage); private: // Number of Actions that are awaiting an UnSmash to finish. uint32_t m_NumActiveUnSmash{}; diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index 5dba7c19..581e28f3 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -817,3 +817,14 @@ void PropertyManagementComponent::SetOwnerId(const LWOOBJID value) { const std::map& PropertyManagementComponent::GetModels() const { return models; } + +void PropertyManagementComponent::OnChatMessageReceived(const std::string& sMessage) const { + for (const auto& modelID : models | std::views::keys) { + auto* const model = Game::entityManager->GetEntity(modelID); + if (!model) continue; + auto* const modelComponent = model->GetComponent(); + if (!modelComponent) continue; + + modelComponent->OnChatMessageReceived(sMessage); + } +} diff --git a/dGame/dComponents/PropertyManagementComponent.h b/dGame/dComponents/PropertyManagementComponent.h index 4dd482ef..708f0f14 100644 --- a/dGame/dComponents/PropertyManagementComponent.h +++ b/dGame/dComponents/PropertyManagementComponent.h @@ -164,6 +164,8 @@ public: LWOOBJID GetId() const noexcept { return propertyId; } + + void OnChatMessageReceived(const std::string& sMessage) const; private: /** * This @@ -193,7 +195,7 @@ private: /** * The models that are placed on this property */ - std::map models = {}; + std::map models = {}; /** * The name of this property diff --git a/dGame/dPropertyBehaviors/CMakeLists.txt b/dGame/dPropertyBehaviors/CMakeLists.txt index 3e03ba1d..c33c5860 100644 --- a/dGame/dPropertyBehaviors/CMakeLists.txt +++ b/dGame/dPropertyBehaviors/CMakeLists.txt @@ -20,6 +20,7 @@ target_include_directories(dPropertyBehaviors PUBLIC "." "ControlBehaviorMessage "${PROJECT_SOURCE_DIR}/dGame/dUtilities" # ObjectIdManager.h "${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # GameMessages.h "${PROJECT_SOURCE_DIR}/dGame/dComponents" # ModelComponent.h + "${PROJECT_SOURCE_DIR}/dChatFilter" # dChatFilter.h ) target_precompile_headers(dPropertyBehaviors REUSE_FROM dGameBase) diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.cpp b/dGame/dPropertyBehaviors/PropertyBehavior.cpp index c4c9d359..1a9bb867 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.cpp +++ b/dGame/dPropertyBehaviors/PropertyBehavior.cpp @@ -172,3 +172,7 @@ void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) { void PropertyBehavior::Update(float deltaTime, ModelComponent& modelComponent) { for (auto& state : m_States | std::views::values) state.Update(deltaTime, modelComponent); } + +void PropertyBehavior::OnChatMessageReceived(const std::string& sMessage) { + for (auto& state : m_States | std::views::values) state.OnChatMessageReceived(sMessage); +} diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.h b/dGame/dPropertyBehaviors/PropertyBehavior.h index 5232d7f0..67df78df 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.h +++ b/dGame/dPropertyBehaviors/PropertyBehavior.h @@ -34,6 +34,7 @@ public: void Deserialize(const tinyxml2::XMLElement& behavior); void Update(float deltaTime, ModelComponent& modelComponent); + void OnChatMessageReceived(const std::string& sMessage); private: // The current active behavior state. Behaviors can only be in ONE state at a time. diff --git a/dGame/dPropertyBehaviors/State.cpp b/dGame/dPropertyBehaviors/State.cpp index 0655c78c..7f95c2a0 100644 --- a/dGame/dPropertyBehaviors/State.cpp +++ b/dGame/dPropertyBehaviors/State.cpp @@ -166,3 +166,7 @@ void State::Deserialize(const tinyxml2::XMLElement& state) { void State::Update(float deltaTime, ModelComponent& modelComponent) { for (auto& strip : m_Strips) strip.Update(deltaTime, modelComponent); } + +void State::OnChatMessageReceived(const std::string& sMessage) { + for (auto& strip : m_Strips) strip.OnChatMessageReceived(sMessage); +} diff --git a/dGame/dPropertyBehaviors/State.h b/dGame/dPropertyBehaviors/State.h index ab6d76a4..580b647d 100644 --- a/dGame/dPropertyBehaviors/State.h +++ b/dGame/dPropertyBehaviors/State.h @@ -22,6 +22,8 @@ public: void Deserialize(const tinyxml2::XMLElement& state); void Update(float deltaTime, ModelComponent& modelComponent); + + void OnChatMessageReceived(const std::string& sMessage); private: // The strips contained within this state. diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index 9ceae743..d42c1f91 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -5,8 +5,12 @@ #include "tinyxml2.h" #include "dEntity/EntityInfo.h" #include "ModelComponent.h" +#include "ChatPackets.h" +#include "PropertyManagementComponent.h" #include "PlayerManager.h" +#include "dChatFilter.h" + #include "DluAssert.h" template <> @@ -103,6 +107,16 @@ void Strip::HandleMsg(GameMessages::ResetModelToDefaults& msg) { m_PreviousFramePosition = NiPoint3Constant::ZERO; } +void Strip::OnChatMessageReceived(const std::string& sMessage) { + if (m_PausedTime > 0.0f || !HasMinimumActions()) return; + + const auto& nextAction = GetNextAction(); + if (nextAction.GetValueParameterString() == sMessage) { + IncrementAction(); + m_WaitingForAction = false; + } +} + void Strip::IncrementAction() { if (m_Actions.empty()) return; m_NextActionIndex++; @@ -131,6 +145,7 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) { auto& entity = *modelComponent.GetParent(); auto& nextAction = GetNextAction(); auto number = nextAction.GetValueParameterDouble(); + auto valueStr = nextAction.GetValueParameterString(); auto numberAsInt = static_cast(number); auto nextActionType = GetNextAction().GetType(); @@ -183,6 +198,14 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) { m_PausedTime = number; } else if (nextActionType == "Wait") { m_PausedTime = number; + } else if (nextActionType == "Chat") { + bool isOk = Game::chatFilter->IsSentenceOkay(valueStr.data(), eGameMasterLevel::CIVILIAN).empty(); + // In case a word is removed from the whitelist after it was approved + const auto modelName = "%[Objects_" + std::to_string(entity.GetLOT()) + "_name]"; + if (isOk) ChatPackets::SendChatMessage(UNASSIGNED_SYSTEM_ADDRESS, 12, modelName, entity.GetObjectID(), false, GeneralUtils::ASCIIToUTF16(valueStr)); + PropertyManagementComponent::Instance()->OnChatMessageReceived(valueStr.data()); + } else if (nextActionType == "PrivateMessage") { + PropertyManagementComponent::Instance()->OnChatMessageReceived(valueStr.data()); } else if (nextActionType == "PlaySound") { GameMessages::PlayBehaviorSound sound; sound.target = modelComponent.GetParent()->GetObjectID(); @@ -304,6 +327,9 @@ void Strip::Update(float deltaTime, ModelComponent& modelComponent) { Game::entityManager->SerializeEntity(entity); m_WaitingForAction = true; + } else if (nextAction.GetType() == "OnChat") { + Game::entityManager->SerializeEntity(entity); + m_WaitingForAction = true; } } else { // should be a normal block ProcNormalAction(deltaTime, modelComponent); diff --git a/dGame/dPropertyBehaviors/Strip.h b/dGame/dPropertyBehaviors/Strip.h index a0085f9e..984a145c 100644 --- a/dGame/dPropertyBehaviors/Strip.h +++ b/dGame/dPropertyBehaviors/Strip.h @@ -40,6 +40,8 @@ public: // 2 actions are required for strips to work bool HasMinimumActions() const { return m_Actions.size() >= 2; } + + void OnChatMessageReceived(const std::string& sMessage); private: // Indicates this Strip is waiting for an action to be taken upon it to progress to its actions bool m_WaitingForAction{ false }; diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 9e9581b7..a1fc8046 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -1367,6 +1367,7 @@ void HandlePacket(Packet* packet) { std::string sMessage = GeneralUtils::UTF16ToWTF8(chatMessage.message); LOG("%s: %s", playerName.c_str(), sMessage.c_str()); ChatPackets::SendChatMessage(packet->systemAddress, chatMessage.chatChannel, playerName, user->GetLoggedInChar(), isMythran, chatMessage.message); + PropertyManagementComponent::Instance()->OnChatMessageReceived(sMessage); } break;