mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: show area trigger messages as screen banners
SMSG_AREA_TRIGGER_MESSAGE events (dungeon enter messages, objective triggers, etc.) were previously only appended to chat. Now they also appear as animated slide-up toasts in the lower-center of the screen: blue-bordered dark panel with light-blue text, 4.5s lifetime with 35ms slide-in/out animation. Up to 4 simultaneous toasts stack vertically. Messages still go to chat as before.
This commit is contained in:
parent
9fe7bbf826
commit
20fef40b7b
4 changed files with 85 additions and 1 deletions
|
|
@ -540,6 +540,15 @@ public:
|
|||
const std::deque<CombatLogEntry>& getCombatLog() const { return combatLog_; }
|
||||
void clearCombatLog() { combatLog_.clear(); }
|
||||
|
||||
// Area trigger messages (SMSG_AREA_TRIGGER_MESSAGE) — drained by UI each frame
|
||||
bool hasAreaTriggerMsg() const { return !areaTriggerMsgs_.empty(); }
|
||||
std::string popAreaTriggerMsg() {
|
||||
if (areaTriggerMsgs_.empty()) return {};
|
||||
std::string msg = areaTriggerMsgs_.front();
|
||||
areaTriggerMsgs_.pop_front();
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Threat
|
||||
struct ThreatEntry {
|
||||
uint64_t victimGuid = 0;
|
||||
|
|
@ -2155,6 +2164,7 @@ private:
|
|||
std::vector<CombatTextEntry> combatText;
|
||||
static constexpr size_t MAX_COMBAT_LOG = 500;
|
||||
std::deque<CombatLogEntry> combatLog_;
|
||||
std::deque<std::string> areaTriggerMsgs_;
|
||||
// unitGuid → sorted threat list (descending by threat value)
|
||||
std::unordered_map<uint64_t, std::vector<ThreatEntry>> threatLists_;
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ private:
|
|||
// Zone entry toast: brief banner when entering a new zone
|
||||
struct ZoneToastEntry { std::string zoneName; float age = 0.0f; };
|
||||
std::vector<ZoneToastEntry> zoneToasts_;
|
||||
|
||||
struct AreaTriggerToast { std::string text; float age = 0.0f; };
|
||||
std::vector<AreaTriggerToast> areaTriggerToasts_;
|
||||
void renderAreaTriggerToasts(float deltaTime, game::GameHandler& gameHandler);
|
||||
std::string lastKnownZone_;
|
||||
static constexpr float kZoneToastLifetime = 3.0f;
|
||||
|
||||
|
|
|
|||
|
|
@ -3877,7 +3877,10 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
/*uint32_t len =*/ packet.readUInt32();
|
||||
std::string msg = packet.readString();
|
||||
if (!msg.empty()) addSystemChatMessage(msg);
|
||||
if (!msg.empty()) {
|
||||
addSystemChatMessage(msg);
|
||||
areaTriggerMsgs_.push_back(msg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -562,6 +562,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderRepToasts(ImGui::GetIO().DeltaTime);
|
||||
renderQuestCompleteToasts(ImGui::GetIO().DeltaTime);
|
||||
renderZoneToasts(ImGui::GetIO().DeltaTime);
|
||||
renderAreaTriggerToasts(ImGui::GetIO().DeltaTime, gameHandler);
|
||||
if (showRaidFrames_) {
|
||||
renderPartyFrames(gameHandler);
|
||||
}
|
||||
|
|
@ -8572,6 +8573,72 @@ void GameScreen::renderZoneToasts(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// ─── Area Trigger Message Toasts ─────────────────────────────────────────────
|
||||
void GameScreen::renderAreaTriggerToasts(float deltaTime, game::GameHandler& gameHandler) {
|
||||
// Drain any pending messages from GameHandler
|
||||
while (gameHandler.hasAreaTriggerMsg()) {
|
||||
AreaTriggerToast t;
|
||||
t.text = gameHandler.popAreaTriggerMsg();
|
||||
t.age = 0.0f;
|
||||
areaTriggerToasts_.push_back(std::move(t));
|
||||
if (areaTriggerToasts_.size() > 4)
|
||||
areaTriggerToasts_.erase(areaTriggerToasts_.begin());
|
||||
}
|
||||
|
||||
// Age and prune
|
||||
constexpr float kLifetime = 4.5f;
|
||||
for (auto& t : areaTriggerToasts_) t.age += deltaTime;
|
||||
areaTriggerToasts_.erase(
|
||||
std::remove_if(areaTriggerToasts_.begin(), areaTriggerToasts_.end(),
|
||||
[](const AreaTriggerToast& t) { return t.age >= kLifetime; }),
|
||||
areaTriggerToasts_.end());
|
||||
if (areaTriggerToasts_.empty()) return;
|
||||
|
||||
auto* window = core::Application::getInstance().getWindow();
|
||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||
|
||||
ImDrawList* draw = ImGui::GetForegroundDrawList();
|
||||
ImFont* font = ImGui::GetFont();
|
||||
constexpr float kSlideDur = 0.35f;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(areaTriggerToasts_.size()); ++i) {
|
||||
const auto& t = areaTriggerToasts_[i];
|
||||
|
||||
float slideIn = std::min(t.age, kSlideDur) / kSlideDur;
|
||||
float slideOut = std::min(std::max(0.0f, kLifetime - t.age), kSlideDur) / kSlideDur;
|
||||
float alpha = std::clamp(std::min(slideIn, slideOut), 0.0f, 1.0f);
|
||||
|
||||
// Measure text
|
||||
ImVec2 txtSz = font->CalcTextSizeA(13.0f, FLT_MAX, 0.0f, t.text.c_str());
|
||||
float toastW = txtSz.x + 30.0f;
|
||||
float toastH = 30.0f;
|
||||
|
||||
// Center horizontally, place below zone text (center of lower-third)
|
||||
float toastX = (screenW - toastW) * 0.5f;
|
||||
float toastY = screenH * 0.62f + i * (toastH + 3.0f);
|
||||
// Slide up from below
|
||||
float offY = (1.0f - std::min(slideIn, slideOut)) * (toastH + 12.0f);
|
||||
toastY += offY;
|
||||
|
||||
ImVec2 tl(toastX, toastY);
|
||||
ImVec2 br(toastX + toastW, toastY + toastH);
|
||||
|
||||
draw->AddRectFilled(tl, br, IM_COL32(8, 12, 22, (int)(alpha * 190)), 5.0f);
|
||||
draw->AddRect(tl, br, IM_COL32(100, 160, 220, (int)(alpha * 200)), 5.0f, 0, 1.0f);
|
||||
|
||||
float cx = tl.x + toastW * 0.5f;
|
||||
// Shadow
|
||||
draw->AddText(font, 13.0f,
|
||||
ImVec2(cx - txtSz.x * 0.5f + 1, tl.y + (toastH - txtSz.y) * 0.5f + 1),
|
||||
IM_COL32(0, 0, 0, (int)(alpha * 180)), t.text.c_str());
|
||||
// Text in light blue
|
||||
draw->AddText(font, 13.0f,
|
||||
ImVec2(cx - txtSz.x * 0.5f, tl.y + (toastH - txtSz.y) * 0.5f),
|
||||
IM_COL32(180, 220, 255, (int)(alpha * 240)), t.text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Boss Encounter Frames
|
||||
// ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue