From e16c05211bdeb6c67a2fa44acd7188a43581a249 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Fri, 7 Aug 2026 19:42:50 -0700 Subject: [PATCH] seed selection + rando file setup working --- mods/randomizer/src/hooks.cpp | 12 +++ mods/randomizer/src/messages.cpp | 2 +- mods/randomizer/src/paths.cpp | 4 + mods/randomizer/src/paths.hpp | 3 +- mods/randomizer/src/randomizer_context.cpp | 14 +-- mods/randomizer/src/session.cpp | 87 ++++++++++++++++++ mods/randomizer/src/session.hpp | 2 + mods/randomizer/src/tools.cpp | 31 ++++++- mods/randomizer/src/tools.h | 7 ++ mods/randomizer/src/ui/rando_config.cpp | 92 ++++++++++++++----- mods/randomizer/src/ui/rando_config.hpp | 7 -- mods/randomizer/src/utilities.h | 13 --- mods/randomizer/src/verify_item_functions.cpp | 2 +- 13 files changed, 221 insertions(+), 55 deletions(-) delete mode 100644 mods/randomizer/src/utilities.h diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index f75972423b..5451c84e33 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -18,6 +18,7 @@ DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); +DEFINE_HOOK(&dFile_select_c::nameInput2, dFile_select_c__nameInput2); DEFINE_HOOK(&dSv_event_c::isEventBit, dSv_event_c__isEventBit); DEFINE_HOOK(&dSv_event_c::onEventBit, dSv_event_c__onEventBit); @@ -131,6 +132,15 @@ HookAction hookPreSelectDataNameMove(ModContext*, void* args, void* retval, void return HOOK_SKIP_ORIGINAL; } +void hookPostNameInput2(ModContext*, void* args, void* retval, void* userdata) { + dFile_select_c* i_this = mods::arg(args, 0); + + if (i_this->mIsSelectEnd) { + if (!randomizer_GetContext().mHash.empty()) { + session::setupRandomizerFile(); + } + } +} HookAction hookPreIsEventBit(ModContext*, void* args, void* retval, void*) { const u16 i_no = mods::arg(args, 1); @@ -603,6 +613,8 @@ ModResult initialize() { ADD_HOOK_POST(dEvt_control_c__talkEnd, hookPostTalkEnd); + ADD_HOOK_POST(dFile_select_c__nameInput2, hookPostNameInput2); + return MOD_OK; } } \ No newline at end of file diff --git a/mods/randomizer/src/messages.cpp b/mods/randomizer/src/messages.cpp index 6e07013e19..2b635e3c2b 100644 --- a/mods/randomizer/src/messages.cpp +++ b/mods/randomizer/src/messages.cpp @@ -6,7 +6,7 @@ #include "d/d_com_inf_game.h" #include "randomizer_context.hpp" #include "custom_flow_ids.hpp" -#include "utilities.h" +#include "tools.h" #include diff --git a/mods/randomizer/src/paths.cpp b/mods/randomizer/src/paths.cpp index 388e3771a9..a0e48cfe3f 100644 --- a/mods/randomizer/src/paths.cpp +++ b/mods/randomizer/src/paths.cpp @@ -21,6 +21,10 @@ std::filesystem::path GetRandomizerPreferencesPath() { return GetRandomizerPath() / "preferences.yaml"; } +std::filesystem::path GetRandomizerPresetsPath() { + return GetRandomizerPath() / "presets"; +} + std::filesystem::path GetRandomizerSeedsPath() { return GetRandomizerPath() / "seeds"; } diff --git a/mods/randomizer/src/paths.hpp b/mods/randomizer/src/paths.hpp index 3b7e49c86d..771e9514c1 100644 --- a/mods/randomizer/src/paths.hpp +++ b/mods/randomizer/src/paths.hpp @@ -4,11 +4,10 @@ namespace randomizer::paths { -// Root of the randomizer's writable data (settings, preferences, generated seeds), -// under the host's configured data path: /randomizer/. std::filesystem::path GetRandomizerPath(); std::filesystem::path GetRandomizerSettingsPath(); std::filesystem::path GetRandomizerPreferencesPath(); +std::filesystem::path GetRandomizerPresetsPath(); std::filesystem::path GetRandomizerSeedsPath(); } // namespace randomizer::paths diff --git a/mods/randomizer/src/randomizer_context.cpp b/mods/randomizer/src/randomizer_context.cpp index 1f75849277..34f3631d78 100644 --- a/mods/randomizer/src/randomizer_context.cpp +++ b/mods/randomizer/src/randomizer_context.cpp @@ -332,12 +332,14 @@ std::optional RandomizerContext::LoadFromHash(const std::string& ha this->mReturnToPlaceOverrides[key] = override; } - // TODO: setup ui service to allow pushing toasts - /*dusk::ui::push_toast(dusk::ui::Toast{ - .title = "Randomizer", - .content = fmt::format("Loaded Randomizer Seed {}", this->mHash), - .duration = std::chrono::seconds(3), - });*/ + UiToastDesc desc = UI_TOAST_DESC_INIT; + desc.type = "success"; + desc.title_rml = "Randomizer"; + std::string body_text = fmt::format("Loaded Randomizer Seed {}", this->mHash); + desc.body_rml = body_text.c_str(); + desc.duration_ms = 3000; + randomizer::session::svc_mng.ui->push_toast(randomizer::session::svc_mng.mod_ctx, &desc); + return std::nullopt; } diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index 76d8b0c5a9..54bd41c92c 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -1,5 +1,16 @@ #include "session.hpp" +#include + +#include "randomizer_context.hpp" +#include "flags.h" +#include "item_ids.h" +#include "tools.h" + +#include "d/d_com_inf_game.h" +#include "d/d_item.h" +#include "d/d_meter2_info.h" + namespace randomizer::session { ServiceManager svc_mng; @@ -9,5 +20,81 @@ ModResult initialize(const ServiceManager& services) { return MOD_OK; } +void setupRandomizerFile() { + // Setup file based on randomizer data + auto& randoData = randomizer_GetContext(); + randoData.mCreatingSave = true; + + // Set starting flags + // Event Flags + for (const auto& flag : randoData.mStartEventFlags) { + dComIfGs_onEventBit(flag); + } + // Region Flags + for (const auto& [region, flags] : randoData.mStartRegionFlags) { + for (const auto& flag : flags) { + onRegionFlag(region, flag); + } + } + + // Map bits (fills in overworld on map) + setRegionBit(randoData.mMapBits); + + // Other flags based on starting flags + if (dComIfGs_isEventBit(CLEARED_FARON_TWILIGHT)) + { + dComIfGs_onDarkClearLV(0); + dComIfGs_setLightDropNum(0, 0x10); + execItemGet(dItemNo_Randomizer_DROP_CONTAINER_e); + execItemGet(dItemNo_Randomizer_WEAR_KOKIRI_e); + } + + if (dComIfGs_isEventBit(CLEARED_ELDIN_TWILIGHT)) + { + dComIfGs_onDarkClearLV(1); + dComIfGs_setLightDropNum(1, 0x10); + execItemGet(dItemNo_Randomizer_DROP_CONTAINER02_e); + } + + if (dComIfGs_isEventBit(CLEARED_LANAYRU_TWILIGHT)) + { + dComIfGs_onDarkClearLV(2); + dComIfGs_setLightDropNum(2, 0x10); + execItemGet(dItemNo_Randomizer_DROP_CONTAINER03_e); + } + + if (randoData.mSettings[RandomizerContext::SKIP_MINOR_CUTSCENES] == RandomizerContext::ON) + { + // Add letter data in this order to more or less reflect an order they can be obtained in game + static const int letterOrder[] = {3, 2, 4, 7, 5, 6, 13, 12, 10, 9, 8, 15, 0, 14, 11}; + int letterNum = 0; + for (int i : letterOrder) { + if (dMenu_Letter::getLetterName(i) != 0) { + dComIfGs_onLetterGetFlag(i); + dComIfGs_setGetNumber(letterNum++, i + 1); + } + } + setAllLetterRead(); + } + + // If MDH and the twilights are pre-completed + if (dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED)) + { + if ((dComIfGs_getSaveData()->getPlayer().getPlayerStatusB().mDarkClearLevelFlag & 0x7) == 0x7) + { + dComIfGs_onDarkClearLV(3); + dComIfGs_onTransformLV(3); // Puts Midna on players back + } + } + + // Set starting inventory + for (const auto& itemId: randoData.mStartingInventory) { + execItemGet(itemId); + } + + g_randomizerState = RandomizerState(); + mods::log::debug("Created Rando Save"); + randoData.mCreatingSave = false; +} } \ No newline at end of file diff --git a/mods/randomizer/src/session.hpp b/mods/randomizer/src/session.hpp index fdf295c779..266000d726 100644 --- a/mods/randomizer/src/session.hpp +++ b/mods/randomizer/src/session.hpp @@ -25,4 +25,6 @@ struct ServiceManager { extern ServiceManager svc_mng; ModResult initialize(const ServiceManager& services); + +void setupRandomizerFile(); } \ No newline at end of file diff --git a/mods/randomizer/src/tools.cpp b/mods/randomizer/src/tools.cpp index f9e794222b..ec6effdd06 100644 --- a/mods/randomizer/src/tools.cpp +++ b/mods/randomizer/src/tools.cpp @@ -10,7 +10,6 @@ #include "randomizer_context.hpp" #include "session.hpp" #include "stages.h" -#include "utilities.h" #include "verify_item_functions.h" #include @@ -746,4 +745,34 @@ bool tracker_isStageItem(int stage, int flag) { // Need to subtract 0x80 (MEMORY_ITEM constant in d_save.cpp) because the above function does it return g_dComIfG_gameInfo.info.getSavedata().getSave(stage).getBit().isItem(flag - 0x80); } +} + +// Kinda hacky, but will do for now +void onRegionFlag(int i_stageNo, int i_no) { + auto regionFlags = reinterpret_cast(&dComIfGs_getSaveData()->getSave(i_stageNo).getBit()); + const int offset = i_no / 8; + const int shift = i_no % 8; + regionFlags[offset] |= (0x80 >> shift); +} + +void setRegionBit(u8 i_region) { + dComIfGs_getSaveData()->getPlayer().getPlayerFieldLastStayInfo().mRegion |= i_region; +} + +void setAllLetterGet() { + dComIfGs_getSaveData()->getPlayer().getLetterInfo().mLetterGetFlags[0] |= 0xFFFF; +} + +void setAllLetterRead() { + dComIfGs_getSaveData()->getPlayer().getLetterInfo().mLetterReadFlags[0] |= 0xFFFF; +} + +u8 getAncientDocumentNum() { + // TODO + return 0; +} + +u8 getAreaKeyNum(int) { + // TODO + return 0; } \ No newline at end of file diff --git a/mods/randomizer/src/tools.h b/mods/randomizer/src/tools.h index bb1c8f79b6..3119c3fcac 100644 --- a/mods/randomizer/src/tools.h +++ b/mods/randomizer/src/tools.h @@ -49,6 +49,13 @@ int getLocationItem(randomizer::logic::location::Location* location); int getStageSaveId(int id); int getStageSaveId(const char* stage); +void onRegionFlag(int i_stageNo, int i_no); +void setRegionBit(u8 i_region); +void setAllLetterGet(); +void setAllLetterRead(); +u8 getAncientDocumentNum(); +u8 getAreaKeyNum(int); + bool tracker_isEventBit(u16 flag); bool tracker_isStageSwitch(int stage, int flag); bool tracker_isStageItem(int stage, int flag); \ No newline at end of file diff --git a/mods/randomizer/src/ui/rando_config.cpp b/mods/randomizer/src/ui/rando_config.cpp index 5e5ed9a3fb..76bb24b034 100644 --- a/mods/randomizer/src/ui/rando_config.cpp +++ b/mods/randomizer/src/ui/rando_config.cpp @@ -15,6 +15,7 @@ #include #include +#include "../randomizer_context.hpp" #include "d/d_file_select.h" namespace randomizer::ui { @@ -133,6 +134,21 @@ void add_string_input(UiElementHandle pane, const char* label, const char* help_ session::svc_mng.ui->pane_add_control(session::svc_mng.mod_ctx, pane, &desc, out_handle); } +void add_select(UiElementHandle pane, const char* label, const char* help_rml, const char** options, + size_t option_count, UiControlGetFn getFn, UiControlSetFn setFn, UiElementHandle* out_handle = nullptr) +{ + UiControlDesc desc = UI_CONTROL_DESC_INIT; + desc.kind = UI_CONTROL_SELECT; + desc.label = label; + desc.help_rml = help_rml; + desc.binding = UI_BINDING_CALLBACKS; + desc.get = getFn; + desc.set = setFn; + desc.options = options; + desc.option_count = option_count; + session::svc_mng.ui->pane_add_control(session::svc_mng.mod_ctx, pane, &desc, out_handle); +} + void add_select_setting(UiElementHandle pane, const char* key, UiElementHandle* out_handle = nullptr) { auto setting = FindSetting(key); @@ -464,11 +480,59 @@ void OnMenuTabSelected(ModContext* ctx, void*) { ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane, UiElementHandle rightPane, void*, ModError*) { - add_button(leftPane, + std::filesystem::path seed_dir = paths::GetRandomizerSeedsPath(); + + std::string help_rml = ""; + if (std::filesystem::is_empty(seed_dir)) { + help_rml = "No seeds generated! You can generate a seed from the Seed Management Tab."; + } else { + help_rml = "Choose which seed you want to play."; + } + + std::vector seedHashes; + for (const auto& entry : std::filesystem::directory_iterator(seed_dir)) { + if (entry.is_directory()) { + seedHashes.push_back(entry.path().filename().string()); + } + } + + std::vector availableSeeds; + for (const auto& hash : seedHashes) { + availableSeeds.push_back(hash.c_str()); + } + + add_select(leftPane, "Selected Seed", - "", - [](ModContext*, void*) { - // TODO + help_rml.c_str(), + availableSeeds.data(), + availableSeeds.size(), + [](ModContext*, void*, UiControlValue* out_value) { + int idx = 0; + for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) { + if (entry.is_directory()) { + std::string hash = entry.path().filename().string(); + if (randomizer_GetContext().mHash == hash) { + break; + } + idx++; + } + } + + out_value->int_value = idx; + }, + [](ModContext*, void*, const UiControlValue* value) { + int idx = 0; + for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) { + if (entry.is_directory()) { + if (idx == value->int_value) { + std::string hash = entry.path().filename().string(); + randomizer_GetContext() = RandomizerContext(); + randomizer_GetContext().LoadFromHash(hash); + break; + } + idx++; + } + } }); add_button(leftPane, @@ -543,24 +607,4 @@ ModResult buildFileSelectGateMenu(dFile_select_c* fileSelect) { 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"; -} - -std::filesystem::path GetRandomizerSettingsPath() { - return GetRandomizerPath() / "settings.yaml"; -} - -std::filesystem::path GetRandomizerPreferencesPath() { - return GetRandomizerPath() / "preferences.yaml"; -} - -std::filesystem::path GetRandomizerPresetsPath() { - return GetRandomizerPath() / "presets"; -} - -std::filesystem::path GetRandomizerSeedsPath() { - return GetRandomizerPath() / "seeds"; -} - } // namespace dusk::ui diff --git a/mods/randomizer/src/ui/rando_config.hpp b/mods/randomizer/src/ui/rando_config.hpp index c0d5c3b608..b3c8dedcb7 100644 --- a/mods/randomizer/src/ui/rando_config.hpp +++ b/mods/randomizer/src/ui/rando_config.hpp @@ -34,11 +34,4 @@ void PastePermalinkFromClipboard(); ModResult buildMenuTab(); ModResult buildFileSelectGateMenu(dFile_select_c*); - -std::filesystem::path GetRandomizerPath(); -std::filesystem::path GetRandomizerSettingsPath(); -std::filesystem::path GetRandomizerPreferencesPath(); -std::filesystem::path GetRandomizerSeedsPath(); -std::filesystem::path GetRandomizerPresetsPath(); - } diff --git a/mods/randomizer/src/utilities.h b/mods/randomizer/src/utilities.h deleted file mode 100644 index 448ea50e24..0000000000 --- a/mods/randomizer/src/utilities.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -inline u8 getAncientDocumentNum() { - // TODO - return 0; -} - -inline u8 getAreaKeyNum(int) { - // TODO - return 0; -} \ No newline at end of file diff --git a/mods/randomizer/src/verify_item_functions.cpp b/mods/randomizer/src/verify_item_functions.cpp index 5a050de16b..fc1b36d653 100644 --- a/mods/randomizer/src/verify_item_functions.cpp +++ b/mods/randomizer/src/verify_item_functions.cpp @@ -6,7 +6,7 @@ #include "d/d_item.h" #include "d/d_item_data.h" #include "item_ids.h" -#include "utilities.h" +#include "tools.h" bool haveItem(u32 item) { return checkItemGet((u8)item, 1);