diff --git a/CHANGELOG.md b/CHANGELOG.md index 39e1c88e32..d925512183 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ https://github.com/nwnxee/unified/compare/build8193.35.40...HEAD - Events: added `NWNX_ON_CREATURE_JUMP_TO_POSITION_{BEFORE|AFTER}` which fires when a creature is being jumped to a new location (area + x/y/z coordinates). - Events: added `NWNX_ON_CREATURE_JUMP_TO_OBJECT_{BEFORE|AFTER}` which fires when a creature is being jumped to a new location (to an object). - Events: added skippable events `NWNX_ON_ITEMPROPERTY_EFFECT_(APPLIED|REMOVED)_*` which fire when the game applies or removes the effects from an itemproperty. - +- Tweaks: added `NWNX_TWEAKS_FIRE_EQUIP_EVENTS_FOR_ALL_CREATURES` which makes the module OnPlayerEquipItem and OnPlayerUnEquipItem events fire for all creatures. +- Tweaks: added `NWNX_TWEAKS_DONT_DELAY_EQUIP_EVENT` which fixes Unequip/Equip events being out of sync if an item is equipped/unequipped multiple times per server tick. ##### New Plugins - Resources: Adds `RESOURCES_*` variables for adding NWSync as a resource source, and specifying a replacement hak list. diff --git a/Plugins/Tweaks/CMakeLists.txt b/Plugins/Tweaks/CMakeLists.txt index 991aabafb4..df80a13728 100644 --- a/Plugins/Tweaks/CMakeLists.txt +++ b/Plugins/Tweaks/CMakeLists.txt @@ -39,4 +39,5 @@ add_plugin(Tweaks "RangedWeaponsUseOnHitCastSpellItemProperties.cpp" "CastAllOnHitCastSpellItemProperties.cpp" "FixAutoMapCrash.cpp" - "SetAreaCallsSetPosition.cpp") + "SetAreaCallsSetPosition.cpp" + "EquipUnequipEventTweaks.cpp") diff --git a/Plugins/Tweaks/EquipUnequipEventTweaks.cpp b/Plugins/Tweaks/EquipUnequipEventTweaks.cpp new file mode 100644 index 0000000000..c470a6c8eb --- /dev/null +++ b/Plugins/Tweaks/EquipUnequipEventTweaks.cpp @@ -0,0 +1,105 @@ +#include "nwnx.hpp" + +#include "API/CNWSCreature.hpp" +#include "API/CNWSCreatureStats.hpp" +#include "API/CNWSItem.hpp" +#include "API/CNWSModule.hpp" +#include "API/CNWSInventory.hpp" +#include "API/CAppManager.hpp" +#include "API/CServerExoApp.hpp" +#include "API/CScriptEvent.hpp" +#include "API/CServerAIMaster.hpp" + +namespace Tweaks { + +using namespace NWNXLib; +using namespace NWNXLib::API; + +static bool s_bEquipForAll; +static bool s_bNoEquipEventDelay; + +void EquipUnequipEventTweaks() __attribute__((constructor)); +void EquipUnequipEventTweaks() +{ + s_bEquipForAll = Config::Get("FIRE_EQUIP_EVENTS_FOR_ALL_CREATURES", false); + s_bNoEquipEventDelay = Config::Get("DONT_DELAY_EQUIP_EVENT", false); + + if (s_bEquipForAll) + LOG_INFO("OnPlayerEquipItem and OnPlayerUnEquipItem will fire for all creatures"); + if (s_bNoEquipEventDelay) + LOG_INFO("OnPlayerEquipItem will fire without delay"); + + if (s_bEquipForAll || s_bNoEquipEventDelay) + { + static Hooks::Hook s_EquipItemHook = Hooks::HookFunction(&CNWSCreature::EquipItem, + +[](CNWSCreature* pCreature, uint32_t nInventorySlot, CNWSItem* pItem, BOOL bApplyPropertyEffects, BOOL bLoadingItem) -> BOOL + { + if (((s_bEquipForAll) || (pCreature->m_pStats->m_bIsPC)) && (!s_bNoEquipEventDelay)) + { + CScriptEvent* pEvent = new CScriptEvent(); + pEvent->m_nType = Constants::ScriptEvent::OnEquipItem; + pEvent->SetObjectID(0, pItem->m_idSelf); + pEvent->SetObjectID(1, pCreature->m_idSelf); + pEvent->SetInteger(0, pCreature->m_pInventory->GetArraySlotFromSlotFlag(nInventorySlot)); + + auto pServerAIMaster = Globals::AppManager()->m_pServerExoApp->GetServerAIMaster(); + pServerAIMaster->AddEventDeltaTime(0, 0, pCreature->m_idSelf, Utils::GetModule()->m_idSelf, Constants::AIMasterEvent::SignalEvent, (void*)pEvent); + } + + if (pCreature->m_idSelf != pItem->m_oidPossessor) + pItem->SetPossessor(pCreature->m_idSelf, false, false, false); + + if (pCreature->m_pInventory->GetItemInInventory(pItem)) + pCreature->UnequipItem(pItem, false); + + if (bApplyPropertyEffects) + pItem->ApplyItemProperties(pCreature, nInventorySlot, bLoadingItem); + + pCreature->m_pInventory->PutItemInSlot(nInventorySlot, pItem); + pCreature->ComputeArmourClass(pItem, true, false); + pCreature->m_pStats->UpdateCombatInformation(); + pCreature->m_nEquippedWeight += pItem->GetWeight(); + + if (((s_bEquipForAll) || (pCreature->m_pStats->m_bIsPC)) && (s_bNoEquipEventDelay)) + { + auto* pModule = Utils::GetModule(); + pModule->m_oidLastItemEquippedBy = pCreature->m_idSelf; + pModule->m_oidLastItemEquipped = pItem->m_idSelf; + pModule->m_nLastItemEquippedSlot = pCreature->m_pInventory->GetArraySlotFromSlotFlag(nInventorySlot); + pModule->RunEventScript(15, nullptr); + } + + return true; + }, Hooks::Order::Final); + } + + if (s_bEquipForAll) + { + static Hooks::Hook s_UnEquipItemHook = Hooks::HookFunction(&CNWSCreature::UnequipItem, + +[](CNWSCreature* pCreature, CNWSItem * pItem, BOOL bUnequipWhilePolymorphed) -> BOOL + { + if ((pCreature->m_bIsPolymorphed) && (!bUnequipWhilePolymorphed)) + return false; + + if ((s_bEquipForAll) || (pCreature->m_pStats->m_bIsPC)) + { + auto* pModule = Utils::GetModule(); + pModule->m_oidLastItemUnequippedBy = pCreature->m_idSelf; + pModule->m_oidLastItemUnequipped = pItem->m_idSelf; + pModule->m_nLastItemUnequippedSlot = pCreature->m_pInventory->GetArraySlotFromSlotFlag(pCreature->m_pInventory->GetSlotFromItem(pItem)); + pModule->RunEventScript(16, nullptr); + } + + pItem->RemoveItemProperties(pCreature, pCreature->m_pInventory->GetSlotFromItem(pItem)); + pCreature->m_pInventory->RemoveItem(pItem); + pCreature->ComputeArmourClass(pItem, true, false); + + pCreature->m_nEquippedWeight -= pItem->GetWeight(); + pCreature->m_bUpdateCombatInformation = true; + + return true; + }, Hooks::Order::Final); + } +} + +} diff --git a/Plugins/Tweaks/README.md b/Plugins/Tweaks/README.md index 711a05315f..8e8a349fdc 100644 --- a/Plugins/Tweaks/README.md +++ b/Plugins/Tweaks/README.md @@ -50,6 +50,8 @@ Tweaks stuff. See below. | `NWNX_TWEAKS_CAST_ALL_ON_HIT_CAST_SPELL_ITEM_PROPERTIES` | true or false | Casts all On Hit: Cast Spell item properties on hit, instead of only the first property. | | `NWNX_TWEAKS_FIX_AUTOMAP_CRASH` | true or false | Fixes a server crash that happens when automap data is outdated for a player. | | `NWNX_TWEAKS_SETAREA_CALLS_SETPOSITION` | true or false | If enabled, a creature getting added to an area will fire the `NWNX_ON_MATERIALCHANGE_*` and `NWNX_ON_CREATURE_TILE_CHANGE_*` events. | +| `NWNX_TWEAKS_FIRE_EQUIP_EVENTS_FOR_ALL_CREATURES` | true or false | The module OnPlayerEquipItem and OnPlayerUnEquipItem events are fired for all creatures | +| `NWNX_TWEAKS_DONT_DELAY_EQUIP_EVENT` | true or false | Fixes Unequip/Equip events being out of sync if an item is equipped/unequipped multiple times per server tick | ## Environment variable values