diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 72bcdd54..6e993d89 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1129,6 +1129,8 @@ public: uint32_t itemId = 0; std::string itemName; uint8_t itemQuality = 0; + uint32_t rollCountdownMs = 60000; // Duration of roll window in ms + std::chrono::steady_clock::time_point rollStartedAt{}; }; bool hasPendingLootRoll() const { return pendingLootRollActive_; } const LootRollEntry& getPendingLootRoll() const { return pendingLootRoll_; } diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 1d525ecb..8403759c 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -2028,7 +2028,7 @@ void GameHandler::handlePacket(network::Packet& packet) { /*uint32_t randSuffix =*/ packet.readUInt32(); /*uint32_t randProp =*/ packet.readUInt32(); } - /*uint32_t countdown =*/ packet.readUInt32(); + uint32_t countdown = packet.readUInt32(); /*uint8_t voteMask =*/ packet.readUInt8(); // Trigger the roll popup for local player pendingLootRollActive_ = true; @@ -2038,6 +2038,8 @@ void GameHandler::handlePacket(network::Packet& packet) { auto* info = getItemInfo(itemId); pendingLootRoll_.itemName = info ? info->name : std::to_string(itemId); pendingLootRoll_.itemQuality = info ? static_cast(info->quality) : 0; + pendingLootRoll_.rollCountdownMs = (countdown > 0 && countdown <= 120000) ? countdown : 60000; + pendingLootRoll_.rollStartedAt = std::chrono::steady_clock::now(); LOG_INFO("SMSG_LOOT_START_ROLL: item=", itemId, " (", pendingLootRoll_.itemName, ") slot=", slot); break; @@ -19931,6 +19933,8 @@ void GameHandler::handleLootRoll(network::Packet& packet) { auto* info = getItemInfo(itemId); pendingLootRoll_.itemName = info ? info->name : std::to_string(itemId); pendingLootRoll_.itemQuality = info ? static_cast(info->quality) : 0; + pendingLootRoll_.rollCountdownMs = 60000; + pendingLootRoll_.rollStartedAt = std::chrono::steady_clock::now(); LOG_INFO("SMSG_LOOT_ROLL: need/greed prompt for item=", itemId, " (", pendingLootRoll_.itemName, ") slot=", slot); return; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 692365c0..dc6a4208 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -7657,6 +7657,34 @@ void GameScreen::renderLootRollPopup(game::GameHandler& gameHandler) { uint8_t q = roll.itemQuality; ImVec4 col = (q < 6) ? kQualityColors[q] : kQualityColors[1]; + // Countdown bar + { + auto now = std::chrono::steady_clock::now(); + float elapsedMs = static_cast( + std::chrono::duration_cast(now - roll.rollStartedAt).count()); + float totalMs = static_cast(roll.rollCountdownMs > 0 ? roll.rollCountdownMs : 60000); + float fraction = 1.0f - std::min(elapsedMs / totalMs, 1.0f); + float remainSec = (totalMs - elapsedMs) / 1000.0f; + if (remainSec < 0.0f) remainSec = 0.0f; + + // Color: green → yellow → red + ImVec4 barColor; + if (fraction > 0.5f) + barColor = ImVec4(0.2f + (1.0f - fraction) * 1.4f, 0.85f, 0.2f, 1.0f); + else if (fraction > 0.2f) + barColor = ImVec4(1.0f, fraction * 1.7f, 0.1f, 1.0f); + else { + float pulse = 0.7f + 0.3f * std::sin(static_cast(ImGui::GetTime()) * 6.0f); + barColor = ImVec4(pulse, 0.1f * pulse, 0.1f * pulse, 1.0f); + } + + char timeBuf[16]; + std::snprintf(timeBuf, sizeof(timeBuf), "%.0fs", remainSec); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, barColor); + ImGui::ProgressBar(fraction, ImVec2(-1, 12), timeBuf); + ImGui::PopStyleColor(); + } + ImGui::Text("An item is up for rolls:"); // Show item icon if available