mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-03 04:00:01 +00:00
add fine grained control of UNIT_* events and new UNIT_*_GUID versions of key events
This commit is contained in:
@@ -43,6 +43,7 @@ For custom Lua functions, see [SCRIPTS.md](SCRIPTS.md). For general usage inform
|
||||
- [SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_OTHER, and SPELL_ENERGIZE_ON_SELF](#spell_energize_by_self-spell_energize_by_other-and-spell_energize_on_self)
|
||||
- [SPELL_MISS_SELF and SPELL_MISS_OTHER](#spell_miss_self-and-spell_miss_other)
|
||||
- [UNIT_DIED](#unit_died)
|
||||
- [Unit GUID Events](#unit-guid-events)
|
||||
|
||||
---
|
||||
|
||||
@@ -571,3 +572,108 @@ frame:RegisterEvent("UNIT_DIED", function(guid)
|
||||
DEFAULT_CHAT_FRAME:AddMessage("Unit died: " .. guid)
|
||||
end)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unit GUID Events
|
||||
|
||||
These events fire once per unit state change, identified by GUID rather than unit token. Unlike the standard WoW unit events (e.g. `UNIT_HEALTH`) which fire per registered token (player, target, party1, etc.), each GUID event fires exactly once and carries flags indicating which tokens the unit currently matches.
|
||||
|
||||
All GUID events share the same parameter format:
|
||||
1. string guid - unit guid like `"0xF5300000000000A5"`
|
||||
2. int isPlayer - 1 if the unit is the active player, 0 otherwise
|
||||
3. int isTarget - 1 if the unit is the current locked target, 0 otherwise
|
||||
4. int isMouseover - 1 if the unit is the current mouseover, 0 otherwise
|
||||
5. int isPet - 1 if the unit is the active player's pet, 0 otherwise
|
||||
6. int partyIndex - party slot (1–4) if the unit is a party member, 0 otherwise
|
||||
7. int raidIndex - raid slot (1–40) if the unit is a raid member, 0 otherwise
|
||||
|
||||
Events (8-parameter format):
|
||||
```
|
||||
UNIT_HEALTH_GUID -- unit health changed
|
||||
UNIT_MANA_GUID -- unit mana changed
|
||||
UNIT_RAGE_GUID -- unit rage changed
|
||||
UNIT_ENERGY_GUID -- unit energy changed
|
||||
UNIT_PET_GUID -- unit pet assignment changed
|
||||
UNIT_FLAGS_GUID -- unit flags changed
|
||||
UNIT_AURA_GUID -- unit aura set changed
|
||||
UNIT_DYNAMIC_FLAGS_GUID -- unit dynamic flags changed
|
||||
UNIT_NAME_UPDATE_GUID -- unit name changed
|
||||
UNIT_PORTRAIT_UPDATE_GUID -- unit portrait changed
|
||||
UNIT_MODEL_CHANGED_GUID -- unit model changed
|
||||
UNIT_INVENTORY_CHANGED_GUID -- unit inventory changed
|
||||
PLAYER_GUILD_UPDATE_GUID -- unit guild changed
|
||||
```
|
||||
|
||||
Example:
|
||||
```lua
|
||||
local function onUnitGuid(event, guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex)
|
||||
local tokens = {}
|
||||
if isPlayer == 1 then table.insert(tokens, "player") end
|
||||
if isTarget == 1 then table.insert(tokens, "target") end
|
||||
if isMouseover == 1 then table.insert(tokens, "mouseover") end
|
||||
if isPet == 1 then table.insert(tokens, "pet") end
|
||||
if partyIndex > 0 then table.insert(tokens, "party"..partyIndex) end
|
||||
if raidIndex > 0 then table.insert(tokens, "raid"..raidIndex) end
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("[%s] %s (%s)", event, guid, table.concat(tokens, ", ")))
|
||||
end
|
||||
|
||||
frame:RegisterEvent("UNIT_HEALTH_GUID", function(...) onUnitGuid("UNIT_HEALTH_GUID", ...) end)
|
||||
frame:RegisterEvent("UNIT_MANA_GUID", function(...) onUnitGuid("UNIT_MANA_GUID", ...) end)
|
||||
```
|
||||
|
||||
### UNIT_COMBAT_GUID
|
||||
Fires once per combat feedback event, identified by GUID. Unlike the other GUID events, this carries the full combat detail (action, damage, school, hitInfo).
|
||||
|
||||
Parameters:
|
||||
1. string guid - unit guid like `"0xF5300000000000A5"`
|
||||
2. string action - action string (`"WOUND"`, `"MISS"`, `"DODGE"`, `"PARRY"`, `"BLOCK"`, `"EVADE"`, `"IMMUNE"`, `"REFLECT"`, `"ABSORB"`, `"INTERRUPT"`)
|
||||
3. int damage - raw damage amount (0 for non-damaging outcomes like dodge/miss)
|
||||
4. int school - damage school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane)
|
||||
5. int hitInfo - bitfield (see below)
|
||||
6. int isPet - 1 if the unit is the active player's pet, 0 otherwise
|
||||
7. int partyIndex - party slot (1–4) if the unit is a party member, 0 otherwise
|
||||
8. int raidIndex - raid slot (1–40) if the unit is a raid member, 0 otherwise
|
||||
|
||||
#### HitInfo Flags (UNIT_COMBAT_GUID)
|
||||
The `hitInfo` field is a 16-bit value from `CGGameUI::ShowCombatFeedback`:
|
||||
```
|
||||
-- Low byte (hitInfo & 0xFF)
|
||||
COMBATHIT_MISS = 0x10 -- Miss
|
||||
COMBATHIT_ABSORB = 0x20 -- Absorbed (damage == 0)
|
||||
COMBATHIT_RESIST = 0x40 -- Resisted (damage == 0)
|
||||
COMBATHIT_CRITICAL = 0x80 -- Critical hit
|
||||
|
||||
-- High byte ((hitInfo >> 8) & 0xFF)
|
||||
COMBATHIT_BLOCK = 0x08 -- Blocked (damage == 0)
|
||||
COMBATHIT_GLANCING = 0x40 -- Glancing blow
|
||||
COMBATHIT_CRUSHING = 0x80 -- Crushing blow
|
||||
```
|
||||
|
||||
Example:
|
||||
```lua
|
||||
local function onUnitCombatGuid(guid, action, damage, school, hitInfo, isPet, partyIndex, raidIndex)
|
||||
local lo = bit.band(hitInfo, 0xFF)
|
||||
local hi = bit.rshift(hitInfo, 8)
|
||||
|
||||
local hitType = "Normal"
|
||||
if damage > 0 then
|
||||
if bit.band(lo, 0x80) ~= 0 then hitType = "Critical"
|
||||
elseif bit.band(hi, 0x40) ~= 0 then hitType = "Glancing"
|
||||
elseif bit.band(hi, 0x80) ~= 0 then hitType = "Crushing"
|
||||
end
|
||||
else
|
||||
if bit.band(lo, 0x20) ~= 0 then hitType = "Absorb"
|
||||
elseif bit.band(hi, 0x08) ~= 0 then hitType = "Block"
|
||||
elseif bit.band(lo, 0x40) ~= 0 then hitType = "Resist"
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format(
|
||||
"[UNIT_COMBAT_GUID] %s %s %d (%s) school=%d",
|
||||
guid, action, damage, hitType, school
|
||||
))
|
||||
end
|
||||
|
||||
frame:RegisterEvent("UNIT_COMBAT_GUID", onUnitCombatGuid)
|
||||
```
|
||||
|
||||
@@ -107,6 +107,18 @@ SET NP_TargetingQueueWindowMs "1000"
|
||||
|
||||
- `NP_EnableSpellEnergizeEvents` - Whether to enable SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_OTHER, and SPELL_ENERGIZE_ON_SELF events. 0 to disable, 1 to enable. Default is 0.
|
||||
|
||||
- `NP_EnableUnitEventsPet` - Whether to fire unit state events for the `pet` and `partypet1`–`partypet4` unit tokens. 0 to disable, 1 to enable. Default is 1.
|
||||
|
||||
- `NP_EnableUnitEventsParty` - Whether to fire unit state events for the `party1`–`party4` unit tokens. 0 to disable, 1 to enable. Default is 1.
|
||||
|
||||
- `NP_EnableUnitEventsRaid` - Whether to fire unit state events for the `raid1`–`raid40` unit tokens. 0 to disable, 1 to enable. Default is 1.
|
||||
|
||||
- `NP_EnableUnitEventsMouseover` - Whether to fire unit state events for the `mouseover` unit token. 0 to disable, 1 to enable. Default is 1.
|
||||
|
||||
- `NP_EnableUnitEventsGuid` - Whether to fire unit state events (UNIT_HEALTH, UNIT_MANA, UNIT_AURA, etc.) using the raw GUID string as the unit token, in addition to the standard named tokens. This mimics the SuperWoW behaviour that passes GUIDs directly into unit events so addons can track any unit by GUID. Because these events fire for every unit the client tracks — not just player, target, party, and raid members — they can generate significant event spam in busy environments (raids, battlegrounds, crowded zones). Many older addons such as PFUI were written expecting UNIT_HEALTH, UNIT_MANA, and UNIT_AURA to only fire for the standard named tokens like 'player', 'target', 'party1', 'raid1', etc and can cause performance issues when they act on events for every GUID around you. The ideal long-term solution is to update the small amount of addons that rely on GUID-based unit events — such as Automarker and Cursive — to use the new dedicated `UNIT_HEALTH_GUID`, `UNIT_MANA_GUID`, `UNIT_AURA_GUID`, etc. events (see [EVENTS.md](EVENTS.md#unit-guid-events)) so that `NP_EnableUnitEventsGuid` can be safely set to `0`. 0 to disable, 1 to enable. Default is 1.
|
||||
|
||||
- `NP_EnableUnitEventsGuidFiltering` - When enabled, suppresses high-frequency raw GUID unit events that would otherwise cause spam: events below `UNIT_COMBAT` (182) such as `UNIT_HEALTH`, `UNIT_MANA`, and `UNIT_AURA`, as well as `UNIT_NAME_UPDATE` (183), `UNIT_PORTRAIT_UPDATE` (184), `UNIT_INVENTORY_CHANGED` (186), and `PLAYER_GUILD_UPDATE` (345). These events all have dedicated `_GUID` variants (e.g. `UNIT_HEALTH_GUID`) that fire once per unit instead of once per token. Only relevant when `NP_EnableUnitEventsGuid` is also enabled. 0 to disable, 1 to enable. Default is 1.
|
||||
|
||||
- `NP_ChannelLatencyReductionPercentage` - The percentage of your latency to subtract from the end of a channel duration to optimize cast time while hopefully not losing any ticks (more explanation below). Default is 75.
|
||||
|
||||
- `NP_NameplateDistance` - The distance in yards to display nameplates. Defaults to whatever was set by the game or vanilla tweaks.
|
||||
|
||||
+12880
-12442
File diff suppressed because it is too large
Load Diff
@@ -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