feat: add duel countdown overlay (3-2-1-Fight!)

Parse SMSG_DUEL_COUNTDOWN to get the countdown duration, track the
start time, and render a large centered countdown overlay. Numbers
display in pulsing gold; transitions to pulsing red 'Fight!' for the
last 0.5 seconds. Countdown clears on SMSG_DUEL_COMPLETE.
This commit is contained in:
Kelsi 2026-03-12 05:06:14 -07:00
parent 29a989e1f4
commit c35bf8d953
4 changed files with 63 additions and 2 deletions

View file

@ -1035,6 +1035,14 @@ public:
const std::string& getDuelChallengerName() const { return duelChallengerName_; }
void acceptDuel();
// forfeitDuel() already declared at line ~399
// Returns remaining duel countdown seconds, or 0 if no active countdown
float getDuelCountdownRemaining() const {
if (duelCountdownMs_ == 0) return 0.0f;
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - duelCountdownStartedAt_).count();
float rem = (static_cast<float>(duelCountdownMs_) - static_cast<float>(elapsed)) / 1000.0f;
return rem > 0.0f ? rem : 0.0f;
}
// ---- Instance lockouts ----
struct InstanceLockout {
@ -2251,6 +2259,8 @@ private:
uint64_t duelChallengerGuid_= 0;
uint64_t duelFlagGuid_ = 0;
std::string duelChallengerName_;
uint32_t duelCountdownMs_ = 0; // 0 = no active countdown
std::chrono::steady_clock::time_point duelCountdownStartedAt_{};
// ---- Guild state ----
std::string guildName_;

View file

@ -293,6 +293,7 @@ private:
void renderQuestCompleteToasts(float deltaTime);
void renderGroupInvitePopup(game::GameHandler& gameHandler);
void renderDuelRequestPopup(game::GameHandler& gameHandler);
void renderDuelCountdown(game::GameHandler& gameHandler);
void renderLootRollPopup(game::GameHandler& gameHandler);
void renderTradeRequestPopup(game::GameHandler& gameHandler);
void renderTradeWindow(game::GameHandler& gameHandler);