* Adds ability for Notifiations to not make a noise This is probably the only feature that will use it, the noise makes sense for most things we want to use notifications for, but it playing on every scene transition was a bit distracting. * Adds a hook for OnSeqPlayerInit * Uses new hook and displays notification instead of overlay text * Changes names to prevent collisions Will be registering other types of hooks that will need different ShipInitFuncs in this same file later. * Change Icon * Change CVarName and remove now-unused duration slider * Update ConfigMigrator for CVar changes. * clang-format * fix * bring back duration control * config v4 * fix v4 migration --------- Co-authored-by: Christopher Leggett <chris@leggett.dev> Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>
141 lines
5.3 KiB
C++
141 lines
5.3 KiB
C++
|
|
#include "Notification.h"
|
|
#include <libultraship/libultraship.h>
|
|
#include "soh/OTRGlobals.h"
|
|
|
|
extern "C" {
|
|
#include "functions.h"
|
|
#include "macros.h"
|
|
#include "variables.h"
|
|
}
|
|
|
|
namespace Notification {
|
|
|
|
static uint32_t nextId = 0;
|
|
static std::vector<Options> notifications = {};
|
|
|
|
void Window::Draw() {
|
|
auto vp = ImGui::GetMainViewport();
|
|
|
|
const float margin = 30.0f;
|
|
const float padding = 10.0f;
|
|
|
|
int position = CVarGetInteger(CVAR_SETTING("Notifications.Position"), 3);
|
|
|
|
// Top Left
|
|
ImVec2 basePosition;
|
|
switch (position) {
|
|
case 0: // Top Left
|
|
basePosition = ImVec2(vp->Pos.x + margin, vp->Pos.y + margin);
|
|
break;
|
|
case 1: // Top Right
|
|
basePosition = ImVec2(vp->Pos.x + vp->Size.x - margin, vp->Pos.y + margin);
|
|
break;
|
|
case 2: // Bottom Left
|
|
basePosition = ImVec2(vp->Pos.x + margin, vp->Pos.y + vp->Size.y - margin);
|
|
break;
|
|
case 3: // Bottom Right
|
|
basePosition = ImVec2(vp->Pos.x + vp->Size.x - margin, vp->Pos.y + vp->Size.y - margin);
|
|
break;
|
|
case 4: // Hidden
|
|
return;
|
|
}
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_WindowBg,
|
|
ImVec4(0, 0, 0, CVarGetFloat(CVAR_SETTING("Notifications.BgOpacity"), 0.5f)));
|
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
|
|
|
|
for (int index = 0; index < notifications.size(); ++index) {
|
|
auto& notification = notifications[index];
|
|
int inverseIndex = -ABS(index - (notifications.size() - 1));
|
|
|
|
ImGui::SetNextWindowViewport(vp->ID);
|
|
if (notification.remainingTime < 4.0f) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, (notification.remainingTime - 1) / 3.0f);
|
|
} else {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.0f);
|
|
}
|
|
|
|
ImGui::Begin(("notification#" + std::to_string(notification.id)).c_str(), nullptr,
|
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing |
|
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar |
|
|
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings);
|
|
|
|
ImGui::SetWindowFontScale(CVarGetFloat(CVAR_SETTING("Notifications.Size"), 1.8f)); // Make this adjustable
|
|
|
|
ImVec2 notificationPos;
|
|
switch (position) {
|
|
case 0: // Top Left
|
|
notificationPos =
|
|
ImVec2(basePosition.x, basePosition.y + ((ImGui::GetWindowSize().y + padding) * inverseIndex));
|
|
break;
|
|
case 1: // Top Right
|
|
notificationPos = ImVec2(basePosition.x - ImGui::GetWindowSize().x,
|
|
basePosition.y + ((ImGui::GetWindowSize().y + padding) * inverseIndex));
|
|
break;
|
|
case 2: // Bottom Left
|
|
notificationPos = ImVec2(basePosition.x,
|
|
basePosition.y - ((ImGui::GetWindowSize().y + padding) * (inverseIndex + 1)));
|
|
break;
|
|
case 3: // Bottom Right
|
|
notificationPos = ImVec2(basePosition.x - ImGui::GetWindowSize().x,
|
|
basePosition.y - ((ImGui::GetWindowSize().y + padding) * (inverseIndex + 1)));
|
|
break;
|
|
}
|
|
|
|
ImGui::SetWindowPos(notificationPos);
|
|
|
|
if (notification.itemIcon != nullptr) {
|
|
ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(notification.itemIcon),
|
|
ImVec2(24, 24));
|
|
ImGui::SameLine();
|
|
}
|
|
if (!notification.prefix.empty()) {
|
|
ImGui::TextColored(notification.prefixColor, "%s", notification.prefix.c_str());
|
|
ImGui::SameLine();
|
|
}
|
|
ImGui::TextColored(notification.messageColor, "%s", notification.message.c_str());
|
|
if (!notification.suffix.empty()) {
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(notification.suffixColor, "%s", notification.suffix.c_str());
|
|
}
|
|
|
|
ImGui::End();
|
|
ImGui::PopStyleVar();
|
|
}
|
|
|
|
ImGui::PopStyleVar();
|
|
ImGui::PopStyleColor(2);
|
|
}
|
|
|
|
void Window::UpdateElement() {
|
|
for (int index = 0; index < notifications.size(); ++index) {
|
|
auto& notification = notifications[index];
|
|
|
|
// decrement remainingTime
|
|
notification.remainingTime -= ImGui::GetIO().DeltaTime;
|
|
|
|
// remove notification if it has expired
|
|
if (notification.remainingTime <= 0) {
|
|
notifications.erase(notifications.begin() + index);
|
|
--index;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Emit(Options notification) {
|
|
notification.id = nextId++;
|
|
if (notification.remainingTime == 0.0f) {
|
|
notification.remainingTime = CVarGetFloat(CVAR_SETTING("Notifications.Duration"), 10.0f);
|
|
}
|
|
notifications.push_back(notification);
|
|
if (!notification.mute) {
|
|
Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
|
|
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
}
|
|
}
|
|
|
|
} // namespace Notification
|