mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
feat: add center-screen raid warning and boss emote overlay
RAID_WARNING messages show as flashing red/yellow large text. RAID_BOSS_EMOTE and MONSTER_EMOTE show as amber text. Each message fades in quickly, holds for 5 seconds, then fades out. Up to 3 messages stack vertically below the target frame area. Dark semi-transparent background box improves readability. Messages are detected from new chat history entries each frame.
This commit is contained in:
parent
66ec35b106
commit
797bb5d964
2 changed files with 103 additions and 0 deletions
|
|
@ -460,6 +460,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderQuestObjectiveTracker(gameHandler);
|
||||
renderNameplates(gameHandler); // player names always shown; NPC plates gated by showNameplates_
|
||||
renderBattlegroundScore(gameHandler);
|
||||
renderRaidWarningOverlay(gameHandler);
|
||||
renderCombatText(gameHandler);
|
||||
renderUIErrors(gameHandler, ImGui::GetIO().DeltaTime);
|
||||
renderRepToasts(ImGui::GetIO().DeltaTime);
|
||||
|
|
@ -5806,6 +5807,96 @@ void GameScreen::renderQuestObjectiveTracker(game::GameHandler& gameHandler) {
|
|||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Raid Warning / Boss Emote Center-Screen Overlay
|
||||
// ============================================================
|
||||
|
||||
void GameScreen::renderRaidWarningOverlay(game::GameHandler& gameHandler) {
|
||||
// Scan chat history for new RAID_WARNING / RAID_BOSS_EMOTE messages
|
||||
const auto& chatHistory = gameHandler.getChatHistory();
|
||||
size_t newCount = chatHistory.size();
|
||||
if (newCount > raidWarnChatSeenCount_) {
|
||||
// Walk only the new messages (deque — iterate from back by skipping old ones)
|
||||
size_t toScan = newCount - raidWarnChatSeenCount_;
|
||||
size_t startIdx = newCount > toScan ? newCount - toScan : 0;
|
||||
for (size_t i = startIdx; i < newCount; ++i) {
|
||||
const auto& msg = chatHistory[i];
|
||||
if (msg.type == game::ChatType::RAID_WARNING ||
|
||||
msg.type == game::ChatType::RAID_BOSS_EMOTE ||
|
||||
msg.type == game::ChatType::MONSTER_EMOTE) {
|
||||
bool isBoss = (msg.type != game::ChatType::RAID_WARNING);
|
||||
// Limit display text length to avoid giant overlay
|
||||
std::string text = msg.message;
|
||||
if (text.size() > 200) text = text.substr(0, 200) + "...";
|
||||
raidWarnEntries_.push_back({text, 0.0f, isBoss});
|
||||
if (raidWarnEntries_.size() > 3)
|
||||
raidWarnEntries_.erase(raidWarnEntries_.begin());
|
||||
}
|
||||
}
|
||||
raidWarnChatSeenCount_ = newCount;
|
||||
}
|
||||
|
||||
// Age and remove expired entries
|
||||
float dt = ImGui::GetIO().DeltaTime;
|
||||
for (auto& e : raidWarnEntries_) e.age += dt;
|
||||
raidWarnEntries_.erase(
|
||||
std::remove_if(raidWarnEntries_.begin(), raidWarnEntries_.end(),
|
||||
[](const RaidWarnEntry& e){ return e.age >= RaidWarnEntry::LIFETIME; }),
|
||||
raidWarnEntries_.end());
|
||||
|
||||
if (raidWarnEntries_.empty()) return;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
float screenW = io.DisplaySize.x;
|
||||
float screenH = io.DisplaySize.y;
|
||||
ImDrawList* fg = ImGui::GetForegroundDrawList();
|
||||
|
||||
// Stack entries vertically near upper-center (below target frame area)
|
||||
float baseY = screenH * 0.28f;
|
||||
for (const auto& e : raidWarnEntries_) {
|
||||
float alpha = std::clamp(1.0f - (e.age / RaidWarnEntry::LIFETIME), 0.0f, 1.0f);
|
||||
// Fade in quickly, hold, then fade out last 20%
|
||||
if (e.age < 0.3f) alpha = e.age / 0.3f;
|
||||
|
||||
// Truncate to fit screen width reasonably
|
||||
const char* txt = e.text.c_str();
|
||||
const float fontSize = 22.0f;
|
||||
ImFont* font = ImGui::GetFont();
|
||||
|
||||
// Word-wrap manually: compute text size, center horizontally
|
||||
float maxW = screenW * 0.7f;
|
||||
ImVec2 textSz = font->CalcTextSizeA(fontSize, maxW, maxW, txt);
|
||||
float tx = (screenW - textSz.x) * 0.5f;
|
||||
|
||||
ImU32 shadowCol = IM_COL32(0, 0, 0, static_cast<int>(alpha * 200));
|
||||
ImU32 mainCol;
|
||||
if (e.isBossEmote) {
|
||||
mainCol = IM_COL32(255, 185, 60, static_cast<int>(alpha * 255)); // amber
|
||||
} else {
|
||||
// Raid warning: alternating red/yellow flash during first second
|
||||
float flashT = std::fmod(e.age * 4.0f, 1.0f);
|
||||
if (flashT < 0.5f)
|
||||
mainCol = IM_COL32(255, 50, 50, static_cast<int>(alpha * 255));
|
||||
else
|
||||
mainCol = IM_COL32(255, 220, 50, static_cast<int>(alpha * 255));
|
||||
}
|
||||
|
||||
// Background dim box for readability
|
||||
float pad = 8.0f;
|
||||
fg->AddRectFilled(ImVec2(tx - pad, baseY - pad),
|
||||
ImVec2(tx + textSz.x + pad, baseY + textSz.y + pad),
|
||||
IM_COL32(0, 0, 0, static_cast<int>(alpha * 120)), 4.0f);
|
||||
|
||||
// Shadow + main text
|
||||
fg->AddText(font, fontSize, ImVec2(tx + 2.0f, baseY + 2.0f), shadowCol, txt,
|
||||
nullptr, maxW);
|
||||
fg->AddText(font, fontSize, ImVec2(tx, baseY), mainCol, txt,
|
||||
nullptr, maxW);
|
||||
|
||||
baseY += textSz.y + 6.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Floating Combat Text (Phase 2)
|
||||
// ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue