From cb564f7465b6704a880f6cd5b8f4e1c77dbf7907 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 6 Aug 2026 18:00:16 -0700 Subject: [PATCH] ui: initial file select gate implementation --- mods/randomizer/CMakeLists.txt | 1 + mods/randomizer/src/hooks.cpp | 111 ++++++++++++++++++++++++ mods/randomizer/src/hooks.hpp | 7 ++ mods/randomizer/src/mod.cpp | 6 ++ mods/randomizer/src/ui/rando_config.cpp | 85 +++++++++++++++++- mods/randomizer/src/ui/rando_config.hpp | 16 ++++ 6 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 mods/randomizer/src/hooks.cpp create mode 100644 mods/randomizer/src/hooks.hpp diff --git a/mods/randomizer/CMakeLists.txt b/mods/randomizer/CMakeLists.txt index f41c9f1eac..2b55767154 100644 --- a/mods/randomizer/CMakeLists.txt +++ b/mods/randomizer/CMakeLists.txt @@ -85,6 +85,7 @@ set(RANDOMIZER_SOURCES src/verify_item_functions.cpp src/paths.cpp src/session.cpp + src/hooks.cpp src/ui/config_store.cpp src/ui/rando_seed_generation.cpp src/ui/rando_config.cpp diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp new file mode 100644 index 0000000000..864a86cd8f --- /dev/null +++ b/mods/randomizer/src/hooks.cpp @@ -0,0 +1,111 @@ +#include "hooks.hpp" +#include "session.hpp" +#include "randomizer_context.hpp" +#include "ui/rando_config.hpp" + +#include +#include + +#include "d/d_file_select.h" + +DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); +DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); + +namespace randomizer::ui { +dialogSelectModeState g_dialogSelectModeState = SelectReady; +} + +namespace randomizer::hooks { +namespace { +UiDialogHandle playModeDialog{0}; + +HookAction hookPreDataSelect(ModContext*, void* args, void* retval, void* userdata) { + ui::g_dialogSelectModeState = ui::SelectReady; + ui::g_file_select_window_ctx.is_proceed = false; + return HOOK_CONTINUE; +} + +HookAction hookPreSelectDataNameMove(ModContext*, void* args, void* retval, void* userdata) { + dFile_select_c* i_this = mods::arg(args, 0); + + // if coming from "start randomizer" button, let transition occur as normal + if (ui::g_file_select_window_ctx.is_proceed) { + return HOOK_CONTINUE; + } + + bool isHeaderTxtChange = i_this->headerTxtChangeAnm(); + bool isFileRecScale = i_this->fileRecScaleAnm2(); + bool isModoruTxtDisp = i_this->modoruTxtDispAnm(); + + // if selected vanilla mode, let transition occur as normal + if (ui::g_dialogSelectModeState == ui::SelectVanilla) { + return HOOK_CONTINUE; + } + + if (ui::g_dialogSelectModeState == ui::SelectReady && isHeaderTxtChange == true && isFileRecScale == true && isModoruTxtDisp == true) { + ui::g_dialogSelectModeState = ui::SelectWait; + + auto buildDialog = [i_this]() { + UiDialogDesc desc = UI_DIALOG_DESC_INIT; + desc.title = "Play Type"; + desc.body_rml = "What mode would you like to play?"; + desc.icon = "question-mark"; + desc.variant = UI_DIALOG_NORMAL; + + UiDialogAction actions[2]; + actions[0] = { + .label = "Vanilla", + .on_pressed = [](ModContext* ctx, UiDialogHandle dialogHandle, void*) { + mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK); + randomizer_GetContext() = RandomizerContext(); + ui::g_dialogSelectModeState = ui::SelectVanilla; + session::svc_mng.ui->dialog_close(ctx, dialogHandle); + }, + .user_data = nullptr, + .keep_open = false, + }; + actions[1] = { + .label = "Randomizer", + .on_pressed = [](ModContext* ctx, UiDialogHandle dialogHandle, void* userdata) { + mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK); + ui::g_dialogSelectModeState = ui::SelectRandomizer; + ui::buildFileSelectGateMenu(static_cast(userdata)); + session::svc_mng.ui->dialog_close(ctx, dialogHandle); + }, + .user_data = i_this, + .keep_open = false, + }; + desc.actions = actions; + desc.action_count = 2; + + if (session::svc_mng.ui->dialog_push(session::svc_mng.mod_ctx, &desc, &playModeDialog) != MOD_OK) { + mods::log::error("Failed to push dialog"); + return MOD_ERROR; + } + + return MOD_OK; + }; + + if (buildDialog() != MOD_OK) { + mods::log::error("Failed to build dialog"); + return HOOK_CONTINUE; + } + } + + return HOOK_SKIP_ORIGINAL; +} +} + +ModResult initialize() { +#define ADD_HOOK_PRE(originalFn, hookFn) \ + if (mods::hook::add_pre(hookFn) != MOD_OK) { \ + mods::log::error("Failed to add pre-hook for " #originalFn); \ + return MOD_ERROR; \ + } + + ADD_HOOK_PRE(dFile_select_c__selectDataNameMove, hookPreSelectDataNameMove); + ADD_HOOK_PRE(dFile_select_c__dataSelect, hookPreDataSelect); + + return MOD_OK; +} +} \ No newline at end of file diff --git a/mods/randomizer/src/hooks.hpp b/mods/randomizer/src/hooks.hpp new file mode 100644 index 0000000000..a27ea0aaba --- /dev/null +++ b/mods/randomizer/src/hooks.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace randomizer::hooks { +ModResult initialize(); +} \ No newline at end of file diff --git a/mods/randomizer/src/mod.cpp b/mods/randomizer/src/mod.cpp index 4421193432..a770ccf44a 100644 --- a/mods/randomizer/src/mod.cpp +++ b/mods/randomizer/src/mod.cpp @@ -3,6 +3,7 @@ #include "session.hpp" #include "ui/ui.hpp" +#include "hooks.hpp" DEFINE_MOD(); IMPORT_SERVICE(HostService, svc_host); @@ -32,6 +33,11 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { return mods::set_error(error, result, "failed to initialize session"); } + result = randomizer::hooks::initialize(); + if (result != MOD_OK) { + return mods::set_error(error, result, "failed to initialize hooks"); + } + result = randomizer::ui::initialize(); if (result != MOD_OK) { return mods::set_error(error, result, "failed to initialize ui"); diff --git a/mods/randomizer/src/ui/rando_config.cpp b/mods/randomizer/src/ui/rando_config.cpp index fabfb28497..5e5ed9a3fb 100644 --- a/mods/randomizer/src/ui/rando_config.cpp +++ b/mods/randomizer/src/ui/rando_config.cpp @@ -15,6 +15,8 @@ #include #include +#include "d/d_file_select.h" + namespace randomizer::ui { seedgen::settings::Setting* FindSetting(const std::string& key) { @@ -95,16 +97,21 @@ const std::vector>& GetStartingInventoryLayo return layoutOrder; } +UiMenuTabHandle g_menu_tab{}; + +FileSelectGateWindowCtx g_file_select_window_ctx{}; + namespace { // Control Helpers void add_button(UiElementHandle pane, const char* label, const char* help_rml, - UiPressedFn on_pressed, UiElementHandle* out_handle = nullptr) + UiPressedFn on_pressed, void* userdata = nullptr, UiElementHandle* out_handle = nullptr) { UiControlDesc desc = UI_CONTROL_DESC_INIT; desc.kind = UI_CONTROL_BUTTON; desc.label = label; desc.help_rml = help_rml; desc.on_pressed = on_pressed; + desc.user_data = userdata; session::svc_mng.ui->pane_add_control(session::svc_mng.mod_ctx, pane, &desc, out_handle); } @@ -452,9 +459,31 @@ void OnMenuTabSelected(ModContext* ctx, void*) { UiWindowHandle window{}; session::svc_mng.ui->window_push(ctx, &desc, &window); } -} -UiMenuTabHandle g_menu_tab{}; +// Play Tab +ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane, + UiElementHandle rightPane, void*, ModError*) +{ + add_button(leftPane, + "Selected Seed", + "", + [](ModContext*, void*) { + // TODO + }); + + add_button(leftPane, + "Start Randomizer", + "", + [](ModContext*, void* userdata) { + // set flag to move to name screen after window close + g_file_select_window_ctx.is_proceed = true; + session::svc_mng.ui->window_close(session::svc_mng.mod_ctx, *static_cast(userdata)); + }, + &g_file_select_window_ctx.window_handle); + + return MOD_OK; +} +} ModResult buildMenuTab() { UiMenuTabDesc desc = UI_MENU_TAB_DESC_INIT; @@ -464,6 +493,56 @@ ModResult buildMenuTab() { return session::svc_mng.ui->register_menu_tab(session::svc_mng.mod_ctx, &desc, &g_menu_tab); } +ModResult buildFileSelectGateMenu(dFile_select_c* fileSelect) { + UiTabDesc tabs[6]{}; + + tabs[0].struct_size = sizeof(UiTabDesc); + tabs[0].title = "Play"; + tabs[0].build = buildPlayTab; + + tabs[1].struct_size = sizeof(UiTabDesc); + tabs[1].title = "Seed Management"; + tabs[1].build = buildSeedManagementTab; + tabs[1].update = updateSeedManagementTab; + + tabs[2].struct_size = sizeof(UiTabDesc); + tabs[2].title = "Seed Options"; + tabs[2].build = buildSeedOptionsTab; + + tabs[3].struct_size = sizeof(UiTabDesc); + tabs[3].title = "Hints"; + tabs[3].build = buildHintsTab; + + tabs[4].struct_size = sizeof(UiTabDesc); + tabs[4].title = "Starting Inventory"; + tabs[4].build = buildStartingInventoryTab; + + tabs[5].struct_size = sizeof(UiTabDesc); + tabs[5].title = "Excluded Locations"; + tabs[5].build = buildExcludedLocationsTab; + + UiWindowDesc desc = UI_WINDOW_DESC_INIT; + desc.tabs = tabs; + desc.tab_count = 6; + desc.user_data = fileSelect; + desc.on_closed = [](ModContext*, UiWindowHandle, void* userdata) { + dFile_select_c* i_this = static_cast(userdata); + + // if closing the window through backing out, return to file select + if (!g_file_select_window_ctx.is_proceed) { + i_this->headerTxtSet(0x43, 1, 0); + i_this->fileRecScaleAnmInitSet2(0.0f, 1.0f); + i_this->nameMoveAnmInitSet(0xd29, 0xd1f); + i_this->modoruTxtDispAnmInit(0); + i_this->mDataSelProc = dFile_select_c::DATASELPROC_NAME_TO_DATA_SELECT_MOVE; + } + + g_dialogSelectModeState = SelectReady; + }; + + session::svc_mng.ui->window_push(session::svc_mng.mod_ctx, &desc, &g_file_select_window_ctx.window_handle); +} + std::filesystem::path GetRandomizerPath() { return paths::GetRandomizerPath() / "randomizer"; } diff --git a/mods/randomizer/src/ui/rando_config.hpp b/mods/randomizer/src/ui/rando_config.hpp index 329428531c..c0d5c3b608 100644 --- a/mods/randomizer/src/ui/rando_config.hpp +++ b/mods/randomizer/src/ui/rando_config.hpp @@ -3,6 +3,7 @@ #include #include #include +#include "mods/svc/ui.h" // Forward declaration namespace randomizer::seedgen::config { @@ -12,12 +13,27 @@ class dFile_select_c; namespace randomizer::ui { +enum dialogSelectModeState : uint8_t { + SelectReady, + SelectWait, + SelectVanilla, + SelectRandomizer +}; +extern dialogSelectModeState g_dialogSelectModeState; + +struct FileSelectGateWindowCtx { + UiWindowHandle window_handle{}; + bool is_proceed{false}; +}; +extern FileSelectGateWindowCtx g_file_select_window_ctx; + void SaveNewRandomizerPreset(const std::string& presetName, bool overwriteExisting = false); void ApplyExistingRandomizerPreset(const std::filesystem::path& presetFilePath); void CopyPermalinkToClipboard(); void PastePermalinkFromClipboard(); ModResult buildMenuTab(); +ModResult buildFileSelectGateMenu(dFile_select_c*); std::filesystem::path GetRandomizerPath(); std::filesystem::path GetRandomizerSettingsPath();