feat: track and visualize global cooldown (GCD) on action bar

- GameHandler tracks GCD in gcdTotal_/gcdStartedAt_ (time-based)
- SMSG_SPELL_COOLDOWN: spellId=0 entries (<=2s) are treated as GCD
- castSpell(): optimistically starts 1.5s GCD client-side on cast
- Action bar: non-cooldown slots show subtle dark sweep + dim tint
  during the GCD window, matching WoW standard behavior
This commit is contained in:
Kelsi 2026-03-12 05:38:13 -07:00
parent 61c0b91e39
commit b6a43d6ce7
3 changed files with 58 additions and 0 deletions

View file

@ -14119,6 +14119,10 @@ void GameHandler::castSpell(uint32_t spellId, uint64_t targetGuid) {
: CastSpellPacket::build(spellId, target, ++castCount);
socket->send(packet);
LOG_INFO("Casting spell: ", spellId, " on 0x", std::hex, target, std::dec);
// Optimistically start GCD immediately on cast — server will confirm or override
gcdTotal_ = 1.5f;
gcdStartedAt_ = std::chrono::steady_clock::now();
}
void GameHandler::cancelCast() {
@ -14477,6 +14481,14 @@ void GameHandler::handleSpellCooldown(network::Packet& packet) {
uint32_t cooldownMs = packet.readUInt32();
float seconds = cooldownMs / 1000.0f;
// spellId=0 is the Global Cooldown marker (server sends it for GCD triggers)
if (spellId == 0 && cooldownMs > 0 && cooldownMs <= 2000) {
gcdTotal_ = seconds;
gcdStartedAt_ = std::chrono::steady_clock::now();
continue;
}
spellCooldowns[spellId] = seconds;
for (auto& slot : actionBar) {
bool match = (slot.type == ActionBarSlot::SPELL && slot.id == spellId)