From e49be12297da9860421cd16371d3cffffa1a3e56 Mon Sep 17 00:00:00 2001 From: qwertyquerty Date: Mon, 4 May 2026 09:25:13 -0700 Subject: [PATCH] RmlUi improvements (#663) * rmlui audio * fix menu select sound * Fixes #662 * fix reset logic and fix popup getting stuck closed * fix X button on menu popup * rmlui achievements, and fix open/close bug * presets, achievements css, and menu sounds toggle * forgor * fix b button causing audio when menu not visible --- files.cmake | 8 +- include/dusk/settings.h | 1 + include/m_Do/m_Do_audio.h | 13 ++ res/rml/window.rcss | 133 ++++++++++++++ src/dusk/imgui/ImGuiAchievements.cpp | 245 ------------------------- src/dusk/imgui/ImGuiAchievements.hpp | 23 --- src/dusk/imgui/ImGuiConsole.cpp | 14 +- src/dusk/imgui/ImGuiConsole.hpp | 3 +- src/dusk/imgui/ImGuiFirstRunPreset.cpp | 141 -------------- src/dusk/imgui/ImGuiFirstRunPreset.hpp | 14 -- src/dusk/imgui/ImGuiMenuTools.cpp | 63 ++++++- src/dusk/imgui/ImGuiMenuTools.hpp | 11 +- src/dusk/settings.cpp | 2 + src/dusk/ui/achievements.cpp | 208 +++++++++++++++++++++ src/dusk/ui/achievements.hpp | 19 ++ src/dusk/ui/bool_button.cpp | 7 +- src/dusk/ui/button.cpp | 4 + src/dusk/ui/document.cpp | 18 +- src/dusk/ui/input.cpp | 5 +- src/dusk/ui/number_button.cpp | 15 +- src/dusk/ui/overlay.cpp | 5 + src/dusk/ui/pane.cpp | 3 + src/dusk/ui/popup.cpp | 29 ++- src/dusk/ui/popup.hpp | 1 + src/dusk/ui/preset.cpp | 191 +++++++++++++++++++ src/dusk/ui/preset.hpp | 26 +++ src/dusk/ui/settings.cpp | 5 + src/dusk/ui/tab_bar.cpp | 66 +++++-- src/dusk/ui/tab_bar.hpp | 3 + src/dusk/ui/window.cpp | 19 +- src/dusk/ui/window.hpp | 1 + 31 files changed, 826 insertions(+), 470 deletions(-) delete mode 100644 src/dusk/imgui/ImGuiAchievements.cpp delete mode 100644 src/dusk/imgui/ImGuiAchievements.hpp delete mode 100644 src/dusk/imgui/ImGuiFirstRunPreset.cpp delete mode 100644 src/dusk/imgui/ImGuiFirstRunPreset.hpp create mode 100644 src/dusk/ui/achievements.cpp create mode 100644 src/dusk/ui/achievements.hpp create mode 100644 src/dusk/ui/preset.cpp create mode 100644 src/dusk/ui/preset.hpp diff --git a/files.cmake b/files.cmake index d238203776..f06884fb35 100644 --- a/files.cmake +++ b/files.cmake @@ -1449,8 +1449,6 @@ set(DUSK_FILES src/dusk/imgui/ImGuiMenuTools.hpp src/dusk/imgui/ImGuiPreLaunchWindow.cpp src/dusk/imgui/ImGuiPreLaunchWindow.hpp - src/dusk/imgui/ImGuiFirstRunPreset.hpp - src/dusk/imgui/ImGuiFirstRunPreset.cpp src/dusk/imgui/ImGuiProcessOverlay.cpp src/dusk/imgui/ImGuiCameraOverlay.cpp src/dusk/imgui/ImGuiHeapOverlay.cpp @@ -1461,8 +1459,6 @@ set(DUSK_FILES src/dusk/imgui/ImGuiSaveEditor.cpp src/dusk/imgui/ImGuiStateShare.hpp src/dusk/imgui/ImGuiStateShare.cpp - src/dusk/imgui/ImGuiAchievements.hpp - src/dusk/imgui/ImGuiAchievements.cpp src/dusk/ui/bool_button.cpp src/dusk/ui/bool_button.hpp src/dusk/ui/button.cpp @@ -1473,6 +1469,10 @@ set(DUSK_FILES src/dusk/ui/controller_config.hpp src/dusk/ui/document.cpp src/dusk/ui/document.hpp + src/dusk/ui/achievements.cpp + src/dusk/ui/achievements.hpp + src/dusk/ui/preset.cpp + src/dusk/ui/preset.hpp src/dusk/ui/editor.cpp src/dusk/ui/editor.hpp src/dusk/ui/event.cpp diff --git a/include/dusk/settings.h b/include/dusk/settings.h index ab9feaecd7..857a6c856d 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -56,6 +56,7 @@ struct UserSettings { ConfigVar fanfareVolume; ConfigVar enableReverb; ConfigVar enableHrtf; + ConfigVar menuSounds; } audio; // Game settings diff --git a/include/m_Do/m_Do_audio.h b/include/m_Do/m_Do_audio.h index 1d58e8d8f2..4bc2f20c94 100644 --- a/include/m_Do/m_Do_audio.h +++ b/include/m_Do/m_Do_audio.h @@ -5,6 +5,7 @@ #include "Z2AudioLib/Z2EnvSeMgr.h" #include "Z2AudioLib/Z2LinkMgr.h" #include "dusk/audio.h" +#include "dusk/settings.h" class mDoAud_zelAudio_c : public Z2AudioMgr { public: @@ -132,6 +133,18 @@ inline void mDoAud_seStart(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_re -1.0f, -1.0f, 0); } +#if TARGET_PC +inline void mDoAud_seStartMenu(u32 i_sfxID) { + if (!mDoAud_zelAudio_c::isInitFlag()) { + return; + } + if (!dusk::getSettings().audio.menuSounds.getValue()) { + return; + } + mDoAud_seStart(i_sfxID, nullptr, 0, 0); +} +#endif + inline void mDoAud_seStartLevel(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_reverb) { DUSK_AUDIO_SKIP() Z2AudioMgr::getInterface()->seStartLevel(i_sfxID, i_sePos, param_2, i_reverb, 1.0f, 1.0f, diff --git a/res/rml/window.rcss b/res/rml/window.rcss index 5cc5a2ca57..1fe36fdf00 100644 --- a/res/rml/window.rcss +++ b/res/rml/window.rcss @@ -256,3 +256,136 @@ icon.warning { decorator: text("" center center); color: #ffcc00; } + +.achievement-row { + display: flex; + align-items: flex-start; + gap: 10dp; + padding: 12dp 0; + border-bottom: 1dp rgba(146, 135, 91, 30%); +} + +.achievement-info { + display: block; + flex: 1 1 0; + min-width: 0; +} + +.achievement-header { + display: flex; + align-items: center; +} + +.achievement-name { + flex: 1; + font-weight: bold; +} + +.achievement-name.unlocked { + color: #ffa826; +} + +.achievement-badge { + font-size: 14dp; + opacity: 0.7; +} + +.achievement-badge.unlocked { + color: #44cc55; + opacity: 1; +} + +.achievement-badge.locked { + color: #cc4444; + opacity: 1; +} + +.achievement-desc { + display: block; + color: rgba(224, 219, 200, 55%); + font-size: 16dp; + margin: 4dp 0 0 0; +} + +.achievement-progress { + display: block; + font-size: 13dp; + color: rgba(224, 219, 200, 45%); +} + +progressbar { + display: block; + width: 100%; + height: 6dp; + border-radius: 3dp; + background-color: rgba(255, 255, 255, 10%); + margin: 6dp 0 2dp 0; +} + +progressbar.progress-done fill { + background-color: #44aa22; + border-radius: 3dp; +} + +progressbar.progress-ongoing fill { + background-color: #2255bb; + border-radius: 3dp; +} + +button.achievement-clear { + flex: 0 0 auto; + align-self: center; + font-size: 14dp; + padding: 2dp 8dp; + opacity: 0.45; +} + +.preset-dialog { + display: flex; + flex-flow: column; + padding: 32dp; + gap: 20dp; + flex: 1 1 0; +} + +.preset-title { + display: block; + font-family: "Fira Sans Condensed"; + font-weight: bold; + font-size: 30dp; + text-align: center; +} + +.preset-intro { + display: block; + font-size: 16dp; + text-align: center; + color: rgba(224, 219, 200, 65%); +} + +.preset-grid { + display: flex; + flex-direction: row; + gap: 20dp; + flex: 1 1 0; + align-items: flex-start; +} + +.preset-col { + display: flex; + flex-flow: column; + gap: 12dp; + flex: 1 1 0; +} + +button.preset-btn { + font-size: 22dp; + padding: 20dp 16dp; +} + +.preset-desc { + display: block; + font-size: 15dp; + color: rgba(224, 219, 200, 55%); + text-align: center; +} diff --git a/src/dusk/imgui/ImGuiAchievements.cpp b/src/dusk/imgui/ImGuiAchievements.cpp deleted file mode 100644 index f03e4176ed..0000000000 --- a/src/dusk/imgui/ImGuiAchievements.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "ImGuiAchievements.hpp" -#include "ImGuiConfig.hpp" -#include "dusk/achievements.h" -#include "dusk/settings.h" -#include "fmt/format.h" -#include "imgui.h" - -namespace dusk { - -void ImGuiAchievements::notify(std::string name) { - if (m_notifyTimer <= 0.f) { - m_notifyName = std::move(name); - m_notifyTimer = NOTIFY_DURATION; - } else { - m_notifyQueue.push(std::move(name)); - } -} - -void ImGuiAchievements::showNotification() { - if (!getSettings().game.enableAchievementNotifications.getValue()) { - return; - } - if (m_notifyTimer <= 0.f) { - if (m_notifyQueue.empty()) { - return; - } - m_notifyName = std::move(m_notifyQueue.front()); - m_notifyQueue.pop(); - m_notifyTimer = NOTIFY_DURATION; - } - - m_notifyTimer -= ImGui::GetIO().DeltaTime; - - const float alpha = std::min({ - m_notifyTimer / NOTIFY_FADE_TIME, - (NOTIFY_DURATION - m_notifyTimer) / NOTIFY_FADE_TIME, - 1.0f - }); - - const ImGuiViewport* viewport = ImGui::GetMainViewport(); - const float padding = 12.0f; - ImGui::SetNextWindowPos( - ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - padding, viewport->WorkPos.y + padding), - ImGuiCond_Always, ImVec2(1.0f, 0.0f) - ); - - ImGui::SetNextWindowBgAlpha(alpha * 0.92f); - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.06f, 0.01f, alpha * 0.92f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.8f, 0.1f, alpha)); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, alpha)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(14.0f, 10.0f)); - - constexpr ImGuiWindowFlags flags = - ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | - ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs; - - if (ImGui::Begin("##achievement_notify", nullptr, flags)) { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.82f, 0.1f, alpha)); - ImGui::TextUnformatted("Achievement Unlocked!"); - ImGui::PopStyleColor(); - ImGui::Spacing(); - ImGui::TextUnformatted(m_notifyName.c_str()); - } - ImGui::End(); - - ImGui::PopStyleVar(2); - ImGui::PopStyleColor(3); -} - -void ImGuiAchievements::draw(bool& open) { - showNotification(); - - if (!open) { - return; - } - - ImGui::SetNextWindowSizeConstraints(ImVec2(800, 200), ImVec2(1280, 900)); - ImGui::SetNextWindowSize(ImVec2(800, 480), ImGuiCond_FirstUseEver); - - if (!ImGui::Begin( - "Achievements", &open, - ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav) - ) - { - ImGui::End(); - return; - } - - const auto achievements = AchievementSystem::get().getAchievements(); - - int unlocked = 0; - for (const auto& a : achievements) { - if (a.unlocked) { - ++unlocked; - } - } - - ImGui::Text("%d / %d achievements unlocked", unlocked, (int)achievements.size()); - ImGui::SameLine(); - config::ImGuiCheckbox("Notifications", getSettings().game.enableAchievementNotifications); - ImGui::Separator(); - - static const struct { - AchievementCategory cat; - const char* label; - ImVec4 color; - } ACHIEVEMENT_CATEGORIES[] = { - {AchievementCategory::Story, "Story", ImVec4(1.0f, 0.82f, 0.1f, 1.0f)}, - {AchievementCategory::Collection, "Collection", ImVec4(0.3f, 0.85f, 0.4f, 1.0f)}, - {AchievementCategory::Challenge, "Challenge", ImVec4(1.0f, 0.65f, 0.15f, 1.0f)}, - {AchievementCategory::Minigame, "Minigame", ImVec4(0.5f, 0.85f, 1.0f, 1.0f)}, - {AchievementCategory::Misc, "Misc", ImVec4(0.65f, 0.65f, 0.65f, 1.0f)}, - {AchievementCategory::Glitched, "Glitched", ImVec4(0.75f, 0.4f, 1.0f, 1.0f)}, - }; - - const float footerHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - - if (ImGui::BeginTabBar("##achievement_tabs", ImGuiTabBarFlags_FittingPolicyScroll)) { - for (const auto& catInfo : ACHIEVEMENT_CATEGORIES) { - int catTotal = 0, catUnlocked = 0; - for (const auto& a : achievements) { - if (a.category == catInfo.cat) { - ++catTotal; - if (a.unlocked) { - ++catUnlocked; - } - } - } - if (catTotal == 0) { - continue; - } - - const std::string tabLabel = fmt::format("{} ({}/{})###{}", catInfo.label, catUnlocked, catTotal, catInfo.label); - - ImGui::PushStyleColor(ImGuiCol_Text, catInfo.color); - const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str()); - ImGui::PopStyleColor(); - - if (tabOpen) { - ImGui::BeginChild( - "##cat_list", - ImVec2(0, -footerHeight), - ImGuiChildFlags_None, - ImGuiWindowFlags_NoBackground - ); - - ImGui::Spacing(); - - for (const auto& a : achievements) { - if (a.category != catInfo.cat) { - continue; - } - ImGui::PushID(a.key); - ImGui::BeginGroup(); - - ImGui::PushStyleColor( - ImGuiCol_Text, - a.unlocked ? - ImVec4(1.0f, 0.65f, 0.15f, 1.0f) : - ImGui::GetStyleColorVec4(ImGuiCol_Text) - ); - - ImGui::TextUnformatted(a.name); - ImGui::PopStyleColor(); - - const char* statusLabel = a.unlocked ? "[Unlocked]" : "[Locked]"; - ImGui::SameLine( - ImGui::GetContentRegionMax().x - - ImGui::CalcTextSize(statusLabel).x - ); - - if (a.unlocked) { - ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s", statusLabel); - } else { - ImGui::TextColored(ImVec4(0.8f, 0.2f, 0.2f, 1.0f), "%s", statusLabel); - } - - ImGui::TextDisabled("%s", a.description); - - if (a.isCounter) { - const float fraction = a.goal > 0 ? (float)(a.progress) / (float)(a.goal) : 1.0f; - const std::string overlay = fmt::format("{} / {}", a.progress, a.goal); - ImGui::PushStyleColor( - ImGuiCol_PlotHistogram, - a.unlocked ? - ImVec4(0.4f, 0.7f, 0.1f, 1.0f) : - ImVec4(0.2f, 0.45f, 0.8f, 1.0f) - ); - ImGui::ProgressBar(fraction, ImVec2(-1.0f, 0.0f), overlay.c_str()); - ImGui::PopStyleColor(); - } - - ImGui::EndGroup(); - - if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - ImGui::OpenPopup("##ctx"); - } - - if (ImGui::BeginPopup("##ctx")) { - ImGui::TextDisabled("%s", a.name); - ImGui::Separator(); - if (ImGui::MenuItem("Clear Achievement")) { - AchievementSystem::get().clearOne(a.key); - } - ImGui::EndPopup(); - } - - ImGui::Spacing(); - ImGui::PopID(); - } - - ImGui::EndChild(); - ImGui::EndTabItem(); - } - } - ImGui::EndTabBar(); - } - - ImGui::Separator(); - ImGui::Spacing(); - - if (ImGui::Button("Clear All Achievements")) { - ImGui::OpenPopup("##confirm_clear"); - } - - if (ImGui::BeginPopup("##confirm_clear")) { - ImGui::Text("Reset all achievement progress?"); - ImGui::Spacing(); - if (ImGui::Button("Yes, reset all")) { - AchievementSystem::get().clearAll(); - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("Cancel")) { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - - ImGui::End(); -} - -} // namespace dusk diff --git a/src/dusk/imgui/ImGuiAchievements.hpp b/src/dusk/imgui/ImGuiAchievements.hpp deleted file mode 100644 index 5ee77373fc..0000000000 --- a/src/dusk/imgui/ImGuiAchievements.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -namespace dusk { - -class ImGuiAchievements { -public: - void draw(bool& open); - void notify(std::string name); - -private: - void showNotification(); - - std::string m_notifyName; - float m_notifyTimer = 0.f; - std::queue m_notifyQueue; - static constexpr float NOTIFY_DURATION = 4.0f; - static constexpr float NOTIFY_FADE_TIME = 0.5f; -}; - -} // namespace dusk diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index b69cf7d355..daeeb472d3 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -10,6 +10,8 @@ #include "fmt/format.h" #include "ImGuiConsole.hpp" +#include "dusk/ui/preset.hpp" +#include "dusk/ui/ui.hpp" #include "JSystem/JUtility/JUTGamePad.h" #include "SDL3/SDL_mouse.h" #include "dusk/achievements.h" @@ -20,6 +22,8 @@ #include "dusk/livesplit.h" #include "dusk/main.h" #include "dusk/settings.h" +#include "f_pc/f_pc_manager.h" +#include "f_pc/f_pc_name.h" #include "m_Do/m_Do_controller_pad.h" #include "m_Do/m_Do_main.h" #include "tracy/Tracy.hpp" @@ -259,7 +263,8 @@ namespace dusk { } } - if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) && + if (!fpcM_SearchByName(fpcNm_LOGO_SCENE_e) && + (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) && ImGui::IsKeyPressed(ImGuiKey_R)) { JUTGamePad::C3ButtonReset::sResetSwitchPushing = true; @@ -300,7 +305,10 @@ namespace dusk { ImGui::PopStyleColor(); if (!getSettings().backend.wasPresetChosen) { - m_firstRunPreset.draw(); + if (!m_presetShown) { + m_presetShown = true; + dusk::ui::push_document(std::make_unique()); + } return; } @@ -342,7 +350,7 @@ namespace dusk { m_menuTools.ShowSaveEditor(); m_menuTools.ShowStateShare(); } - m_menuTools.ShowAchievements(); + m_menuTools.showAchievementNotification(); DuskDebugPad(); // temporary, remove later // Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds. diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index dcb170d3eb..c203ba5a5f 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -7,7 +7,6 @@ #include -#include "ImGuiFirstRunPreset.hpp" #include "ImGuiMenuGame.hpp" #include "ImGuiMenuTools.hpp" #include "ImGuiPreLaunchWindow.hpp" @@ -45,7 +44,7 @@ private: ImVec2 m_dragScrollLastMousePos = {}; std::deque m_toasts; - ImGuiFirstRunPreset m_firstRunPreset; + bool m_presetShown = false; ImGuiMenuGame m_menuGame; ImGuiPreLaunchWindow m_preLaunchWindow; diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.cpp b/src/dusk/imgui/ImGuiFirstRunPreset.cpp deleted file mode 100644 index 6b561cd877..0000000000 --- a/src/dusk/imgui/ImGuiFirstRunPreset.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "ImGuiFirstRunPreset.hpp" - -#include "imgui.h" -#include "ImGuiConsole.hpp" -#include "ImGuiEngine.hpp" -#include "dusk/settings.h" -#include "dusk/config.hpp" -#include - -namespace dusk { - -static void ApplyPresetClassic() { - auto& s = getSettings(); - s.video.lockAspectRatio.setValue(true); - s.game.bloomMode.setValue(BloomMode::Classic); - AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT); -} - -static void ApplyPresetHD() { - auto& s = getSettings(); - s.game.bloomMode.setValue(BloomMode::Classic); - s.game.hideTvSettingsScreen.setValue(true); - s.game.skipWarningScreen.setValue(true); - s.game.noReturnRupees.setValue(true); - s.game.disableRupeeCutscenes.setValue(true); - s.game.noSwordRecoil.setValue(true); - s.game.fastClimbing.setValue(true); - s.game.noMissClimbing.setValue(true); - s.game.fastTears.setValue(true); - s.game.biggerWallets.setValue(true); - s.game.invertCameraXAxis.setValue(true); - s.game.freeCamera.setValue(true); - s.game.no2ndFishForCat.setValue(true); -} - -static void ApplyPresetDusk() { - ApplyPresetHD(); - - auto& s = getSettings(); - s.game.enableAchievementNotifications.setValue(true); - s.game.enableQuickTransform.setValue(true); - s.game.instantSaves.setValue(true); - s.game.midnasLamentNonStop.setValue(true); - s.game.enableFrameInterpolation.setValue(true); - s.game.sunsSong.setValue(true); - s.game.bloomMode.setValue(BloomMode::Dusk); - s.game.autoSave.setValue(true); -} - -// ========================================================================= - -void ImGuiFirstRunPreset::draw() { - const char* modalTitle = "Welcome to Dusk!"; - - if (m_done) return; - - if (!m_opened) { - ImGui::OpenPopup(modalTitle); - m_opened = true; - } - - const ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::SetNextWindowSize(ImVec2(800.0f * ImGuiScale(), 0.0f), ImGuiCond_Always); - - if (!ImGui::BeginPopupModal(modalTitle, nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) { - // force the user to actually pick one, and not just hit escape to skip the dialog - m_opened = false; - return; - } - - ImGui::TextWrapped("Choose a preset to get started. You can change any setting later from the Enhancements menu."); - ImGui::Spacing(); - ImGui::Separator(); - ImGui::Spacing(); - - int chosen = -1; - - if (ImGui::BeginTable("##presets", 5, ImGuiTableFlags_None)) { - ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale()); - ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale()); - ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextRow(); - - ImGui::PushFont(ImGuiEngine::fontLarge); - - ImGui::TableSetColumnIndex(0); - if (ImGui::Button("Classic##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) { - chosen = 0; - } - - ImGui::TableSetColumnIndex(2); - if (ImGui::Button("HD##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) { - chosen = 1; - } - - ImGui::TableSetColumnIndex(4); - if (ImGui::Button("Dusk##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) - { - chosen = 2; - } - - ImGui::PopFont(); - - ImGui::TableNextRow(); - - ImGui::TableSetColumnIndex(0); - ImGui::Spacing(); - ImGui::TextWrapped("All enhancements disabled to match the GameCube version. Good for speedrunning or simple nostalgia!"); - - ImGui::TableSetColumnIndex(2); - ImGui::Spacing(); - ImGui::TextWrapped("Some enhancements enabled to match the HD version. A good starting point for most players!"); - - ImGui::TableSetColumnIndex(4); - ImGui::Spacing(); - ImGui::TextWrapped("More enhancements enabled than the HD preset. Veteran players will appreciate the additional tweaks!"); - - ImGui::EndTable(); - } - - if (chosen >= 0) { - if (chosen == 0) ApplyPresetClassic(); - if (chosen == 1) ApplyPresetHD(); - if (chosen == 2) ApplyPresetDusk(); - - getSettings().backend.wasPresetChosen.setValue(true); - config::Save(); - - m_done = true; - - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); -} - -} // namespace dusk diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.hpp b/src/dusk/imgui/ImGuiFirstRunPreset.hpp deleted file mode 100644 index 33ba6b3d0f..0000000000 --- a/src/dusk/imgui/ImGuiFirstRunPreset.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -namespace dusk { - -class ImGuiFirstRunPreset { -public: - void draw(); - -private: - bool m_opened = false; - bool m_done = false; -}; - -} // namespace dusk diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index da217022f8..714add87c4 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -66,7 +66,6 @@ namespace dusk { ImGui::EndDisabled(); } - ImGui::MenuItem("Achievements", nullptr, &m_showAchievements); #if DUSK_CAN_OPEN_DATA_FOLDER ImGui::Separator(); @@ -269,11 +268,65 @@ namespace dusk { ImGui::PopFont(); } - void ImGuiMenuTools::ShowAchievements() { - m_achievementsWindow.draw(m_showAchievements); + void ImGuiMenuTools::notifyAchievement(std::string name) { + if (m_notifyTimer <= 0.f) { + m_notifyName = std::move(name); + m_notifyTimer = NOTIFY_DURATION; + } else { + m_notifyQueue.push(std::move(name)); + } } - void ImGuiMenuTools::notifyAchievement(std::string name) { - m_achievementsWindow.notify(std::move(name)); + void ImGuiMenuTools::showAchievementNotification() { + if (!getSettings().game.enableAchievementNotifications.getValue()) { + return; + } + if (m_notifyTimer <= 0.f) { + if (m_notifyQueue.empty()) { + return; + } + m_notifyName = std::move(m_notifyQueue.front()); + m_notifyQueue.pop(); + m_notifyTimer = NOTIFY_DURATION; + } + + m_notifyTimer -= ImGui::GetIO().DeltaTime; + + const float alpha = std::min({ + m_notifyTimer / NOTIFY_FADE_TIME, + (NOTIFY_DURATION - m_notifyTimer) / NOTIFY_FADE_TIME, + 1.0f + }); + + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + const float padding = 12.0f; + ImGui::SetNextWindowPos( + ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - padding, viewport->WorkPos.y + padding), + ImGuiCond_Always, ImVec2(1.0f, 0.0f) + ); + + ImGui::SetNextWindowBgAlpha(alpha * 0.92f); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.06f, 0.01f, alpha * 0.92f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.8f, 0.1f, alpha)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, alpha)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(14.0f, 10.0f)); + + constexpr ImGuiWindowFlags flags = + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs; + + if (ImGui::Begin("##achievement_notify", nullptr, flags)) { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.82f, 0.1f, alpha)); + ImGui::TextUnformatted("Achievement Unlocked!"); + ImGui::PopStyleColor(); + ImGui::Spacing(); + ImGui::TextUnformatted(m_notifyName.c_str()); + } + ImGui::End(); + + ImGui::PopStyleVar(2); + ImGui::PopStyleColor(3); } } diff --git a/src/dusk/imgui/ImGuiMenuTools.hpp b/src/dusk/imgui/ImGuiMenuTools.hpp index de94ad2d8f..7ae0e5bec6 100644 --- a/src/dusk/imgui/ImGuiMenuTools.hpp +++ b/src/dusk/imgui/ImGuiMenuTools.hpp @@ -2,10 +2,10 @@ #define DUSK_IMGUI_MENUTOOLS_HPP #include +#include #include #include "imgui.h" -#include "ImGuiAchievements.hpp" #include "ImGuiSaveEditor.hpp" #include "ImGuiStateShare.hpp" @@ -27,8 +27,8 @@ namespace dusk { void ShowAudioDebug(); void ShowSaveEditor(); void ShowStateShare(); - void ShowAchievements(); void notifyAchievement(std::string name); + void showAchievementNotification(); private: bool m_showDebugOverlay = false; @@ -69,8 +69,11 @@ namespace dusk { bool m_showStateShare = false; ImGuiStateShare m_stateShare; - bool m_showAchievements = false; - ImGuiAchievements m_achievementsWindow; + std::string m_notifyName; + float m_notifyTimer = 0.f; + std::queue m_notifyQueue; + static constexpr float NOTIFY_DURATION = 4.0f; + static constexpr float NOTIFY_FADE_TIME = 0.5f; }; } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index e1f45ac26f..b0ac897d3a 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -18,6 +18,7 @@ UserSettings g_userSettings = { .fanfareVolume {"audio.fanfareVolume", 100}, .enableReverb {"audio.enableReverb", true}, .enableHrtf {"audio.enableHrtf", false}, + .menuSounds {"audio.menuSounds", true}, }, .game = { @@ -135,6 +136,7 @@ void registerSettings() { Register(g_userSettings.audio.fanfareVolume); Register(g_userSettings.audio.enableReverb); Register(g_userSettings.audio.enableHrtf); + Register(g_userSettings.audio.menuSounds); // Game Register(g_userSettings.game.language); diff --git a/src/dusk/ui/achievements.cpp b/src/dusk/ui/achievements.cpp new file mode 100644 index 0000000000..93993bb380 --- /dev/null +++ b/src/dusk/ui/achievements.cpp @@ -0,0 +1,208 @@ +#include "achievements.hpp" + +#include "Z2AudioLib/Z2SeMgr.h" +#include "dusk/achievements.h" +#include "fmt/format.h" +#include "m_Do/m_Do_audio.h" +#include "nav_types.hpp" +#include "pane.hpp" + +namespace dusk::ui { +namespace { + +struct CategoryInfo { + AchievementCategory cat; + const char* label; +}; + +constexpr CategoryInfo kCategories[] = { + {AchievementCategory::Story, "Story"}, + {AchievementCategory::Collection, "Collection"}, + {AchievementCategory::Challenge, "Challenge"}, + {AchievementCategory::Minigame, "Minigame"}, + {AchievementCategory::Misc, "Misc"}, + {AchievementCategory::Glitched, "Glitched"}, +}; + +Rml::String build_achievement_info_rml(const Achievement& a) { + Rml::String s = fmt::format( + R"(
)" + R"({})" + R"({})" + R"(
)" + R"(

{}

)", + a.unlocked ? " unlocked" : "", + a.name, + a.unlocked ? " unlocked" : " locked", + a.unlocked ? "Unlocked" : "Locked", + a.description + ); + + if (a.isCounter) { + float fraction = a.goal > 0 ? float(a.progress) / float(a.goal) : 1.0f; + s += fmt::format( + R"()" + R"({} / {})", + fraction, + a.unlocked ? "progress-done" : "progress-ongoing", + a.progress, + a.goal + ); + } + + return s; +} + +class AchievementRow : public FluentComponent { +public: + AchievementRow(Rml::Element* parent, const Achievement& a) + : FluentComponent(createRowRoot(parent)) + { + auto& btn = add_child