From 2d53ff0c07d25042237bebd86b6a43c8e770bf29 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 10:54:07 -0700 Subject: [PATCH] feat: show environmental damage type in combat text and log Fall, lava, drowning, fatigue, slime, and fire damage now display their specific type instead of generic "Environmental damage" in both floating combat text and the combat log window. The envType byte from SMSG_ENVIRONMENTAL_DAMAGE_LOG is propagated via the powerType field to the display layer. Added powerType to CombatLogEntry for consistent access in the persistent combat log. --- include/game/spell_defines.hpp | 1 + src/game/game_handler.cpp | 9 +++++---- src/ui/game_screen.cpp | 29 +++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/include/game/spell_defines.hpp b/include/game/spell_defines.hpp index ffaf6bb2..10c4a5cd 100644 --- a/include/game/spell_defines.hpp +++ b/include/game/spell_defines.hpp @@ -74,6 +74,7 @@ struct CombatLogEntry { int32_t amount = 0; uint32_t spellId = 0; bool isPlayerSource = false; + uint8_t powerType = 0; // For ENERGIZE/DRAIN: power type; for ENVIRONMENTAL: env damage type time_t timestamp = 0; // Wall-clock time (std::time(nullptr)) std::string sourceName; // Resolved display name of attacker/caster std::string targetName; // Resolved display name of victim/target diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index b1ae2ece..21c146d2 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -4259,17 +4259,17 @@ void GameHandler::handlePacket(network::Packet& packet) { } case Opcode::SMSG_ENVIRONMENTAL_DAMAGE_LOG: { // uint64 victimGuid + uint8 envDmgType + uint32 damage + uint32 absorbed + uint32 resisted - // envDmgType: 1=Exhausted(fatigue), 2=Drowning, 3=Fall, 4=Lava, 5=Slime, 6=Fire + // envDmgType: 0=Exhausted(fatigue), 1=Drowning, 2=Fall, 3=Lava, 4=Slime, 5=Fire if (packet.getSize() - packet.getReadPos() < 21) { packet.setReadPos(packet.getSize()); break; } uint64_t victimGuid = packet.readUInt64(); - /*uint8_t envType =*/ packet.readUInt8(); + uint8_t envType = packet.readUInt8(); uint32_t dmg = packet.readUInt32(); uint32_t envAbs = packet.readUInt32(); uint32_t envRes = packet.readUInt32(); if (victimGuid == playerGuid) { - // Environmental damage: no caster GUID, victim = player + // Environmental damage: pass envType via powerType field for display differentiation if (dmg > 0) - addCombatText(CombatTextEntry::ENVIRONMENTAL, static_cast(dmg), 0, false, 0, 0, victimGuid); + addCombatText(CombatTextEntry::ENVIRONMENTAL, static_cast(dmg), 0, false, envType, 0, victimGuid); if (envAbs > 0) addCombatText(CombatTextEntry::ABSORB, static_cast(envAbs), 0, false, 0, 0, victimGuid); if (envRes > 0) @@ -15160,6 +15160,7 @@ void GameHandler::addCombatText(CombatTextEntry::Type type, int32_t amount, uint log.amount = amount; log.spellId = spellId; log.isPlayerSource = isPlayerSource; + log.powerType = powerType; log.timestamp = std::time(nullptr); // If the caller provided an explicit destination GUID but left source GUID as 0, // preserve "unknown/no source" (e.g. environmental damage) instead of diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 2dfb55b0..315c15e2 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -8467,10 +8467,21 @@ void GameScreen::renderCombatText(game::GameHandler& gameHandler) { snprintf(text, sizeof(text), "+%d", entry.amount); color = ImVec4(0.4f, 1.0f, 0.5f, alpha); break; - case game::CombatTextEntry::ENVIRONMENTAL: - snprintf(text, sizeof(text), "-%d", entry.amount); + case game::CombatTextEntry::ENVIRONMENTAL: { + const char* envLabel = ""; + switch (entry.powerType) { + case 0: envLabel = "Fatigue "; break; + case 1: envLabel = "Drowning "; break; + case 2: envLabel = ""; break; // Fall: just show the number (WoW convention) + case 3: envLabel = "Lava "; break; + case 4: envLabel = "Slime "; break; + case 5: envLabel = "Fire "; break; + default: envLabel = ""; break; + } + snprintf(text, sizeof(text), "%s-%d", envLabel, entry.amount); color = ImVec4(0.9f, 0.5f, 0.2f, alpha); // Orange for environmental break; + } case game::CombatTextEntry::ENERGIZE: snprintf(text, sizeof(text), "+%d", entry.amount); switch (entry.powerType) { @@ -20538,10 +20549,20 @@ void GameScreen::renderCombatLog(game::GameHandler& gameHandler) { snprintf(desc, sizeof(desc), "%s reflects %s's attack", tgt, src); color = ImVec4(0.8f, 0.7f, 1.0f, 1.0f); break; - case T::ENVIRONMENTAL: - snprintf(desc, sizeof(desc), "Environmental damage: %d", e.amount); + case T::ENVIRONMENTAL: { + const char* envName = "Environmental"; + switch (e.powerType) { + case 0: envName = "Fatigue"; break; + case 1: envName = "Drowning"; break; + case 2: envName = "Falling"; break; + case 3: envName = "Lava"; break; + case 4: envName = "Slime"; break; + case 5: envName = "Fire"; break; + } + snprintf(desc, sizeof(desc), "%s damage: %d", envName, e.amount); color = ImVec4(1.0f, 0.5f, 0.2f, 1.0f); break; + } case T::ENERGIZE: if (spell) snprintf(desc, sizeof(desc), "%s gains %d power (%s)", tgt, e.amount, spell);