From a90f2acd267e0cec3d2a19989d2a4979b23beca7 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 12:07:46 -0700 Subject: [PATCH] feat: populate unitAurasCache_ from SMSG_PARTY_MEMBER_STATS aura block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SMSG_PARTY_MEMBER_STATS includes a 64-bit aura presence mask + per-slot spellId/flags that was previously read and discarded. Now populate unitAurasCache_[memberGuid] from this data so party frame debuff dots show even when no dedicated SMSG_AURA_UPDATE has been received for that unit. For Classic/TBC (no flags byte), infer debuff status from dispel type — any spell with a non-zero dispel type is treated as a debuff. --- src/game/game_handler.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 4e0b8ef2..df1da0df 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -15093,20 +15093,34 @@ void GameHandler::handlePartyMemberStats(network::Packet& packet, bool isFull) { if (updateFlags & 0x0200) { // AURAS if (remaining() >= 8) { uint64_t auraMask = packet.readUInt64(); + // Collect aura updates for this member and store in unitAurasCache_ + // so party frame debuff dots can use them. + std::vector newAuras; for (int i = 0; i < 64; ++i) { if (auraMask & (uint64_t(1) << i)) { + AuraSlot a; + a.level = static_cast(i); // use slot index if (isWotLK) { // WotLK: uint32 spellId + uint8 auraFlags if (remaining() < 5) break; - packet.readUInt32(); - packet.readUInt8(); + a.spellId = packet.readUInt32(); + a.flags = packet.readUInt8(); } else { - // Classic/TBC: uint16 spellId only + // Classic/TBC: uint16 spellId only; negative auras not indicated here if (remaining() < 2) break; - packet.readUInt16(); + a.spellId = packet.readUInt16(); + // Infer negative/positive from dispel type: non-zero dispel → debuff + uint8_t dt = getSpellDispelType(a.spellId); + if (dt > 0) a.flags = 0x80; // mark as debuff } + if (a.spellId != 0) newAuras.push_back(a); } } + // Populate unitAurasCache_ for this member (merge: keep existing per-GUID data + // only if we already have a richer source; otherwise replace with stats data) + if (memberGuid != 0 && memberGuid != playerGuid && memberGuid != targetGuid) { + unitAurasCache_[memberGuid] = std::move(newAuras); + } } } if (updateFlags & 0x0400) { // PET_GUID