fix luaSlot not handling hidden buffs on BUFF/DEBUFF event, add IsAuraHidden function

This commit is contained in:
avitasia
2026-02-27 11:27:07 -08:00
parent ccea6956cd
commit 18045d602f
10 changed files with 8688 additions and 8516 deletions
+125 -35
View File
@@ -9,8 +9,115 @@
#include "helper.hpp"
namespace Nampower {
constexpr uint32_t SPELL_AURA_TRACK_CREATURES = 44;
constexpr uint32_t SPELL_AURA_TRACK_RESOURCES = 45;
constexpr uint32_t SPELL_AURA_TRACK_STEALTHED = 151;
uint32_t gAuraExpirationTime[MAX_AURA_SLOTS] = {};
static bool IsTrackingAura(const game::SpellRec *spellRec) {
if (!spellRec) {
return false;
}
for (uint32_t auraName: spellRec->EffectApplyAuraName) {
if (auraName == SPELL_AURA_TRACK_CREATURES ||
auraName == SPELL_AURA_TRACK_RESOURCES ||
auraName == SPELL_AURA_TRACK_STEALTHED) {
return true;
}
}
return false;
}
bool IsAuraHiddenForLua(uint32_t spellId) {
if (spellId == 0) {
return true;
}
auto const *spellRec = game::GetSpellInfo(spellId);
if (!spellRec) {
return false;
}
if ((spellRec->Attributes & game::SPELL_ATTR_HIDDEN_CLIENTSIDE) != 0) {
return true;
}
if ((spellRec->AttributesEx & game::SPELL_ATTR_EX_NO_AURA_ICON) != 0) {
return true;
}
if (IsTrackingAura(spellRec)) {
return true;
}
return false;
}
static bool IsAuraVisibleInPackedFlags(const game::UnitFields *unitFields, uint32_t slot, uint8_t *packedOut = nullptr,
uint8_t *nibbleOut = nullptr) {
auto *auraFlags = reinterpret_cast<const uint8_t *>(unitFields->auraFlags);
uint8_t packedSlotFlags = auraFlags[slot / 2];
uint8_t nibble = (slot & 1u) != 0 ? (packedSlotFlags >> 4) : packedSlotFlags;
if (packedOut) {
*packedOut = packedSlotFlags;
}
if (nibbleOut) {
*nibbleOut = nibble;
}
return (nibble & 0x0Eu) != 0; // (flags & (NEGATIVE | PASSIVE | PERMANENT)) != 0
}
static bool CountsAsEmptyForLua(const game::UnitFields *unitFields, uint32_t slot) {
uint32_t spellId = unitFields->aura[slot];
if (spellId == 0) {
return true;
}
if (IsAuraHiddenForLua(spellId)) {
return true;
}
uint8_t packedSlotFlags = 0;
uint8_t nibble = 0;
bool isVisible = IsAuraVisibleInPackedFlags(unitFields, slot, &packedSlotFlags, &nibble);
return !isVisible;
}
static bool IsHiddenAura(const game::UnitFields *unitFields, uint32_t slot, uint32_t spellId, uint32_t state) {
if (spellId != 0 && IsAuraHiddenForLua(spellId)) {
return true;
}
// Removed events run after the aura was cleared, so do not use slot/flags visibility there.
if (state == 1) {
return false;
}
return CountsAsEmptyForLua(unitFields, slot);
}
static int CalculateLuaUnitSlotFromAuraSlot(const game::UnitFields *unitFields, uint32_t slot) {
bool isBuff = slot < 32;
int luaUnitSlot = static_cast<int>(slot) + 1;
if (!isBuff) {
luaUnitSlot -= 32;
}
uint32_t firstSlot = isBuff ? 0 : 32;
int emptyCount = 0;
for (uint32_t i = firstSlot; i < slot; ++i) {
if (CountsAsEmptyForLua(unitFields, i)) {
++emptyCount;
}
}
return luaUnitSlot - emptyCount;
}
// Helper function to trigger buff/debuff events
// state: 0 = newly added, 1 = newly removed, 2 = modified (stack change)
void TriggerAuraEvent(uintptr_t *unit, uint32_t slot, uint32_t spellId, bool wasAdded, uint32_t state) {
@@ -23,26 +130,16 @@ namespace Nampower {
// Trigger the appropriate event based on buff/debuff, add/remove, and self/other
game::Events eventToTrigger;
int luaUnitSlot = slot + 1; // lua uses 1-based indexing for slots. This is the slot for UnitBuff/UnitDebuff
auto *unitFields = *reinterpret_cast<game::UnitFields **>(unit + 68);
auto auras = unitFields->aura;
auto auraLevels = unitFields->auraLevels;
auto auraStacks = unitFields->auraApplications;
int luaUnitSlot = CalculateLuaUnitSlotFromAuraSlot(unitFields, slot);
auto luaStacks = (state == 1) ? auraStacks[slot] : auraStacks[slot] + 1; // for some crazy reason the stack count is 0 indexed but also 0 instead of -1 when removed
auto luaStacks = (state == 1) ? auraStacks[slot] : auraStacks[slot] + 1;
// for some crazy reason the stack count is 0 indexed but also 0 instead of -1 when removed
if (isBuff) {
// count empty buff slots before slot to adjust luaSlot for actual buff/debuff index
int emptyCount = 0;
for (uint32_t i = 0; i < slot; ++i) {
if (auras[i] == 0) {
emptyCount++;
}
}
luaUnitSlot = luaUnitSlot - emptyCount;
if (wasAdded) {
// Buff added
eventToTrigger = isSelf ? game::BUFF_ADDED_SELF : game::BUFF_ADDED_OTHER;
@@ -51,17 +148,6 @@ namespace Nampower {
eventToTrigger = isSelf ? game::BUFF_REMOVED_SELF : game::BUFF_REMOVED_OTHER;
}
} else {
luaUnitSlot = luaUnitSlot - 32; // adjust slot for debuffs
// count empty debuff slots before slot to adjust luaSlot for actual buff/debuff index
int emptyCount = 0;
for (uint32_t i = 32; i < slot; ++i) {
if (auras[i] == 0) {
emptyCount++;
}
}
luaUnitSlot = luaUnitSlot - emptyCount;
if (wasAdded) {
// Debuff added
eventToTrigger = isSelf ? game::DEBUFF_ADDED_SELF : game::DEBUFF_ADDED_OTHER;
@@ -71,20 +157,23 @@ namespace Nampower {
}
}
bool isHidden = IsHiddenAura(unitFields, slot, spellId, state);
static char format[] = "%s%d%d%d%d%d%d";
char *guidStr = ConvertGuidToString(isSelf ? playerGuid : unitGuid);
int eventLuaSlot = isHidden ? 0 : luaUnitSlot;
// Trigger the event with spellId as parameter
((int (__cdecl *)(int, char *, char *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)) Offsets::SignalEventParam)(
eventToTrigger,
format,
guidStr,
luaUnitSlot,
spellId,
luaStacks,
auraLevels[slot],
slot,
state);
((int (__cdecl *)(int, char *, char *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t))
Offsets::SignalEventParam)(
eventToTrigger,
format,
guidStr,
eventLuaSlot,
spellId,
luaStacks,
auraLevels[slot],
slot,
state);
FreeGuidString(guidStr);
}
@@ -109,7 +198,8 @@ namespace Nampower {
TriggerAuraEvent(unit, slot, spellId, true, 0);
}
void CGUnit_C_OnAuraStacksChangedHook(hadesmem::PatchDetourBase *detour, uintptr_t *unit, void *dummy_edx, int slot, uint8_t stackCount) {
void CGUnit_C_OnAuraStacksChangedHook(hadesmem::PatchDetourBase *detour, uintptr_t *unit, void *dummy_edx, int slot,
uint8_t stackCount) {
auto const onAuraStacksChanged = detour->GetTrampolineT<CGUnit_C_OnAuraStacksChangedT>();
// Get spell ID from the aura slot
+2
View File
@@ -18,6 +18,8 @@ namespace Nampower {
using UnitCombatLogUnitDeadT = void (__fastcall *)(uint64_t guid);
using CGBuffBar_UpdateDurationT = void (__fastcall *)(uint8_t auraSlot, int durationMs);
bool IsAuraHiddenForLua(uint32_t spellId);
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_OnAuraStacksChangedHook(hadesmem::PatchDetourBase *detour, uintptr_t *unit, void *dummy_edx, int slot, uint8_t stackCount);
+3
View File
@@ -1837,6 +1837,9 @@ namespace Nampower {
char unitGUID[] = "UnitGUID";
RegisterLuaFunction(unitGUID, reinterpret_cast<uintptr_t *>(Script_UnitGUID));
char isAuraHidden[] = "IsAuraHidden";
RegisterLuaFunction(isAuraHidden, reinterpret_cast<uintptr_t *>(Script_IsAuraHidden));
}
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 = 41;
constexpr uint32_t MINOR_VERSION = 42;
constexpr uint32_t PATCH_VERSION = 0;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
+13
View File
@@ -945,6 +945,19 @@ namespace Nampower {
return 1;
}
uint32_t Script_IsAuraHidden(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: IsAuraHidden(spellId)");
return 0;
}
auto const spellId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
lua_pushnumber(luaState, IsAuraHiddenForLua(spellId) ? 1 : 0);
return 1;
}
uint64_t GetGUIDFromNameHook(hadesmem::PatchDetourBase *detour, const char *nameStr) {
auto const original = detour->GetTrampolineT<GetGUIDFromNameT>();
+1
View File
@@ -39,6 +39,7 @@ namespace Nampower {
uint32_t Script_SetMouseoverUnit(uintptr_t *luaState);
uint32_t Script_UnitGUID(uintptr_t *luaState);
uint32_t Script_IsAuraHidden(uintptr_t *luaState);
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint64_t GetGUIDFromNameHook(hadesmem::PatchDetourBase *detour, const char *nameStr);