From 18d0e6a2520ca001cb0723b8defd085904917a80 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 21:33:19 -0700 Subject: [PATCH] feat: use UNIT_FIELD_AURAFLAGS to correctly classify Classic buffs vs debuffs Classic WoW stores aura flags in UNIT_FIELD_AURAFLAGS (12 uint32 fields packed 4 bytes per uint32, one byte per aura slot). Flag bit 0x02 = harmful (debuff), 0x04 = helpful (buff). - Add UNIT_FIELD_AURAFLAGS to update_field_table.hpp (Classic wire index 98) - Add wire index 98 to Classic and Turtle WoW JSON update field tables - Both Classic aura rebuild paths (CREATE_OBJECT and VALUES) now read the flag byte for each aura slot to populate AuraSlot.flags, enabling the buff/debuff bar to correctly separate buffs from debuffs on Classic --- Data/expansions/classic/update_fields.json | 1 + Data/expansions/turtle/update_fields.json | 1 + include/game/update_field_table.hpp | 1 + src/game/game_handler.cpp | 24 ++++++++++++++++++---- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Data/expansions/classic/update_fields.json b/Data/expansions/classic/update_fields.json index bb269d8a..4f340df5 100644 --- a/Data/expansions/classic/update_fields.json +++ b/Data/expansions/classic/update_fields.json @@ -14,6 +14,7 @@ "UNIT_FIELD_DISPLAYID": 131, "UNIT_FIELD_MOUNTDISPLAYID": 133, "UNIT_FIELD_AURAS": 50, + "UNIT_FIELD_AURAFLAGS": 98, "UNIT_NPC_FLAGS": 147, "UNIT_DYNAMIC_FLAGS": 143, "UNIT_FIELD_RESISTANCES": 154, diff --git a/Data/expansions/turtle/update_fields.json b/Data/expansions/turtle/update_fields.json index 74b873ae..a27e84f7 100644 --- a/Data/expansions/turtle/update_fields.json +++ b/Data/expansions/turtle/update_fields.json @@ -14,6 +14,7 @@ "UNIT_FIELD_DISPLAYID": 131, "UNIT_FIELD_MOUNTDISPLAYID": 133, "UNIT_FIELD_AURAS": 50, + "UNIT_FIELD_AURAFLAGS": 98, "UNIT_NPC_FLAGS": 147, "UNIT_DYNAMIC_FLAGS": 143, "UNIT_FIELD_RESISTANCES": 154, diff --git a/include/game/update_field_table.hpp b/include/game/update_field_table.hpp index 09446d65..bc8a53f6 100644 --- a/include/game/update_field_table.hpp +++ b/include/game/update_field_table.hpp @@ -31,6 +31,7 @@ enum class UF : uint16_t { UNIT_FIELD_DISPLAYID, UNIT_FIELD_MOUNTDISPLAYID, UNIT_FIELD_AURAS, // Start of aura spell ID array (48 consecutive uint32 slots, classic/vanilla only) + UNIT_FIELD_AURAFLAGS, // Aura flags packed 4-per-uint32 (12 uint32 slots); 0x01=cancelable,0x02=harmful,0x04=helpful UNIT_NPC_FLAGS, UNIT_DYNAMIC_FLAGS, UNIT_FIELD_RESISTANCES, // Physical armor (index 0 of the resistance array) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index d6f03a03..98b81291 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -8991,7 +8991,8 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { } // Classic: rebuild playerAuras from UNIT_FIELD_AURAS on initial object create if (block.guid == playerGuid && isClassicLikeExpansion()) { - const uint16_t ufAuras = fieldIndex(UF::UNIT_FIELD_AURAS); + const uint16_t ufAuras = fieldIndex(UF::UNIT_FIELD_AURAS); + const uint16_t ufAuraFlags = fieldIndex(UF::UNIT_FIELD_AURAFLAGS); if (ufAuras != 0xFFFF) { bool hasAuraField = false; for (const auto& [fk, fv] : block.fields) { @@ -9009,7 +9010,14 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { if (it != allFields.end() && it->second != 0) { AuraSlot& a = playerAuras[slot]; a.spellId = it->second; - a.flags = 0; + // Read aura flag byte: packed 4-per-uint32 at ufAuraFlags + uint8_t aFlag = 0; + if (ufAuraFlags != 0xFFFF) { + auto fit = allFields.find(static_cast(ufAuraFlags + slot / 4)); + if (fit != allFields.end()) + aFlag = static_cast((fit->second >> ((slot % 4) * 8)) & 0xFF); + } + a.flags = aFlag; a.durationMs = -1; a.maxDurationMs = -1; a.casterGuid = playerGuid; @@ -9435,7 +9443,8 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { // Classic: sync playerAuras from UNIT_FIELD_AURAS when those fields are updated if (block.guid == playerGuid && isClassicLikeExpansion()) { - const uint16_t ufAuras = fieldIndex(UF::UNIT_FIELD_AURAS); + const uint16_t ufAuras = fieldIndex(UF::UNIT_FIELD_AURAS); + const uint16_t ufAuraFlags = fieldIndex(UF::UNIT_FIELD_AURAFLAGS); if (ufAuras != 0xFFFF) { bool hasAuraUpdate = false; for (const auto& [fk, fv] : block.fields) { @@ -9453,7 +9462,14 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { if (it != allFields.end() && it->second != 0) { AuraSlot& a = playerAuras[slot]; a.spellId = it->second; - a.flags = 0; + // Read aura flag byte: packed 4-per-uint32 at ufAuraFlags + uint8_t aFlag = 0; + if (ufAuraFlags != 0xFFFF) { + auto fit = allFields.find(static_cast(ufAuraFlags + slot / 4)); + if (fit != allFields.end()) + aFlag = static_cast((fit->second >> ((slot % 4) * 8)) & 0xFF); + } + a.flags = aFlag; a.durationMs = -1; a.maxDurationMs = -1; a.casterGuid = playerGuid;