From 004ad3aea3ff940b314d2c49e19998269fe7b2b1 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 30 Sep 2025 19:00:05 -0700 Subject: [PATCH] LUS Bump, Mouse Capture/Cursor Visibility Improvements (#5797) * Bump LUS to include FileDropMgr's new registration system and initial cursor visibility changes. * New LUS ref. * Remove default on for cursor always visible. Add option to camera controls next to enable mouse input for autocapture. Set autocapture on startup. * next LUS * clang again * Add "EnableMouse" CVar check to startup SetAutoCaptureMouse. * Back to LUS main. * Final LUS ref bump. --- libultraship | 2 +- .../controls/SohInputEditorWindow.cpp | 29 ++++++++++++- .../GameInteractor_HookTable.h | 1 - .../Enhancements/randomizer/randomizer.cpp | 26 ++++++++++++ soh/soh/OTRGlobals.cpp | 41 ++++++++----------- 5 files changed, 72 insertions(+), 27 deletions(-) diff --git a/libultraship b/libultraship index 7f737f8be..64f16273a 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 7f737f8be9580980f5a1fe7784d6e1045f0309da +Subproject commit 64f16273a37075bd5d2b5ee6291848370ea7b822 diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 6fc7f6624..95af88a74 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -1,5 +1,6 @@ #include "SohInputEditorWindow.h" #include +#include "graphic/Fast3D/Fast3dWindow.h" #include "soh/OTRGlobals.h" #include "soh/SohGui/SohMenu.h" #include "soh/SohGui/SohGui.hpp" @@ -15,6 +16,7 @@ using namespace UIWidgets; static WidgetInfo freeLook; static WidgetInfo mouseControl; +static WidgetInfo mouseAutoCapture; static WidgetInfo rightStickOcarina; static WidgetInfo dpadOcarina; static WidgetInfo dpadPause; @@ -1366,6 +1368,9 @@ void SohInputEditorWindow::DrawCameraControlPanel() { ImVec2 cursor = ImGui::GetCursorPos(); ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); SohGui::mSohMenu->MenuDrawItem(mouseControl, ImGui::GetContentRegionAvail().x, THEME_COLOR); + cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); + SohGui::mSohMenu->MenuDrawItem(mouseAutoCapture, ImGui::GetContentRegionAvail().x, THEME_COLOR); Ship::GuiWindow::BeginGroupPanel("Aiming/First-Person Camera", ImGui::GetContentRegionAvail()); CVarCheckbox("Right Stick Aiming", CVAR_SETTING("Controls.RightStickAim"), @@ -1914,13 +1919,35 @@ void RegisterInputEditorWidgets() { mouseControl = { .name = "Enable Mouse Controls", .type = WidgetType::WIDGET_CVAR_CHECKBOX }; mouseControl.CVar(CVAR_SETTING("EnableMouse")) + .Callback([](WidgetInfo& info) { + bool enabled = + CVarGetInteger(CVAR_SETTING("EnableMouse"), 0) && CVarGetInteger(CVAR_SETTING("AutoCaptureMouse"), 1); + auto wnd = std::dynamic_pointer_cast(Ship::Context::GetInstance()->GetWindow()); + wnd->SetAutoCaptureMouse(enabled); + }) .Options( CheckboxOptions() .Color(THEME_COLOR) .Tooltip("Allows for using the mouse to control the camera (must enable Free Look), " - "aim with the shield, and perform quickspin attacks (quickly rotate the mouse then press B)")); + "aim with the shield, and perform quickspin attacks (quickly rotate the mouse then press B)\n" + "Press F2 to toggle mouse capture manually.")); SohGui::mSohMenu->AddSearchWidget({ mouseControl, "Settings", "Controls", "Camera Controls" }); + mouseAutoCapture = { .name = "Auto Capture Mouse Input", .type = WidgetType::WIDGET_CVAR_CHECKBOX }; + mouseAutoCapture.CVar(CVAR_SETTING("AutoCaptureMouse")) + .Callback([](WidgetInfo& info) { + bool enabled = + CVarGetInteger(CVAR_SETTING("EnableMouse"), 0) && CVarGetInteger(CVAR_SETTING("AutoCaptureMouse"), 1); + auto wnd = std::dynamic_pointer_cast(Ship::Context::GetInstance()->GetWindow()); + wnd->SetAutoCaptureMouse(enabled); + }) + .Options(CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("When Mouse Controls are enabled, this toggles whether the program will automatically " + "hide the cursor " + "and capture mouse input when closing the menu.")); + SohGui::mSohMenu->AddSearchWidget({ mouseAutoCapture, "Settings", "Controls", "Camera Controls" }); + rightStickOcarina = { .name = "Right Stick Ocarina Playback", .type = WidgetType::WIDGET_CVAR_CHECKBOX }; rightStickOcarina.CVar(CVAR_SETTING("CustomOcarina.RightStick")).Options(CheckboxOptions().Color(THEME_COLOR)); SohGui::mSohMenu->AddSearchWidget({ rightStickOcarina, "Settings", "Controls", "Ocarina Controls" }); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 3ac408e78..2b28198d6 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -71,6 +71,5 @@ DEFINE_HOOK(OnFileChooseMain, (void* gameState)); DEFINE_HOOK(OnGenerationCompletion, ()); DEFINE_HOOK(OnSetGameLanguage, ()); -DEFINE_HOOK(OnFileDropped, (std::string filePath)); DEFINE_HOOK(OnAssetAltChange, ()); DEFINE_HOOK(OnKaleidoUpdate, ()); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 49f5fa45a..ac0b7ea4a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -27,6 +27,7 @@ #include #include "draw.h" #include "soh/OTRGlobals.h" +#include "window/FileDropMgr.h" #include "soh/SohGui/UIWidgets.hpp" #include "static_data.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" @@ -387,6 +388,29 @@ static const char* frenchRupeeNames[39] = { "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", }; +bool Rando_HandleSpoilerDrop(char* filePath) { + if (SohUtils::IsStringEmpty(filePath)) { + return false; + } + + try { + std::ifstream stream(filePath); + if (!stream) { + return false; + } + + nlohmann::json json; + stream >> json; + + if (json.contains("version") && json.contains("finalSeed")) { + CVarSetString(CVAR_GENERAL("RandomizerDroppedFile"), filePath); + CVarSetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 1); + return true; + } + } catch (std::exception& e) {} + return false; +} + Randomizer::Randomizer() { Rando::StaticData::InitItemTable(); Rando::StaticData::InitLocationTable(); @@ -403,6 +427,8 @@ Randomizer::Randomizer() { for (size_t c = 0; c < Rando::StaticData::hintTypeNames.size(); c++) { SpoilerfileHintTypeNameToEnum[Rando::StaticData::hintTypeNames[(HintType)c].GetEnglish(MF_CLEAN)] = (HintType)c; } + + Ship::Context::GetInstance()->GetFileDropMgr()->RegisterDropHandler(Rando_HandleSpoilerDrop); } Randomizer::~Randomizer() { diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 11dcc220e..963a5b00d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -134,7 +134,7 @@ extern "C" { #include "src/overlays/actors/ovl_En_Dns/z_en_dns.h" } -void SoH_ProcessDroppedFiles(std::string filePath); +bool SoH_HandleConfigDrop(char* filePath); OTRGlobals* OTRGlobals::Instance; SaveManager* SaveManager::Instance; @@ -327,6 +327,10 @@ void OTRGlobals::Initialize() { std::make_shared(std::vector>({ sohInputEditorWindow })); context->InitWindow(sohFast3dWindow); + context->GetWindow()->SetAutoCaptureMouse(CVarGetInteger(CVAR_SETTING("EnableMouse"), 0) && + CVarGetInteger(CVAR_SETTING("AutoCaptureMouse"), 1)); + context->GetWindow()->SetForceCursorVisibility(CVarGetInteger(CVAR_SETTING("CursorVisibility"), 0)); + auto overlay = context->GetInstance()->GetWindow()->GetGui()->GetGameOverlay(); overlay->LoadFont("Press Start 2P", 12.0f, "fonts/PressStart2P-Regular.ttf"); overlay->LoadFont("Fipps", 32.0f, "fonts/Fipps-Regular.otf"); @@ -1270,7 +1274,8 @@ extern "C" void InitOTR() { CVarClear(CVAR_GENERAL("RandomizerNewFileDropped")); CVarClear(CVAR_GENERAL("RandomizerDroppedFile")); // #endregion - GameInteractor::Instance->RegisterGameHook(SoH_ProcessDroppedFiles); + + Ship::Context::GetInstance()->GetFileDropMgr()->RegisterDropHandler(SoH_HandleConfigDrop); RegisterImGuiItemIcons(); @@ -1455,15 +1460,6 @@ extern "C" void Graph_StartFrame() { } } #endif - - auto dropMgr = Ship::Context::GetInstance()->GetFileDropMgr(); - if (dropMgr->FileDropped()) { - std::string filePath = dropMgr->GetDroppedFile(); - if (!filePath.empty()) { - GameInteractor::Instance->ExecuteHooks(filePath); - } - dropMgr->ClearDroppedFile(); - } } void RunCommands(Gfx* Commands, const std::vector>& mtx_replacements) { @@ -2761,26 +2757,21 @@ extern "C" void Gfx_TextureCacheDelete(const uint8_t* texAddr) { } } -void SoH_ProcessDroppedFiles(std::string filePath) { +bool SoH_HandleConfigDrop(char* filePath) { + if (SohUtils::IsStringEmpty(filePath)) { + return false; + } try { std::ifstream configStream(filePath); if (!configStream) { - return; + return false; } nlohmann::json configJson; configStream >> configJson; - // #region SOH [Randomizer] TODO: Refactor spoiler file handling for randomizer - if (configJson.contains("version") && configJson.contains("finalSeed")) { - CVarSetString(CVAR_GENERAL("RandomizerDroppedFile"), filePath.c_str()); - CVarSetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 1); - return; - } - // #endregion - if (!configJson.contains("CVars")) { - return; + return false; } CVarClearBlock(CVAR_PREFIX_ENHANCEMENT); @@ -2826,17 +2817,19 @@ void SoH_ProcessDroppedFiles(std::string filePath) { uint32_t finalHash = SohUtils::Hash(configJson.dump()); gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash); + return true; } catch (std::exception& e) { SPDLOG_ERROR("Failed to load config file: {}", e.what()); auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file"); - return; + return false; } catch (...) { SPDLOG_ERROR("Failed to load config file"); auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file"); - return; + return false; } + return false; } extern "C" void CheckTracker_RecalculateAvailableChecks() {