diff --git a/files.cmake b/files.cmake index 7d07408613..c6cf3b5d3d 100644 --- a/files.cmake +++ b/files.cmake @@ -1358,6 +1358,8 @@ set(DUSK_FILES src/dusk/imgui/ImGuiMenuTools.hpp src/dusk/imgui/ImGuiMenuEnhancements.cpp src/dusk/imgui/ImGuiMenuEnhancements.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 diff --git a/include/dusk/config.hpp b/include/dusk/config.hpp index 258e012f2b..0fd0ad8556 100644 --- a/include/dusk/config.hpp +++ b/include/dusk/config.hpp @@ -111,6 +111,13 @@ void Save(); */ ConfigVarBase* GetConfigVar(std::string_view name); +/** + * \brief Returns true if no config file was found on the last LoadFromUserPreferences() call. + * + * For detect first run to prompt the user to choose a preset. + */ +bool WasConfigFileMissing(); + template const ConfigImplBase* GetConfigImpl() { static ConfigImpl config; diff --git a/src/dusk/config.cpp b/src/dusk/config.cpp index 159706fac8..1fa2e689b6 100644 --- a/src/dusk/config.cpp +++ b/src/dusk/config.cpp @@ -21,6 +21,7 @@ aurora::Module DuskConfigLog("dusk::config"); static absl::flat_hash_map RegisteredConfigVars; static bool RegistrationDone; +static bool s_configFileMissing = false; static std::string GetConfigJsonPath() { return fmt::format("{}{}", configPath, ConfigFileName); @@ -187,6 +188,7 @@ void dusk::config::LoadFromFileName(const char* path) { } catch (const std::system_error& e) { if (e.code() == std::errc::no_such_file_or_directory) { DuskConfigLog.info("Config file did not exist, staying with defaults"); + s_configFileMissing = true; } else { DuskConfigLog.error("Failed to load from config! {}", e.what()); } @@ -212,6 +214,10 @@ void dusk::config::Save() { io::FileStream::WriteAllText(configJsonPath.c_str(), j.dump(4)); } +bool dusk::config::WasConfigFileMissing() { + return s_configFileMissing; +} + ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) { const auto configVar = RegisteredConfigVars.find(name); if (configVar != RegisteredConfigVars.end()) { diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index fe5d3e7c9a..7140af5fc7 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -184,6 +184,10 @@ namespace dusk { m_isLaunchInitialized = true; } + if (config::WasConfigFileMissing()) { + m_firstRunPreset.draw(); + } + getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab); if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) && diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 27032ed39b..75da71f6a3 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -5,6 +5,7 @@ #include #include +#include "ImGuiFirstRunPreset.hpp" #include "ImGuiMenuEnhancements.hpp" #include "ImGuiMenuGame.hpp" #include "ImGuiMenuTools.hpp" @@ -32,6 +33,7 @@ private: bool m_isLaunchInitialized = false; std::deque m_toasts; + ImGuiFirstRunPreset m_firstRunPreset; ImGuiMenuGame m_menuGame; ImGuiMenuEnhancements m_menuEnhancements; diff --git a/src/dusk/imgui/ImGuiEngine.cpp b/src/dusk/imgui/ImGuiEngine.cpp index a6abc5b0b9..37e0415f41 100644 --- a/src/dusk/imgui/ImGuiEngine.cpp +++ b/src/dusk/imgui/ImGuiEngine.cpp @@ -145,7 +145,7 @@ void ImGuiEngine_Initialize(float scale) { colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); - colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f); } diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.cpp b/src/dusk/imgui/ImGuiFirstRunPreset.cpp new file mode 100644 index 0000000000..275af7a0fc --- /dev/null +++ b/src/dusk/imgui/ImGuiFirstRunPreset.cpp @@ -0,0 +1,130 @@ +#include "ImGuiFirstRunPreset.hpp" + +#include "imgui.h" +#include "ImGuiConsole.hpp" +#include "ImGuiEngine.hpp" +#include "dusk/settings.h" +#include "dusk/config.hpp" + +namespace dusk { + +static void ApplyPresetVanilla() { + auto& s = getSettings(); + // todo: lock aspect ratio +} + +static void ApplyPresetDefault() { + auto& s = getSettings(); + // todo: unlock aspect ratio + // todo: instant saving + s.game.hideTvSettingsScreen.setValue(true); +} + +static void ApplyPresetQoL() { + auto& s = getSettings(); + // todo: unlock aspect ratio + // todo: instant saving + // todo: more save files + // todo: autosave + s.game.enableQuickTransform.setValue(true); + s.game.hideTvSettingsScreen.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.noLowHpSound.setValue(true); + s.game.midnasLamentNonStop.setValue(true); + s.game.enableFastIronBoots.setValue(true); + s.game.canTransformAnywhere.setValue(true); + s.game.enableTurboKeybind.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("Vanilla##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) { + chosen = 0; + } + + ImGui::TableSetColumnIndex(2); + if (ImGui::Button("Default##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) { + chosen = 1; + } + + ImGui::TableSetColumnIndex(4); + if (ImGui::Button("Quality of Life##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) { + chosen = 2; + } + + ImGui::PopFont(); + + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::Spacing(); + ImGui::TextWrapped("All enhancements disabled. Plays closest to the original game; good for speedrunning or simple nostalgia!"); + + ImGui::TableSetColumnIndex(2); + ImGui::Spacing(); + ImGui::TextWrapped("Some enhancements enabled, maintaining a vanilla feel. A good starting point for most players!"); + + ImGui::TableSetColumnIndex(4); + ImGui::Spacing(); + ImGui::TextWrapped("Many quality of life enhancements enabled. Good for seasoned players!"); + + ImGui::EndTable(); + } + + if (chosen >= 0) { + if (chosen == 0) ApplyPresetVanilla(); + if (chosen == 1) ApplyPresetDefault(); + if (chosen == 2) ApplyPresetQoL(); + 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 new file mode 100644 index 0000000000..33ba6b3d0f --- /dev/null +++ b/src/dusk/imgui/ImGuiFirstRunPreset.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace dusk { + +class ImGuiFirstRunPreset { +public: + void draw(); + +private: + bool m_opened = false; + bool m_done = false; +}; + +} // namespace dusk