game: clear unit cast bars on SMSG_SPELL_FAILURE and SMSG_SPELL_FAILED_OTHER

When a spell fails or is interrupted, the server sends SMSG_SPELL_FAILURE
(for the caster's own POV) or SMSG_SPELL_FAILED_OTHER (for observers).
Previously these were consumed without updating cast state, leaving stale
cast bars for interrupted enemies. Now:

- SMSG_SPELL_FAILURE: erases unitCastStates_[failGuid] for non-player
  casters (still clears player casting/currentCastSpellId for own casts)
- SMSG_SPELL_FAILED_OTHER: erases unitCastStates_[guid] for the caster
  so boss/enemy cast bars immediately clear on interrupt/kick
This commit is contained in:
Kelsi 2026-03-09 23:16:15 -07:00
parent 07d0485a31
commit 640eaacb8c

View file

@ -1779,10 +1779,15 @@ void GameHandler::handlePacket(network::Packet& packet) {
}
// ---- Spell failed on another unit ----
case Opcode::SMSG_SPELL_FAILED_OTHER:
// packed_guid + uint8 castCount + uint32 spellId + uint8 reason — just consume
case Opcode::SMSG_SPELL_FAILED_OTHER: {
// packed_guid + uint8 castCount + uint32 spellId + uint8 reason
uint64_t failOtherGuid = UpdateObjectParser::readPackedGuid(packet);
if (failOtherGuid != 0 && failOtherGuid != playerGuid) {
unitCastStates_.erase(failOtherGuid);
}
packet.setReadPos(packet.getSize());
break;
}
// ---- Spell proc resist log ----
case Opcode::SMSG_PROCRESIST: {
@ -2526,16 +2531,24 @@ void GameHandler::handlePacket(network::Packet& packet) {
case Opcode::SMSG_SPELL_GO:
handleSpellGo(packet);
break;
case Opcode::SMSG_SPELL_FAILURE:
// Spell failed mid-cast
casting = false;
currentCastSpellId = 0;
if (auto* renderer = core::Application::getInstance().getRenderer()) {
if (auto* ssm = renderer->getSpellSoundManager()) {
ssm->stopPrecast();
case Opcode::SMSG_SPELL_FAILURE: {
// packed_guid caster + uint8 castCount + uint32 spellId + uint8 failReason
uint64_t failGuid = UpdateObjectParser::readPackedGuid(packet);
if (failGuid == playerGuid || failGuid == 0) {
// Player's own cast failed
casting = false;
currentCastSpellId = 0;
if (auto* renderer = core::Application::getInstance().getRenderer()) {
if (auto* ssm = renderer->getSpellSoundManager()) {
ssm->stopPrecast();
}
}
} else {
// Another unit's cast failed — clear their tracked cast bar
unitCastStates_.erase(failGuid);
}
break;
}
case Opcode::SMSG_SPELL_COOLDOWN:
handleSpellCooldown(packet);
break;