mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
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:
parent
29a989e1f4
commit
c35bf8d953
4 changed files with 63 additions and 2 deletions
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -3214,9 +3214,16 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
case Opcode::SMSG_DUEL_INBOUNDS:
|
||||
// Re-entered the duel area; no special action needed.
|
||||
break;
|
||||
case Opcode::SMSG_DUEL_COUNTDOWN:
|
||||
// Countdown timer — no action needed; server also sends UNIT_FIELD_FLAGS update.
|
||||
case Opcode::SMSG_DUEL_COUNTDOWN: {
|
||||
// uint32 countdown in milliseconds (typically 3000 ms)
|
||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
uint32_t ms = packet.readUInt32();
|
||||
duelCountdownMs_ = (ms > 0 && ms <= 30000) ? ms : 3000;
|
||||
duelCountdownStartedAt_ = std::chrono::steady_clock::now();
|
||||
LOG_INFO("SMSG_DUEL_COUNTDOWN: ", duelCountdownMs_, " ms");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_PARTYKILLLOG: {
|
||||
// uint64 killerGuid + uint64 victimGuid
|
||||
if (packet.getSize() - packet.getReadPos() < 16) break;
|
||||
|
|
@ -10472,6 +10479,7 @@ void GameHandler::handleDuelComplete(network::Packet& packet) {
|
|||
uint8_t started = packet.readUInt8();
|
||||
// started=1: duel began, started=0: duel was cancelled before starting
|
||||
pendingDuelRequest_ = false;
|
||||
duelCountdownMs_ = 0; // clear countdown once duel is resolved
|
||||
if (!started) {
|
||||
addSystemChatMessage("The duel was cancelled.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -482,6 +482,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderBossFrames(gameHandler);
|
||||
renderGroupInvitePopup(gameHandler);
|
||||
renderDuelRequestPopup(gameHandler);
|
||||
renderDuelCountdown(gameHandler);
|
||||
renderLootRollPopup(gameHandler);
|
||||
renderTradeRequestPopup(gameHandler);
|
||||
renderTradeWindow(gameHandler);
|
||||
|
|
@ -7488,6 +7489,47 @@ void GameScreen::renderDuelRequestPopup(game::GameHandler& gameHandler) {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
void GameScreen::renderDuelCountdown(game::GameHandler& gameHandler) {
|
||||
float remaining = gameHandler.getDuelCountdownRemaining();
|
||||
if (remaining <= 0.0f) return;
|
||||
|
||||
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
||||
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
||||
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
||||
|
||||
auto* dl = ImGui::GetForegroundDrawList();
|
||||
ImFont* font = ImGui::GetFont();
|
||||
float fontSize = ImGui::GetFontSize();
|
||||
|
||||
// Show integer countdown or "Fight!" when under 0.5s
|
||||
char buf[32];
|
||||
if (remaining > 0.5f) {
|
||||
snprintf(buf, sizeof(buf), "%d", static_cast<int>(std::ceil(remaining)));
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "Fight!");
|
||||
}
|
||||
|
||||
// Large font by scaling — use 4x font size for dramatic effect
|
||||
float scale = 4.0f;
|
||||
float scaledSize = fontSize * scale;
|
||||
ImVec2 textSz = font->CalcTextSizeA(scaledSize, FLT_MAX, 0.0f, buf);
|
||||
float tx = (screenW - textSz.x) * 0.5f;
|
||||
float ty = screenH * 0.35f - textSz.y * 0.5f;
|
||||
|
||||
// Pulsing alpha: fades in and out per second
|
||||
float pulse = 0.75f + 0.25f * std::sin(static_cast<float>(ImGui::GetTime()) * 6.28f);
|
||||
uint8_t alpha = static_cast<uint8_t>(255 * pulse);
|
||||
|
||||
// Color: golden countdown, red "Fight!"
|
||||
ImU32 color = (remaining > 0.5f)
|
||||
? IM_COL32(255, 200, 50, alpha)
|
||||
: IM_COL32(255, 60, 60, alpha);
|
||||
|
||||
// Drop shadow
|
||||
dl->AddText(font, scaledSize, ImVec2(tx + 2.0f, ty + 2.0f), IM_COL32(0, 0, 0, alpha / 2), buf);
|
||||
dl->AddText(font, scaledSize, ImVec2(tx, ty), color, buf);
|
||||
}
|
||||
|
||||
void GameScreen::renderItemTextWindow(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.isItemTextOpen()) return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue