From 2594463f8408714320403b413c609de99ba5b3e3 Mon Sep 17 00:00:00 2001 From: plenarius Date: Mon, 26 Oct 2020 23:51:02 -0400 Subject: [PATCH 01/10] Add Chat to Events --- CHANGELOG.md | 1 + Plugins/Events/CMakeLists.txt | 3 +- Plugins/Events/Events.cpp | 2 + Plugins/Events/Events.hpp | 2 + Plugins/Events/Events/ChatEvents.cpp | 63 +++++++++++++++++++++++++ Plugins/Events/Events/ChatEvents.hpp | 19 ++++++++ Plugins/Events/NWScript/nwnx_events.nss | 13 +++++ 7 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 Plugins/Events/Events/ChatEvents.cpp create mode 100644 Plugins/Events/Events/ChatEvents.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c8d962f43..e5486d57138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ https://github.com/nwnxee/unified/compare/build8193.16...HEAD - Events: added skippable Disarm event to CombatEvents - Events: added `ACTION_RESULT` to Feat/Skill/Lock events for use in the _AFTER - Events: added Spell Interruption events to SpellEvents +- Events: added skippable Chat events - Tweaks: `NWNX_TWEAKS_HIDE_PLAYERS_ON_CHAR_LIST` - Tweaks: `NWNX_TWEAKS_FIX_ARMOR_DEX_BONUS_UNDER_ONE` - Tweaks: `NWNX_TWEAKS_FIX_ITEM_NULLPTR_IN_CITEMREPOSITORY` diff --git a/Plugins/Events/CMakeLists.txt b/Plugins/Events/CMakeLists.txt index edfca9cad36..08eaa2e128f 100644 --- a/Plugins/Events/CMakeLists.txt +++ b/Plugins/Events/CMakeLists.txt @@ -30,4 +30,5 @@ add_plugin(Events "Events/ResourceEvents.cpp" "Events/QuickbarEvents.cpp" "Events/DebugEvents.cpp" - "Events/StoreEvents.cpp") + "Events/StoreEvents.cpp" + "Events/ChatEvents.cpp") diff --git a/Plugins/Events/Events.cpp b/Plugins/Events/Events.cpp index 008f25d7118..ad4cd1fc21a 100644 --- a/Plugins/Events/Events.cpp +++ b/Plugins/Events/Events.cpp @@ -34,6 +34,7 @@ #include "Events/QuickbarEvents.hpp" #include "Events/DebugEvents.hpp" #include "Events/StoreEvents.hpp" +#include "Events/ChatEvents.hpp" #include "Services/Config/Config.hpp" #include "Services/Messaging/Messaging.hpp" @@ -125,6 +126,7 @@ Events::Events(Services::ProxyServiceList* services) m_quickbarEvents = std::make_unique(hooker); m_debugEvents = std::make_unique(hooker); m_storeEvents = std::make_unique(hooker); + m_chatEvents = std::make_unique(hooker); } Events::~Events() diff --git a/Plugins/Events/Events.hpp b/Plugins/Events/Events.hpp index b117bffb3b3..137a5786a15 100644 --- a/Plugins/Events/Events.hpp +++ b/Plugins/Events/Events.hpp @@ -44,6 +44,7 @@ class ResourceEvents; class QuickbarEvents; class DebugEvents; class StoreEvents; +class ChatEvents; class Events : public NWNXLib::Plugin { @@ -138,6 +139,7 @@ class Events : public NWNXLib::Plugin std::unique_ptr m_quickbarEvents; std::unique_ptr m_debugEvents; std::unique_ptr m_storeEvents; + std::unique_ptr m_chatEvents; }; } diff --git a/Plugins/Events/Events/ChatEvents.cpp b/Plugins/Events/Events/ChatEvents.cpp new file mode 100644 index 00000000000..c075413a363 --- /dev/null +++ b/Plugins/Events/Events/ChatEvents.cpp @@ -0,0 +1,63 @@ +#include "Events/ChatEvents.hpp" +#include "API/CNWSPlayer.hpp" +#include "API/Functions.hpp" +#include "Events.hpp" +#include "Utils.hpp" + +namespace Events { + +using namespace NWNXLib; +using namespace NWNXLib::API; +using namespace NWNXLib::API::Constants; + +static NWNXLib::Hooking::FunctionHook* s_HandlePlayerToServerChatMessageHook; + +ChatEvents::ChatEvents(Services::HooksProxy* hooker) +{ + Events::InitOnFirstSubscribe("NWNX_ON_CHAT_SEND_.*", [hooker]() { + s_HandlePlayerToServerChatMessageHook = hooker->RequestExclusiveHook< + API::Functions::_ZN11CNWSMessage31HandlePlayerToServerChatMessageEP10CNWSPlayerh>(&ChatEvents::HandlePlayerToServerChatMessageHook); + }); +} + +int32_t ChatEvents::HandlePlayerToServerChatMessageHook(CNWSMessage* pMessage, CNWSPlayer* pPlayer, uint8_t nMinor) +{ + int32_t retVal = 0; + + auto channel = nMinor; + std::string sMessage; + if (nMinor != ChatChannel::PlayerTell) + { + sMessage = Utils::PeekMessage(pMessage, 0); + } + + ObjectID senderOid = API::Constants::OBJECT_INVALID; + if (pPlayer != nullptr) + { + if (pPlayer->GetIsDM()) + channel |= 16; + senderOid = pPlayer->m_oidNWSObject; + } + + Events::PushEventData("CHANNEL", std::to_string(channel)); + + // We only send the fact that a player sent a tell, we don't include the message nor recipient for privacy reasons + if (channel != Constants::ChatChannel::PlayerTell && channel != Constants::ChatChannel::DmTell) + { + Events::PushEventData("MESSAGE", sMessage); + } + + if (Events::SignalEvent("NWNX_ON_CHAT_SEND_BEFORE", senderOid)) + { + retVal = s_HandlePlayerToServerChatMessageHook->CallOriginal(pMessage, pPlayer, nMinor); + } + else + { + retVal = 0; + } + + Events::SignalEvent("NWNX_ON_CHAT_SEND_AFTER", senderOid); + return retVal; +} + +} diff --git a/Plugins/Events/Events/ChatEvents.hpp b/Plugins/Events/Events/ChatEvents.hpp new file mode 100644 index 00000000000..d8aec3584d9 --- /dev/null +++ b/Plugins/Events/Events/ChatEvents.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "API/Vector.hpp" +#include "Common.hpp" +#include "Services/Hooks/Hooks.hpp" + +namespace Events { + +class ChatEvents +{ +public: + ChatEvents(NWNXLib::Services::HooksProxy* hooker); + +private: + static int32_t HandlePlayerToServerChatMessageHook(CNWSMessage*, CNWSPlayer*, uint8_t); + +}; + +} diff --git a/Plugins/Events/NWScript/nwnx_events.nss b/Plugins/Events/NWScript/nwnx_events.nss index 0558c996d32..061625103f8 100644 --- a/Plugins/Events/NWScript/nwnx_events.nss +++ b/Plugins/Events/NWScript/nwnx_events.nss @@ -1231,6 +1231,18 @@ _______________________________________ AREA | object | The area the server is sending. Convert to object with StringToObject() | PLAYER_NEW_TO_MODULE | int | TRUE if it's the player's first time logging into the server since a restart | +_______________________________________ + ## Chat Events + - NWNX_ON_CHAT_SEND_BEFORE + - NWNX_ON_CHAT_SEND_AFTER + + `OBJECT_SELF` = The player who sent the chat message + + Event Data Tag | Type | Notes + ----------------------|--------|------- + CHANNEL | int | The channel the chat message was sent (see @ref chat_channels in the NWNX_Chat Plugin) + MESSAGE | string | The message sent + _______________________________________ */ /* @@ -1318,6 +1330,7 @@ string NWNX_Events_GetEventData(string tag); /// - Debug events /// - Store events /// - Disarm event +/// - Chat events void NWNX_Events_SkipEvent(); /// Set the return value of the event. From 5cb921abd1ccaef02f514bbd51aec5b5ac72f345 Mon Sep 17 00:00:00 2001 From: plenarius Date: Mon, 26 Oct 2020 23:55:36 -0400 Subject: [PATCH 02/10] targeted messages can now be sent on party channel --- CHANGELOG.md | 1 + Plugins/Chat/Chat.cpp | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5486d57138..b440c86d143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). https://github.com/nwnxee/unified/compare/build8193.16...HEAD ### Added +- Chat: targeted messages can now be sent on party channel - Events: added skippable Acquire events to ItemEvents - Events: added skippable Disarm event to CombatEvents - Events: added `ACTION_RESULT` to Feat/Skill/Lock events for use in the _AFTER diff --git a/Plugins/Chat/Chat.cpp b/Plugins/Chat/Chat.cpp index db1dc59b4b1..08f20a6e596 100644 --- a/Plugins/Chat/Chat.cpp +++ b/Plugins/Chat/Chat.cpp @@ -204,7 +204,7 @@ Events::ArgumentStack Chat::SendMessage(Events::ArgumentStack&& args) if (playerId != Constants::PLAYERID_INVALIDID) { bool sentMessage = false; - CNWSMessage* messageDispatch = static_cast(Globals::AppManager()->m_pServerExoApp->GetNWSMessage()); + auto* messageDispatch = static_cast(Globals::AppManager()->m_pServerExoApp->GetNWSMessage()); if (hasManualPlayerId) { @@ -236,6 +236,16 @@ Events::ArgumentStack Chat::SendMessage(Events::ArgumentStack&& args) messageDispatch->SendServerToPlayerChat_DM_Whisper(playerId, speaker, message.c_str()); sentMessage = true; } + else if (channel == Constants::ChatChannel::PlayerParty) + { + messageDispatch->SendServerToPlayerChat_Party(playerId, speaker, message.c_str()); + sentMessage = true; + } + else if (channel == Constants::ChatChannel::DmParty) + { + messageDispatch->SendServerToPlayerChat_Party(playerId, speaker, message.c_str()); + sentMessage = true; + } } if (!sentMessage) From ab1b98a17af5aeb833b8c7a13f74274bc162c41b Mon Sep 17 00:00:00 2001 From: plenarius Date: Mon, 26 Oct 2020 23:55:53 -0400 Subject: [PATCH 03/10] Add deprecation notices to Chat plugin --- CHANGELOG.md | 1 + Plugins/Chat/Chat.cpp | 3 +++ Plugins/Chat/NWScript/nwnx_chat.nss | 12 ++++++++++++ Plugins/Chat/README.md | 4 +++- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b440c86d143..69698b6c4a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ The following plugins were added: - Weapon: Good Aim Feat now takes value from ruleset.2da ### Deprecated +- Chat: NWNX_Chat_RegisterChatScript() and the functionality to retrieve chat message information has been deprecated and handled now through Chat Events - Tweaks: `NWNX_TWEAKS_HIDE_DMS_ON_CHAR_LIST` has been deprecated, use `NWNX_TWEAKS_HIDE_PLAYERS_ON_CHAR_LIST` now ### Removed diff --git a/Plugins/Chat/Chat.cpp b/Plugins/Chat/Chat.cpp index 08f20a6e596..c339070fc12 100644 --- a/Plugins/Chat/Chat.cpp +++ b/Plugins/Chat/Chat.cpp @@ -101,6 +101,7 @@ void Chat::SendServerToPlayerChatMessage(CNWSMessage* thisPtr, Constants::ChatCh if (plugin.m_depth > 0 || !plugin.m_skipMessage) { + // TODO: Only keep this functionality after deprecation period, it may make sense to just put it in Player if (g_plugin->m_customHearingDistances) { auto server = Globals::AppManager()->m_pServerExoApp; @@ -259,6 +260,7 @@ Events::ArgumentStack Chat::SendMessage(Events::ArgumentStack&& args) return Events::Arguments(retVal); } +// TODO: Remove next six functions after deprecation period Events::ArgumentStack Chat::RegisterChatScript(Events::ArgumentStack&& args) { m_chatScript = Events::ExtractArgument(args); @@ -291,6 +293,7 @@ Events::ArgumentStack Chat::GetTarget(Events::ArgumentStack&&) return Events::Arguments(m_activeTargetObjectId); } +// TODO Should these be moved to Player after deprecation period? Events::ArgumentStack Chat::SetChatHearingDistance(Events::ArgumentStack&& args) { const auto distance = Services::Events::ExtractArgument(args); diff --git a/Plugins/Chat/NWScript/nwnx_chat.nss b/Plugins/Chat/NWScript/nwnx_chat.nss index 90ca95e4910..51d734411c1 100644 --- a/Plugins/Chat/NWScript/nwnx_chat.nss +++ b/Plugins/Chat/NWScript/nwnx_chat.nss @@ -38,30 +38,36 @@ int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SE /// @brief Registers the script which receives all chat messages. /// @note If a script was previously registered, this one will take over. /// @param script The script name to handle the chat events. +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) void NWNX_Chat_RegisterChatScript(string script); /// @brief Skips a chat message /// @note Must be called from a chat or system script handler. +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) void NWNX_Chat_SkipMessage(); /// @brief Gets the chat @ref chat_channels "channel". /// @note Must be called from a chat or system script handler. /// @return The @ref chat_channels "channel" the message is sent. +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) int NWNX_Chat_GetChannel(); /// @brief Gets the message. /// @note Must be called from a chat or system script handler. /// @return The message sent. +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) string NWNX_Chat_GetMessage(); /// @brief Gets the sender of the message. /// @note Must be called from a chat or system script handler. /// @return The object sending the message. +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) object NWNX_Chat_GetSender(); /// @brief Gets the target of the message. /// @note Must be called from an chat or system script handler. /// @return The target of the message or OBJECT_INVALID if no target. +/// @deprecated Targets are only available in tells and those messages are suppressed for NWNX object NWNX_Chat_GetTarget(); /// @brief Sets the distance with which the player hears talks or whispers. @@ -92,6 +98,7 @@ int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SE void NWNX_Chat_RegisterChatScript(string script) { + WriteTimestampedLogEntry("NWNX_Chat: RegisterChatScript() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); string sFunc = "RegisterChatScript"; NWNX_PushArgumentString(NWNX_Chat, sFunc, script); @@ -100,6 +107,7 @@ void NWNX_Chat_RegisterChatScript(string script) void NWNX_Chat_SkipMessage() { + WriteTimestampedLogEntry("NWNX_Chat: SkipMessage() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); string sFunc = "SkipMessage"; NWNX_CallFunction(NWNX_Chat, sFunc); @@ -107,6 +115,7 @@ void NWNX_Chat_SkipMessage() int NWNX_Chat_GetChannel() { + WriteTimestampedLogEntry("NWNX_Chat: GetChannel() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); string sFunc = "GetChannel"; NWNX_CallFunction(NWNX_Chat, sFunc); @@ -115,6 +124,7 @@ int NWNX_Chat_GetChannel() string NWNX_Chat_GetMessage() { + WriteTimestampedLogEntry("NWNX_Chat: GetMessage() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); string sFunc = "GetMessage"; NWNX_CallFunction(NWNX_Chat, sFunc); @@ -123,6 +133,7 @@ string NWNX_Chat_GetMessage() object NWNX_Chat_GetSender() { + WriteTimestampedLogEntry("NWNX_Chat: GetSender() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); string sFunc = "GetSender"; NWNX_CallFunction(NWNX_Chat, sFunc); @@ -131,6 +142,7 @@ object NWNX_Chat_GetSender() object NWNX_Chat_GetTarget() { + WriteTimestampedLogEntry("NWNX_Chat: GetTarget() is deprecated. This function is no longer useful since targets are only determined in tells and those have been suppressed."); string sFunc = "GetTarget"; NWNX_CallFunction(NWNX_Chat, sFunc); diff --git a/Plugins/Chat/README.md b/Plugins/Chat/README.md index 37e0e26ef40..30e70ba8061 100644 --- a/Plugins/Chat/README.md +++ b/Plugins/Chat/README.md @@ -3,8 +3,10 @@ Allows chat events to be captured, skipped, and manual chat messages to be dispatched. +@note The functionality of this plugin has been deprecated and reduced to the sending of manual chat messages and altering chat distances for which players can hear. Please use the Events plugin for capturing and skipping chat messages now. + ## Environment Variables | Variable Name | Value | Notes | | ------------- | :---: | ----- | -| `NWNX_CHAT_CHAT_SCRIPT` | string | Set the nwscript that receives all the chat messages +| `NWNX_CHAT_CHAT_SCRIPT` | string | Set the nwscript that receives all the chat messages @deprecated From 4b9f15bda025158acbf90a53efcf5026b1df0e20 Mon Sep 17 00:00:00 2001 From: plenarius Date: Wed, 28 Oct 2020 14:05:53 -0400 Subject: [PATCH 04/10] Properly send tells to nwscript but suppress them in logs, be a bit more informative in deprecation notes --- Plugins/Chat/NWScript/nwnx_chat.nss | 10 +++++----- Plugins/Events/Events.cpp | 4 ++-- Plugins/Events/Events.hpp | 2 +- Plugins/Events/Events/ChatEvents.cpp | 25 +++++++++++++++++++------ 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Plugins/Chat/NWScript/nwnx_chat.nss b/Plugins/Chat/NWScript/nwnx_chat.nss index 51d734411c1..acc46c22310 100644 --- a/Plugins/Chat/NWScript/nwnx_chat.nss +++ b/Plugins/Chat/NWScript/nwnx_chat.nss @@ -43,31 +43,31 @@ void NWNX_Chat_RegisterChatScript(string script); /// @brief Skips a chat message /// @note Must be called from a chat or system script handler. -/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) and NWNX_Events_SkipEvent() void NWNX_Chat_SkipMessage(); /// @brief Gets the chat @ref chat_channels "channel". /// @note Must be called from a chat or system script handler. /// @return The @ref chat_channels "channel" the message is sent. -/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) and NWNX_Events_GetEventData("CHANNEL") int NWNX_Chat_GetChannel(); /// @brief Gets the message. /// @note Must be called from a chat or system script handler. /// @return The message sent. -/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) and NWNX_Events_GetEventData("MESSAGE") string NWNX_Chat_GetMessage(); /// @brief Gets the sender of the message. /// @note Must be called from a chat or system script handler. /// @return The object sending the message. -/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) OBJECT_SELF object NWNX_Chat_GetSender(); /// @brief Gets the target of the message. /// @note Must be called from an chat or system script handler. /// @return The target of the message or OBJECT_INVALID if no target. -/// @deprecated Targets are only available in tells and those messages are suppressed for NWNX +/// @deprecated Please use the events system (NWNX_ON_CHAT_SEND_*) and NWNX_Events_GetEventData("TARGET") object NWNX_Chat_GetTarget(); /// @brief Sets the distance with which the player hears talks or whispers. diff --git a/Plugins/Events/Events.cpp b/Plugins/Events/Events.cpp index ad4cd1fc21a..6fcc1c355da 100644 --- a/Plugins/Events/Events.cpp +++ b/Plugins/Events/Events.cpp @@ -133,9 +133,9 @@ Events::~Events() { } -void Events::PushEventData(const std::string& tag, const std::string& data) +void Events::PushEventData(const std::string& tag, const std::string& data, bool bHideDataInLog) { - LOG_DEBUG("Pushing event data: '%s' -> '%s'.", tag, data); + LOG_DEBUG("Pushing event data: '%s' -> '%s'.", tag, !bHideDataInLog ? data : ""); g_plugin->CreateNewEventDataIfNeeded(); g_plugin->m_eventData.top().m_EventDataMap[tag] = data; } diff --git a/Plugins/Events/Events.hpp b/Plugins/Events/Events.hpp index 137a5786a15..b5c58be876d 100644 --- a/Plugins/Events/Events.hpp +++ b/Plugins/Events/Events.hpp @@ -69,7 +69,7 @@ class Events : public NWNXLib::Plugin virtual ~Events(); // Pushes event data to the stack - won't do anything until SignalEvent is called. - static void PushEventData(const std::string& tag, const std::string& data); + static void PushEventData(const std::string& tag, const std::string& data, bool bHideDataInLog = false); // Get event data static std::string GetEventData(const std::string& tag); diff --git a/Plugins/Events/Events/ChatEvents.cpp b/Plugins/Events/Events/ChatEvents.cpp index c075413a363..251c22339fe 100644 --- a/Plugins/Events/Events/ChatEvents.cpp +++ b/Plugins/Events/Events/ChatEvents.cpp @@ -1,4 +1,6 @@ #include "Events/ChatEvents.hpp" +#include "API/CAppManager.hpp" +#include "API/CServerExoApp.hpp" #include "API/CNWSPlayer.hpp" #include "API/Functions.hpp" #include "Events.hpp" @@ -26,7 +28,20 @@ int32_t ChatEvents::HandlePlayerToServerChatMessageHook(CNWSMessage* pMessage, C auto channel = nMinor; std::string sMessage; - if (nMinor != ChatChannel::PlayerTell) + + ObjectID oidTarget = Constants::OBJECT_INVALID; + + if (nMinor == ChatChannel::PlayerTell) + { + auto nID = Utils::PeekMessage(pMessage, 0); + sMessage = Utils::PeekMessage(pMessage, 4); + + if (auto *pClient = Globals::AppManager()->m_pServerExoApp->GetClientObjectByPlayerId(nID, 0)) + { + oidTarget = static_cast(pClient)->m_oidPCObject; + } + } + else { sMessage = Utils::PeekMessage(pMessage, 0); } @@ -39,13 +54,11 @@ int32_t ChatEvents::HandlePlayerToServerChatMessageHook(CNWSMessage* pMessage, C senderOid = pPlayer->m_oidNWSObject; } + Events::PushEventData("TARGET", Utils::ObjectIDToString(oidTarget)); Events::PushEventData("CHANNEL", std::to_string(channel)); - // We only send the fact that a player sent a tell, we don't include the message nor recipient for privacy reasons - if (channel != Constants::ChatChannel::PlayerTell && channel != Constants::ChatChannel::DmTell) - { - Events::PushEventData("MESSAGE", sMessage); - } + // Hide the message from appearing in the log if the target is valid to keep tell content out of logs + Events::PushEventData("MESSAGE", sMessage, oidTarget != OBJECT_INVALID); if (Events::SignalEvent("NWNX_ON_CHAT_SEND_BEFORE", senderOid)) { From 95b9f9bca8e3f454ded10ae0ebd7425cd6daeefa Mon Sep 17 00:00:00 2001 From: plenarius Date: Wed, 28 Oct 2020 16:16:17 -0400 Subject: [PATCH 05/10] Strip color tokens from messages, add back TARGET as an event data in nwnx_events.nss --- Plugins/Events/Events/ChatEvents.cpp | 6 ++++-- Plugins/Events/NWScript/nwnx_events.nss | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Plugins/Events/Events/ChatEvents.cpp b/Plugins/Events/Events/ChatEvents.cpp index 251c22339fe..17a19a08657 100644 --- a/Plugins/Events/Events/ChatEvents.cpp +++ b/Plugins/Events/Events/ChatEvents.cpp @@ -27,7 +27,7 @@ int32_t ChatEvents::HandlePlayerToServerChatMessageHook(CNWSMessage* pMessage, C int32_t retVal = 0; auto channel = nMinor; - std::string sMessage; + CExoString sMessage; ObjectID oidTarget = Constants::OBJECT_INVALID; @@ -46,6 +46,8 @@ int32_t ChatEvents::HandlePlayerToServerChatMessageHook(CNWSMessage* pMessage, C sMessage = Utils::PeekMessage(pMessage, 0); } + Globals::AppManager()->m_pServerExoApp->StripColorTokens(sMessage); + ObjectID senderOid = API::Constants::OBJECT_INVALID; if (pPlayer != nullptr) { @@ -58,7 +60,7 @@ int32_t ChatEvents::HandlePlayerToServerChatMessageHook(CNWSMessage* pMessage, C Events::PushEventData("CHANNEL", std::to_string(channel)); // Hide the message from appearing in the log if the target is valid to keep tell content out of logs - Events::PushEventData("MESSAGE", sMessage, oidTarget != OBJECT_INVALID); + Events::PushEventData("MESSAGE", sMessage.CStr(), oidTarget != OBJECT_INVALID); if (Events::SignalEvent("NWNX_ON_CHAT_SEND_BEFORE", senderOid)) { diff --git a/Plugins/Events/NWScript/nwnx_events.nss b/Plugins/Events/NWScript/nwnx_events.nss index 061625103f8..dfb686b8003 100644 --- a/Plugins/Events/NWScript/nwnx_events.nss +++ b/Plugins/Events/NWScript/nwnx_events.nss @@ -1242,6 +1242,7 @@ _______________________________________ ----------------------|--------|------- CHANNEL | int | The channel the chat message was sent (see @ref chat_channels in the NWNX_Chat Plugin) MESSAGE | string | The message sent + TARGET | object | The recipient of the message (for tells only) _______________________________________ */ From 6feea9588563128bb96ef99e93cda0117e09f301 Mon Sep 17 00:00:00 2001 From: plenarius Date: Mon, 2 Nov 2020 18:09:19 -0500 Subject: [PATCH 06/10] Move CHAT_CHANNEL consts into nwnx_consts --- Core/NWScript/nwnx_consts.nss | 21 +++++++ Plugins/Chat/NWScript/nwnx_chat.nss | 88 ++++++++++++----------------- 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/Core/NWScript/nwnx_consts.nss b/Core/NWScript/nwnx_consts.nss index d89a88a547d..d449b31e4c4 100644 --- a/Core/NWScript/nwnx_consts.nss +++ b/Core/NWScript/nwnx_consts.nss @@ -1,9 +1,30 @@ /// @ingroup nwnx /// @addtogroup consts NWNX Constants /// @brief Provides various NWScript <-> NWNX Constants Translation Table functions +/// as well as defining shared constants for multiple plugins /// @{ /// @file nwnx_consts.nss +/// @name Chat Channels +/// @anchor chat_channels +/// +/// Constants defining the various chat channels. +/// @{ +const int NWNX_CHAT_CHANNEL_PLAYER_TALK = 1; +const int NWNX_CHAT_CHANNEL_PLAYER_SHOUT = 2; +const int NWNX_CHAT_CHANNEL_PLAYER_WHISPER = 3; +const int NWNX_CHAT_CHANNEL_PLAYER_TELL = 4; +const int NWNX_CHAT_CHANNEL_SERVER_MSG = 5; +const int NWNX_CHAT_CHANNEL_PLAYER_PARTY = 6; +const int NWNX_CHAT_CHANNEL_PLAYER_DM = 14; +const int NWNX_CHAT_CHANNEL_DM_TALK = 17; +const int NWNX_CHAT_CHANNEL_DM_SHOUT = 18; +const int NWNX_CHAT_CHANNEL_DM_WHISPER = 19; +const int NWNX_CHAT_CHANNEL_DM_TELL = 20; +const int NWNX_CHAT_CHANNEL_DM_PARTY = 22; +const int NWNX_CHAT_CHANNEL_DM_DM = 30; +/// @} + /// @brief Translates ANIMATION_LOOPING_* and ANIMATION_FIREFORGET_* constants to their NWNX equivalent. /// @param nAnimation The nwn animation constant /// @return The NWNX equivalent of the constant diff --git a/Plugins/Chat/NWScript/nwnx_chat.nss b/Plugins/Chat/NWScript/nwnx_chat.nss index acc46c22310..edb68144c46 100644 --- a/Plugins/Chat/NWScript/nwnx_chat.nss +++ b/Plugins/Chat/NWScript/nwnx_chat.nss @@ -3,29 +3,10 @@ /// @{ /// @file nwnx_chat.nss #include "nwnx" +#include "nwnx_consts" const string NWNX_Chat = "NWNX_Chat"; ///< @private -/// @name Chat Channels -/// @anchor chat_channels -/// -/// Constants defining the various chat channels. -/// @{ -const int NWNX_CHAT_CHANNEL_PLAYER_TALK = 1; -const int NWNX_CHAT_CHANNEL_PLAYER_SHOUT = 2; -const int NWNX_CHAT_CHANNEL_PLAYER_WHISPER = 3; -const int NWNX_CHAT_CHANNEL_PLAYER_TELL = 4; -const int NWNX_CHAT_CHANNEL_SERVER_MSG = 5; -const int NWNX_CHAT_CHANNEL_PLAYER_PARTY = 6; -const int NWNX_CHAT_CHANNEL_PLAYER_DM = 14; -const int NWNX_CHAT_CHANNEL_DM_TALK = 17; -const int NWNX_CHAT_CHANNEL_DM_SHOUT = 18; -const int NWNX_CHAT_CHANNEL_DM_WHISPER = 19; -const int NWNX_CHAT_CHANNEL_DM_TELL = 20; -const int NWNX_CHAT_CHANNEL_DM_PARTY = 22; -const int NWNX_CHAT_CHANNEL_DM_DM = 30; -/// @} - /// @brief Sends a chat message. /// @remark If no target is provided, then it broadcasts to all eligible targets. /// @param channel The @ref chat_channels "channel" to send the message. @@ -33,6 +14,7 @@ const int NWNX_CHAT_CHANNEL_DM_DM = 30; /// @param sender The sender of the message. /// @param target The receiver of the message. /// @return TRUE if successful, FALSE otherwise. +/// @deprecated Please use NWNX_Creature_SendMessage() int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SELF, object target = OBJECT_INVALID); /// @brief Registers the script which receives all chat messages. @@ -86,85 +68,89 @@ float NWNX_Chat_GetChatHearingDistance(object listener = OBJECT_INVALID, int cha int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SELF, object target = OBJECT_INVALID) { + WriteTimestampedLogEntry("NWNX_Chat: NWNX_Chat_SendMessage() is deprecated. Please use NWNX_Creature_SendMessage() now. Note the argument order has changed"); string sFunc = "SendMessage"; + NWNX_PushArgumentObject("NWNX_Creature", sFunc, target); + NWNX_PushArgumentInt("NWNX_Creature", sFunc, channel); + NWNX_PushArgumentString("NWNX_Creature", sFunc, message); + NWNX_PushArgumentObject("NWNX_Creature", sFunc, sender); + NWNX_CallFunction("NWNX_Creature", sFunc); - NWNX_PushArgumentObject(NWNX_Chat, sFunc, target); - NWNX_PushArgumentObject(NWNX_Chat, sFunc, sender); - NWNX_PushArgumentString(NWNX_Chat, sFunc, message); - NWNX_PushArgumentInt(NWNX_Chat, sFunc, channel); - NWNX_CallFunction(NWNX_Chat, sFunc); - return NWNX_GetReturnValueInt(NWNX_Chat, sFunc); + return NWNX_GetReturnValueInt("NWNX_Creature", sFunc); } void NWNX_Chat_RegisterChatScript(string script) { WriteTimestampedLogEntry("NWNX_Chat: RegisterChatScript() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); - string sFunc = "RegisterChatScript"; - NWNX_PushArgumentString(NWNX_Chat, sFunc, script); - NWNX_CallFunction(NWNX_Chat, sFunc); + NWNX_PushArgumentString("NWNX_Events", "SubscribeEvent", script); + NWNX_PushArgumentString("NWNX_Events", "SubscribeEvent", "NWNX_ON_CHAT_SEND_BEFORE"); + NWNX_CallFunction("NWNX_Events", "SubscribeEvent"); } void NWNX_Chat_SkipMessage() { WriteTimestampedLogEntry("NWNX_Chat: SkipMessage() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); - string sFunc = "SkipMessage"; - NWNX_CallFunction(NWNX_Chat, sFunc); + NWNX_CallFunction("NWNX_Events", "SkipEvent"); } int NWNX_Chat_GetChannel() { WriteTimestampedLogEntry("NWNX_Chat: GetChannel() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); - string sFunc = "GetChannel"; - NWNX_CallFunction(NWNX_Chat, sFunc); - return NWNX_GetReturnValueInt(NWNX_Chat, sFunc); + NWNX_PushArgumentString("NWNX_Events", "GetEventData", "CHANNEL"); + NWNX_CallFunction("NWNX_Events", "GetEventData"); + return StringToInt(NWNX_GetReturnValueString("NWNX_Events", "GetEventData")); } string NWNX_Chat_GetMessage() { WriteTimestampedLogEntry("NWNX_Chat: GetMessage() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); - string sFunc = "GetMessage"; - NWNX_CallFunction(NWNX_Chat, sFunc); - return NWNX_GetReturnValueString(NWNX_Chat, sFunc); + NWNX_PushArgumentString("NWNX_Events", "GetEventData", "MESSAGE"); + NWNX_CallFunction("NWNX_Events", "GetEventData"); + return NWNX_GetReturnValueString("NWNX_Events", "GetEventData"); } object NWNX_Chat_GetSender() { WriteTimestampedLogEntry("NWNX_Chat: GetSender() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); - string sFunc = "GetSender"; - NWNX_CallFunction(NWNX_Chat, sFunc); - return NWNX_GetReturnValueObject(NWNX_Chat, sFunc); + NWNX_PushArgumentString("NWNX_Events", "GetEventData", "SENDER"); + NWNX_CallFunction("NWNX_Events", "GetEventData"); + return StringToObject(NWNX_GetReturnValueString("NWNX_Events", "GetEventData")); } object NWNX_Chat_GetTarget() { WriteTimestampedLogEntry("NWNX_Chat: GetTarget() is deprecated. This function is no longer useful since targets are only determined in tells and those have been suppressed."); - string sFunc = "GetTarget"; - NWNX_CallFunction(NWNX_Chat, sFunc); - return NWNX_GetReturnValueObject(NWNX_Chat, sFunc); + NWNX_PushArgumentString("NWNX_Events", "GetEventData", "TARGET"); + NWNX_CallFunction("NWNX_Events", "GetEventData"); + return StringToObject(NWNX_GetReturnValueString("NWNX_Events", "GetEventData")); } void NWNX_Chat_SetChatHearingDistance(float distance, object listener = OBJECT_INVALID, int channel = NWNX_CHAT_CHANNEL_PLAYER_TALK) { + WriteTimestampedLogEntry("NWNX_Chat: SetChatHearingDistance() is deprecated. This function has been moved to NWNX_Player. Note the argument order has changed"); + string sFunc = "SetChatHearingDistance"; - NWNX_PushArgumentInt(NWNX_Chat, sFunc, channel); - NWNX_PushArgumentObject(NWNX_Chat, sFunc, listener); - NWNX_PushArgumentFloat(NWNX_Chat, sFunc, distance); - NWNX_CallFunction(NWNX_Chat, sFunc); + NWNX_PushArgumentInt("NWNX_Player", sFunc, channel); + NWNX_PushArgumentFloat("NWNX_Player", sFunc, distance); + NWNX_PushArgumentObject("NWNX_Player", sFunc, listener); + NWNX_CallFunction("NWNX_Player", sFunc); } float NWNX_Chat_GetChatHearingDistance(object listener = OBJECT_INVALID, int channel = NWNX_CHAT_CHANNEL_PLAYER_TALK) { + WriteTimestampedLogEntry("NWNX_Chat: GetChatHearingDistance() is deprecated. This function has been moved to NWNX_Player. Note the argument order has changed."); + string sFunc = "GetChatHearingDistance"; - NWNX_PushArgumentInt(NWNX_Chat, sFunc, channel); - NWNX_PushArgumentObject(NWNX_Chat, sFunc, listener); - NWNX_CallFunction(NWNX_Chat, sFunc); - return NWNX_GetReturnValueFloat(NWNX_Chat, sFunc); + NWNX_PushArgumentInt("NWNX_Player", sFunc, channel); + NWNX_PushArgumentObject("NWNX_Player", sFunc, listener); + NWNX_CallFunction("NWNX_Player", sFunc); + return NWNX_GetReturnValueFloat("NWNX_Player", sFunc); } From a8976d5706425977cd5a712e7a98bff07efcc339 Mon Sep 17 00:00:00 2001 From: plenarius Date: Mon, 2 Nov 2020 18:14:47 -0500 Subject: [PATCH 07/10] Move CHAT_CHANNEL consts into nwnx_consts Move SendMessage into Creature Move Get|SetChatHearingDistance into Player Add shims in nwnx_chat for all functionality Warn of deprecation on Chat constructor and in all functions Add chat hearing distance test unit to nwnx_player_t --- CHANGELOG.md | 6 +- Plugins/Chat/Chat.cpp | 5 +- Plugins/Creature/Creature.cpp | 71 ++++++++++ Plugins/Creature/Creature.hpp | 1 + Plugins/Creature/NWScript/nwnx_creature.nss | 22 ++++ Plugins/Player/NWScript/nwnx_player.nss | 32 +++++ Plugins/Player/NWScript/nwnx_player_t.nss | 8 ++ Plugins/Player/Player.cpp | 135 ++++++++++++++++++++ Plugins/Player/Player.hpp | 4 + 9 files changed, 280 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69698b6c4a3..990a3eec4ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). https://github.com/nwnxee/unified/compare/build8193.16...HEAD ### Added -- Chat: targeted messages can now be sent on party channel +- Creature: targeted messages can now be sent on party channel with updated SendMessage() - Events: added skippable Acquire events to ItemEvents - Events: added skippable Disarm event to CombatEvents - Events: added `ACTION_RESULT` to Feat/Skill/Lock events for use in the _AFTER @@ -29,9 +29,11 @@ The following plugins were added: - Creature: {Get|Set}WalkAnimation() - Creature: SetAttackRollOverride() - Creature: SetParryAllAttacks() +- Creature: SendMessage() - Feat: SetFeatModifier() - Object: GetCurrentAnimation() - Player: AddCustomJournalEntry() and GetJournalEntry() +- Player: {Get|Set}ChatHearingDistance() - Util: GetScriptParamIsSet() ### Changed @@ -45,7 +47,7 @@ The following plugins were added: - Weapon: Good Aim Feat now takes value from ruleset.2da ### Deprecated -- Chat: NWNX_Chat_RegisterChatScript() and the functionality to retrieve chat message information has been deprecated and handled now through Chat Events +- Chat: NWNX_Chat has been deprecated and handled now through Chat Events (NWNX_ON_CHAT_SEND_*), NWNX_Creature_SendMessage() and NWNX_Player_{Get|Set}ChatHearingDistance(). - Tweaks: `NWNX_TWEAKS_HIDE_DMS_ON_CHAR_LIST` has been deprecated, use `NWNX_TWEAKS_HIDE_PLAYERS_ON_CHAR_LIST` now ### Removed diff --git a/Plugins/Chat/Chat.cpp b/Plugins/Chat/Chat.cpp index c339070fc12..5e75e343947 100644 --- a/Plugins/Chat/Chat.cpp +++ b/Plugins/Chat/Chat.cpp @@ -60,8 +60,9 @@ Chat::Chat(Services::ProxyServiceList* services) m_hearingDistances[Constants::ChatChannel::DmWhisper] = 3.0f; m_hearingDistances[Constants::ChatChannel::PlayerWhisper] = 3.0f; m_customHearingDistances = false; - - m_hook = GetServices()->m_hooks->RequestExclusiveHook(&Chat::SendServerToPlayerChatMessage); + LOG_INFO("NWNX_Chat has been deprecated. Please use NWNX_Creature_SendMessage(), " + "NWNX_Player_{Get|Set}ChatHearingDistance() and the NWNX_ON_CHAT_SEND_* event for chat functionality."); + //m_hook = GetServices()->m_hooks->RequestExclusiveHook(&Chat::SendServerToPlayerChatMessage); } Chat::~Chat() diff --git a/Plugins/Creature/Creature.cpp b/Plugins/Creature/Creature.cpp index f306cac3bf2..71a444ac328 100644 --- a/Plugins/Creature/Creature.cpp +++ b/Plugins/Creature/Creature.cpp @@ -176,6 +176,7 @@ Creature::Creature(Services::ProxyServiceList* services) REGISTER(SetWalkAnimation); REGISTER(SetAttackRollOverride); REGISTER(SetParryAllAttacks); + REGISTER(SendMessage); #undef REGISTER } @@ -2958,5 +2959,75 @@ ArgumentStack Creature::SetParryAllAttacks(ArgumentStack&& args) return Services::Events::Arguments(); } +ArgumentStack Creature::SendMessage(ArgumentStack&& args) +{ + int32_t retVal = false; + const auto speaker = Services::Events::ExtractArgument(args); + const auto message = Services::Events::ExtractArgument(args); + const auto channel = static_cast(Services::Events::ExtractArgument(args)); + const auto target = Services::Events::ExtractArgument(args); + + const bool hasManualPlayerId = target != Constants::OBJECT_INVALID; + + const PlayerID playerId = hasManualPlayerId ? + Globals::AppManager()->m_pServerExoApp->GetPlayerIDByGameObjectID(target) : + Constants::PLAYERID_ALL_CLIENTS; + + if (playerId != Constants::PLAYERID_INVALIDID) + { + bool sentMessage = false; + auto* messageDispatch = static_cast(Globals::AppManager()->m_pServerExoApp->GetNWSMessage()); + + if (hasManualPlayerId) + { + // This means we're sending this to one player only. + // The normal function broadcasts in an area for talk, shout, and whisper, therefore + // we need to call these functions directly if we are in those categories. + if (channel == Constants::ChatChannel::PlayerTalk) + { + messageDispatch->SendServerToPlayerChat_Talk(playerId, speaker, message.c_str()); + sentMessage = true; + } + else if (channel == Constants::ChatChannel::DmTalk) + { + messageDispatch->SendServerToPlayerChat_DM_Talk(playerId, speaker, message.c_str()); + sentMessage = true; + } + else if (channel == Constants::ChatChannel::PlayerShout || channel == Constants::ChatChannel::DmShout) + { + messageDispatch->SendServerToPlayerChat_Shout(playerId, speaker, message.c_str()); + sentMessage = true; + } + else if (channel == Constants::ChatChannel::PlayerWhisper) + { + messageDispatch->SendServerToPlayerChat_Whisper(playerId, speaker, message.c_str()); + sentMessage = true; + } + else if (channel == Constants::ChatChannel::DmWhisper) + { + messageDispatch->SendServerToPlayerChat_DM_Whisper(playerId, speaker, message.c_str()); + sentMessage = true; + } + else if (channel == Constants::ChatChannel::PlayerParty) + { + messageDispatch->SendServerToPlayerChat_Party(playerId, speaker, message.c_str()); + sentMessage = true; + } + else if (channel == Constants::ChatChannel::DmParty) + { + messageDispatch->SendServerToPlayerChat_Party(playerId, speaker, message.c_str()); + sentMessage = true; + } + } + if (!sentMessage) + { + messageDispatch->SendServerToPlayerChatMessage(static_cast(channel), speaker, message.c_str(), playerId, nullptr); + } + + retVal = true; + } + + return Services::Events::Arguments(retVal); +} } diff --git a/Plugins/Creature/Creature.hpp b/Plugins/Creature/Creature.hpp index bb701dc24f2..04f19200311 100644 --- a/Plugins/Creature/Creature.hpp +++ b/Plugins/Creature/Creature.hpp @@ -130,6 +130,7 @@ class Creature : public NWNXLib::Plugin ArgumentStack SetWalkAnimation (ArgumentStack&& args); ArgumentStack SetAttackRollOverride (ArgumentStack&& args); ArgumentStack SetParryAllAttacks (ArgumentStack&& args); + ArgumentStack SendMessage (ArgumentStack&& args); CNWSCreature *creature(ArgumentStack& args); std::unordered_map> m_RollModifier; diff --git a/Plugins/Creature/NWScript/nwnx_creature.nss b/Plugins/Creature/NWScript/nwnx_creature.nss index ec294971062..083277123d4 100644 --- a/Plugins/Creature/NWScript/nwnx_creature.nss +++ b/Plugins/Creature/NWScript/nwnx_creature.nss @@ -3,6 +3,7 @@ /// @{ /// @file nwnx_creature.nss #include "nwnx" +#include "nwnx_consts" const string NWNX_Creature = "NWNX_Creature"; ///< @private @@ -814,6 +815,15 @@ void NWNX_Creature_SetAttackRollOverride(object oCreature, int nRoll, int nModif /// @note Use this command on_module_load instead of the NWNX_TWEAKS_PARRY_ALL_ATTACKS tweak if using NWNX_Creature_SetAttackRollOverride() void NWNX_Creature_SetParryAllAttacks(object oCreature, int bParry); +/// @brief Sends a chat message from a creature. +/// @remark If no target is provided, then it broadcasts to all eligible targets. +/// @param oCreature The sender of the message. +/// @param nChannel The @ref chat_channels "channel" to send the message. +/// @param sMessage The message to send. +/// @param oTarget The receiver of the message +/// @return TRUE if successful, FALSE otherwise. +int NWNX_Creature_SendMessage(object oCreature, string sMessage, int nChannel = NWNX_CHAT_CHANNEL_PLAYER_TALK, object oTarget = OBJECT_INVALID); + /// @} void NWNX_Creature_AddFeat(object creature, int feat) @@ -2061,3 +2071,15 @@ void NWNX_Creature_SetParryAllAttacks(object oCreature, int bParry) NWNX_PushArgumentObject(NWNX_Creature, sFunc, oCreature); NWNX_CallFunction(NWNX_Creature, sFunc); } + +int NWNX_Creature_SendMessage(object oCreature, string sMessage, int nChannel = NWNX_CHAT_CHANNEL_PLAYER_TALK, object oTarget = OBJECT_INVALID) +{ + string sFunc = "SendMessage"; + NWNX_PushArgumentObject(NWNX_Creature, sFunc, oTarget); + NWNX_PushArgumentInt(NWNX_Creature, sFunc, nChannel); + NWNX_PushArgumentString(NWNX_Creature, sFunc, sMessage); + NWNX_PushArgumentObject(NWNX_Creature, sFunc, oCreature); + NWNX_CallFunction(NWNX_Creature, sFunc); + + return NWNX_GetReturnValueInt(NWNX_Creature, sFunc); +} \ No newline at end of file diff --git a/Plugins/Player/NWScript/nwnx_player.nss b/Plugins/Player/NWScript/nwnx_player.nss index 517963f2017..69f1a3f7453 100644 --- a/Plugins/Player/NWScript/nwnx_player.nss +++ b/Plugins/Player/NWScript/nwnx_player.nss @@ -3,6 +3,7 @@ /// @{ /// @file nwnx_player.nss #include "nwnx" +#include "nwnx_consts" const string NWNX_Player = "NWNX_Player"; ///< @private @@ -378,6 +379,17 @@ int NWNX_Player_AddCustomJournalEntry(object oPlayer, struct NWNX_Player_Journal /// that is the active one that the player currently sees. struct NWNX_Player_JournalEntry NWNX_Player_GetJournalEntry(object oPlayer, string questTag); +/// @brief Sets the distance with which the player hears talks or whispers. +/// @remark Per player settings override server wide. +/// @param oPlayer The listener, if OBJECT_INVALID then it will be set server wide. +/// @param fDistance The distance in meters. +/// @param nChannel The @ref chat_channels "channel" to modify the distance heard. Only applicable for talk and whisper. +void NWNX_Player_SetChatHearingDistance(object oPlayer, float fDistance, int nChannel); + +/// @brief Gets the distance with which the player hears talks or whisper +/// @param oPlayer The listener, if OBJECT_INVALID then will return server wide setting. +/// @param nChannel The @ref chat_channels "channel". Only applicable for talk and whisper. +float NWNX_Player_GetChatHearingDistance(object oPlayer = OBJECT_INVALID, int nChannel = NWNX_CHAT_CHANNEL_PLAYER_TALK); /// @} void NWNX_Player_ForcePlaceableExamineWindow(object player, object placeable) @@ -949,3 +961,23 @@ struct NWNX_Player_JournalEntry NWNX_Player_GetJournalEntry(object oPlayer, stri entry.sTag = questTag; return entry; } + +void NWNX_Player_SetChatHearingDistance(object oPlayer, float fDistance, int nChannel) +{ + string sFunc = "SetChatHearingDistance"; + + NWNX_PushArgumentInt(NWNX_Player, sFunc, nChannel); + NWNX_PushArgumentFloat(NWNX_Player, sFunc, fDistance); + NWNX_PushArgumentObject(NWNX_Player, sFunc, oPlayer); + NWNX_CallFunction(NWNX_Player, sFunc); +} + +float NWNX_Player_GetChatHearingDistance(object oPlayer = OBJECT_INVALID, int nChannel = NWNX_CHAT_CHANNEL_PLAYER_TALK) +{ + string sFunc = "GetChatHearingDistance"; + + NWNX_PushArgumentInt(NWNX_Player, sFunc, nChannel); + NWNX_PushArgumentObject(NWNX_Player, sFunc, oPlayer); + NWNX_CallFunction(NWNX_Player, sFunc); + return NWNX_GetReturnValueFloat(NWNX_Player, sFunc); +} \ No newline at end of file diff --git a/Plugins/Player/NWScript/nwnx_player_t.nss b/Plugins/Player/NWScript/nwnx_player_t.nss index 574431cb990..c7ae33ebfd5 100644 --- a/Plugins/Player/NWScript/nwnx_player_t.nss +++ b/Plugins/Player/NWScript/nwnx_player_t.nss @@ -11,6 +11,14 @@ void main() WriteTimestampedLogEntry("NWNX_Player test: No PC found"); return; } + float fDefaultTalk = NWNX_Player_GetChatHearingDistance(); + NWNX_Player_SetChatHearingDistance(OBJECT_INVALID, fDefaultTalk + 10.0f, NWNX_CHAT_CHANNEL_PLAYER_TALK); + NWNX_Tests_Report("NWNX_Player", "SetChatHearingDistance Default", fDefaultTalk == NWNX_Player_GetChatHearingDistance() - 10.0f); + + float fPCWhisper = NWNX_Player_GetChatHearingDistance(oPC, NWNX_CHAT_CHANNEL_PLAYER_WHISPER); + NWNX_Player_SetChatHearingDistance(oPC, fPCWhisper + 2.0f, NWNX_CHAT_CHANNEL_PLAYER_WHISPER); + + NWNX_Tests_Report("NWNX_Player", "SetChatHearingDistance Per PC", fPCWhisper == NWNX_Player_GetChatHearingDistance(oPC, NWNX_CHAT_CHANNEL_PLAYER_WHISPER) - 2.0f); WriteTimestampedLogEntry("NWNX_Player unit test end."); } diff --git a/Plugins/Player/Player.cpp b/Plugins/Player/Player.cpp index e5d1686db8e..62ea41916b1 100644 --- a/Plugins/Player/Player.cpp +++ b/Plugins/Player/Player.cpp @@ -107,9 +107,17 @@ Player::Player(Services::ProxyServiceList* services) REGISTER(SendDMAllCreatorLists); REGISTER(AddCustomJournalEntry); REGISTER(GetJournalEntry); + REGISTER(SetChatHearingDistance); + REGISTER(GetChatHearingDistance); #undef REGISTER + m_hearingDistances[Constants::ChatChannel::DmTalk] = 20.0f; + m_hearingDistances[Constants::ChatChannel::PlayerTalk] = 20.0f; + m_hearingDistances[Constants::ChatChannel::DmWhisper] = 3.0f; + m_hearingDistances[Constants::ChatChannel::PlayerWhisper] = 3.0f; + m_customHearingDistances = false; + } Player::~Player() @@ -1848,4 +1856,131 @@ ArgumentStack Player::GetJournalEntry(ArgumentStack&& args) return Services::Events::Arguments(-1); } +Services::Events::ArgumentStack Player::SetChatHearingDistance(Services::Events::ArgumentStack&& args) +{ + const auto playerOid = Services::Events::ExtractArgument(args); + const auto distance = Services::Events::ExtractArgument(args); + const auto channel = (Constants::ChatChannel::TYPE)Services::Events::ExtractArgument(args); + + static NWNXLib::Hooking::FunctionHook* pSendServerToPlayerChatMessage_hook; + + if (!pSendServerToPlayerChatMessage_hook) + { + pSendServerToPlayerChatMessage_hook = GetServices()->m_hooks->RequestExclusiveHook + ( + +[](CNWSMessage* thisPtr, Constants::ChatChannel::TYPE channel, ObjectID sender, + CExoString message, PlayerID target, CExoString* tellName) -> int32_t + { + auto server = Globals::AppManager()->m_pServerExoApp; + auto *playerList = server->m_pcExoAppInternal->m_pNWSPlayerList->m_pcExoLinkedListInternal; + if (playerList) + { + if (channel == Constants::ChatChannel::PlayerShout && + server->GetServerInfo()->m_PlayOptions.bDisallowShouting) + { + channel = Constants::ChatChannel::PlayerTalk; + } + if (channel == Constants::ChatChannel::PlayerTalk || + channel == Constants::ChatChannel::PlayerWhisper || + channel == Constants::ChatChannel::DmTalk || + channel == Constants::ChatChannel::DmWhisper) + { + auto *pPOS = g_plugin->GetServices()->m_perObjectStorage.get(); + auto pSpeaker = Utils::AsNWSObject(server->GetGameObject(sender)); + auto distance = g_plugin->m_hearingDistances[channel]; + auto speakerPos = Vector{0.0f, 0.0f, 0.0f}; + CNWSArea *speakerArea = nullptr; + if (pSpeaker != nullptr) + { + speakerArea = pSpeaker->GetArea(); + speakerPos = pSpeaker->m_vPosition; + pSpeaker->BroadcastDialog(*tellName, distance); + } + + for (auto *head = playerList->pHead; head; head = head->pNext) + { + auto *pClient = static_cast(head->pObject); + auto *listenerClient = server->GetClientObjectByPlayerId(pClient->m_nPlayerID, 0); + auto *listener = static_cast(listenerClient); + auto *listenerObj = Utils::AsNWSObject(listener->GetGameObject()); + + auto pDistance = *pPOS->Get(listenerObj->m_idSelf, "HEARING_DISTANCE:" + std::to_string(channel)); + if (pDistance >= 0) + { + distance = pDistance; + + auto v = listenerObj->m_vPosition; + v.x -= speakerPos.x; + v.y -= speakerPos.y; + v.z -= speakerPos.z; + float vSquared = v.x*v.x + v.y*v.y + v.z*v.z; + if (speakerArea == listenerObj->GetArea() && vSquared <= distance*distance) + { + switch (channel) + { + case Constants::ChatChannel::PlayerTalk: + thisPtr->SendServerToPlayerChat_Talk(listener->m_nPlayerID, sender, message); + break; + case Constants::ChatChannel::DmTalk: + thisPtr->SendServerToPlayerChat_DM_Talk(listener->m_nPlayerID, sender, message); + break; + case Constants::ChatChannel::PlayerWhisper: + thisPtr->SendServerToPlayerChat_Whisper(listener->m_nPlayerID, sender, message); + break; + case Constants::ChatChannel::DmWhisper: + thisPtr->SendServerToPlayerChat_DM_Whisper(listener->m_nPlayerID, sender, message); + break; + default: + break; + } + } + } + } + } + else + { + return pSendServerToPlayerChatMessage_hook->CallOriginal(thisPtr, channel, sender, message, target, tellName); + } + } + return pSendServerToPlayerChatMessage_hook->CallOriginal(thisPtr, channel, sender, message, target, tellName); + }); + } + + if (playerOid == Constants::OBJECT_INVALID) + { + m_customHearingDistances = true; + m_hearingDistances[channel] = distance; + } + else + { + auto *pPlayer = Globals::AppManager()->m_pServerExoApp->GetClientObjectByObjectId(playerOid); + if (!pPlayer) + { + LOG_NOTICE("NWNX_Player function called on non-player object %x", playerOid); + } + else + { + auto *pPOS = g_plugin->GetServices()->m_perObjectStorage.get(); + m_customHearingDistances = true; + pPOS->Set(playerOid, "HEARING_DISTANCE:" + std::to_string(channel), distance, true); + } + } + + return Services::Events::Arguments(); +} + +Services::Events::ArgumentStack Player::GetChatHearingDistance(Services::Events::ArgumentStack&& args) +{ + const auto playerOid = Services::Events::ExtractArgument(args); + const auto channel = (Constants::ChatChannel::TYPE)Services::Events::ExtractArgument(args); + float retVal = m_hearingDistances[channel]; + + if (playerOid != Constants::OBJECT_INVALID) + { + auto *pPOS = g_plugin->GetServices()->m_perObjectStorage.get(); + auto playerHearingDistance = *pPOS->Get(playerOid, "HEARING_DISTANCE:" + std::to_string(channel)); + retVal = playerHearingDistance > 0 ? playerHearingDistance : m_hearingDistances[channel]; + } + return Services::Events::Arguments(retVal); +} } diff --git a/Plugins/Player/Player.hpp b/Plugins/Player/Player.hpp index a7d76202121..7fa8147d1aa 100644 --- a/Plugins/Player/Player.hpp +++ b/Plugins/Player/Player.hpp @@ -63,10 +63,14 @@ class Player : public NWNXLib::Plugin ArgumentStack SendDMAllCreatorLists (ArgumentStack&& args); ArgumentStack AddCustomJournalEntry (ArgumentStack&& args); ArgumentStack GetJournalEntry (ArgumentStack&& args); + ArgumentStack SetChatHearingDistance (ArgumentStack&& args); + ArgumentStack GetChatHearingDistance (ArgumentStack&& args); CNWSPlayer *player(ArgumentStack& args); std::unordered_map> m_PersistentLocationWP; + bool m_customHearingDistances; + std::unordered_map m_hearingDistances; }; } From 44f15604e563caf44e57afa59560e207e9123bc9 Mon Sep 17 00:00:00 2001 From: plenarius Date: Mon, 2 Nov 2020 20:43:27 -0500 Subject: [PATCH 08/10] Creature: SendMessage() now can be targeted on dm and party channel --- CHANGELOG.md | 2 +- Plugins/Creature/Creature.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 990a3eec4ea..5f0fd31208b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). https://github.com/nwnxee/unified/compare/build8193.16...HEAD ### Added -- Creature: targeted messages can now be sent on party channel with updated SendMessage() +- Creature: targeted messages can now be sent on party or dm channel with updated SendMessage() - Events: added skippable Acquire events to ItemEvents - Events: added skippable Disarm event to CombatEvents - Events: added `ACTION_RESULT` to Feat/Skill/Lock events for use in the _AFTER diff --git a/Plugins/Creature/Creature.cpp b/Plugins/Creature/Creature.cpp index 71a444ac328..f7abe021ffc 100644 --- a/Plugins/Creature/Creature.cpp +++ b/Plugins/Creature/Creature.cpp @@ -2993,6 +2993,11 @@ ArgumentStack Creature::SendMessage(ArgumentStack&& args) messageDispatch->SendServerToPlayerChat_DM_Talk(playerId, speaker, message.c_str()); sentMessage = true; } + else if (channel == Constants::ChatChannel::DmDm || channel == Constants::ChatChannel::PlayerDm) + { + messageDispatch->SendServerToPlayerChat_DM_Silent_Shout(playerId, speaker, message.c_str()); + sentMessage = true; + } else if (channel == Constants::ChatChannel::PlayerShout || channel == Constants::ChatChannel::DmShout) { messageDispatch->SendServerToPlayerChat_Shout(playerId, speaker, message.c_str()); @@ -3008,12 +3013,7 @@ ArgumentStack Creature::SendMessage(ArgumentStack&& args) messageDispatch->SendServerToPlayerChat_DM_Whisper(playerId, speaker, message.c_str()); sentMessage = true; } - else if (channel == Constants::ChatChannel::PlayerParty) - { - messageDispatch->SendServerToPlayerChat_Party(playerId, speaker, message.c_str()); - sentMessage = true; - } - else if (channel == Constants::ChatChannel::DmParty) + else if (channel == Constants::ChatChannel::PlayerParty || channel == Constants::ChatChannel::DmParty) { messageDispatch->SendServerToPlayerChat_Party(playerId, speaker, message.c_str()); sentMessage = true; From ed19556cfc95362869dd36aa7a53f4f6de0da61a Mon Sep 17 00:00:00 2001 From: plenarius Date: Tue, 3 Nov 2020 13:09:25 -0500 Subject: [PATCH 09/10] Update deprecation warning for GetTarget --- Plugins/Chat/NWScript/nwnx_chat.nss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Chat/NWScript/nwnx_chat.nss b/Plugins/Chat/NWScript/nwnx_chat.nss index edb68144c46..a067188dc65 100644 --- a/Plugins/Chat/NWScript/nwnx_chat.nss +++ b/Plugins/Chat/NWScript/nwnx_chat.nss @@ -124,7 +124,7 @@ object NWNX_Chat_GetSender() object NWNX_Chat_GetTarget() { - WriteTimestampedLogEntry("NWNX_Chat: GetTarget() is deprecated. This function is no longer useful since targets are only determined in tells and those have been suppressed."); + WriteTimestampedLogEntry("NWNX_Chat: GetTarget() is deprecated. Please use the event system (NWNX_ON_CHAT_SEND_*)"); NWNX_PushArgumentString("NWNX_Events", "GetEventData", "TARGET"); NWNX_CallFunction("NWNX_Events", "GetEventData"); From 64a0fa4095f7689016dfe46d80798756cc2ad249 Mon Sep 17 00:00:00 2001 From: plenarius Date: Tue, 3 Nov 2020 13:30:36 -0500 Subject: [PATCH 10/10] Update doc on where chat_channels constants reside --- Plugins/Events/NWScript/nwnx_events.nss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Events/NWScript/nwnx_events.nss b/Plugins/Events/NWScript/nwnx_events.nss index dfb686b8003..2bc2453f126 100644 --- a/Plugins/Events/NWScript/nwnx_events.nss +++ b/Plugins/Events/NWScript/nwnx_events.nss @@ -1240,7 +1240,7 @@ _______________________________________ Event Data Tag | Type | Notes ----------------------|--------|------- - CHANNEL | int | The channel the chat message was sent (see @ref chat_channels in the NWNX_Chat Plugin) + CHANNEL | int | The channel the chat message was sent (see @ref chat_channels nwnx_consts.nss) MESSAGE | string | The message sent TARGET | object | The recipient of the message (for tells only)