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().
This commit is contained in:
Kelsi 2026-03-17 18:51:48 -07:00
parent 36fed15d43
commit 488ec945b6
4 changed files with 41 additions and 5 deletions

View file

@ -53,7 +53,7 @@ struct CombatTextEntry {
MELEE_DAMAGE, SPELL_DAMAGE, HEAL, MISS, DODGE, PARRY, BLOCK, MELEE_DAMAGE, SPELL_DAMAGE, HEAL, MISS, DODGE, PARRY, BLOCK,
EVADE, CRIT_DAMAGE, CRIT_HEAL, PERIODIC_DAMAGE, PERIODIC_HEAL, ENVIRONMENTAL, EVADE, CRIT_DAMAGE, CRIT_HEAL, PERIODIC_DAMAGE, PERIODIC_HEAL, ENVIRONMENTAL,
ENERGIZE, POWER_DRAIN, XP_GAIN, IMMUNE, ABSORB, RESIST, DEFLECT, REFLECT, PROC_TRIGGER, 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; Type type;
int32_t amount = 0; int32_t amount = 0;

View file

@ -1719,8 +1719,10 @@ struct AttackerStateUpdateData {
uint32_t blocked = 0; uint32_t blocked = 0;
bool isValid() const { return attackerGuid != 0; } bool isValid() const { return attackerGuid != 0; }
bool isCrit() const { return (hitInfo & 0x200) != 0; } bool isCrit() const { return (hitInfo & 0x0200) != 0; }
bool isMiss() const { return (hitInfo & 0x10) != 0; } bool isMiss() const { return (hitInfo & 0x0010) != 0; }
bool isGlancing() const { return (hitInfo & 0x0800) != 0; }
bool isCrushing() const { return (hitInfo & 0x1000) != 0; }
}; };
class AttackerStateUpdateParser { class AttackerStateUpdateParser {

View file

@ -17772,7 +17772,15 @@ void GameHandler::handleAttackerStateUpdate(network::Packet& packet) {
// VICTIMSTATE_DEFLECT: Attack was deflected (e.g. shield slam reflect). // VICTIMSTATE_DEFLECT: Attack was deflected (e.g. shield slam reflect).
addCombatText(CombatTextEntry::DEFLECT, 0, 0, isPlayerAttacker, 0, data.attackerGuid, data.targetGuid); addCombatText(CombatTextEntry::DEFLECT, 0, 0, isPlayerAttacker, 0, data.attackerGuid, data.targetGuid);
} else { } 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); addCombatText(type, data.totalDamage, 0, isPlayerAttacker, 0, data.attackerGuid, data.targetGuid);
// Show partial absorb/resist from sub-damage entries // Show partial absorb/resist from sub-damage entries
uint32_t totalAbsorbed = 0, totalResisted = 0; uint32_t totalAbsorbed = 0, totalResisted = 0;

View file

@ -9273,6 +9273,18 @@ void GameScreen::renderCombatText(game::GameHandler& gameHandler) {
snprintf(text, sizeof(text), "+%d Honor", entry.amount); snprintf(text, sizeof(text), "+%d Honor", entry.amount);
color = ImVec4(1.0f, 0.85f, 0.0f, alpha); // Gold for honor color = ImVec4(1.0f, 0.85f, 0.0f, alpha); // Gold for honor
break; 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: default:
snprintf(text, sizeof(text), "%d", entry.amount); snprintf(text, sizeof(text), "%d", entry.amount);
color = ImVec4(1.0f, 1.0f, 1.0f, alpha); 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::SPELL_DAMAGE:
case game::CombatTextEntry::CRIT_DAMAGE: case game::CombatTextEntry::CRIT_DAMAGE:
case game::CombatTextEntry::PERIODIC_DAMAGE: case game::CombatTextEntry::PERIODIC_DAMAGE:
case game::CombatTextEntry::GLANCING:
case game::CombatTextEntry::CRUSHING:
dpsEncounterDamage_ += static_cast<float>(e.amount); dpsEncounterDamage_ += static_cast<float>(e.amount);
break; break;
case game::CombatTextEntry::HEAL: case game::CombatTextEntry::HEAL:
@ -9367,6 +9381,8 @@ void GameScreen::renderDPSMeter(game::GameHandler& gameHandler) {
case game::CombatTextEntry::SPELL_DAMAGE: case game::CombatTextEntry::SPELL_DAMAGE:
case game::CombatTextEntry::CRIT_DAMAGE: case game::CombatTextEntry::CRIT_DAMAGE:
case game::CombatTextEntry::PERIODIC_DAMAGE: case game::CombatTextEntry::PERIODIC_DAMAGE:
case game::CombatTextEntry::GLANCING:
case game::CombatTextEntry::CRUSHING:
totalDamage += static_cast<float>(e.amount); totalDamage += static_cast<float>(e.amount);
break; break;
case game::CombatTextEntry::HEAL: case game::CombatTextEntry::HEAL:
@ -21218,7 +21234,7 @@ void GameScreen::renderCombatLog(game::GameHandler& gameHandler) {
using T = game::CombatTextEntry; using T = game::CombatTextEntry;
return t == T::MELEE_DAMAGE || t == T::SPELL_DAMAGE || return t == T::MELEE_DAMAGE || t == T::SPELL_DAMAGE ||
t == T::CRIT_DAMAGE || t == T::PERIODIC_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) { auto isHealType = [](game::CombatTextEntry::Type t) {
using T = game::CombatTextEntry; using T = game::CombatTextEntry;
@ -21492,6 +21508,16 @@ void GameScreen::renderCombatLog(game::GameHandler& gameHandler) {
snprintf(desc, sizeof(desc), "You gain %d honor", e.amount); snprintf(desc, sizeof(desc), "You gain %d honor", e.amount);
color = ImVec4(1.0f, 0.85f, 0.0f, 1.0f); color = ImVec4(1.0f, 0.85f, 0.0f, 1.0f);
break; 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: default:
snprintf(desc, sizeof(desc), "Combat event (type %d, amount %d)", (int)e.type, e.amount); 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); color = ImVec4(0.7f, 0.7f, 0.7f, 1.0f);