From 488ec945b6aae3dd6ec86703c7c9ce9eea06cb77 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 18:51:48 -0700 Subject: [PATCH] feat: display glancing and crushing blows in combat text and log Add GLANCING (hitInfo 0x800) and CRUSHING (hitInfo 0x1000) as distinct combat text types so players see mechanics feedback they expect from Classic/TBC content: - Glancing: shown as "~{amount}" in muted yellow/red; "glances for N" in the combat log - Crushing: shown as "{amount}!" in bright orange/red; "crushes for N!" in the combat log Both types are counted toward DPS meter accumulation. AttackerStateUpdateData gains isGlancing()/isCrushing() helpers alongside the existing isCrit()/isMiss(). --- include/game/spell_defines.hpp | 2 +- include/game/world_packets.hpp | 6 ++++-- src/game/game_handler.cpp | 10 +++++++++- src/ui/game_screen.cpp | 28 +++++++++++++++++++++++++++- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/include/game/spell_defines.hpp b/include/game/spell_defines.hpp index 55759128..67d94c2d 100644 --- a/include/game/spell_defines.hpp +++ b/include/game/spell_defines.hpp @@ -53,7 +53,7 @@ struct CombatTextEntry { MELEE_DAMAGE, SPELL_DAMAGE, HEAL, MISS, DODGE, PARRY, BLOCK, EVADE, CRIT_DAMAGE, CRIT_HEAL, PERIODIC_DAMAGE, PERIODIC_HEAL, ENVIRONMENTAL, ENERGIZE, POWER_DRAIN, XP_GAIN, IMMUNE, ABSORB, RESIST, DEFLECT, REFLECT, PROC_TRIGGER, - DISPEL, STEAL, INTERRUPT, INSTAKILL, HONOR_GAIN + DISPEL, STEAL, INTERRUPT, INSTAKILL, HONOR_GAIN, GLANCING, CRUSHING }; Type type; int32_t amount = 0; diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index c2e92f06..a30194f4 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -1719,8 +1719,10 @@ struct AttackerStateUpdateData { uint32_t blocked = 0; bool isValid() const { return attackerGuid != 0; } - bool isCrit() const { return (hitInfo & 0x200) != 0; } - bool isMiss() const { return (hitInfo & 0x10) != 0; } + bool isCrit() const { return (hitInfo & 0x0200) != 0; } + bool isMiss() const { return (hitInfo & 0x0010) != 0; } + bool isGlancing() const { return (hitInfo & 0x0800) != 0; } + bool isCrushing() const { return (hitInfo & 0x1000) != 0; } }; class AttackerStateUpdateParser { diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index a4ec7081..2a16211f 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -17772,7 +17772,15 @@ void GameHandler::handleAttackerStateUpdate(network::Packet& packet) { // VICTIMSTATE_DEFLECT: Attack was deflected (e.g. shield slam reflect). addCombatText(CombatTextEntry::DEFLECT, 0, 0, isPlayerAttacker, 0, data.attackerGuid, data.targetGuid); } else { - auto type = data.isCrit() ? CombatTextEntry::CRIT_DAMAGE : CombatTextEntry::MELEE_DAMAGE; + CombatTextEntry::Type type; + if (data.isCrit()) + type = CombatTextEntry::CRIT_DAMAGE; + else if (data.isCrushing()) + type = CombatTextEntry::CRUSHING; + else if (data.isGlancing()) + type = CombatTextEntry::GLANCING; + else + type = CombatTextEntry::MELEE_DAMAGE; addCombatText(type, data.totalDamage, 0, isPlayerAttacker, 0, data.attackerGuid, data.targetGuid); // Show partial absorb/resist from sub-damage entries uint32_t totalAbsorbed = 0, totalResisted = 0; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 277ecf7b..556f3917 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -9273,6 +9273,18 @@ void GameScreen::renderCombatText(game::GameHandler& gameHandler) { snprintf(text, sizeof(text), "+%d Honor", entry.amount); color = ImVec4(1.0f, 0.85f, 0.0f, alpha); // Gold for honor break; + case game::CombatTextEntry::GLANCING: + snprintf(text, sizeof(text), "~%d", entry.amount); + color = outgoing ? + ImVec4(0.75f, 0.75f, 0.5f, alpha) : // Outgoing glancing = muted yellow + ImVec4(0.75f, 0.35f, 0.35f, alpha); // Incoming glancing = muted red + break; + case game::CombatTextEntry::CRUSHING: + snprintf(text, sizeof(text), "%d!", entry.amount); + color = outgoing ? + ImVec4(1.0f, 0.55f, 0.1f, alpha) : // Outgoing crushing = orange + ImVec4(1.0f, 0.15f, 0.15f, alpha); // Incoming crushing = bright red + break; default: snprintf(text, sizeof(text), "%d", entry.amount); color = ImVec4(1.0f, 1.0f, 1.0f, alpha); @@ -9343,6 +9355,8 @@ void GameScreen::renderDPSMeter(game::GameHandler& gameHandler) { case game::CombatTextEntry::SPELL_DAMAGE: case game::CombatTextEntry::CRIT_DAMAGE: case game::CombatTextEntry::PERIODIC_DAMAGE: + case game::CombatTextEntry::GLANCING: + case game::CombatTextEntry::CRUSHING: dpsEncounterDamage_ += static_cast(e.amount); break; case game::CombatTextEntry::HEAL: @@ -9367,6 +9381,8 @@ void GameScreen::renderDPSMeter(game::GameHandler& gameHandler) { case game::CombatTextEntry::SPELL_DAMAGE: case game::CombatTextEntry::CRIT_DAMAGE: case game::CombatTextEntry::PERIODIC_DAMAGE: + case game::CombatTextEntry::GLANCING: + case game::CombatTextEntry::CRUSHING: totalDamage += static_cast(e.amount); break; case game::CombatTextEntry::HEAL: @@ -21218,7 +21234,7 @@ void GameScreen::renderCombatLog(game::GameHandler& gameHandler) { using T = game::CombatTextEntry; return t == T::MELEE_DAMAGE || t == T::SPELL_DAMAGE || t == T::CRIT_DAMAGE || t == T::PERIODIC_DAMAGE || - t == T::ENVIRONMENTAL; + t == T::ENVIRONMENTAL || t == T::GLANCING || t == T::CRUSHING; }; auto isHealType = [](game::CombatTextEntry::Type t) { using T = game::CombatTextEntry; @@ -21492,6 +21508,16 @@ void GameScreen::renderCombatLog(game::GameHandler& gameHandler) { snprintf(desc, sizeof(desc), "You gain %d honor", e.amount); color = ImVec4(1.0f, 0.85f, 0.0f, 1.0f); break; + case T::GLANCING: + snprintf(desc, sizeof(desc), "%s glances %s for %d", src, tgt, e.amount); + color = e.isPlayerSource ? ImVec4(0.75f, 0.75f, 0.5f, 1.0f) + : ImVec4(0.75f, 0.4f, 0.4f, 1.0f); + break; + case T::CRUSHING: + snprintf(desc, sizeof(desc), "%s crushes %s for %d!", src, tgt, e.amount); + color = e.isPlayerSource ? ImVec4(1.0f, 0.55f, 0.1f, 1.0f) + : ImVec4(1.0f, 0.15f, 0.15f, 1.0f); + break; default: snprintf(desc, sizeof(desc), "Combat event (type %d, amount %d)", (int)e.type, e.amount); color = ImVec4(0.7f, 0.7f, 0.7f, 1.0f);