From 2b4598821b2571f9a7a1ccd113adb7bf56deaf58 Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Thu, 16 Apr 2026 19:50:17 -0700 Subject: [PATCH] add imgui menu for generating and selecting seeds --- files.cmake | 4 + include/d/d_com_inf_game.h | 12 +- include/d/d_save.h | 15 - src/d/actor/d_a_alink_demo.inc | 16 +- src/d/actor/d_a_demo_item.cpp | 2 +- src/d/actor/d_a_npc_fairy.cpp | 2 +- src/d/actor/d_a_obj_item.cpp | 4 +- src/d/actor/d_a_tbox.cpp | 4 +- src/d/d_a_itembase_static.cpp | 2 +- src/d/d_com_inf_game.cpp | 97 ++---- src/d/d_file_select.cpp | 4 +- src/d/d_item.cpp | 92 +++--- src/d/d_item_data.cpp | 6 +- src/d/d_menu_ring.cpp | 4 +- src/d/d_s_play.cpp | 1 + src/d/d_save.cpp | 13 +- src/d/d_stage.cpp | 4 +- src/dusk/imgui/ImGuiConsole.cpp | 3 + src/dusk/imgui/ImGuiConsole.hpp | 2 + src/dusk/imgui/ImGuiMenuRandomizer.cpp | 304 ++++++++++++++++++ src/dusk/imgui/ImGuiMenuRandomizer.hpp | 21 ++ .../randomizer/game/randomizer_context.cpp | 109 +++++++ .../randomizer/game/randomizer_context.hpp | 36 +++ src/dusk/randomizer/game/tools.cpp | 6 + src/dusk/randomizer/game/tools.h | 1 + .../generator/logic/spoiler_log.cpp | 22 +- src/dusk/randomizer/generator/randomizer.cpp | 13 +- src/dusk/randomizer/generator/randomizer.hpp | 12 +- .../randomizer/generator/seedgen/config.cpp | 22 +- src/dusk/randomizer/randomizer.cmake | 5 +- src/f_op/f_op_actor_mng.cpp | 6 +- 31 files changed, 629 insertions(+), 215 deletions(-) create mode 100644 src/dusk/imgui/ImGuiMenuRandomizer.cpp create mode 100644 src/dusk/imgui/ImGuiMenuRandomizer.hpp create mode 100644 src/dusk/randomizer/game/randomizer_context.cpp create mode 100644 src/dusk/randomizer/game/randomizer_context.hpp diff --git a/files.cmake b/files.cmake index ea3fa2cc74..8738077607 100644 --- a/files.cmake +++ b/files.cmake @@ -1362,6 +1362,8 @@ set(DUSK_FILES src/dusk/imgui/ImGuiMenuTools.hpp src/dusk/imgui/ImGuiMenuEnhancements.cpp src/dusk/imgui/ImGuiMenuEnhancements.hpp + src/dusk/imgui/ImGuiMenuRandomizer.cpp + src/dusk/imgui/ImGuiMenuRandomizer.hpp src/dusk/imgui/ImGuiPreLaunchWindow.cpp src/dusk/imgui/ImGuiPreLaunchWindow.hpp src/dusk/imgui/ImGuiFirstRunPreset.hpp @@ -1389,6 +1391,8 @@ set(DUSK_FILES src/dusk/randomizer/game/tools.h src/dusk/randomizer/game/verify_item_functions.cpp src/dusk/randomizer/game/verify_item_functions.h + src/dusk/randomizer/game/randomizer_context.cpp + src/dusk/randomizer/game/randomizer_context.hpp src/dusk/randomizer/generator/logic/area.cpp src/dusk/randomizer/generator/logic/area.hpp src/dusk/randomizer/generator/logic/dungeon.cpp diff --git a/include/d/d_com_inf_game.h b/include/d/d_com_inf_game.h index ecc4be6686..307dbcbf75 100644 --- a/include/d/d_com_inf_game.h +++ b/include/d/d_com_inf_game.h @@ -1,6 +1,10 @@ #ifndef D_COM_D_COM_INF_GAME_H #define D_COM_D_COM_INF_GAME_H +#if TARGET_PC +#include "dusk/randomizer/game/randomizer_context.hpp" +#endif + #include "d/d_attention.h" #include "d/d_cc_s.h" #include "d/d_event.h" @@ -1269,14 +1273,6 @@ int dComIfGd_setShadow(u32 param_0, s8 param_1, J3DModel* param_2, cXyz* param_3 dKy_tevstr_c* param_9, s16 param_10, f32 param_11, TGXTexObj* param_12); #if TARGET_PC -inline dSv_randomizer_c& dComIfG_getRandomizer() { - return g_dComIfG_gameInfo.info.mRandomizer; -} - -inline BOOL dComIfG_isRandomizer() { - return g_dComIfG_gameInfo.info.mRandomizer.mActive; -} - void dComIfGs_setupRandomizerSave(); #endif diff --git a/include/d/d_save.h b/include/d/d_save.h index ab4ccdf7d9..f437ba77a3 100644 --- a/include/d/d_save.h +++ b/include/d/d_save.h @@ -964,18 +964,6 @@ public: /* 0x8 */ s8 m_no; }; -#if TARGET_PC -class dSv_randomizer_c { -public: - BOOL mActive{false}; - std::unordered_map> mTreasureChestOverrides{}; - std::unordered_map> mPoeOverrides{}; - - u8 mStartHour{0}; - - void clear(); -}; -#endif class dSv_info_c { public: @@ -1059,9 +1047,6 @@ public: #if DEBUG /* 0xF80 */ flagFile_c mFlagFile; #endif -#if TARGET_PC - /* 0xF38 */ dSv_randomizer_c mRandomizer; -#endif }; // Size: 0xF38 class dSv_event_flag_c { diff --git a/src/d/actor/d_a_alink_demo.inc b/src/d/actor/d_a_alink_demo.inc index 14331422bc..49f4c5c4d8 100644 --- a/src/d/actor/d_a_alink_demo.inc +++ b/src/d/actor/d_a_alink_demo.inc @@ -1023,12 +1023,12 @@ void daAlink_c::setGetItemFace(u16 i_itemNo) { || i_itemNo == dItemNo_DROP_CONTAINER02_e #if TARGET_PC // Rando items for facial expressions - || (dComIfG_isRandomizer() && i_itemNo == dItemNo_Randomizer_WOOD_STICK_e) - || (dComIfG_isRandomizer() && i_itemNo == dItemNo_Randomizer_SWORD_e) - || (dComIfG_isRandomizer() && i_itemNo == dItemNo_Randomizer_SHIELD_e) - || (dComIfG_isRandomizer() && i_itemNo == dItemNo_Randomizer_MASTER_SWORD_e) - || (dComIfG_isRandomizer() && i_itemNo == dItemNo_Randomizer_LIGHT_SWORD_e) - || (dComIfG_isRandomizer() && i_itemNo == dItemNo_Randomizer_MAGIC_LV1_e) + || (randomizer_IsActive() && i_itemNo == dItemNo_Randomizer_WOOD_STICK_e) + || (randomizer_IsActive() && i_itemNo == dItemNo_Randomizer_SWORD_e) + || (randomizer_IsActive() && i_itemNo == dItemNo_Randomizer_SHIELD_e) + || (randomizer_IsActive() && i_itemNo == dItemNo_Randomizer_MASTER_SWORD_e) + || (randomizer_IsActive() && i_itemNo == dItemNo_Randomizer_LIGHT_SWORD_e) + || (randomizer_IsActive() && i_itemNo == dItemNo_Randomizer_MAGIC_LV1_e) #endif || i_itemNo == dItemNo_DROP_CONTAINER03_e) { @@ -1037,7 +1037,7 @@ void daAlink_c::setGetItemFace(u16 i_itemNo) { setFaceBasicTexture(FTANM_K_A); setFaceBasicBck(dRes_ID_ALANM_BCK_FK_e); #if TARGET_PC - } else if (i_itemNo == dItemNo_TOMATO_PUREE_e || i_itemNo == dItemNo_TASTE_e || (dComIfG_isRandomizer() && i_itemNo == dItemNo_Randomizer_FOOLISH_ITEM_e)) { + } else if (i_itemNo == dItemNo_TOMATO_PUREE_e || i_itemNo == dItemNo_TASTE_e || (randomizer_IsActive() && i_itemNo == dItemNo_Randomizer_FOOLISH_ITEM_e)) { #else } else if (i_itemNo == dItemNo_TOMATO_PUREE_e || i_itemNo == dItemNo_TASTE_e) { #endif @@ -2494,7 +2494,7 @@ void daAlink_c::setGetSubBgm(int i_itemNo) { }; #if TARGET_PC - u32 se_type = dComIfG_isRandomizer() ? getSeTypeRandomizer[i_itemNo] : getSeType[i_itemNo]; + u32 se_type = randomizer_IsActive() ? getSeTypeRandomizer[i_itemNo] : getSeType[i_itemNo]; #else u32 se_type = getSeType[i_itemNo]; #endif diff --git a/src/d/actor/d_a_demo_item.cpp b/src/d/actor/d_a_demo_item.cpp index ac053c35eb..e42b679a9b 100644 --- a/src/d/actor/d_a_demo_item.cpp +++ b/src/d/actor/d_a_demo_item.cpp @@ -533,7 +533,7 @@ int daDitem_c::execute() { #if TARGET_PC // Certain items use field models that are too big to fit in link's hands so we want to scale them down to fit. - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { switch (m_itemNo) { case dItemNo_Randomizer_MIRROR_PIECE_1_e: diff --git a/src/d/actor/d_a_npc_fairy.cpp b/src/d/actor/d_a_npc_fairy.cpp index 77f9f99e24..5c142d2b1f 100644 --- a/src/d/actor/d_a_npc_fairy.cpp +++ b/src/d/actor/d_a_npc_fairy.cpp @@ -1270,7 +1270,7 @@ void daNpc_Fairy_c::AppearDemoCall() { mEvtNo = EVT_APPEAR_50F_02; } #if TARGET_PC - } else if (dComIfGs_checkEmptyBottle() || dComIfG_isRandomizer()) { + } else if (dComIfGs_checkEmptyBottle() || randomizer_IsActive()) { #else } else if (dComIfGs_checkEmptyBottle()) { #endif diff --git a/src/d/actor/d_a_obj_item.cpp b/src/d/actor/d_a_obj_item.cpp index f70b6d2fa0..ec351eb29f 100644 --- a/src/d/actor/d_a_obj_item.cpp +++ b/src/d/actor/d_a_obj_item.cpp @@ -153,7 +153,7 @@ void daItem_c::CreateInit() { show(); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { // Adjust item scale based on item ID switch(m_itemNo) { @@ -230,7 +230,7 @@ void daItem_c::CreateInit() { animPlay(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); #if TARGET_PC - if (m_itemNo == dItemNo_BOOMERANG_e && !dComIfG_isRandomizer()) { + if (m_itemNo == dItemNo_BOOMERANG_e && !randomizer_IsActive()) { #else if (m_itemNo == dItemNo_BOOMERANG_e) { #endif diff --git a/src/d/actor/d_a_tbox.cpp b/src/d/actor/d_a_tbox.cpp index 6223dd4391..6bf7ba2710 100644 --- a/src/d/actor/d_a_tbox.cpp +++ b/src/d/actor/d_a_tbox.cpp @@ -1787,11 +1787,11 @@ cPhs_Step daTbox_c::create1st() { field_0x980 = home.angle.x; #if TARGET_PC // The upper 8 bits of home.angle.z hold the itemId - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { home.angle.z &= ~0xFF00; auto stage = dComIfGp_getStartStageName(); auto tboxId = static_cast(getTboxNo()); - home.angle.z |= dComIfG_getRandomizer().mTreasureChestOverrides[stage][tboxId] << 8; + home.angle.z |= randomizer_GetContext().mTreasureChestOverrides[stage][tboxId] << 8; } #endif field_0x982 = home.angle.z; diff --git a/src/d/d_a_itembase_static.cpp b/src/d/d_a_itembase_static.cpp index f51b8e7c22..dab95b2413 100644 --- a/src/d/d_a_itembase_static.cpp +++ b/src/d/d_a_itembase_static.cpp @@ -62,7 +62,7 @@ int CheckFieldItemCreateHeap(fopAc_ac_c* i_this) { u8 item_no = a_this->getItemNo(); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { switch (item_no) { case dItemNo_Randomizer_EMPTY_BOTTLE_e: diff --git a/src/d/d_com_inf_game.cpp b/src/d/d_com_inf_game.cpp index bce9de013d..13a1307d77 100644 --- a/src/d/d_com_inf_game.cpp +++ b/src/d/d_com_inf_game.cpp @@ -29,11 +29,10 @@ #if TARGET_PC #include "dusk/randomizer/generator/randomizer.hpp" -#include "dusk/randomizer/generator/utility/yaml.hpp" #include "dusk/randomizer/game/tools.h" #include "dusk/randomizer/game/stages.h" #include "dusk/randomizer/game/flags.h" -#include "dusk/randomizer//game/verify_item_functions.h" +#include "dusk/randomizer/game/randomizer_context.hpp" #endif @@ -173,7 +172,7 @@ int dComIfG_play_c::getLayerNo_common_common(const char* i_stageName, int i_room #if TARGET_PC // Special layer checks for randomizer - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { int stageID = getStageID(i_stageName); bool condition = false; bool darkIsClear = false; @@ -2866,65 +2865,26 @@ int dComIfGd_setShadow(u32 param_0, s8 param_1, J3DModel* param_2, cXyz* param_3 #if TARGET_PC void dComIfGs_setupRandomizerSave() { - randomizer::Randomizer randomizer; - if (randomizer.Generate()) - throw std::runtime_error("Randomizer Generation Faild"); - auto world = randomizer.GetWorlds()[0].get(); - // Setup randomizer data - auto& randoData = g_dComIfG_gameInfo.info.mRandomizer; - randoData.mActive = TRUE; - for (const auto& location : world->GetAllLocations()) { - const auto& metaData = location->GetMetadata(); - if (location->HasCategories("Chest")) { - const auto& stage = metaData[0]["Stage"].as(); - const auto& tboxId = metaData[0]["Tbox ID"].as(); - const auto& itemId = location->GetCurrentItem()->GetID(); - randoData.mTreasureChestOverrides[stage][tboxId] = itemId; - } - } + // Setup file based on randomizer data + auto& randoData = randomizer_GetContext(); + randoData.mActive = true; + randoData.mCreatingSave = true; // Set starting flags - auto startFlags = LoadYAML(RANDO_DATA_PATH "startflags.yaml"); // Event Flags - for (const auto& flagNode : startFlags["EventFlags"]) { - if (flagNode.IsScalar()) { - const auto& flag = flagNode.as(); - dComIfGs_onEventBit(flag); - } else if (flagNode.IsMap()) { - const auto& condition = flagNode.begin()->first.as(); - if (world->EvaluateSettingCondition(condition)) { - DuskLog.debug("Setting flags for {}", condition); - for (const auto& conditionalFlag : flagNode.begin()->second) { - const auto& flag = conditionalFlag.as(); - dComIfGs_onEventBit(flag); - } - } + for (const auto& flag : randoData.mStartEventFlags) { + dComIfGs_onEventBit(flag); + } + // Region Flags + for (const auto& [region, flags] : randoData.mStartRegionFlags) { + for (const auto& flag : flags) { + dComIfGs_onRegionFlag(region, flag); } } - // Region Flags - for (const auto& regionNode : startFlags["RegionFlags"]) { - const auto& region = regionNode.first.as(); - const auto& index = regionNode.second["Index"].as(); - const auto& flags = regionNode.second["Flags"]; - DuskLog.debug("Setting region flags for {}", region); - // This seems kinda scuffed so maybe we change it later - for (const auto& flagNode : flags) { - if (flagNode.IsScalar()) { - const auto& flag = flagNode.as(); - dComIfGs_onRegionFlag(index, flag); - } else if (flagNode.IsMap()) { - const auto& condition = flagNode.begin()->first.as(); - if (world->EvaluateSettingCondition(condition)) { - for (const auto& conditionalFlag : flagNode.begin()->second) { - const auto& flag = conditionalFlag.as(); - dComIfGs_onRegionFlag(index, flag); - } - } - } - } - } + // Map bits (fills in overworld on map) + dComIfGs_setRegionBit(randoData.mMapBits); // Other flags based on starting flags if (dComIfGs_isEventBit(CLEARED_FARON_TWILIGHT)) @@ -2963,35 +2923,14 @@ void dComIfGs_setupRandomizerSave() { dComIfGs_onTransformLV(3); } } - - if (world->Setting("Unlock Map Regions") == "On") - { - int mapBits = 0x20; - if (world->Setting("Snowpeak Does Not Require Reekfish Scent") == "On") {mapBits |= 0x40;} - if (dComIfGs_isEventBit(CLEARED_LANAYRU_TWILIGHT)) {mapBits |= 0x10;} - if (dComIfGs_isEventBit(CLEARED_ELDIN_TWILIGHT)) {mapBits |= 0x08;} - if (dComIfGs_isEventBit(CLEARED_FARON_TWILIGHT)) {mapBits |= 0x04;} - if (world->Setting("Skip Prologue") == "On") {mapBits |= 0x02;} - dComIfGs_setRegionBit(mapBits); - } // Set starting inventory - for (const auto& item: world->GetStartingItemPool()) { - execItemGet(static_cast(item->GetID())); + for (const auto& itemId: randoData.mStartingInventory) { + execItemGet(itemId); } - // Set starting time of day - const auto startTimeSetting = world->Setting("Starting Time of Day"); - if (startTimeSetting == "Morning") - randoData.mStartHour = 6; - else if (startTimeSetting == "Noon") - randoData.mStartHour = 12; - else if (startTimeSetting == "Evening") - randoData.mStartHour = 18; - else if (startTimeSetting == "Night") - randoData.mStartHour = 24; - DuskLog.debug("Created Rando Save"); + randoData.mCreatingSave = false; } #endif diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 8fb441a91f..08b800685d 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -1536,8 +1536,8 @@ void dFile_select_c::nameInput2() { dComIfGs_setHorseName(mpName->getInputStrPtr()); mIsSelectEnd = true; #if TARGET_PC - // Create a randomizer save if Z is being held - if (mDoCPd_c::getHoldZ(PAD_1)) { + // Create a randomizer save if one is selected + if (!randomizer_GetContext().mHash.empty()) { dComIfGs_setupRandomizerSave(); } #endif diff --git a/src/d/d_item.cpp b/src/d/d_item.cpp index e41138c6a6..eaf8feb83c 100644 --- a/src/d/d_item.cpp +++ b/src/d/d_item.cpp @@ -537,7 +537,7 @@ static void (*item_func_ptr_randomizer[256])() = { inline void getItemFunc(u8 i_itemNo) { dComIfGs_onItemFirstBit(i_itemNo); #if TARGET_PC - (dComIfG_isRandomizer() ? item_func_ptr_randomizer : item_func_ptr)[i_itemNo](); + (randomizer_IsActive() ? item_func_ptr_randomizer : item_func_ptr)[i_itemNo](); #else item_func_ptr[i_itemNo](); #endif @@ -545,7 +545,7 @@ inline void getItemFunc(u8 i_itemNo) { void execItemGet(u8 i_itemNo) { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) i_itemNo = verifyProgressiveItem(i_itemNo); #endif getItemFunc(i_itemNo); @@ -1074,7 +1074,7 @@ static int (*item_getcheck_func_ptr_randomizer[256])() = { inline int getCheckItemFunc(u8 i_itemNo) { #if TARGET_PC - return (dComIfG_isRandomizer() ? item_getcheck_func_ptr_randomizer : item_getcheck_func_ptr)[i_itemNo](); + return (randomizer_IsActive() ? item_getcheck_func_ptr_randomizer : item_getcheck_func_ptr)[i_itemNo](); #else return item_getcheck_func_ptr[i_itemNo](); #endif @@ -1233,7 +1233,7 @@ void item_func_SMALL_KEY() { void item_func_KAKERA_HEART() { dComIfGp_setItemMaxLifeCount(1); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { // TODO rando /* Pasting rando code until the framework has been updated @@ -1259,7 +1259,7 @@ void item_func_UTUWA_HEART() { int tmp = dStage_stagInfo_GetSaveTbl(stag_info); dComIfGs_onStageLife(); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { // TODO rando /* Pasting rando code until the framework has been updated @@ -1334,7 +1334,7 @@ void item_func_WEAR_ZORA() {} void item_func_MAGIC_LV1() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onEventBit(0xD04); // Can transform at will dComIfGs_onEventBit(0x501); // Midna Charge Unlocked return; @@ -1355,7 +1355,7 @@ void item_func_WALLET_LV1() { void item_func_WALLET_LV2() { dComIfGs_setWalletSize(BIG_WALLET); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { // TODO rando // Putting rando code here until the framework gets built: /* @@ -1386,7 +1386,7 @@ void item_func_WALLET_LV3() { dComIfGs_setWalletSize(GIANT_WALLET); #if TARGET_PC // TODO rando - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { // Putting rando code here until the framework gets built: /* Basically we fill the wallet if desired and then we change the color of the rupee icon to red @@ -1451,7 +1451,7 @@ void item_func_WOOD_STICK() { dComIfGs_setCollectSword(COLLECT_WOODEN_SWORD); dComIfGs_setSelectEquipSword(dItemNo_WOOD_STICK_e); #if TARGET_PC - if (!dComIfG_isRandomizer()) + if (!randomizer_IsActive()) #endif dComIfGs_onSwitch(28, dComIfGp_roomControl_getStayNo()); } @@ -1514,7 +1514,7 @@ void item_func_PACHINKO() { void item_func_COPY_ROD_2() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) dComIfGs_onEventBit(0x2580); // Power up dominion rod else #endif @@ -1729,7 +1729,7 @@ void item_func_LV3_SOUP() { void item_func_LETTER() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (dComIfGs_getItem(SLOT_21, true) != dItemNo_Randomizer_HORSE_FLUTE_e) dComIfGs_setItem(SLOT_21, dItemNo_Randomizer_LETTER_e); } else @@ -1739,7 +1739,7 @@ void item_func_LETTER() { void item_func_BILL() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (dComIfGs_getItem(SLOT_21, true) != dItemNo_Randomizer_HORSE_FLUTE_e) dComIfGs_setItem(SLOT_21, dItemNo_Randomizer_BILL_e); } else @@ -1751,7 +1751,7 @@ void item_func_WOOD_STATUE() { /* dSv_event_flag_c::F_283 - Hyrule Field - Get wood carving */ dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[283]); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (dComIfGs_getItem(SLOT_21, true) != dItemNo_Randomizer_HORSE_FLUTE_e) dComIfGs_setItem(SLOT_21, dItemNo_Randomizer_WOOD_STATUE_e); } else @@ -1761,7 +1761,7 @@ void item_func_WOOD_STATUE() { void item_func_IRIAS_PENDANT() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (dComIfGs_getItem(SLOT_21, true) != dItemNo_Randomizer_HORSE_FLUTE_e) dComIfGs_setItem(SLOT_21, dItemNo_Randomizer_IRIAS_PENDANT_e); } else @@ -1771,7 +1771,7 @@ void item_func_IRIAS_PENDANT() { void item_func_HORSE_FLUTE() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (dComIfGs_getItem(SLOT_21, true) != dItemNo_Randomizer_NONE_e) dComIfGs_setItem(SLOT_21, dItemNo_Randomizer_HORSE_FLUTE_e); } else @@ -1837,7 +1837,7 @@ void item_func_LAKE_HYLIA_PORTAL() { void item_func_RAFRELS_MEMO() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) dComIfGs_setItem(SLOT_7, dItemNo_Randomizer_RAFRELS_MEMO_e); else #endif @@ -1846,7 +1846,7 @@ void item_func_RAFRELS_MEMO() { void item_func_ASHS_SCRIBBLING() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (!dComIfGs_isEventBit(0x3B80)) { // Got earring from Ralis dComIfGs_setItem(SLOT_19, dItemNo_Randomizer_ASHS_SCRIBBLING_e); } @@ -1928,7 +1928,7 @@ void item_func_DROP_CONTAINER02() { void item_func_DROP_CONTAINER03() { dComIfGs_onLightDropGetFlag(LANAYRU_VESSEL); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onEventBit(0x1E80); // Malo Mart Fundraising Starts } #endif @@ -1938,7 +1938,7 @@ void item_func_FILLED_CONTAINER() {} void item_func_MIRROR_PIECE_2() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onCollectMirror(1); // TODO rando /* @@ -1955,7 +1955,7 @@ void item_func_MIRROR_PIECE_2() { void item_func_MIRROR_PIECE_3() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onCollectMirror(2); // TODO rando /* @@ -1972,7 +1972,7 @@ void item_func_MIRROR_PIECE_3() { void item_func_MIRROR_PIECE_4() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onCollectMirror(3); // TODO rando /* @@ -2144,7 +2144,7 @@ void item_func_FUSED_SHADOW_1() { } void item_func_FUSED_SHADOW_2() { - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onCollectCrystal(1); /* Adding rando code until framework is implemented @@ -2158,7 +2158,7 @@ void item_func_FUSED_SHADOW_2() { } void item_func_FUSED_SHADOW_3() { - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onCollectCrystal(2); /* Adding rando code until framework is implemented @@ -2179,7 +2179,7 @@ void item_func_FUSED_SHADOW_3() { void item_func_MIRROR_PIECE_1() { - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onCollectMirror(0); // TODO rando /* @@ -2197,7 +2197,7 @@ void item_func_MIRROR_PIECE_1() { void item_func_POU_SPIRIT() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_addPohSpiritNum(); // TODO rando /* @@ -2252,7 +2252,7 @@ void item_func_ANCIENT_DOCUMENT() { void item_func_AIR_LETTER() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { u8 letterCount = dComIfGs_getAncientDocumentNum(); dComIfGs_setAncientDocumentNum(letterCount + 1); } else @@ -2262,7 +2262,7 @@ void item_func_AIR_LETTER() { void item_func_ANCIENT_DOCUMENT2() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onEventBit(0x3B08); // Repaired Cannon at Lake dComIfGs_setAncientDocumentNum(6); } @@ -2280,7 +2280,7 @@ void item_func_LINKS_SAVINGS() { void item_func_SMALL_KEY2() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) dComIfGs_onStageSwitch(0x2, 0x14); // Unlock North Faron Gate else #endif @@ -2297,7 +2297,7 @@ void item_func_POU_FIRE4() {} void item_func_BOSSRIDER_KEY() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onStageSwitch(0x3, 0x69); // Started Escort dComIfGs_onStageSwitch(0x3, 0x65); // Followed Rutella to Graveyard dComIfGs_onEventBit(0x840); // Started Zora Escort @@ -2308,7 +2308,7 @@ void item_func_BOSSRIDER_KEY() { void item_func_TOMATO_PUREE() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onEventBit(0x480); // Told Yeta about Pumpkin dComIfGs_onEventBit(0x2); // Yeto put Pumpkin in Soup dComIfGs_onEventBit(0x1440); // SPR Lobby Door Unlocked @@ -2319,7 +2319,7 @@ void item_func_TOMATO_PUREE() { void item_func_TASTE() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onEventBit(0x120); // Told Yeta about Cheese dComIfGs_onEventBit(0x1); // Yeto put Pumpkin in Soup dComIfGs_onEventBit(0x1420); // SPR Lobby west Door Unlocked @@ -2330,7 +2330,7 @@ void item_func_TASTE() { void item_func_LV5_BOSS_KEY() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) dComIfGs_onDungeonItemBossKey(0x14); else #endif @@ -2351,7 +2351,7 @@ void item_func_KEY_OF_CARAVAN() {} void item_func_LV2_BOSS_KEY() { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { dComIfGs_onDungeonItemBossKey(0x11); execItemGet(dItemNo_Randomizer_L2_KEY_PIECES3_e); } else @@ -2361,7 +2361,7 @@ void item_func_LV2_BOSS_KEY() { void item_func_KEY_OF_FILONE() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) dComIfGs_onStageSwitch(0x2, 0xC); // Unlock Coro Gate else #endif @@ -2564,7 +2564,7 @@ int item_getcheck_func_WEAR_ZORA() { int item_getcheck_func_MAGIC_LV1() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_MAGIC_LV1_e); #endif return -1; @@ -2580,7 +2580,7 @@ int item_getcheck_func_WALLET_LV1() { int item_getcheck_func_WALLET_LV2() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_WALLET_LV2_e); #endif return -1; @@ -2588,7 +2588,7 @@ int item_getcheck_func_WALLET_LV2() { int item_getcheck_func_WALLET_LV3() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_WALLET_LV3_e); #endif return -1; @@ -2855,7 +2855,7 @@ int item_getcheck_func_LV3_SOUP() { int item_getcheck_func_LETTER() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_LETTER_e); #endif return dComIfGs_getItem(SLOT_21, true) == dItemNo_LETTER_e ? TRUE : FALSE; @@ -2863,7 +2863,7 @@ int item_getcheck_func_LETTER() { int item_getcheck_func_BILL() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_BILL_e); #endif return dComIfGs_getItem(SLOT_21, true) == dItemNo_BILL_e ? TRUE : FALSE; @@ -2871,7 +2871,7 @@ int item_getcheck_func_BILL() { int item_getcheck_func_WOOD_STATUE() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_WOOD_STATUE_e); #endif return dComIfGs_getItem(SLOT_21, true) == dItemNo_WOOD_STATUE_e ? TRUE : FALSE; @@ -2879,7 +2879,7 @@ int item_getcheck_func_WOOD_STATUE() { int item_getcheck_func_IRIAS_PENDANT() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_IRIAS_PENDANT_e); #endif return dComIfGs_getItem(SLOT_21, true) == dItemNo_IRIAS_PENDANT_e ? TRUE : FALSE; @@ -2887,7 +2887,7 @@ int item_getcheck_func_IRIAS_PENDANT() { int item_getcheck_func_HORSE_FLUTE() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_HORSE_FLUTE_e); #endif return dComIfGs_getItem(SLOT_22, true) == dItemNo_HORSE_FLUTE_e ? TRUE : FALSE; @@ -2945,7 +2945,7 @@ int item_getcheck_func_FILLED_CONTAINER() { int item_getcheck_func_MIRROR_PIECE_2() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_MIRROR_PIECE_2_e); #endif return -1; @@ -2953,7 +2953,7 @@ int item_getcheck_func_MIRROR_PIECE_2() { int item_getcheck_func_MIRROR_PIECE_3() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_MIRROR_PIECE_3_e); #endif return -1; @@ -2961,7 +2961,7 @@ int item_getcheck_func_MIRROR_PIECE_3() { int item_getcheck_func_MIRROR_PIECE_4() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_MIRROR_PIECE_4_e); #endif return -1; @@ -3181,7 +3181,7 @@ int item_getcheck_func_POU_FIRE4() { int item_getcheck_func_BOSSRIDER_KEY() { #if TARGET_PC - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) return dComIfGs_isItemFirstBit(dItemNo_Randomizer_BOSSRIDER_KEY_e); #endif return -1; diff --git a/src/d/d_item_data.cpp b/src/d/d_item_data.cpp index 204bba2bb4..1ecc27f0df 100644 --- a/src/d/d_item_data.cpp +++ b/src/d/d_item_data.cpp @@ -1147,12 +1147,12 @@ dItem_itemInfo dItem_data::item_info_randomizer[] = { }; dItem_itemResource* dItem_data_getItemResource() { - return dComIfG_isRandomizer() ? dItem_data::item_resource_randomizer : dItem_data::item_resource; + return randomizer_IsActive() ? dItem_data::item_resource_randomizer : dItem_data::item_resource; } dItem_fieldItemResource* dItem_data_getFieldItemResource() { - return dComIfG_isRandomizer() ? dItem_data::field_item_res_randomizer : dItem_data::field_item_res; + return randomizer_IsActive() ? dItem_data::field_item_res_randomizer : dItem_data::field_item_res; } dItem_itemInfo* dItem_data_getItemInfo() { - return dComIfG_isRandomizer() ? dItem_data::item_info_randomizer : dItem_data::item_info; + return randomizer_IsActive() ? dItem_data::item_info_randomizer : dItem_data::item_info; } #endif diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index f26f7aa1a3..4615d4b2a6 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -1620,7 +1620,7 @@ u8 dMenu_Ring_c::getItemNum(u8 i_slotNo) { case dItemNo_Randomizer_ANCIENT_DOCUMENT_e: case dItemNo_Randomizer_AIR_LETTER_e: case dItemNo_Randomizer_ANCIENT_DOCUMENT2_e: - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) ret = dComIfGs_getAncientDocumentNum(); break; #endif @@ -1667,7 +1667,7 @@ u8 dMenu_Ring_c::getItemMaxNum(u8 i_slotNo) { case dItemNo_Randomizer_ANCIENT_DOCUMENT_e: case dItemNo_Randomizer_AIR_LETTER_e: case dItemNo_Randomizer_ANCIENT_DOCUMENT2_e: - if (dComIfG_isRandomizer()) + if (randomizer_IsActive()) ret = 6; break; #endif diff --git a/src/d/d_s_play.cpp b/src/d/d_s_play.cpp index 3f3cd073b9..8516601e9b 100644 --- a/src/d/d_s_play.cpp +++ b/src/d/d_s_play.cpp @@ -41,6 +41,7 @@ #if TARGET_PC #include "dusk/memory.h" +#include "dusk/randomizer/game/tools.h" #endif #if DEBUG diff --git a/src/d/d_save.cpp b/src/d/d_save.cpp index b4dfa7ac51..4286e0676c 100644 --- a/src/d/d_save.cpp +++ b/src/d/d_save.cpp @@ -449,7 +449,7 @@ void dSv_player_item_c::setLineUpItem() { #if TARGET_PC // Allow rando to use all item slots - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { mItemSlots[23] = 7; } #endif @@ -1205,7 +1205,7 @@ void dSv_memBit_c::onDungeonItem(int i_no) { JUT_ASSERT(2969, 0 <= i_no && i_no < DSV_MEMBIT_ENUM_MAX); #if TARGET_PC // Don't use the stage life collection flag for rando - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (i_no == STAGE_LIFE) { return; } @@ -1251,7 +1251,7 @@ void dSv_memBit_c::offDungeonItem(int i_no) { JUT_ASSERT(2983, 0 <= i_no && i_no < DSV_MEMBIT_ENUM_MAX); #if TARGET_PC // Don't use the stage life collection flag for rando - if (dComIfG_isRandomizer() && i_no == STAGE_LIFE) { + if (randomizer_IsActive() && i_no == STAGE_LIFE) { return; } #endif @@ -1261,7 +1261,7 @@ void dSv_memBit_c::offDungeonItem(int i_no) { s32 dSv_memBit_c::isDungeonItem(int i_no) const { JUT_ASSERT(2998, 0 <= i_no && i_no < DSV_MEMBIT_ENUM_MAX); #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { // Don't use the stage life collection flag for rando if (i_no == STAGE_LIFE) { return FALSE; @@ -1644,11 +1644,6 @@ dSv_memory2_c* dSv_save_c::getSave2(int i_stage2No) { return &mSave2[i_stage2No]; } -void dSv_randomizer_c::clear() { - mTreasureChestOverrides.clear(); - mPoeOverrides.clear(); - mActive = FALSE; -} void dSv_info_c::getSave(int i_stageNo) { JUT_ASSERT(4133, 0 <= i_stageNo && i_stageNo < dSv_save_c::STAGE_MAX); diff --git a/src/d/d_stage.cpp b/src/d/d_stage.cpp index 79facbd69e..3904697598 100644 --- a/src/d/d_stage.cpp +++ b/src/d/d_stage.cpp @@ -2931,9 +2931,9 @@ int dStage_changeScene4Event(int i_exitId, s8 room_no, int i_wipe, bool param_3, if (timeH < 31) { #if TARGET_PC // If randomizer is active and we're loading the first spawn, set our starting time of day - if (dComIfG_isRandomizer() && strcmp(scls_info->mStage, "F_SP103") == 0 && + if (randomizer_IsActive() && strcmp(scls_info->mStage, "F_SP103") == 0 && scls_info->mRoom == 1 && scls_info->mStart == 1) - timeH = dComIfG_getRandomizer().mStartHour; + timeH = randomizer_GetContext().mStartHour; #endif dKy_set_nexttime(15.0f * timeH); } diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 0cf3906d75..b07bea378b 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -239,6 +239,7 @@ namespace dusk { if (showMenu && ImGui::BeginMainMenuBar()) { m_menuGame.draw(); m_menuEnhancements.draw(); + m_menuRandomizer.draw(); m_menuTools.draw(); ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 80.0f * ImGuiScale()); @@ -276,6 +277,8 @@ namespace dusk { m_menuTools.ShowSaveEditor(); } m_menuTools.ShowStateShare(); + m_menuRandomizer.windowRandoStats(); + m_menuRandomizer.windowRandoGeneration(); DuskDebugPad(); // temporary, remove later // Only show cursor when menu or any windows are open diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 0296dc24cc..2e0f2194ae 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -10,6 +10,7 @@ #include "ImGuiMenuEnhancements.hpp" #include "ImGuiMenuGame.hpp" #include "ImGuiMenuTools.hpp" +#include "ImGuiMenuRandomizer.hpp" #include "ImGuiPreLaunchWindow.hpp" #include "imgui.h" @@ -39,6 +40,7 @@ private: ImGuiFirstRunPreset m_firstRunPreset; ImGuiMenuGame m_menuGame; ImGuiMenuEnhancements m_menuEnhancements; + ImGuiMenuRandomizer m_menuRandomizer; ImGuiPreLaunchWindow m_preLaunchWindow; // Keep always last diff --git a/src/dusk/imgui/ImGuiMenuRandomizer.cpp b/src/dusk/imgui/ImGuiMenuRandomizer.cpp new file mode 100644 index 0000000000..90c894e726 --- /dev/null +++ b/src/dusk/imgui/ImGuiMenuRandomizer.cpp @@ -0,0 +1,304 @@ +#include "imgui.h" + +#include "ImGuiConsole.hpp" +#include "ImGuiMenuRandomizer.hpp" + +#include + +#include "dusk/app_info.hpp" +#include "dusk/logging.h" +#include "dusk/randomizer/game/randomizer_context.hpp" +#include "dusk/randomizer/game/tools.h" +#include "dusk/randomizer/generator/randomizer.hpp" + +#include "SDL3/SDL_filesystem.h" + +#include +#include + +namespace dusk { + + static bool generatingSeed = false; + static std::string generationStatusMsg{}; + static std::mutex generationStatusMsgMutex{}; + + void GenerateSeed() { + std::lock_guard lock(generationStatusMsgMutex); + const auto result = SDL_GetPrefPath(dusk::OrgName, dusk::AppName); + if (!result) { + DuskLog.fatal("Unable to get PrefPath: {}", SDL_GetError()); + } + randomizer::Randomizer r; + r.SetBaseOutputPath(result); + auto generationResult = r.Generate(); + if (generationResult.has_value()) { + generationStatusMsg = fmt::format("Generation failed with the following error:\n{}", generationResult.value()); + return; + } + + auto& world = r.GetWorlds()[0]; + + RandomizerContext randoData{}; + // Chest overrides + for (const auto& location : world->GetAllLocations()) { + const auto& metaData = location->GetMetadata(); + if (location->HasCategories("Chest")) { + const auto& stage = metaData[0]["Stage"].as(); + const auto& tboxId = metaData[0]["Tbox ID"].as(); + const auto& itemId = location->GetCurrentItem()->GetID(); + randoData.mTreasureChestOverrides[stage][tboxId] = itemId; + } + } + + // Set starting inventory + for (const auto& item: world->GetStartingItemPool()) { + randoData.mStartingInventory.push_back(item->GetID()); + } + + // Set starting flags + auto startFlags = LoadYAML(RANDO_DATA_PATH "startflags.yaml"); + // Event Flags + for (const auto& flagNode : startFlags["EventFlags"]) { + if (flagNode.IsScalar()) { + const auto& flag = flagNode.as(); + randoData.mStartEventFlags.push_back(flag); + } else if (flagNode.IsMap()) { + const auto& condition = flagNode.begin()->first.as(); + if (world->EvaluateSettingCondition(condition)) { + DuskLog.debug("Setting flags for {}", condition); + for (const auto& conditionalFlag : flagNode.begin()->second) { + const auto& flag = conditionalFlag.as(); + randoData.mStartEventFlags.push_back(flag); + } + } + } + } + + // Region Flags + for (const auto& regionNode : startFlags["RegionFlags"]) { + const auto& region = regionNode.first.as(); + const auto& index = regionNode.second["Index"].as(); + const auto& flags = regionNode.second["Flags"]; + DuskLog.debug("Setting region flags for {}", region); + // This seems kinda scuffed so maybe we change it later + for (const auto& flagNode : flags) { + if (flagNode.IsScalar()) { + const auto& flag = flagNode.as(); + randoData.mStartRegionFlags[index].push_back(flag); + } else if (flagNode.IsMap()) { + const auto& condition = flagNode.begin()->first.as(); + if (world->EvaluateSettingCondition(condition)) { + for (const auto& conditionalFlag : flagNode.begin()->second) { + const auto& flag = conditionalFlag.as(); + randoData.mStartRegionFlags[index].push_back(flag); + } + } + } + } + } + + if (world->Setting("Unlock Map Regions") == "On") + { + auto& bits = randoData.mMapBits; + bits = 0x20; + if (world->Setting("Snowpeak Does Not Require Reekfish Scent") == "On") {bits |= 0x40;} + if (world->Setting("Lanayru Twilight Cleared") == "On") {bits |= 0x10;} + if (world->Setting("Eldin Twilight Cleared") == "On") {bits |= 0x08;} + if (world->Setting("Faron Twilight Cleared") == "On") {bits |= 0x04;} + if (world->Setting("Skip Prologue") == "On") {bits |= 0x02;} + } + + // Set starting time of day + const auto startTimeSetting = world->Setting("Starting Time of Day"); + if (startTimeSetting == "Morning") + randoData.mStartHour = 6; + else if (startTimeSetting == "Noon") + randoData.mStartHour = 12; + else if (startTimeSetting == "Evening") + randoData.mStartHour = 18; + else if (startTimeSetting == "Night") + randoData.mStartHour = 24; + + randoData.mHash = r.GetConfig().GetHash(); + auto writeToFileResult = randoData.WriteToFile(); + if (writeToFileResult.has_value()) { + generationStatusMsg = + fmt::format("Failed to write seed data. Reason: {}", writeToFileResult.value()); + return; + } + + generationStatusMsg = fmt::format("Seed generated! Hash: {}", randoData.mHash); + } + + static void StartSeedGeneration() { + if (generatingSeed) { + return; + } + generatingSeed = true; + GenerateSeed(); + generatingSeed = false; + DuskLog.debug("{}", generationStatusMsg); + } + + ImGuiMenuRandomizer::ImGuiMenuRandomizer() {} + + void ImGuiMenuRandomizer::draw() { + if (ImGui::BeginMenu("Randomizer")) { + if (ImGui::BeginMenu("Generate")) { + if (ImGui::MenuItem("Generate Seed", nullptr, false, !generatingSeed)) { + + std::thread randoGenerationThread(StartSeedGeneration); + randoGenerationThread.detach(); + m_showRandoGeneration = true; + } + + ImGui::EndMenu(); + } + + std::string loadSeedText = "Load Seed"; + if (!playerIsOnTitleScreen()) { + loadSeedText += " (Must be on title screen)"; + } + if (ImGui::BeginMenu(loadSeedText.c_str(), playerIsOnTitleScreen())) { + + std::string seedDirectory = + std::string(SDL_GetPrefPath(dusk::OrgName, dusk::AppName)) + "randomizer/seeds"; + + std::list hashes{}; + + for (const auto& entry : std::filesystem::directory_iterator(seedDirectory)) { + if (entry.is_directory()) { + hashes.push_back(entry.path().filename().string()); + } + } + + if (hashes.empty()) { + if (ImGui::MenuItem("No seeds generated")) {} + } + + for (const auto& hash : hashes) { + std::string name = hash; + if (randomizer_GetContext().mHash == hash) { + name += " (Current Seed)"; + } + if (ImGui::MenuItem(name.c_str())) { + if (!randomizer_IsActive()) { + randomizer_GetContext() = RandomizerContext(); + randomizer_GetContext().LoadFromHash(hash); + } + } + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Delete Seed")) { + + std::string seedDirectory = + std::string(SDL_GetPrefPath(dusk::OrgName, dusk::AppName)) + "randomizer/seeds"; + + std::list hashes{}; + + for (const auto& entry : std::filesystem::directory_iterator(seedDirectory)) { + if (entry.is_directory()) { + hashes.push_back(entry.path().filename().string()); + } + } + + if (hashes.empty()) { + if (ImGui::MenuItem("No seeds generated")) {} + } + + for (const auto& hash : hashes) { + std::string name = hash; + if (randomizer_GetContext().mHash == hash) { + name += " (Current Seed)"; + } + if (ImGui::MenuItem(name.c_str(), nullptr, false, playerIsOnTitleScreen() || randomizer_GetContext().mHash != hash)) { + const std::string hashDirectory = seedDirectory + "/" + hash; + if (randomizer_GetContext().mHash != hash) { + std::filesystem::remove_all(hashDirectory); + } else if (!randomizer_IsActive()){ + // If the user selected the currently seed, but it's not active, we'll allow the delete + std::filesystem::remove_all(hashDirectory); + randomizer_GetContext() = RandomizerContext(); + } + } + } + + ImGui::EndMenu(); + } + + std::string name = "Deactive Randomizer"; + if (!playerIsOnTitleScreen()) { + name += " (Must be on title screen)"; + } + + if (ImGui::MenuItem(name.c_str(), nullptr, false, playerIsOnTitleScreen())) { + // Reset the main randomizer context only if we're not active + if (!randomizer_IsActive()) { + randomizer_GetContext() = RandomizerContext(); + } + } + ImGui::Checkbox("Show Rando Stats", &m_showRandoStats); + + ImGui::EndMenu(); + } + } + + void ImGuiMenuRandomizer::windowRandoGeneration() { + if (!m_showRandoGeneration) { + return; + } + + ImGuiWindowFlags windowFlags = + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_AlwaysAutoResize; + + ImGui::SetNextWindowBgAlpha(0.65f); + + if (!ImGui::Begin("Randomizer Generation", &m_showRandoGeneration, windowFlags)) { + ImGui::End(); + return; + } + + // Print "Generating..." until the seed finishes generating (at which point it will release + // the lock on the mutex) + std::string generationText = "Generating Randomizer Seed..."; + if (generationStatusMsgMutex.try_lock()) { + generationText = generationStatusMsg; + generationStatusMsgMutex.unlock(); + } + + ImGui::Text(generationText.c_str()); + + ImGui::End(); + } + + void ImGuiMenuRandomizer::windowRandoStats() { + if (!m_showRandoStats) { + return; + } + + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + + ImGui::SetNextWindowBgAlpha(0.65f); + if (ImGui::Begin("Rando Stats", nullptr, windowFlags)) { + + const char* seed = randomizer_GetContext().mHash.empty() ? "None" : randomizer_GetContext().mHash.c_str(); + + ImGui::Text("Current Seed: %s", seed); + ImGui::Text("Status: "); + ImGui::SameLine(); + if (randomizer_IsActive()) { + ImGui::TextColored(ImVec4(0.f, 1.f, 0.f, 1.f), "ACTIVE"); + } else { + ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "NOT ACTIVE"); + } + } + + ImGui::End(); + } + +} // namespace dusk \ No newline at end of file diff --git a/src/dusk/imgui/ImGuiMenuRandomizer.hpp b/src/dusk/imgui/ImGuiMenuRandomizer.hpp new file mode 100644 index 0000000000..b9e3b051e8 --- /dev/null +++ b/src/dusk/imgui/ImGuiMenuRandomizer.hpp @@ -0,0 +1,21 @@ + + +#ifndef DUSK_IMGUI_MENU_RANDOMIZER_HPP +#define DUSK_IMGUI_MENU_RANDOMIZER_HPP + +namespace dusk { +class ImGuiMenuRandomizer { +public: + ImGuiMenuRandomizer(); + void draw(); + + void windowRandoStats(); + void windowRandoGeneration(); + +private: + bool m_showRandoStats{false}; + bool m_showRandoGeneration{false}; +}; +} + +#endif //DUSK_IMGUI_MENU_RANDOMIZER_HPP diff --git a/src/dusk/randomizer/game/randomizer_context.cpp b/src/dusk/randomizer/game/randomizer_context.cpp new file mode 100644 index 0000000000..e2726a8a28 --- /dev/null +++ b/src/dusk/randomizer/game/randomizer_context.cpp @@ -0,0 +1,109 @@ +#include "randomizer_context.hpp" + +#include "../generator/utility/yaml.hpp" + +#include "dusk/app_info.hpp" +#include "dusk/logging.h" +#include "dusk/main.h" +#include "dusk/randomizer/game/tools.h" + +#include "SDL3/SDL_filesystem.h" + +#include + +RandomizerContext& randomizer_GetContext() { + static RandomizerContext instance; + return instance; +} + +bool randomizer_IsActive() { + return dusk::IsGameLaunched && (!playerIsOnTitleScreen() || randomizer_GetContext().mCreatingSave) && randomizer_GetContext().mActive; +} + +std::optional RandomizerContext::WriteToFile() { + + std::ofstream seedData(this->GetSeedDataPath()); + if (!seedData.is_open()) { + return "Could not open seed data file"; + } + + YAML::Node out{}; + + // NOTE: When dumping u8s, they must be converted to u16s (or higher), otherwise they get dumped + // as single characters and not numbers + + out["mStartEventFlags"] = this->mStartEventFlags; + for (const auto& [region, flags] : this->mStartRegionFlags) { + const std::list u16Flags(flags.begin(), flags.end()); + out["mStartRegionFlags"][static_cast(region)] = u16Flags; + } + + const std::list u16Inventory(this->mStartingInventory.begin(), this->mStartingInventory.end()); + out["mStartingInventory"] = u16Inventory; + + for (const auto& [stageName, chestOverride] : this->mTreasureChestOverrides) { + for (const auto& [tboxId, itemId] : chestOverride) { + out["mTreasureChestOverrides"][stageName][static_cast(tboxId)] = static_cast(itemId); + } + } + + out["mStartHour"] = static_cast(this->mStartHour); + out["mMapBits"] = static_cast(this->mMapBits); + + seedData << YAML::Dump(out); + seedData.close(); + + return std::nullopt; +} + +std::optional RandomizerContext::LoadFromHash(const std::string& hash) { + this->mHash = hash; + + auto in = LoadYAML(this->GetSeedDataPath()); + + // Event flags + for (const auto& flag : in["mStartEventFlags"]) { + this->mStartEventFlags.push_back(flag.as()); + } + // Region Flags + for (const auto& regionNode : in["mStartRegionFlags"]) { + const auto& regionId = regionNode.first.as(); + for (const auto& flag : regionNode.second) { + this->mStartRegionFlags[regionId].push_back(flag.as()); + } + } + + // Starting inventory + for (const auto& itemId : in["mStartingInventory"]) { + this->mStartingInventory.push_back(itemId.as()); + } + + // Chest overrides + for (const auto& stageNode : in["mTreasureChestOverrides"]) { + const auto& stageName = stageNode.first.as(); + // Single nodes with a zero in their key will get dumped as sequences + if (stageNode.second.IsSequence()) { + this->mTreasureChestOverrides[stageName][0] = stageNode.second[0].as(); + } else { + for (const auto& chestItemPair : stageNode.second) { + auto tboxId = chestItemPair.first.as(); + auto itemId = chestItemPair.second.as(); + this->mTreasureChestOverrides[stageName][tboxId] = itemId; + } + } + } + + // Starting hour + this->mStartHour = in["mStartHour"].as(); + // Starting map bits + this->mMapBits = in["mMapBits"].as(); + + DuskLog.debug("Loaded Randomizer Seed {}", this->mHash); + + return std::nullopt; +} + +std::string RandomizerContext::GetSeedDataPath() const { + return std::string(SDL_GetPrefPath(dusk::OrgName, dusk::AppName)) + "randomizer/seeds/" + this->mHash + "/seed.dat"; +} + diff --git a/src/dusk/randomizer/game/randomizer_context.hpp b/src/dusk/randomizer/game/randomizer_context.hpp new file mode 100644 index 0000000000..83b4ca24af --- /dev/null +++ b/src/dusk/randomizer/game/randomizer_context.hpp @@ -0,0 +1,36 @@ +#ifndef DUSK_RANDOMIZER_CONTEXT_HPP +#define DUSK_RANDOMIZER_CONTEXT_HPP + +#include +#include + +#include +#include + +class RandomizerContext { +public: + RandomizerContext() = default; + + bool mActive{false}; + bool mCreatingSave{false}; + u32 mSeedID{0}; + std::string mHash{""}; + + std::list mStartEventFlags{}; + std::unordered_map> mStartRegionFlags{}; + std::list mStartingInventory{}; + std::unordered_map> mTreasureChestOverrides{}; + std::unordered_map> mPoeOverrides{}; + u8 mStartHour{0}; + u8 mMapBits{}; + + std::optional WriteToFile(); + std::optional LoadFromHash(const std::string& hash); + std::string GetSeedDataPath() const; +}; + +RandomizerContext& randomizer_GetContext(); + +bool randomizer_IsActive(); + +#endif //DUSK_RANDOMIZER_CONTEXT_HPP diff --git a/src/dusk/randomizer/game/tools.cpp b/src/dusk/randomizer/game/tools.cpp index 7edbc8eb5d..bd1a7c6d25 100644 --- a/src/dusk/randomizer/game/tools.cpp +++ b/src/dusk/randomizer/game/tools.cpp @@ -106,4 +106,10 @@ int getStageID(const char* stage) } // Didn't find the current stage for some reason return -1; +} + +bool playerIsOnTitleScreen() { + // Player is either on title screen movie stage (S_MV000) or on title screen map layer 10 + return strcmp(dComIfGp_getStartStageName(), "S_MV000") == 0 || + (strcmp(dComIfGp_getStartStageName(), "F_SP102") == 0 && dComIfG_play_c::getLayerNo(0) == 10); } \ No newline at end of file diff --git a/src/dusk/randomizer/game/tools.h b/src/dusk/randomizer/game/tools.h index 21c374918e..2da0af66aa 100644 --- a/src/dusk/randomizer/game/tools.h +++ b/src/dusk/randomizer/game/tools.h @@ -8,3 +8,4 @@ u8 setNextWarashibeItem(); void offWarashibeItem(u8 item); int initCreatePlayerItem(u32 item, u32 flag, const cXyz* pos, int roomNo, const csXyz* angle, const cXyz* scale); int getStageID(const char* stage); +bool playerIsOnTitleScreen(); diff --git a/src/dusk/randomizer/generator/logic/spoiler_log.cpp b/src/dusk/randomizer/generator/logic/spoiler_log.cpp index 0ff484aac2..3f20e342e7 100644 --- a/src/dusk/randomizer/generator/logic/spoiler_log.cpp +++ b/src/dusk/randomizer/generator/logic/spoiler_log.cpp @@ -45,20 +45,20 @@ namespace randomizer::logic::spoiler_log log << YAML::Dump(randomizer->GetConfig().SettingsToYaml()) << std::endl; } - void GenerateSpoilerLog(randomizer::Randomizer* randomizer) + void GenerateSpoilerLog(Randomizer* randomizer) { - randomizer::utility::platform::Log("Generating Spoiler Log"); + utility::platform::Log("Generating Spoiler Log"); - // Create logs folder if it doesn't exist - if (!randomizer::utility::file::dirExists(LOGS_PATH)) + // Create folders + if (!utility::file::dirExists(randomizer->GetSeedOutputPath())) { - randomizer::utility::file::create_directories(LOGS_PATH); + utility::file::create_directories(randomizer->GetSeedOutputPath()); } auto& config = randomizer->GetConfig(); auto& worlds = randomizer->GetWorlds(); - std::string filepath = std::string(LOGS_PATH) + config.GetHash() + " Spoiler Log.txt"; + std::string filepath = std::string(randomizer->GetSeedOutputPath()) + config.GetHash() + " Spoiler Log.txt"; std::ofstream spoilerLog; spoilerLog.open(filepath); @@ -228,18 +228,18 @@ namespace randomizer::logic::spoiler_log spoilerLog.close(); - randomizer::utility::platform::Log("Wrote spoiler log to " + filepath); + utility::platform::Log("Wrote spoiler log to " + filepath); } - void GenerateAntiSpoilerLog(randomizer::Randomizer* randomizer) + void GenerateAntiSpoilerLog(Randomizer* randomizer) { // Create logs folder if it doesn't exist - if (!randomizer::utility::file::dirExists(LOGS_PATH)) + if (!utility::file::dirExists(randomizer->GetSeedOutputPath())) { - randomizer::utility::file::create_directories(LOGS_PATH); + utility::file::create_directories(randomizer->GetSeedOutputPath()); } - std::string filepath = std::string(LOGS_PATH) + randomizer->GetConfig().GetHash() + " Anti-Spoiler Log.txt"; + std::string filepath = std::string(randomizer->GetSeedOutputPath()) + randomizer->GetConfig().GetHash() + " Anti-Spoiler Log.txt"; std::ofstream antiSpoilerLog; antiSpoilerLog.open(filepath); diff --git a/src/dusk/randomizer/generator/randomizer.cpp b/src/dusk/randomizer/generator/randomizer.cpp index e4f63c791d..7b8f2fa5d4 100644 --- a/src/dusk/randomizer/generator/randomizer.cpp +++ b/src/dusk/randomizer/generator/randomizer.cpp @@ -15,7 +15,7 @@ namespace randomizer { - int Randomizer::Generate() + std::optional Randomizer::Generate() { try { @@ -25,17 +25,17 @@ namespace randomizer { std::cout << "============================================================" << std::endl; std::cout << "The following exception occured: " << e.what() << std::endl; - return 1; + return e.what(); } - return 0; + return std::nullopt; } void Randomizer::GenerateWorlds() { utility::time::ScopedTimer<"Seed generation took ", std::chrono::milliseconds> timer; this->_worlds.clear(); - this->_config.LoadFromFile(SETTINGS_PATH, PREFERENCES_PATH); + this->_config.LoadFromFile(this->GetBaseOutputPath() + "settings.yaml", this->GetBaseOutputPath() + "preferences.yaml"); utility::platform::Log(std::string("Seed: ") + this->_config.GetSeed()); @@ -111,4 +111,9 @@ namespace randomizer } logic::spoiler_log::GenerateAntiSpoilerLog(this); } + + std::string Randomizer::GetSeedOutputPath() + { + return this->_baseOutputPath + "seeds/" + this->_config.GetHash() + "/"; + } } // namespace randomizer diff --git a/src/dusk/randomizer/generator/randomizer.hpp b/src/dusk/randomizer/generator/randomizer.hpp index 38a3e62c33..b7f7d281e9 100644 --- a/src/dusk/randomizer/generator/randomizer.hpp +++ b/src/dusk/randomizer/generator/randomizer.hpp @@ -2,6 +2,8 @@ #include "logic/world.hpp" +#include + namespace randomizer { class Randomizer @@ -12,9 +14,9 @@ namespace randomizer /** * @brief Generates a complete randomizer seed * - * @return 0 if no errors. 1 if there were errors + * @return a std::optional containing a message if there was an error. */ - int Generate(); + std::optional Generate(); void GenerateWorlds(); auto& GetConfig() { return this->_config; } @@ -26,6 +28,10 @@ namespace randomizer auto& GetPlaythroughSpheres() { return this->_playthroughSpheres; } auto& GetEntranceSpheres() { return this->_entranceSpheres; } + + std::string GetSeedOutputPath(); + const std::string& GetBaseOutputPath() const { return this->_baseOutputPath; }; + void SetBaseOutputPath(const std::string& path) { this->_baseOutputPath = path + "randomizer/"; }; private: seedgen::config::Config _config{}; logic::world::WorldPool _worlds{}; @@ -37,5 +43,7 @@ namespace randomizer // Playthrough data std::list> _playthroughSpheres{}; std::list> _entranceSpheres{}; + + std::string _baseOutputPath{RANDO_SAVE_PATH}; }; } // namespace randomizer diff --git a/src/dusk/randomizer/generator/seedgen/config.cpp b/src/dusk/randomizer/generator/seedgen/config.cpp index 7b8e7fac60..93ab7b185b 100644 --- a/src/dusk/randomizer/generator/seedgen/config.cpp +++ b/src/dusk/randomizer/generator/seedgen/config.cpp @@ -72,7 +72,7 @@ namespace randomizer::seedgen::config // If the option doesn't exist, revert to default and rewrite later if necessary if (settingInfo->GetIndexOfOption(settingOption) == -1) { - randomizer::utility::platform::Log(std::string("Setting \"") + settingName + "\" has no option \"" + + utility::platform::Log(std::string("Setting \"") + settingName + "\" has no option \"" + settingOption + "\". Reverting to default \"" + settingInfo->GetDefaultOption() + "\""); settingOption = settingInfo->GetDefaultOption(); @@ -148,7 +148,7 @@ namespace randomizer::seedgen::config // If the option doesn't exist, revert to default and rewrite later if necessary if (preferenceInfo->GetIndexOfOption(preferenceOption) == -1) { - randomizer::utility::platform::Log(std::string("Preference \"") + preferenceName + " has no option \"" + + utility::platform::Log(std::string("Preference \"") + preferenceName + " has no option \"" + preferenceOption + "\". Reverting to default \"" + preferenceInfo->GetDefaultOption() + "\""); preferenceOption = preferenceInfo->GetDefaultOption(); @@ -182,7 +182,7 @@ namespace randomizer::seedgen::config { settings.InsertSetting(settingName, settings::Setting(settingInfo.get(), settingInfo->GetDefaultOption())); - randomizer::utility::platform::Log(std::string("Added missing setting \"") + settingName + "\""); + utility::platform::Log(std::string("Added missing setting \"") + settingName + "\""); if (settingInfo->GetType() == settings::Type::STANDARD) { rewriteSettings = true; @@ -196,7 +196,7 @@ namespace randomizer::seedgen::config if (!settingsTree["Seed"]) { this->_seed = seed::GenerateSeed(); - randomizer::utility::platform::Log("Seed is missing. Generated new seed."); + utility::platform::Log("Seed is missing. Generated new seed."); rewriteSettings = true; } if (!settingsTree["Plandomizer"] || !settingsTree["Generate Spoiler Log"] || !settingsTree["Starting Inventory"] || @@ -212,12 +212,12 @@ namespace randomizer::seedgen::config // Rewrite files if deemed necessary if (allowRewrite && rewriteSettings) { - randomizer::utility::platform::Log(std::string("Rewriting ") + settingsPath.generic_string()); + utility::platform::Log(std::string("Rewriting ") + settingsPath.generic_string()); this->WriteSettingsToFile(settingsPath); } if (allowRewrite && rewritePreferences) { - randomizer::utility::platform::Log(std::string("Rewriting ") + preferencesPath.generic_string()); + utility::platform::Log(std::string("Rewriting ") + preferencesPath.generic_string()); this->WritePreferencesToFile(preferencesPath); } } @@ -335,7 +335,7 @@ namespace randomizer::seedgen::config int WriteDefaultSettings(const fspath& settingsPath) { - randomizer::utility::platform::Log("Creating Default Settings"); + utility::platform::Log("Creating Default Settings"); std::ofstream settingsFile(settingsPath); if (settingsFile.is_open() == false) { @@ -369,7 +369,7 @@ namespace randomizer::seedgen::config int WriteDefaultPreferences(const fspath& preferencesPath) { - randomizer::utility::platform::Log("Creating Default Preferences"); + utility::platform::Log("Creating Default Preferences"); std::ofstream preferencesFile(preferencesPath); if (preferencesFile.is_open() == false) { @@ -402,7 +402,7 @@ namespace randomizer::seedgen::config { // Seed with system time incase we have to choose random preferences during seeding auto seed = static_cast(std::random_device {}()); - randomizer::utility::random::RandomInit(seed); + utility::random::RandomInit(seed); // Seed the rng using a combination of the seed and standard settings std::string hashStr = config.GetSeed(); @@ -444,7 +444,7 @@ namespace randomizer::seedgen::config if (config.IsUsingPlandomizer()) { std::string plandomizerContents; - auto retVal = randomizer::utility::file::GetContents(config.GetPlandomizerPath(), plandomizerContents); + auto retVal = utility::file::GetContents(config.GetPlandomizerPath(), plandomizerContents); if (!ignoreInvalidPlandomizer && retVal != 0) { LOG_TO_ERROR("Could not read plandomizer file at \"" + config.GetPlandomizerPath().generic_string() + "\""); @@ -460,7 +460,7 @@ namespace randomizer::seedgen::config } const size_t integerSeed = zng_crc32(0L, reinterpret_cast(hashStr.data()), hashStr.length()); - randomizer::utility::random::RandomInit(integerSeed); + utility::random::RandomInit(integerSeed); return 0; } diff --git a/src/dusk/randomizer/randomizer.cmake b/src/dusk/randomizer/randomizer.cmake index a140f8cacd..0055783cd1 100644 --- a/src/dusk/randomizer/randomizer.cmake +++ b/src/dusk/randomizer/randomizer.cmake @@ -1,12 +1,11 @@ cmake_minimum_required(VERSION 3.16) set(RANDOMIZER_ONLY "0" CACHE STRING "Runs only the randomizer generator") -set(RANDO_SAVE_PATH "${CMAKE_BINARY_DIR}/randomizer") +set(RANDO_SAVE_PATH "${CMAKE_BINARY_DIR}/randomizer/") set(GAME_COMPILE_DEFS ${GAME_COMPILE_DEFS} RANDOMIZER_ONLY=${RANDOMIZER_ONLY} - RANDO_SAVE_PATH="${RANDO_SAVE_PATH}" - LOGS_PATH="${RANDO_SAVE_PATH}/logs/" + RANDO_SAVE_PATH="${RANDO_SAVE_PATH}" RANDO_DATA_PATH="${CMAKE_SOURCE_DIR}/src/dusk/randomizer/generator/data/") if(RANDO_ERROR_LOG) diff --git a/src/f_op/f_op_actor_mng.cpp b/src/f_op/f_op_actor_mng.cpp index bb415b0fdb..108ea3aef3 100644 --- a/src/f_op/f_op_actor_mng.cpp +++ b/src/f_op/f_op_actor_mng.cpp @@ -1395,7 +1395,7 @@ fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 p int i_itemBitNo, int i_roomNo, csXyz const* i_angle, cXyz const* i_scale) { #if TARGET_PC - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { i_itemNo = verifyProgressiveItem(i_itemNo); } #endif @@ -1624,7 +1624,7 @@ fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomN return fpcM_ERROR_PROCESS_ID_e; } - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { i_itemNo = verifyProgressiveItem(i_itemNo); if (i_itemNo == dItemNo_Randomizer_UTAWA_HEART_e) { @@ -1665,7 +1665,7 @@ fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_ro } // If we are fighting Ook in randomizer, we want to handle the boomerang check a different way. - if (dComIfG_isRandomizer()) { + if (randomizer_IsActive()) { if (daAlink_c::checkStageName("D_MN05B")) { i_itemNo = verifyProgressiveItem(i_itemNo); return initCreatePlayerItem(i_itemNo, 0xFF, i_pos, i_roomNo, i_angle, i_scale);