feat: fire UNIT_SPELLCAST_SENT and UNIT_SPELLCAST_STOP events

Fire UNIT_SPELLCAST_SENT when the player initiates a spell cast (before
server confirms), enabling cast bar addons like Quartz to show latency.
Includes target name and spell ID as arguments.

Fire UNIT_SPELLCAST_STOP whenever a cast bar should disappear:
- On successful cast completion (SMSG_SPELL_GO)
- On cast failure (SMSG_CAST_RESULT with error)
- On spell interrupt (SMSG_SPELL_FAILURE/SMSG_SPELL_FAILED_OTHER)
- On manual cast cancel

These events are essential for cast bar replacement addons to properly
track when casts begin and end.
This commit is contained in:
Kelsi 2026-03-21 02:10:09 -07:00
parent 6e863a323a
commit c20db42479

View file

@ -2304,8 +2304,10 @@ void GameHandler::handlePacket(network::Packet& packet) {
: ("Spell cast failed (error " + std::to_string(castResult) + ")");
addUIError(errMsg);
if (spellCastFailedCallback_) spellCastFailedCallback_(castResultSpellId);
if (addonEventCallback_)
if (addonEventCallback_) {
addonEventCallback_("UNIT_SPELLCAST_FAILED", {"player", std::to_string(castResultSpellId)});
addonEventCallback_("UNIT_SPELLCAST_STOP", {"player", std::to_string(castResultSpellId)});
}
MessageChatData msg;
msg.type = ChatType::SYSTEM;
msg.language = ChatLanguage::UNIVERSAL;
@ -3418,8 +3420,10 @@ void GameHandler::handlePacket(network::Packet& packet) {
if (failGuid == playerGuid || failGuid == 0) unitId = "player";
else if (failGuid == targetGuid) unitId = "target";
else if (failGuid == focusGuid) unitId = "focus";
if (!unitId.empty())
if (!unitId.empty()) {
addonEventCallback_("UNIT_SPELLCAST_INTERRUPTED", {unitId});
addonEventCallback_("UNIT_SPELLCAST_STOP", {unitId});
}
}
if (failGuid == playerGuid || failGuid == 0) {
// Player's own cast failed — clear gather-node loot target so the
@ -18728,6 +18732,13 @@ void GameHandler::castSpell(uint32_t spellId, uint64_t targetGuid) {
socket->send(packet);
LOG_INFO("Casting spell: ", spellId, " on 0x", std::hex, target, std::dec);
// Fire UNIT_SPELLCAST_SENT for cast bar addons (fires on client intent, before server confirms)
if (addonEventCallback_) {
std::string targetName;
if (target != 0) targetName = lookupName(target);
addonEventCallback_("UNIT_SPELLCAST_SENT", {"player", targetName, std::to_string(spellId)});
}
// Optimistically start GCD immediately on cast, but do not restart it while
// already active (prevents timeout animation reset on repeated key presses).
if (!isGCDActive()) {
@ -18756,6 +18767,8 @@ void GameHandler::cancelCast() {
craftQueueRemaining_ = 0;
queuedSpellId_ = 0;
queuedSpellTarget_ = 0;
if (addonEventCallback_)
addonEventCallback_("UNIT_SPELLCAST_STOP", {"player"});
}
void GameHandler::startCraftQueue(uint32_t spellId, int count) {
@ -19255,6 +19268,10 @@ void GameHandler::handleSpellGo(network::Packet& packet) {
spellCastAnimCallback_(playerGuid, false, false);
}
// Fire UNIT_SPELLCAST_STOP — cast bar should disappear
if (addonEventCallback_)
addonEventCallback_("UNIT_SPELLCAST_STOP", {"player", std::to_string(data.spellId)});
// Spell queue: fire the next queued spell now that casting has ended
if (queuedSpellId_ != 0) {
uint32_t nextSpell = queuedSpellId_;