remove accidental debug log

add auraSlot to buff/debuff events
add buff/debuff duration events
add GetPlayerAuraDuration
This commit is contained in:
avitasia
2026-02-09 12:24:32 -08:00
parent 453e656e3e
commit 983ea93c71
12 changed files with 12611 additions and 12309 deletions
+35 -3
View File
@@ -14,6 +14,7 @@ For custom Lua functions, see [SCRIPTS.md](SCRIPTS.md). For general usage inform
- [SPELL_CHANNEL_START and SPELL_CHANNEL_UPDATE](#spell_channel_start-and-spell_channel_update)
- [SPELL_DAMAGE_EVENT_SELF and SPELL_DAMAGE_EVENT_OTHER](#spell_damage_event_self-and-spell_damage_event_other)
- [Buff/Debuff Events](#buffdebuff-events)
- [BUFF_UPDATE_DURATION_SELF and DEBUFF_UPDATE_DURATION_SELF](#buff_update_duration_self-and-debuff_update_duration_self)
- [AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER](#aura_cast_on_self-and-aura_cast_on_other)
- [AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER](#auto_attack_self-and-auto_attack_other)
- [SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_OTHER, and SPELL_HEAL_ON_SELF](#spell_heal_by_self-spell_heal_by_other-and-spell_heal_on_self)
@@ -228,17 +229,18 @@ DEBUFF_REMOVED_OTHER
All eight events pass the same parameters:
1. string guid - unit guid like "0xF5300000000000A5"
2. int slot - 1-based Lua slot index for the buff/debuff (skips empty slots to match UnitBuff/UnitDebuff ordering)
2. int luaSlot - 1-based Lua slot index for the buff/debuff (skips empty slots to match UnitBuff/UnitDebuff ordering). This is the slot you would pass to UnitBuff/UnitDebuff.
3. int spellId
4. int stackCount - current stack count for the aura (1 for a new aura; 0 when fully removed)
5. int auraLevel - caster level for the aura from UnitFields.auraLevels (uint8 per slot, 48 entries)
6. int auraSlot - the raw 0-based aura slot index (0-31 for buffs, 32-47 for debuffs). This is the raw internal slot, not the Lua slot. Consistent with unit data fields, GetPlayerAuraDuration, and BUFF/DEBUFF_UPDATE_DURATION_SELF events.
Buff stack gains also fire the appropriate *_ADDED_* events.
Example:
```
local function onAuraEvent(eventName, guid, slot, spellId, stacks, auraLevel)
DEFAULT_CHAT_FRAME:AddMessage(string.format("[%s] %s slot=%d spell=%d stacks=%d level=%d", eventName, guid, slot, spellId, stacks, auraLevel))
local function onAuraEvent(eventName, guid, luaSlot, spellId, stacks, auraLevel, auraSlot)
DEFAULT_CHAT_FRAME:AddMessage(string.format("[%s] %s luaSlot=%d spell=%d stacks=%d level=%d auraSlot=%d", eventName, guid, luaSlot, spellId, stacks, auraLevel, auraSlot))
end
for _, eventName in ipairs({"BUFF_ADDED_SELF", "BUFF_REMOVED_SELF", "DEBUFF_ADDED_OTHER", "DEBUFF_REMOVED_OTHER"}) do
@@ -246,6 +248,36 @@ for _, eventName in ipairs({"BUFF_ADDED_SELF", "BUFF_REMOVED_SELF", "DEBUFF_ADDE
end
```
### BUFF_UPDATE_DURATION_SELF and DEBUFF_UPDATE_DURATION_SELF
Fire when the client updates the duration of a buff or debuff on the active player. This happens when the server refreshes an aura's duration (e.g., reapplying a buff that already exists). Only fires for the active player's own auras.
Events:
```
BUFF_UPDATE_DURATION_SELF -- aura slot 0-31
DEBUFF_UPDATE_DURATION_SELF -- aura slot 32-47
```
Parameters:
1. int auraSlot - the raw 0-based aura slot index (0-31 for buffs, 32-47 for debuffs). This is not the slot used by GetPlayerBuff that has its own sorting, this is the raw aura slot index which is consistent with the unit data fields and the internal aura storage.
2. int spellId - the spell ID occupying this aura slot
3. int durationMs - the updated duration in milliseconds
4. int expirationTimeMs - the calculated expiration time (GetWowTimeMs() + durationMs), or 0 if no duration
Example:
```lua
local function onDurationUpdate(auraSlot, spellId, durationMs, expirationTimeMs)
local isBuff = auraSlot < 32
local type = isBuff and "Buff" or "Debuff"
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"[%s] slot=%d spell=%d duration=%dms expires=%d",
type, auraSlot, spellId, durationMs, expirationTimeMs
))
end
frame:RegisterEvent("BUFF_UPDATE_DURATION_SELF", onDurationUpdate)
frame:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF", onDurationUpdate)
```
### AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER
Fire when a spell cast applies an aura. "Self" covers casts that land on the active player (including cases where the active player is the caster with no explicit target); "Other" covers all other targets.
+2 -1
View File
@@ -152,6 +152,7 @@ This includes functions for:
- Cast information (GetCastInfo, GetCurrentCastingInfo)
- Cooldown tracking (GetSpellIdCooldown, GetItemIdCooldown), including item metadata on cooldown detail tables
- Inventory helpers (GetTrinketCooldown, GetTrinkets, GetAmmo)
- Aura duration tracking (GetPlayerAuraDuration)
- Spell lookups and utilities
Cooldown detail tables now also expose `itemId`, `itemHasActiveSpell`, and `itemActiveSpellId` alongside the existing per-category timing data.
@@ -168,7 +169,7 @@ Available events:
- SPELL_START_SELF and SPELL_START_OTHER - Triggered by the server to notify that a spell with a cast time has begun.
- SPELL_GO_SELF and SPELL_GO_OTHER - Triggered by the server to indicate a spell completed casting.
- SPELL_DAMAGE_EVENT_SELF and SPELL_DAMAGE_EVENT_OTHER - Combat damage events
- Buff/Debuff Events (BUFF_ADDED_SELF, BUFF_REMOVED_SELF, etc.)
- Buff/Debuff Events (BUFF_ADDED_SELF, BUFF_REMOVED_SELF, BUFF_UPDATE_DURATION_SELF, etc.)
- AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER - Aura application events (fires once per aura effect per target; "Self" fires when the aura lands on the active player, including self-cast with no explicit target; includes aura metadata + amplitude/misc + aura cap bitfield for buff/debuff slots). Handles single-target and multi-target (AOE) spells. Some auras don't have spell effects and won't trigger this; use BUFF/DEBUFF gains for those. Set `NP_EnableAuraCastEvents=1` to enable. See EVENTS.md for details on multi-target behavior and known limitations.
- AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER - Auto attack round events (fires when auto attack hits are processed; includes damage, hit info, victim state, absorb/resist/block amounts). Set `NP_EnableAutoAttackEvents=1` to enable. See EVENTS.md for details.
- SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_OTHER, and SPELL_HEAL_ON_SELF - Spell healing events (fires when spell heals are processed; includes target, caster, spell ID, amount, critical flag, and periodic flag). Set `NP_EnableSpellHealEvents=1` to enable. See EVENTS.md for details.
+31
View File
@@ -29,6 +29,7 @@ For installation, configuration, and general usage information, see the main [RE
- [GetItemLevel](#getitemlevelitemid)
- [GetItemIconTexture](#getitemicontexturedisplayinfoid)
- [GetSpellIconTexture](#getspellicontexturespelliconid)
- [GetPlayerAuraDuration](#getplayerauradurationauraslot)
- [Spell Casting and Queuing](#spell-casting-and-queuing)
- [QueueSpellByName](#queuespellbynamespellname)
- [CastSpellByNameNoQueue](#castspellbynamenoqueuespellname)
@@ -463,6 +464,36 @@ if ammoId and count < 200 then
end
```
#### GetPlayerAuraDuration(auraSlot)
Returns the spell ID, remaining duration, and expiration time for a given aura slot on the active player.
**Parameters:**
- `auraSlot` (number): The 0-based aura slot index (0-31 for buffs, 32-47 for debuffs). This is not the slot used by GetPlayerBuff which has its own sorting, this is the raw aura slot index which is consistent with the unit data fields and the internal aura storage.
**Returns:**
- If the slot is out of range or the player unit is unavailable: returns `nil`
- Otherwise returns three values:
- `spellId` (number): The spell ID occupying the aura slot
- `remainingDurationMs` (number): Remaining duration in milliseconds (0 if expired or no duration)
- `expirationTimeMs` (number): The expiration timestamp from GetWowTimeMs() (0 if no duration)
**Examples:**
```lua
-- Check remaining duration on buff slot 0
local spellId, remainingDurationMs, expirationTimeMs = GetPlayerAuraDuration(0)
if spellId and spellId > 0 then
print("Spell: " .. spellId .. " remaining: " .. remainingDurationMs .. "ms")
end
-- Check all debuff slots (32-47)
for slot = 32, 47 do
local spellId, remainingDurationMs, expirationTimeMs = GetPlayerAuraDuration(slot)
if spellId and spellId > 0 then
print("Debuff slot " .. slot .. ": spell=" .. spellId .. " duration=" .. remainingDurationMs .. "ms")
end
end
```
#### GetSpellRec(spellId, [copy])
Returns a Lua table reference containing all fields for the spell's `SpellRec` record (including localized `name` and `rank`). Returns nil if the spell cannot be found.
+12442 -12299
View File
File diff suppressed because it is too large Load Diff
+40 -3
View File
@@ -8,6 +8,8 @@
#include "game.hpp"
namespace Nampower {
uint32_t gAuraExpirationTime[MAX_AURA_SLOTS] = {};
// Helper function to trigger buff/debuff events
void TriggerAuraEvent(uintptr_t *unit, uint32_t slot, uint32_t spellId, bool wasAdded) {
// Determine if the unit is the active player (self) or another unit (other)
@@ -71,7 +73,7 @@ namespace Nampower {
}
}
static char format[] = "%s%d%d%d%d";
static char format[] = "%s%d%d%d%d%d";
char *guidStr = new char[21]; // 2 for 0x prefix, 18 for the number, and 1 for '\0'
if (isSelf) {
std::snprintf(guidStr, 21, "0x%016llX", static_cast<unsigned long long>(playerGuid));
@@ -80,14 +82,15 @@ namespace Nampower {
}
// Trigger the event with spellId as parameter
((int (__cdecl *)(int, char *, char *, uint32_t, uint32_t, uint32_t, uint32_t)) Offsets::SignalEventParam)(
((int (__cdecl *)(int, char *, char *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)) Offsets::SignalEventParam)(
eventToTrigger,
format,
guidStr,
luaUnitSlot,
spellId,
luaStacks,
auraLevels[slot]);
auraLevels[slot],
slot);
delete[] guidStr;
}
@@ -127,6 +130,40 @@ namespace Nampower {
TriggerAuraEvent(unit, slot, spellId, true);
}
void CGBuffBar_UpdateDurationHook(hadesmem::PatchDetourBase *detour, uint8_t auraSlot, int durationMs) {
auto const updateDuration = detour->GetTrampolineT<CGBuffBar_UpdateDurationT>();
// Store expiration time indexed by aura slot
if (auraSlot < MAX_AURA_SLOTS) {
auto now = static_cast<uint32_t>(GetWowTimeMs() & 0xFFFFFFFF);
uint32_t expirationTime = durationMs > 0 ? now + durationMs : 0;
gAuraExpirationTime[auraSlot] = expirationTime;
// Get player unit to read spellId from aura fields
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto *playerUnit = game::GetObjectPtr(playerGuid);
if (playerUnit) {
auto *unitFields = *reinterpret_cast<game::UnitFields **>(playerUnit + 68);
uint32_t spellId = unitFields->aura[auraSlot];
bool isBuff = auraSlot < 32;
game::Events eventToTrigger = isBuff ? game::BUFF_UPDATE_DURATION_SELF : game::DEBUFF_UPDATE_DURATION_SELF;
static char format[] = "%d%d%d%d";
((int (__cdecl *)(int, char *, uint32_t, uint32_t, int, uint32_t)) Offsets::SignalEventParam)(
eventToTrigger,
format,
auraSlot,
spellId,
durationMs,
expirationTime);
}
}
// Call the original function
updateDuration(auraSlot, durationMs);
}
void UnitCombatLogUnitDeadHook(hadesmem::PatchDetourBase *detour, uint64_t guid) {
auto const unitCombatLogUnitDead = detour->GetTrampolineT<UnitCombatLogUnitDeadT>();
+6
View File
@@ -8,13 +8,19 @@
#include "main.hpp"
namespace Nampower {
constexpr int MAX_AURA_SLOTS = 48;
extern uint32_t gAuraExpirationTime[MAX_AURA_SLOTS];
using CGUnit_C_OnAuraRemovedT = void (__fastcall *)(uintptr_t *unit, void *dummy_edx,uint32_t slot, uint32_t spellId);
using CGUnit_C_OnAuraAddedT = void (__fastcall *)(uintptr_t *unit, void *dummy_edx,uint32_t slot, uint32_t spellId);
using CGUnit_C_OnAuraAddedStackT = void (__fastcall *)(uintptr_t *unit, int slot);
using UnitCombatLogUnitDeadT = void (__fastcall *)(uint64_t guid);
using CGBuffBar_UpdateDurationT = void (__fastcall *)(uint8_t auraSlot, int durationMs);
void CGUnit_C_OnAuraRemovedHook(hadesmem::PatchDetourBase *detour, uintptr_t *unit, void *dummy_edx, uint32_t slot, uint32_t spellId);
void CGUnit_C_OnAuraAddedHook(hadesmem::PatchDetourBase *detour, uintptr_t *unit, void *dummy_edx, uint32_t slot, uint32_t spellId);
void CGUnit_C_OnAuraAddedStackHook(hadesmem::PatchDetourBase *detour, uintptr_t *unit, int slot);
void UnitCombatLogUnitDeadHook(hadesmem::PatchDetourBase *detour, uint64_t guid);
void CGBuffBar_UpdateDurationHook(hadesmem::PatchDetourBase *detour, uint8_t auraSlot, int durationMs);
}
+3
View File
@@ -1556,6 +1556,9 @@ namespace game {
SPELL_ENERGIZE_BY_SELF = 577,
SPELL_ENERGIZE_BY_OTHER = 578,
SPELL_ENERGIZE_ON_SELF = 579,
BUFF_UPDATE_DURATION_SELF = 580,
DEBUFF_UPDATE_DURATION_SELF = 581,
};
enum TypeMask {
+12
View File
@@ -145,6 +145,7 @@ namespace Nampower {
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraAddedT> > gCGUnit_C_OnAuraAddedDetour;
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraAddedStackT> > gCGUnit_C_OnAuraAddedStackDetour;
std::unique_ptr<hadesmem::PatchDetour<UnitCombatLogUnitDeadT> > gUnitCombatLogUnitDeadDetour;
std::unique_ptr<hadesmem::PatchDetour<CGBuffBar_UpdateDurationT> > gCGBuffBar_UpdateDurationDetour;
std::unique_ptr<hadesmem::PatchDetour<InvalidFunctionPtrCheckT> > gInvalidFunctionPtrCheckDetour;
@@ -1385,6 +1386,8 @@ namespace Nampower {
&CGUnit_C_OnAuraAddedStackHook);
gUnitCombatLogUnitDeadDetour = createHook<UnitCombatLogUnitDeadT>(process, Offsets::UnitCombatLogUnitDead,
&UnitCombatLogUnitDeadHook);
gCGBuffBar_UpdateDurationDetour = createHook<CGBuffBar_UpdateDurationT>(process, Offsets::CGBuffBar_UpdateDuration,
&CGBuffBar_UpdateDurationHook);
gAttackRoundInfo_ReadPacketDetour = createHook<AttackRoundInfo_ReadPacketT>(process, Offsets::AttackRoundInfo_ReadPacket,
&AttackRoundInfo_ReadPacketHook);
gCSimpleFrame_GetNameDetour = createHook<LuaScriptT>(process, Offsets::CSimpleFrame_GetName,
@@ -1517,6 +1520,12 @@ namespace Nampower {
char SPELL_ENERGIZE_ON_SELF[] = "SPELL_ENERGIZE_ON_SELF";
addCustomEvent(game::SPELL_ENERGIZE_ON_SELF, SPELL_ENERGIZE_ON_SELF);
char BUFF_UPDATE_DURATION_SELF[] = "BUFF_UPDATE_DURATION_SELF";
addCustomEvent(game::BUFF_UPDATE_DURATION_SELF, BUFF_UPDATE_DURATION_SELF);
char DEBUFF_UPDATE_DURATION_SELF[] = "DEBUFF_UPDATE_DURATION_SELF";
addCustomEvent(game::DEBUFF_UPDATE_DURATION_SELF, DEBUFF_UPDATE_DURATION_SELF);
}
void FrameScript_CreateEventsHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t maxEventId) {
@@ -1661,6 +1670,9 @@ namespace Nampower {
char getAmmo[] = "GetAmmo";
RegisterLuaFunction(getAmmo, reinterpret_cast<uintptr_t *>(Script_GetAmmo));
char getPlayerAuraDuration[] = "GetPlayerAuraDuration";
RegisterLuaFunction(getPlayerAuraDuration, reinterpret_cast<uintptr_t *>(Script_GetPlayerAuraDuration));
}
void load() {
+1 -1
View File
@@ -25,7 +25,7 @@ namespace Nampower {
constexpr uint32_t DISENCHANT_QUALITY_PURPLE = 0x04; // Epic
constexpr uint32_t MAJOR_VERSION = 2;
constexpr uint32_t MINOR_VERSION = 29;
constexpr uint32_t MINOR_VERSION = 30;
constexpr uint32_t PATCH_VERSION = 0;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
+36 -2
View File
@@ -9,6 +9,7 @@
#include "unit_fields.hpp"
#include "helper.hpp"
#include "lua_refs.hpp"
#include "auras.hpp"
#include <cstring>
namespace Nampower {
@@ -841,6 +842,41 @@ namespace Nampower {
return 1;
}
uint32_t Script_GetPlayerAuraDuration(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: GetPlayerAuraDuration(auraSlot)");
return 0;
}
auto auraSlot = static_cast<uint32_t>(lua_tonumber(luaState, 1));
if (auraSlot >= MAX_AURA_SLOTS) {
lua_pushnil(luaState);
return 1;
}
// Get spellId from player unit fields
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto *playerUnit = game::GetObjectPtr(playerGuid);
if (!playerUnit) {
lua_pushnil(luaState);
return 1;
}
auto *unitFields = *reinterpret_cast<game::UnitFields **>(playerUnit + 68);
uint32_t spellId = unitFields->aura[auraSlot];
uint32_t expirationTime = gAuraExpirationTime[auraSlot];
auto now = static_cast<uint32_t>(GetWowTimeMs() & 0xFFFFFFFF);
int remainingDurationMs = expirationTime > now ? static_cast<int>(expirationTime - now) : 0;
lua_pushnumber(luaState, spellId);
lua_pushnumber(luaState, remainingDurationMs);
lua_pushnumber(luaState, expirationTime);
return 3;
}
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
luaState = GetLuaStatePtr();
@@ -885,8 +921,6 @@ namespace Nampower {
uint32_t guidHigh = frameObj[0x13b];
uint64_t guid = (static_cast<uint64_t>(guidHigh) << 32) | guidLow;
DEBUG_LOG(guid);
char guidStr[21] = {0};
std::snprintf(guidStr, sizeof(guidStr), "0x%016llX", static_cast<unsigned long long>(guid));
lua_pushstring(luaState, guidStr);
+2
View File
@@ -40,5 +40,7 @@ namespace Nampower {
bool RunQueuedScript(int priority);
uint32_t Script_GetPlayerAuraDuration(uintptr_t *luaState);
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
}
+1
View File
@@ -191,6 +191,7 @@ enum class Offsets : std::uint32_t {
CastEventStringPtr = 0X00BE1A08, // unused event 540 0x21C
CGBuffBar__m_buffs = 0X00BC6040,
CGBuffBar_UpdateDuration = 0x004E4390,
GetBuffByIndex = 0X004E4430,
CGUnit_C_ClearCastingSpell = 0x0060d040,