mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-24 13:10:49 +00:00
add fine grained control of UNIT_* events and new UNIT_*_GUID versions of key events
This commit is contained in:
@@ -45,6 +45,8 @@ set(SOURCE_FILES
|
||||
autoattack.cpp
|
||||
player_scripts.hpp
|
||||
player_scripts.cpp
|
||||
unit.hpp
|
||||
unit.cpp
|
||||
)
|
||||
|
||||
add_library(${DLL_NAME} SHARED ${SOURCE_FILES})
|
||||
|
||||
@@ -1145,6 +1145,63 @@ namespace game {
|
||||
};
|
||||
|
||||
enum Events : std::uint32_t {
|
||||
UNIT_PET = 0u,
|
||||
UNIT_PET_02 = 2u,
|
||||
UNIT_HEALTH = 16u,
|
||||
UNIT_MANA = 17u,
|
||||
UNIT_RAGE = 18u,
|
||||
UNIT_FOCUS = 19u,
|
||||
UNIT_ENERGY = 20u,
|
||||
UNIT_HAPPINESS = 21u,
|
||||
UNIT_MAXHEALTH = 22u,
|
||||
UNIT_MAXMANA = 23u,
|
||||
UNIT_MAXRAGE = 24u,
|
||||
UNIT_MAXFOCUS = 25u,
|
||||
UNIT_MAXENERGY = 26u,
|
||||
UNIT_MAXHAPPINESS = 27u,
|
||||
UNIT_LEVEL = 28u,
|
||||
UNIT_FACTION = 29u,
|
||||
UNIT_DISPLAYPOWER = 30u,
|
||||
UNIT_FLAGS = 40u,
|
||||
UNIT_AURA = 41u,
|
||||
UNIT_AURA_02 = 107u,
|
||||
UNIT_ATTACK_SPEED_01 = 120u,
|
||||
UNIT_ATTACK_SPEED_02 = 121u,
|
||||
UNIT_RANGEDDAMAGE_01 = 122u,
|
||||
UNIT_DAMAGE_01 = 128u,
|
||||
UNIT_DAMAGE_02 = 129u,
|
||||
UNIT_DAMAGE_03 = 130u,
|
||||
UNIT_DAMAGE_04 = 131u,
|
||||
UNIT_LOYALTY = 132u,
|
||||
UNIT_PET_EXPERIENCE_01 = 135u,
|
||||
UNIT_PET_EXPERIENCE_02 = 136u,
|
||||
UNIT_DYNAMIC_FLAGS = 137u,
|
||||
UNIT_PET_TRAINING_POINTS = 143u,
|
||||
UNIT_STATS_01 = 144u,
|
||||
UNIT_STATS_02 = 145u,
|
||||
UNIT_STATS_03 = 146u,
|
||||
UNIT_STATS_04 = 147u,
|
||||
UNIT_STATS_05 = 148u,
|
||||
UNIT_RESISTANCES_01 = 149u,
|
||||
UNIT_RESISTANCES_02 = 150u,
|
||||
UNIT_RESISTANCES_03 = 151u,
|
||||
UNIT_RESISTANCES_04 = 152u,
|
||||
UNIT_RESISTANCES_05 = 153u,
|
||||
UNIT_RESISTANCES_06 = 154u,
|
||||
UNIT_RESISTANCES_07 = 155u,
|
||||
UNIT_ATTACK_POWER_01 = 159u,
|
||||
UNIT_ATTACK_POWER_02 = 160u,
|
||||
UNIT_ATTACK_POWER_03 = 161u,
|
||||
UNIT_RANGED_ATTACK_POWER_01 = 162u,
|
||||
UNIT_RANGED_ATTACK_POWER_02 = 163u,
|
||||
UNIT_RANGED_ATTACK_POWER_03 = 164u,
|
||||
UNIT_RANGEDDAMAGE_02 = 165u,
|
||||
UNIT_RANGEDDAMAGE_03 = 166u,
|
||||
|
||||
UNIT_MANA1 = 167,
|
||||
UNIT_MANA2 = 174,
|
||||
|
||||
UNIT_COMBAT = 182,
|
||||
UNIT_NAME_UPDATE = 183,
|
||||
UNIT_PORTRAIT_UPDATE = 184,
|
||||
UNIT_MODEL_CHANGED = 185,
|
||||
@@ -1562,6 +1619,22 @@ namespace game {
|
||||
|
||||
SPELL_MISS_SELF = 582,
|
||||
SPELL_MISS_OTHER = 583,
|
||||
|
||||
UNIT_COMBAT_GUID = 584,
|
||||
UNIT_NAME_UPDATE_GUID = 585,
|
||||
UNIT_PORTRAIT_UPDATE_GUID = 586,
|
||||
UNIT_MODEL_CHANGED_GUID = 587,
|
||||
UNIT_INVENTORY_CHANGED_GUID = 588,
|
||||
|
||||
UNIT_PET_GUID = 589,
|
||||
UNIT_HEALTH_GUID = 590,
|
||||
UNIT_MANA_GUID = 591,
|
||||
UNIT_RAGE_GUID = 592,
|
||||
UNIT_ENERGY_GUID = 593,
|
||||
UNIT_FLAGS_GUID = 594,
|
||||
UNIT_AURA_GUID = 595,
|
||||
UNIT_DYNAMIC_FLAGS_GUID = 596,
|
||||
PLAYER_GUILD_UPDATE_GUID = 597,
|
||||
};
|
||||
|
||||
enum TypeMask {
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "auras.hpp"
|
||||
#include "lua_refs.hpp"
|
||||
#include "autoattack.hpp"
|
||||
#include "unit.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -157,6 +158,8 @@ namespace Nampower {
|
||||
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT> > gCSimpleFrame_GetNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT> > gCastSpellByNameDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<SendUnitSignalT> > gSendUnitSignalDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CGGameUI_ShowCombatFeedbackT> > gCGGameUI_ShowCombatFeedbackDetour;
|
||||
|
||||
// Flags for one-time initialization
|
||||
std::once_flag loadFlag;
|
||||
@@ -681,6 +684,24 @@ namespace Nampower {
|
||||
} else if (strcmp(cvar, "NP_SpamProtectionEnabled") == 0) {
|
||||
gUserSettings.spamProtectionEnabled = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_SpamProtectionEnabled to " << gUserSettings.spamProtectionEnabled);
|
||||
} else if (strcmp(cvar, "NP_EnableUnitEventsPet") == 0) {
|
||||
gUserSettings.enableUnitEventsPet = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableUnitEventsPet to " << gUserSettings.enableUnitEventsPet);
|
||||
} else if (strcmp(cvar, "NP_EnableUnitEventsParty") == 0) {
|
||||
gUserSettings.enableUnitEventsParty = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableUnitEventsParty to " << gUserSettings.enableUnitEventsParty);
|
||||
} else if (strcmp(cvar, "NP_EnableUnitEventsRaid") == 0) {
|
||||
gUserSettings.enableUnitEventsRaid = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableUnitEventsRaid to " << gUserSettings.enableUnitEventsRaid);
|
||||
} else if (strcmp(cvar, "NP_EnableUnitEventsMouseover") == 0) {
|
||||
gUserSettings.enableUnitEventsMouseover = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableUnitEventsMouseover to " << gUserSettings.enableUnitEventsMouseover);
|
||||
} else if (strcmp(cvar, "NP_EnableUnitEventsGuid") == 0) {
|
||||
gUserSettings.enableUnitEventsGuid = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableUnitEventsGuid to " << gUserSettings.enableUnitEventsGuid);
|
||||
} else if (strcmp(cvar, "NP_EnableUnitEventsGuidFiltering") == 0) {
|
||||
gUserSettings.enableUnitEventsGuidFiltering = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableUnitEventsGuidFiltering to " << gUserSettings.enableUnitEventsGuidFiltering);
|
||||
} else if (strcmp(cvar, "NP_EnableAuraCastEvents") == 0) {
|
||||
gUserSettings.enableAuraCastEvents = atoi(value) != 0;
|
||||
DEBUG_LOG("Set NP_EnableAuraCastEvents to " << gUserSettings.enableAuraCastEvents);
|
||||
@@ -1016,6 +1037,30 @@ namespace Nampower {
|
||||
0, // unk2
|
||||
0); // unk3
|
||||
|
||||
char NP_EnableUnitEventsPet[] = "NP_EnableUnitEventsPet";
|
||||
CVarRegister(NP_EnableUnitEventsPet, nullptr, 0,
|
||||
gUserSettings.enableUnitEventsPet ? defaultTrue : defaultFalse, nullptr, 1, 0, 0);
|
||||
|
||||
char NP_EnableUnitEventsParty[] = "NP_EnableUnitEventsParty";
|
||||
CVarRegister(NP_EnableUnitEventsParty, nullptr, 0,
|
||||
gUserSettings.enableUnitEventsParty ? defaultTrue : defaultFalse, nullptr, 1, 0, 0);
|
||||
|
||||
char NP_EnableUnitEventsRaid[] = "NP_EnableUnitEventsRaid";
|
||||
CVarRegister(NP_EnableUnitEventsRaid, nullptr, 0,
|
||||
gUserSettings.enableUnitEventsRaid ? defaultTrue : defaultFalse, nullptr, 1, 0, 0);
|
||||
|
||||
char NP_EnableUnitEventsMouseover[] = "NP_EnableUnitEventsMouseover";
|
||||
CVarRegister(NP_EnableUnitEventsMouseover, nullptr, 0,
|
||||
gUserSettings.enableUnitEventsMouseover ? defaultTrue : defaultFalse, nullptr, 1, 0, 0);
|
||||
|
||||
char NP_EnableUnitEventsGuid[] = "NP_EnableUnitEventsGuid";
|
||||
CVarRegister(NP_EnableUnitEventsGuid, nullptr, 0,
|
||||
gUserSettings.enableUnitEventsGuid ? defaultTrue : defaultFalse, nullptr, 1, 0, 0);
|
||||
|
||||
char NP_EnableUnitEventsGuidFiltering[] = "NP_EnableUnitEventsGuidFiltering";
|
||||
CVarRegister(NP_EnableUnitEventsGuidFiltering, nullptr, 0,
|
||||
gUserSettings.enableUnitEventsGuidFiltering ? defaultTrue : defaultFalse, nullptr, 1, 0, 0);
|
||||
|
||||
char NP_EnableAuraCastEvents[] = "NP_EnableAuraCastEvents";
|
||||
CVarRegister(NP_EnableAuraCastEvents, // name
|
||||
nullptr, // help
|
||||
@@ -1211,6 +1256,14 @@ namespace Nampower {
|
||||
loadUserVar("NP_DoubleCastToEndChannelEarly");
|
||||
|
||||
loadUserVar("NP_SpamProtectionEnabled");
|
||||
|
||||
loadUserVar("NP_EnableUnitEventsPet");
|
||||
loadUserVar("NP_EnableUnitEventsParty");
|
||||
loadUserVar("NP_EnableUnitEventsRaid");
|
||||
loadUserVar("NP_EnableUnitEventsMouseover");
|
||||
loadUserVar("NP_EnableUnitEventsGuid");
|
||||
loadUserVar("NP_EnableUnitEventsGuidFiltering");
|
||||
|
||||
loadUserVar("NP_EnableAuraCastEvents");
|
||||
loadUserVar("NP_EnableAutoAttackEvents");
|
||||
loadUserVar("NP_EnableSpellStartEvents");
|
||||
@@ -1405,6 +1458,10 @@ namespace Nampower {
|
||||
&AttackRoundInfo_ReadPacketHook);
|
||||
gCSimpleFrame_GetNameDetour = createHook<LuaScriptT>(process, Offsets::CSimpleFrame_GetName,
|
||||
&CSimpleFrame_GetNameHook);
|
||||
gSendUnitSignalDetour = createHook<SendUnitSignalT>(process, Offsets::SendUnitSignal,
|
||||
&SendUnitSignalHook);
|
||||
gCGGameUI_ShowCombatFeedbackDetour = createHook<CGGameUI_ShowCombatFeedbackT>(
|
||||
process, Offsets::CGGameUI_ShowCombatFeedback, &CGGameUI_ShowCombatFeedbackHook);
|
||||
gLoadScriptFunctionsDetour = createHook<LoadScriptFunctionsT>(process, Offsets::LoadScriptFunctions,
|
||||
&LoadScriptFunctionsHook);
|
||||
gCreateEventsDetour = createHook<FrameScript_CreateEventsT>(process, Offsets::FrameScript_CreateEvents,
|
||||
@@ -1545,6 +1602,48 @@ namespace Nampower {
|
||||
|
||||
char SPELL_MISS_OTHER[] = "SPELL_MISS_OTHER";
|
||||
addCustomEvent(game::SPELL_MISS_OTHER, SPELL_MISS_OTHER);
|
||||
|
||||
char UNIT_COMBAT_GUID[] = "UNIT_COMBAT_GUID";
|
||||
addCustomEvent(game::UNIT_COMBAT_GUID, UNIT_COMBAT_GUID);
|
||||
|
||||
char UNIT_NAME_UPDATE_GUID[] = "UNIT_NAME_UPDATE_GUID";
|
||||
addCustomEvent(game::UNIT_NAME_UPDATE_GUID, UNIT_NAME_UPDATE_GUID);
|
||||
|
||||
char UNIT_PORTRAIT_UPDATE_GUID[] = "UNIT_PORTRAIT_UPDATE_GUID";
|
||||
addCustomEvent(game::UNIT_PORTRAIT_UPDATE_GUID, UNIT_PORTRAIT_UPDATE_GUID);
|
||||
|
||||
char UNIT_MODEL_CHANGED_GUID[] = "UNIT_MODEL_CHANGED_GUID";
|
||||
addCustomEvent(game::UNIT_MODEL_CHANGED_GUID, UNIT_MODEL_CHANGED_GUID);
|
||||
|
||||
char UNIT_INVENTORY_CHANGED_GUID[] = "UNIT_INVENTORY_CHANGED_GUID";
|
||||
addCustomEvent(game::UNIT_INVENTORY_CHANGED_GUID, UNIT_INVENTORY_CHANGED_GUID);
|
||||
|
||||
char UNIT_PET_GUID[] = "UNIT_PET_GUID";
|
||||
addCustomEvent(game::UNIT_PET_GUID, UNIT_PET_GUID);
|
||||
|
||||
char UNIT_HEALTH_GUID[] = "UNIT_HEALTH_GUID";
|
||||
addCustomEvent(game::UNIT_HEALTH_GUID, UNIT_HEALTH_GUID);
|
||||
|
||||
char UNIT_MANA_GUID[] = "UNIT_MANA_GUID";
|
||||
addCustomEvent(game::UNIT_MANA_GUID, UNIT_MANA_GUID);
|
||||
|
||||
char UNIT_RAGE_GUID[] = "UNIT_RAGE_GUID";
|
||||
addCustomEvent(game::UNIT_RAGE_GUID, UNIT_RAGE_GUID);
|
||||
|
||||
char UNIT_ENERGY_GUID[] = "UNIT_ENERGY_GUID";
|
||||
addCustomEvent(game::UNIT_ENERGY_GUID, UNIT_ENERGY_GUID);
|
||||
|
||||
char UNIT_FLAGS_GUID[] = "UNIT_FLAGS_GUID";
|
||||
addCustomEvent(game::UNIT_FLAGS_GUID, UNIT_FLAGS_GUID);
|
||||
|
||||
char UNIT_AURA_GUID[] = "UNIT_AURA_GUID";
|
||||
addCustomEvent(game::UNIT_AURA_GUID, UNIT_AURA_GUID);
|
||||
|
||||
char UNIT_DYNAMIC_FLAGS_GUID[] = "UNIT_DYNAMIC_FLAGS_GUID";
|
||||
addCustomEvent(game::UNIT_DYNAMIC_FLAGS_GUID, UNIT_DYNAMIC_FLAGS_GUID);
|
||||
|
||||
char PLAYER_GUILD_UPDATE_GUID[] = "PLAYER_GUILD_UPDATE_GUID";
|
||||
addCustomEvent(game::PLAYER_GUILD_UPDATE_GUID, PLAYER_GUILD_UPDATE_GUID);
|
||||
}
|
||||
|
||||
void FrameScript_CreateEventsHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t maxEventId) {
|
||||
|
||||
+5
-2
@@ -25,8 +25,8 @@ namespace Nampower {
|
||||
constexpr uint32_t DISENCHANT_QUALITY_PURPLE = 0x04; // Epic
|
||||
|
||||
constexpr uint32_t MAJOR_VERSION = 2;
|
||||
constexpr uint32_t MINOR_VERSION = 38;
|
||||
constexpr uint32_t PATCH_VERSION = 1;
|
||||
constexpr uint32_t MINOR_VERSION = 39;
|
||||
constexpr uint32_t PATCH_VERSION = 0;
|
||||
|
||||
constexpr int32_t LUA_REGISTRYINDEX = -10000;
|
||||
constexpr int32_t LUA_GLOBALSINDEX = -10001;
|
||||
@@ -125,6 +125,9 @@ namespace Nampower {
|
||||
using LuaScriptT = uint32_t (__fastcall *)(uintptr_t *luaState);
|
||||
using GetGUIDFromNameT = std::uint64_t (__fastcall *)(const char *);
|
||||
using GetUnitFromNameT = uintptr_t *(__fastcall *)(const char *);
|
||||
using GetNamesFromGUIDT = char **(__fastcall *)(uint64_t *guid, int *numNamesReturn);
|
||||
using SignalEventParamSingleStringT = int (__cdecl *)(uint32_t eventCode, const char *format, const char *str);
|
||||
using SendUnitSignalT = void (__fastcall *)(uint64_t *guid, uint32_t eventCode);
|
||||
using lua_gettableT = void (__fastcall *)(uintptr_t *luaState, int globalsIndex);
|
||||
using lua_gettopT = int (__fastcall *)(uintptr_t *);
|
||||
using lua_typeT = int (__fastcall *)(uintptr_t *, int);
|
||||
|
||||
@@ -38,6 +38,8 @@ enum class Offsets : std::uint32_t {
|
||||
GetObjectPtr = 0x464870,
|
||||
GetActivePlayer = 0x468550,
|
||||
GetUnitFromName = 0x00515940,
|
||||
GetNamesFromGUID = 0x00000000, // TODO: set correct offset
|
||||
SendUnitSignal = 0x00515e50,
|
||||
GetDurationObject = 0x00C0D828,
|
||||
GetCastingTimeIndex = 0x2D,
|
||||
Language = 0xC0E080,
|
||||
@@ -89,10 +91,18 @@ enum class Offsets : std::uint32_t {
|
||||
OnSpriteRightClick = 0x00492820,
|
||||
CGGameUI_HandleObjectTrackChange = 0x00492890,
|
||||
CGGameUI_Target = 0X00493540,
|
||||
CGGameUI_ShowCombatFeedback = 0x00494600,
|
||||
ScriptTargetUnit = 0X00489A40,
|
||||
|
||||
MouseoverGuidLow = 0x00B4E2C8,
|
||||
MouseoverGuidHigh = 0x00B4E2CC,
|
||||
NpcGuidLow = 0x00B4E2D0,
|
||||
NpcGuidHigh = 0x00B4E2D4,
|
||||
|
||||
CGGameUI_GetPartyMember = 0X004E81A0,
|
||||
CGGameUI_GetPartyMemberPet = 0X004E81D0,
|
||||
CGGameUI_GetRaidMember = 0X00491940,
|
||||
CGGameUI_GetRaidMemberPet = 0X00491960,
|
||||
|
||||
SpellVisualsHandleCastStart = 0X006EC220,
|
||||
PlaySpellVisualHandler = 0X006E98D0,
|
||||
@@ -191,6 +201,7 @@ enum class Offsets : std::uint32_t {
|
||||
SpellVisualsInitialize = 0x006ec0e0,
|
||||
SysMsgInitialize = 0x0044cd10,
|
||||
|
||||
UnitCombatParamFormat = 0x0084333c,
|
||||
IntIntParamFormat = 0X00843342,
|
||||
StringIntParamFormat = 0X00847FBC,
|
||||
StringParamFormat = 0X0082E280,
|
||||
|
||||
@@ -29,6 +29,14 @@ struct UserSettings {
|
||||
|
||||
bool spamProtectionEnabled = true;
|
||||
bool enableAuraCastEvents = false;
|
||||
|
||||
bool enableUnitEventsPet = true;
|
||||
bool enableUnitEventsParty = true;
|
||||
bool enableUnitEventsRaid = true;
|
||||
bool enableUnitEventsMouseover = true;
|
||||
bool enableUnitEventsGuid = true;
|
||||
bool enableUnitEventsGuidFiltering = false;
|
||||
|
||||
bool enableAutoAttackEvents = false;
|
||||
bool enableSpellStartEvents = false;
|
||||
bool enableSpellGoEvents = false;
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
//
|
||||
// Created by pmacc on 2/19/2026.
|
||||
//
|
||||
|
||||
#include "unit.hpp"
|
||||
#include "offsets.hpp"
|
||||
#include "game.hpp"
|
||||
#include "helper.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
UnitNames GetUnitNames(uint64_t *guid) {
|
||||
UnitNames names;
|
||||
|
||||
if (!guid || *guid == 0) return names;
|
||||
|
||||
names.guid = *guid;
|
||||
|
||||
uint64_t playerGuid = game::ClntObjMgrGetActivePlayerGuid();
|
||||
if (playerGuid == 0) return names;
|
||||
|
||||
uintptr_t *playerObj = game::GetObjectPtr(playerGuid);
|
||||
if (!playerObj) return names;
|
||||
|
||||
// player: compare guid directly against active player guid
|
||||
if (*guid == playerGuid) {
|
||||
names.player = true;
|
||||
}
|
||||
|
||||
// pet: playerObj+0x110 is a pointer to the pet GUID; advance past null entry if present
|
||||
// (mirrors: puVar2 = *(uint**)(uVar1+0x110); if (!*puVar2 && !puVar2[1]) puVar2+=2)
|
||||
auto *petGuidPtr = *reinterpret_cast<uint64_t **>(reinterpret_cast<uint8_t *>(playerObj) + 0x110);
|
||||
if (petGuidPtr) {
|
||||
if (*petGuidPtr == 0) petGuidPtr++; // skip null slot (uint64_t++ == uint32_t+=2)
|
||||
if (*petGuidPtr != 0 && *guid == *petGuidPtr) {
|
||||
names.pet = true;
|
||||
}
|
||||
}
|
||||
|
||||
// target (locked target)
|
||||
uint64_t targetGuid = *reinterpret_cast<uint64_t *>(Offsets::LockedTargetGuid);
|
||||
if (*guid == targetGuid) {
|
||||
names.target = true;
|
||||
}
|
||||
|
||||
// party members (0-4, stored 1-indexed in struct)
|
||||
auto const GetPartyMember = reinterpret_cast<CGGameUI_GetPartyMemberT>(Offsets::CGGameUI_GetPartyMember);
|
||||
auto const GetPartyMemberPet = reinterpret_cast<CGGameUI_GetPartyMemberPetT>(
|
||||
Offsets::CGGameUI_GetPartyMemberPet);
|
||||
|
||||
if (GetPartyMember && GetPartyMemberPet) {
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (*guid == GetPartyMember(i)) {
|
||||
names.partyIndex = static_cast<int>(i) + 1;
|
||||
break;
|
||||
}
|
||||
if (*guid == GetPartyMemberPet(i)) {
|
||||
names.partyPetIndex = static_cast<int>(i) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// raid members (0-39, stored 1-indexed in struct)
|
||||
auto const GetRaidMember = reinterpret_cast<CGGameUI_GetRaidMemberT>(Offsets::CGGameUI_GetRaidMember);
|
||||
auto const GetRaidMemberPet = reinterpret_cast<CGGameUI_GetRaidMemberPetT>(Offsets::CGGameUI_GetRaidMemberPet);
|
||||
|
||||
if (GetRaidMember && GetRaidMemberPet) {
|
||||
for (uint32_t i = 0; i < 40; i++) {
|
||||
if (*guid == GetRaidMember(i)) {
|
||||
names.raidIndex = static_cast<int>(i) + 1;
|
||||
break;
|
||||
}
|
||||
if (*guid == GetRaidMemberPet(i)) {
|
||||
names.raidPetIndex = static_cast<int>(i) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// npc (separate tracked GUID at 0x00B4E2D0)
|
||||
uint64_t npcGuid = *reinterpret_cast<uint64_t *>(Offsets::NpcGuidLow);
|
||||
if (npcGuid != 0 && *guid == npcGuid) {
|
||||
names.npc = true;
|
||||
}
|
||||
|
||||
// mouseover
|
||||
uint64_t mouseoverGuid = *reinterpret_cast<uint64_t *>(Offsets::MouseoverGuidLow);
|
||||
if (mouseoverGuid != 0 && *guid == mouseoverGuid) {
|
||||
names.mouseover = true;
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
// Applies per-category filtering and invokes callback(name) for each passing name.
|
||||
// Category mapping mirrors the original string-prefix logic:
|
||||
void TriggerUnitEvents(const UnitNames &names, uint32_t eventCode) {
|
||||
auto const SignalEventParam = reinterpret_cast<SignalEventParamSingleStringT>(Offsets::SignalEventParam);
|
||||
auto *format = reinterpret_cast<char *>(Offsets::StringParamFormat);
|
||||
|
||||
if (names.player)
|
||||
SignalEventParam(eventCode, format, UNIT_PLAYER); // always trigger for the current player
|
||||
if (names.pet)
|
||||
SignalEventParam(eventCode, format, UNIT_PET); // always trigger for your own pet
|
||||
if (names.target)
|
||||
SignalEventParam(eventCode, format, UNIT_TARGET); // always trigger for your target
|
||||
if (names.npc)
|
||||
SignalEventParam(eventCode, format, UNIT_NPC); // always trigger for interact npc
|
||||
if (names.mouseover && gUserSettings.enableUnitEventsMouseover)
|
||||
SignalEventParam(eventCode, format, UNIT_MOUSEOVER);
|
||||
if (names.partyIndex > 0 && gUserSettings.enableUnitEventsParty)
|
||||
SignalEventParam(eventCode, format, UNIT_PARTY[names.partyIndex - 1]);
|
||||
if (names.partyPetIndex > 0 && gUserSettings.enableUnitEventsParty && gUserSettings.enableUnitEventsPet)
|
||||
SignalEventParam(eventCode, format, UNIT_PARTYPET[names.partyPetIndex - 1]);
|
||||
if (names.raidIndex > 0 && gUserSettings.enableUnitEventsRaid)
|
||||
SignalEventParam(eventCode, format, UNIT_RAID[names.raidIndex - 1]);
|
||||
if (names.raidPetIndex > 0 && gUserSettings.enableUnitEventsRaid && gUserSettings.enableUnitEventsPet)
|
||||
SignalEventParam(eventCode, format, UNIT_RAIDPET[names.raidPetIndex - 1]);
|
||||
if (names.guid != 0 && gUserSettings.enableUnitEventsGuid) {
|
||||
// when filtering is enabled, suppress high-frequency guid events that cause spam in older addons:
|
||||
// skip eventCodes below UNIT_COMBAT(182), and skip UNIT_NAME_UPDATE(183), UNIT_PORTRAIT_UPDATE(184),
|
||||
// UNIT_INVENTORY_CHANGED(186), PLAYER_GUILD_UPDATE(345) — these cover UNIT_AURA/UNIT_HEALTH/UNIT_MANA etc.
|
||||
bool filtered = gUserSettings.enableUnitEventsGuidFiltering &&
|
||||
(eventCode < 182 || eventCode == 183 || eventCode == 184 ||
|
||||
eventCode == 186 || eventCode == 345);
|
||||
if (!filtered) {
|
||||
char *guidStr = ConvertGuidToString(names.guid);
|
||||
SignalEventParam(eventCode, format, guidStr);
|
||||
delete[] guidStr;
|
||||
}
|
||||
}
|
||||
|
||||
// trigger the appropriate guid event for curated subset of the most useful unit events
|
||||
if (names.guid != 0) {
|
||||
uint32_t guidEventCode = 0;
|
||||
switch (eventCode) {
|
||||
case game::UNIT_PET:
|
||||
case game::UNIT_PET_02:
|
||||
guidEventCode = game::UNIT_PET_GUID;
|
||||
break;
|
||||
case game::UNIT_HEALTH:
|
||||
guidEventCode = game::UNIT_HEALTH_GUID;
|
||||
break;
|
||||
case game::UNIT_MANA:
|
||||
case game::UNIT_MANA1:
|
||||
case game::UNIT_MANA2:
|
||||
guidEventCode = game::UNIT_MANA_GUID;
|
||||
break;
|
||||
case game::UNIT_RAGE:
|
||||
guidEventCode = game::UNIT_RAGE_GUID;
|
||||
break;
|
||||
case game::UNIT_ENERGY:
|
||||
guidEventCode = game::UNIT_ENERGY_GUID;
|
||||
break;
|
||||
case game::UNIT_FLAGS:
|
||||
guidEventCode = game::UNIT_FLAGS_GUID;
|
||||
break;
|
||||
case game::UNIT_AURA:
|
||||
case game::UNIT_AURA_02:
|
||||
guidEventCode = game::UNIT_AURA_GUID;
|
||||
break;
|
||||
case game::UNIT_DYNAMIC_FLAGS:
|
||||
guidEventCode = game::UNIT_DYNAMIC_FLAGS_GUID;
|
||||
break;
|
||||
case game::UNIT_NAME_UPDATE:
|
||||
guidEventCode = game::UNIT_NAME_UPDATE_GUID;
|
||||
break;
|
||||
case game::UNIT_PORTRAIT_UPDATE:
|
||||
guidEventCode = game::UNIT_PORTRAIT_UPDATE_GUID;
|
||||
break;
|
||||
case game::UNIT_MODEL_CHANGED:
|
||||
DEBUG_LOG("UNIT_MODEL_CHANGED hit in switch, guid=0x" << std::hex << names.guid << std::dec);
|
||||
guidEventCode = game::UNIT_MODEL_CHANGED_GUID;
|
||||
break;
|
||||
case game::UNIT_INVENTORY_CHANGED:
|
||||
guidEventCode = game::UNIT_INVENTORY_CHANGED_GUID;
|
||||
break;
|
||||
case game::PLAYER_GUILD_UPDATE:
|
||||
guidEventCode = game::PLAYER_GUILD_UPDATE_GUID;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (guidEventCode != 0) {
|
||||
DEBUG_LOG(
|
||||
"FireUnitGuidEvent guidEventCode=" << guidEventCode << " guid=0x" << std::hex << names.guid << std::
|
||||
dec);
|
||||
FireUnitGuidEvent(names, guidEventCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FireUnitGuidEvent(const UnitNames &names, uint32_t guidEventCode) {
|
||||
static constexpr char kGuidFormat[] = "%s%d%d%d%d%d%d";
|
||||
auto const SignalGuidEvent = reinterpret_cast<SignalEventParamGuidT>(Offsets::SignalEventParam);
|
||||
char *guidStr = ConvertGuidToString(names.guid);
|
||||
SignalGuidEvent(guidEventCode, kGuidFormat,
|
||||
guidStr,
|
||||
names.player ? 1 : 0,
|
||||
names.target ? 1 : 0,
|
||||
names.mouseover ? 1 : 0,
|
||||
names.pet ? 1 : 0,
|
||||
names.partyIndex,
|
||||
names.raidIndex);
|
||||
delete[] guidStr;
|
||||
}
|
||||
|
||||
void SendUnitSignalHook(hadesmem::PatchDetourBase *detour, uint64_t *guid, uint32_t eventCode) {
|
||||
if (!guid || *guid == 0) return;
|
||||
|
||||
DEBUG_LOG("SendUnitSignal eventCode=" << eventCode << " guid=0x" << std::hex << *guid << std::dec);
|
||||
|
||||
auto names = GetUnitNames(guid);
|
||||
TriggerUnitEvents(names, eventCode);
|
||||
}
|
||||
|
||||
void CGGameUI_ShowCombatFeedbackHook(hadesmem::PatchDetourBase *detour, uint64_t *guid,
|
||||
const char *action, int damage, int school, int hitInfo) {
|
||||
if (!guid || *guid == 0) return;
|
||||
|
||||
auto names = GetUnitNames(guid);
|
||||
|
||||
// derive critIndicator string from hitInfo bitfield (mirrors WoW internal logic)
|
||||
auto lo = static_cast<uint8_t>(hitInfo);
|
||||
auto hi = static_cast<uint8_t>(hitInfo >> 8);
|
||||
const char *crit = "";
|
||||
if (damage > 0) {
|
||||
if (lo & 0x80) crit = "CRITICAL";
|
||||
else if (hi & 0x40) crit = "GLANCING";
|
||||
else if (hi & 0x80) crit = "CRUSHING";
|
||||
} else {
|
||||
if (lo & 0x20) crit = "ABSORB";
|
||||
else if (hi & 0x08) crit = "BLOCK";
|
||||
else if (lo & 0x40) crit = "RESIST";
|
||||
}
|
||||
|
||||
auto const SignalEventParam = reinterpret_cast<SignalEventParamUnitCombatT>(Offsets::SignalEventParam);
|
||||
auto *format = reinterpret_cast<const char *>(Offsets::UnitCombatParamFormat);
|
||||
|
||||
if (names.player)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_PLAYER, action, crit, damage, school);
|
||||
if (names.pet && gUserSettings.enableUnitEventsPet)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_PET, action, crit, damage, school);
|
||||
if (names.target)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_TARGET, action, crit, damage, school);
|
||||
if (names.npc)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_NPC, action, crit, damage, school);
|
||||
if (names.mouseover && gUserSettings.enableUnitEventsMouseover)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_MOUSEOVER, action, crit, damage, school);
|
||||
if (names.partyIndex > 0 && gUserSettings.enableUnitEventsParty)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_PARTY[names.partyIndex - 1], action, crit, damage, school);
|
||||
if (names.partyPetIndex > 0 && gUserSettings.enableUnitEventsPet)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_PARTYPET[names.partyPetIndex - 1], action, crit, damage,
|
||||
school);
|
||||
if (names.raidIndex > 0 && gUserSettings.enableUnitEventsRaid)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_RAID[names.raidIndex - 1], action, crit, damage, school);
|
||||
if (names.raidPetIndex > 0 && gUserSettings.enableUnitEventsRaid)
|
||||
SignalEventParam(game::UNIT_COMBAT, format, UNIT_RAIDPET[names.raidPetIndex - 1], action, crit, damage,
|
||||
school);
|
||||
|
||||
if (names.guid != 0) {
|
||||
static constexpr char kCombatGuidFormat[] = "%s%s%d%d%d%d%d%d";
|
||||
auto const SignalCombatGuid = reinterpret_cast<SignalEventParamUnitCombatGuidT>(Offsets::SignalEventParam);
|
||||
char *guidStr = ConvertGuidToString(names.guid);
|
||||
SignalCombatGuid(game::UNIT_COMBAT_GUID, kCombatGuidFormat,
|
||||
guidStr,
|
||||
action,
|
||||
damage,
|
||||
school,
|
||||
hitInfo,
|
||||
names.pet ? 1 : 0,
|
||||
names.partyIndex,
|
||||
names.raidIndex);
|
||||
delete[] guidStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Created by pmacc on 2/19/2026.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "main.hpp"
|
||||
|
||||
namespace Nampower {
|
||||
constexpr const char *UNIT_PLAYER = "player";
|
||||
constexpr const char *UNIT_PET = "pet";
|
||||
constexpr const char *UNIT_TARGET = "target";
|
||||
constexpr const char *UNIT_NPC = "npc";
|
||||
constexpr const char *UNIT_MOUSEOVER = "mouseover";
|
||||
|
||||
constexpr const char *UNIT_PARTY[4] = {
|
||||
"party1", "party2", "party3", "party4"
|
||||
};
|
||||
constexpr const char *UNIT_PARTYPET[4] = {
|
||||
"partypet1", "partypet2", "partypet3", "partypet4"
|
||||
};
|
||||
constexpr const char *UNIT_RAID[40] = {
|
||||
"raid1", "raid2", "raid3", "raid4", "raid5",
|
||||
"raid6", "raid7", "raid8", "raid9", "raid10",
|
||||
"raid11", "raid12", "raid13", "raid14", "raid15",
|
||||
"raid16", "raid17", "raid18", "raid19", "raid20",
|
||||
"raid21", "raid22", "raid23", "raid24", "raid25",
|
||||
"raid26", "raid27", "raid28", "raid29", "raid30",
|
||||
"raid31", "raid32", "raid33", "raid34", "raid35",
|
||||
"raid36", "raid37", "raid38", "raid39", "raid40"
|
||||
};
|
||||
constexpr const char *UNIT_RAIDPET[40] = {
|
||||
"raidpet1", "raidpet2", "raidpet3", "raidpet4", "raidpet5",
|
||||
"raidpet6", "raidpet7", "raidpet8", "raidpet9", "raidpet10",
|
||||
"raidpet11", "raidpet12", "raidpet13", "raidpet14", "raidpet15",
|
||||
"raidpet16", "raidpet17", "raidpet18", "raidpet19", "raidpet20",
|
||||
"raidpet21", "raidpet22", "raidpet23", "raidpet24", "raidpet25",
|
||||
"raidpet26", "raidpet27", "raidpet28", "raidpet29", "raidpet30",
|
||||
"raidpet31", "raidpet32", "raidpet33", "raidpet34", "raidpet35",
|
||||
"raidpet36", "raidpet37", "raidpet38", "raidpet39", "raidpet40"
|
||||
};
|
||||
|
||||
struct UnitNames {
|
||||
uint64_t guid = 0;
|
||||
bool player = false;
|
||||
bool pet = false;
|
||||
bool target = false;
|
||||
bool npc = false;
|
||||
bool mouseover = false;
|
||||
int partyIndex = 0; // 1-4 (party has no 5th slot), 0 = none
|
||||
int partyPetIndex = 0; // 1-4, 0 = none
|
||||
int raidIndex = 0; // 1-40, 0 = none
|
||||
int raidPetIndex = 0; // 1-40, 0 = none
|
||||
|
||||
bool any() const {
|
||||
return player || pet || target || npc || mouseover ||
|
||||
partyIndex > 0 || partyPetIndex > 0 ||
|
||||
raidIndex > 0 || raidPetIndex > 0;
|
||||
}
|
||||
};
|
||||
|
||||
using CGGameUI_GetPartyMemberT = uint64_t (__fastcall *)(uint32_t index);
|
||||
using CGGameUI_GetPartyMemberPetT = uint64_t (__fastcall *)(uint32_t index);
|
||||
using CGGameUI_GetRaidMemberT = uint64_t (__fastcall *)(uint32_t index);
|
||||
using CGGameUI_GetRaidMemberPetT = uint64_t (__fastcall *)(uint32_t index);
|
||||
|
||||
using CGGameUI_ShowCombatFeedbackT = void (__fastcall *)(uint64_t *guid, const char *action, int damage, int school, int hitInfo);
|
||||
using SignalEventParamUnitCombatT = int (__cdecl *)(uint32_t eventCode, const char *format, const char *unit, const char *action, const char *critIndicator, int damage, int school);
|
||||
using SignalEventParamGuidT = int (__cdecl *)(uint32_t eventCode, const char *format, const char *guid, int isPlayer, int isTarget, int isMouseover, int isPet, int partyIndex, int raidIndex);
|
||||
using SignalEventParamUnitCombatGuidT = int (__cdecl *)(uint32_t eventCode, const char *format, const char *guid, const char *action, int damage, int school, int hitInfo, int isPet, int partyIndex, int raidIndex);
|
||||
|
||||
UnitNames GetUnitNames(uint64_t *guid);
|
||||
void FireUnitGuidEvent(const UnitNames &names, uint32_t guidEventCode);
|
||||
void TriggerUnitEvents(const UnitNames &names, uint32_t eventCode);
|
||||
|
||||
void SendUnitSignalHook(hadesmem::PatchDetourBase *detour, uint64_t *guid, uint32_t eventCode);
|
||||
void CGGameUI_ShowCombatFeedbackHook(hadesmem::PatchDetourBase *detour, uint64_t *guid, const char *action, int damage, int school, int hitInfo);
|
||||
}
|
||||
Reference in New Issue
Block a user