mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: show whisper toast notification when a player whispers you
Adds a slide-in toast overlay at the bottom-left of the screen whenever an incoming whisper arrives. Toasts display "Whisper from:", the sender name in gold, and a truncated message preview. Up to 3 toasts stack with a 5s lifetime; each fades in over 0.25s and fades out in the final 1s.
This commit is contained in:
parent
77879769d3
commit
5216582f15
2 changed files with 112 additions and 0 deletions
|
|
@ -527,6 +527,17 @@ private:
|
|||
bool areaDiscoveryCallbackSet_ = false;
|
||||
void renderDiscoveryToast();
|
||||
|
||||
// Whisper toast — brief overlay at screen top when a whisper arrives while chat is not focused
|
||||
struct WhisperToastEntry {
|
||||
std::string sender;
|
||||
std::string preview; // first ~60 chars of message
|
||||
float age = 0.0f;
|
||||
};
|
||||
static constexpr float WHISPER_TOAST_DURATION = 5.0f;
|
||||
std::vector<WhisperToastEntry> whisperToasts_;
|
||||
size_t whisperSeenCount_ = 0; // how many chat entries have been scanned for whispers
|
||||
void renderWhisperToasts();
|
||||
|
||||
// Zone discovery text ("Entering: <ZoneName>")
|
||||
static constexpr float ZONE_TEXT_DURATION = 5.0f;
|
||||
float zoneTextTimer_ = 0.0f;
|
||||
|
|
|
|||
|
|
@ -639,6 +639,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderDingEffect();
|
||||
renderAchievementToast();
|
||||
renderDiscoveryToast();
|
||||
renderWhisperToasts();
|
||||
renderZoneText();
|
||||
|
||||
// World map (M key toggle handled inside)
|
||||
|
|
@ -1689,6 +1690,32 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
chatMentionSeenCount_ = chatHistory.size(); // reset if history was cleared
|
||||
}
|
||||
|
||||
// Scan NEW messages for incoming whispers and push a toast notification
|
||||
{
|
||||
size_t histSize = chatHistory.size();
|
||||
if (histSize < whisperSeenCount_) whisperSeenCount_ = histSize; // cleared
|
||||
for (size_t wi = whisperSeenCount_; wi < histSize; ++wi) {
|
||||
const auto& wMsg = chatHistory[wi];
|
||||
if (wMsg.type == game::ChatType::WHISPER ||
|
||||
wMsg.type == game::ChatType::RAID_BOSS_WHISPER) {
|
||||
WhisperToastEntry toast;
|
||||
toast.sender = wMsg.senderName;
|
||||
if (toast.sender.empty() && wMsg.senderGuid != 0)
|
||||
toast.sender = gameHandler.lookupName(wMsg.senderGuid);
|
||||
if (toast.sender.empty()) toast.sender = "Unknown";
|
||||
// Truncate preview to 60 chars
|
||||
toast.preview = wMsg.message.size() > 60
|
||||
? wMsg.message.substr(0, 57) + "..."
|
||||
: wMsg.message;
|
||||
toast.age = 0.0f;
|
||||
// Keep at most 3 stacked toasts
|
||||
if (whisperToasts_.size() >= 3) whisperToasts_.erase(whisperToasts_.begin());
|
||||
whisperToasts_.push_back(std::move(toast));
|
||||
}
|
||||
}
|
||||
whisperSeenCount_ = histSize;
|
||||
}
|
||||
|
||||
int chatMsgIdx = 0;
|
||||
for (const auto& msg : chatHistory) {
|
||||
if (!shouldShowMessage(msg, activeChatTab_)) continue;
|
||||
|
|
@ -18012,6 +18039,80 @@ void GameScreen::renderDiscoveryToast() {
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Whisper toast notifications — brief overlay when a player whispers you
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void GameScreen::renderWhisperToasts() {
|
||||
if (whisperToasts_.empty()) return;
|
||||
|
||||
float dt = ImGui::GetIO().DeltaTime;
|
||||
|
||||
// Age and prune expired toasts
|
||||
for (auto& t : whisperToasts_) t.age += dt;
|
||||
whisperToasts_.erase(
|
||||
std::remove_if(whisperToasts_.begin(), whisperToasts_.end(),
|
||||
[](const WhisperToastEntry& t) { return t.age >= WHISPER_TOAST_DURATION; }),
|
||||
whisperToasts_.end());
|
||||
if (whisperToasts_.empty()) return;
|
||||
|
||||
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
||||
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
||||
|
||||
// Stack toasts at bottom-left, above the action bars (y ≈ screenH * 0.72)
|
||||
// Each toast is ~56px tall with a 4px gap between them.
|
||||
constexpr float TOAST_W = 280.0f;
|
||||
constexpr float TOAST_H = 56.0f;
|
||||
constexpr float TOAST_GAP = 4.0f;
|
||||
constexpr float TOAST_X = 14.0f; // left edge (won't cover action bars)
|
||||
float baseY = screenH * 0.72f;
|
||||
|
||||
ImDrawList* bgDL = ImGui::GetBackgroundDrawList();
|
||||
|
||||
const int count = static_cast<int>(whisperToasts_.size());
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto& toast = whisperToasts_[i];
|
||||
|
||||
// Fade in over 0.25s; fade out in last 1.0s
|
||||
float alpha;
|
||||
float remaining = WHISPER_TOAST_DURATION - toast.age;
|
||||
if (toast.age < 0.25f)
|
||||
alpha = toast.age / 0.25f;
|
||||
else if (remaining < 1.0f)
|
||||
alpha = remaining;
|
||||
else
|
||||
alpha = 1.0f;
|
||||
alpha = std::clamp(alpha, 0.0f, 1.0f);
|
||||
|
||||
// Slide-in from left: offset 0→0 after 0.25s
|
||||
float slideX = (toast.age < 0.25f) ? (TOAST_W * (1.0f - toast.age / 0.25f)) : 0.0f;
|
||||
float tx = TOAST_X - slideX;
|
||||
float ty = baseY - (count - i) * (TOAST_H + TOAST_GAP);
|
||||
|
||||
uint8_t bgA = static_cast<uint8_t>(210 * alpha);
|
||||
uint8_t fgA = static_cast<uint8_t>(255 * alpha);
|
||||
|
||||
// Background panel — dark purple tint (whisper color convention)
|
||||
bgDL->AddRectFilled(ImVec2(tx, ty), ImVec2(tx + TOAST_W, ty + TOAST_H),
|
||||
IM_COL32(25, 10, 40, bgA), 6.0f);
|
||||
// Purple border
|
||||
bgDL->AddRect(ImVec2(tx, ty), ImVec2(tx + TOAST_W, ty + TOAST_H),
|
||||
IM_COL32(160, 80, 220, static_cast<uint8_t>(180 * alpha)), 6.0f, 0, 1.5f);
|
||||
|
||||
// "Whisper" label (small, purple-ish)
|
||||
bgDL->AddText(ImVec2(tx + 10.0f, ty + 6.0f),
|
||||
IM_COL32(190, 110, 255, fgA), "Whisper from:");
|
||||
|
||||
// Sender name (gold)
|
||||
bgDL->AddText(ImVec2(tx + 10.0f, ty + 20.0f),
|
||||
IM_COL32(255, 210, 50, fgA), toast.sender.c_str());
|
||||
|
||||
// Message preview (white, dimmer)
|
||||
bgDL->AddText(ImVec2(tx + 10.0f, ty + 36.0f),
|
||||
IM_COL32(220, 220, 220, static_cast<uint8_t>(200 * alpha)),
|
||||
toast.preview.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Zone discovery text — "Entering: <ZoneName>" fades in/out at screen centre
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue