From 4507a223cc0e9495ae5d8d186074a3cfb9000cc8 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 13 Mar 2026 06:08:21 -0700 Subject: [PATCH] feat: color-code ENERGIZE combat text by power type Mana (0)=blue, Rage (1)=red, Focus (2)=orange, Energy (3)=yellow, Runic Power (6)=teal. Previously all energize events showed as blue regardless of resource type, making it impossible to distinguish e.g. a Warrior's Rage generation from a Mage's Mana return. Power type is now captured from SMSG_SPELLENERGIZELOG (uint8) and SMSG_PERIODICAURALOG OBS_MOD_POWER/PERIODIC_ENERGIZE (uint32 cast to uint8) and stored in CombatTextEntry::powerType. --- include/game/game_handler.hpp | 2 +- include/game/spell_defines.hpp | 1 + src/game/game_handler.cpp | 15 ++++++++------- src/ui/game_screen.cpp | 8 +++++++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 7e35e203..044c5007 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -2314,7 +2314,7 @@ private: void handleLogoutResponse(network::Packet& packet); void handleLogoutComplete(network::Packet& packet); - void addCombatText(CombatTextEntry::Type type, int32_t amount, uint32_t spellId, bool isPlayerSource); + void addCombatText(CombatTextEntry::Type type, int32_t amount, uint32_t spellId, bool isPlayerSource, uint8_t powerType = 0); void addSystemChatMessage(const std::string& message); /** diff --git a/include/game/spell_defines.hpp b/include/game/spell_defines.hpp index dc38f813..d8f8c1df 100644 --- a/include/game/spell_defines.hpp +++ b/include/game/spell_defines.hpp @@ -59,6 +59,7 @@ struct CombatTextEntry { uint32_t spellId = 0; float age = 0.0f; // Seconds since creation (for fadeout) bool isPlayerSource = false; // True if player dealt this + uint8_t powerType = 0; // For ENERGIZE: 0=mana,1=rage,2=focus,3=energy,6=runicpower static constexpr float LIFETIME = 2.5f; bool isExpired() const { return age >= LIFETIME; } diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 1fcedce0..06140aeb 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -3879,11 +3879,11 @@ void GameHandler::handlePacket(network::Packet& packet) { // OBS_MOD_POWER / PERIODIC_ENERGIZE: miscValue(powerType) + amount // Common in WotLK: Replenishment, Mana Spring Totem, Divine Plea, etc. if (packet.getSize() - packet.getReadPos() < 8) break; - /*uint32_t powerType =*/ packet.readUInt32(); + uint8_t periodicPowerType = static_cast(packet.readUInt32()); uint32_t amount = packet.readUInt32(); if ((isPlayerVictim || isPlayerCaster) && amount > 0) addCombatText(CombatTextEntry::ENERGIZE, static_cast(amount), - spellId, isPlayerCaster); + spellId, isPlayerCaster, periodicPowerType); } else if (auraType == 98) { // PERIODIC_MANA_LEECH: miscValue(powerType) + amount + float multiplier if (packet.getSize() - packet.getReadPos() < 12) break; @@ -3916,13 +3916,13 @@ void GameHandler::handlePacket(network::Packet& packet) { ? packet.readUInt64() : UpdateObjectParser::readPackedGuid(packet); rem = packet.getSize() - packet.getReadPos(); if (rem < 6) { packet.setReadPos(packet.getSize()); break; } - uint32_t spellId = packet.readUInt32(); - /*uint8_t powerType =*/ packet.readUInt8(); - int32_t amount = static_cast(packet.readUInt32()); + uint32_t spellId = packet.readUInt32(); + uint8_t energizePowerType = packet.readUInt8(); + int32_t amount = static_cast(packet.readUInt32()); bool isPlayerVictim = (victimGuid == playerGuid); bool isPlayerCaster = (casterGuid == playerGuid); if ((isPlayerVictim || isPlayerCaster) && amount > 0) - addCombatText(CombatTextEntry::ENERGIZE, amount, spellId, isPlayerCaster); + addCombatText(CombatTextEntry::ENERGIZE, amount, spellId, isPlayerCaster, energizePowerType); packet.setReadPos(packet.getSize()); break; } @@ -13629,13 +13629,14 @@ void GameHandler::stopAutoAttack() { LOG_INFO("Stopping auto-attack"); } -void GameHandler::addCombatText(CombatTextEntry::Type type, int32_t amount, uint32_t spellId, bool isPlayerSource) { +void GameHandler::addCombatText(CombatTextEntry::Type type, int32_t amount, uint32_t spellId, bool isPlayerSource, uint8_t powerType) { CombatTextEntry entry; entry.type = type; entry.amount = amount; entry.spellId = spellId; entry.age = 0.0f; entry.isPlayerSource = isPlayerSource; + entry.powerType = powerType; combatText.push_back(entry); // Persistent combat log diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index dd81e8eb..a7897e8d 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -8287,7 +8287,13 @@ void GameScreen::renderCombatText(game::GameHandler& gameHandler) { break; case game::CombatTextEntry::ENERGIZE: snprintf(text, sizeof(text), "+%d", entry.amount); - color = ImVec4(0.3f, 0.6f, 1.0f, alpha); // Blue for mana/energy + switch (entry.powerType) { + case 1: color = ImVec4(1.0f, 0.2f, 0.2f, alpha); break; // Rage: red + case 2: color = ImVec4(1.0f, 0.6f, 0.1f, alpha); break; // Focus: orange + case 3: color = ImVec4(1.0f, 0.9f, 0.2f, alpha); break; // Energy: yellow + case 6: color = ImVec4(0.3f, 0.9f, 0.8f, alpha); break; // Runic Power: teal + default: color = ImVec4(0.3f, 0.6f, 1.0f, alpha); break; // Mana (0): blue + } break; case game::CombatTextEntry::XP_GAIN: snprintf(text, sizeof(text), "+%d XP", entry.amount);