From c2d894eab895b6cc39985c52880d2b9c0ac7dcb1 Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 8 Dec 2025 19:25:19 -0500 Subject: [PATCH 01/15] Add Transformation Mask Hints --- mm/2s2h/Rando/ActorBehavior/EnTalk.cpp | 63 ++++++++++++++++++++++++++ mm/2s2h/Rando/Menu.cpp | 16 ++----- mm/2s2h/Rando/StaticData/Options.cpp | 1 + mm/2s2h/Rando/Types.h | 1 + 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp index d95069ab60..b135686d59 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp @@ -7,6 +7,67 @@ extern "C" { #include "variables.h" } +void ApplyTransformationHints(u16* textId, bool* loadFromMessageTable) { + static std::string placeholderMsg = "Last seen in"; + static int transformHintIndex = 0; + + if (transformHintIndex > 3) { + transformHintIndex = 0; + } + + u8 icon = 0xFE; + std::string msg; + RandoItemId randoItemId = RI_NONE; + + if (transformHintIndex == 0) { + msg = " %yDeparted Soul Alert%w!\n" + "The souls of the departed lay restless, " + "find and heal them!"; + } else { + msg = " %g{{mask}}%w:\n" + "Last seen in\n" + "%y{{location}}%w"; + + switch (transformHintIndex) { + case 1: + CustomMessage::Replace(&msg, "{{mask}}", " Butler's Son"); + randoItemId = RI_MASK_DEKU; + break; + case 2: + CustomMessage::Replace(&msg, "{{mask}}", " Darmani"); + randoItemId = RI_MASK_GORON; + break; + case 3: + CustomMessage::Replace(&msg, "{{mask}}", " Mikau"); + randoItemId = RI_MASK_ZORA; + break; + default: + break; + } + + icon = Rando::StaticData::GetIconForZMessage(randoItemId); + RandoCheckId randoCheckId = Rando::FindItemPlacement(randoItemId); + if (RANDO_SAVE_CHECKS[randoCheckId].obtained == true) { + CustomMessage::Replace(&msg, "{{location}}", "your %gpocket%w!"); + } else { + CustomMessage::Replace(&msg, "{{location}}", + Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + msg += "."; + } + } + + CustomMessage::Entry entry = { + .icon = icon, + .nextMessageID = transformHintIndex >= 3 ? (u16)0xFFFF : (u16)0x1C18, + .msg = msg, + }; + + CustomMessage::LoadCustomMessageIntoFont(entry); + *loadFromMessageTable = false; + transformHintIndex++; + +} + void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { static int remainsHintIndex = 0; @@ -66,4 +127,6 @@ void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { void Rando::ActorBehavior::InitEnTalkBehavior() { // "Recruiting Soldiers..." Posters around Clock Town COND_ID_HOOK(OnOpenText, 0x1C06, IS_RANDO && RANDO_SAVE_OPTIONS[RO_HINTS_BOSS_REMAINS], ApplyRemainsHint); + + COND_ID_HOOK(OnOpenText, 0x1C18, IS_RANDO && RANDO_SAVE_OPTIONS[RO_HINTS_TRANSFORMATIONS], ApplyTransformationHints); } diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index a57ae24469..bf05350542 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -724,8 +724,7 @@ static void DrawCheckFilterTab() { static void DrawHintsTab() { f32 columnWidth = ImGui::GetContentRegionAvail().x / 3 - (ImGui::GetStyle().ItemSpacing.x * 2); - f32 halfHeight = ImGui::GetContentRegionAvail().y / 2 - (ImGui::GetStyle().ItemSpacing.y * 2); - ImGui::BeginChild("randoHintsColumn1", ImVec2(columnWidth, halfHeight)); + ImGui::BeginChild("randoHintsColumn1", ImVec2(columnWidth, ImGui::GetContentRegionAvail().y)); CVarCheckbox( "Spider House", Rando::StaticData::Options[RO_HINTS_SPIDER_HOUSES].cvar, CheckboxOptions( @@ -750,15 +749,10 @@ static void DrawHintsTab() { CheckboxOptions({ { .tooltip = "Once you have the Moon Access Requirements, talking to Skull Kid on " "the Clock Tower Rooftop will hint the location of Oath to Order" } })); CVarCheckbox( - "General Actor Hints", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, - .disabledTooltip = "Soon you will be able to disable these. Currently hinted:\n- Bomb Shop " - "4th Item\n- Lottery\n- Great Fairy Fountains\n- Mountain Smithy" } }) - .DefaultValue(true)); - CVarCheckbox("Saria's Song", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); - CVarCheckbox("Song of Soaring", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); + "Transformation Masks", Rando::StaticData::Options[RO_HINTS_TRANSFORMATIONS].cvar, + CheckboxOptions({ { .tooltip = "Checking the sign near the Business Scrub in South Clock Town " + "will reveal the location of Transformation Masks.\n" + "Note: This excludes Fierce Deity."} })); CVarCheckbox( "Hookshot Location", Rando::StaticData::Options[RO_HINTS_HOOKSHOT].cvar, CheckboxOptions( diff --git a/mm/2s2h/Rando/StaticData/Options.cpp b/mm/2s2h/Rando/StaticData/Options.cpp index 0b8f6f0629..2ff01306c7 100644 --- a/mm/2s2h/Rando/StaticData/Options.cpp +++ b/mm/2s2h/Rando/StaticData/Options.cpp @@ -27,6 +27,7 @@ std::map Options = { RO(RO_HINTS_OATH_TO_ORDER, RO_GENERIC_OFF), RO(RO_HINTS_PURCHASEABLE, RO_GENERIC_OFF), RO(RO_HINTS_SPIDER_HOUSES, RO_GENERIC_OFF), + RO(RO_HINTS_TRANSFORMATIONS, RO_GENERIC_OFF), RO(RO_TRAP_AMOUNT, 5), RO(RO_LOGIC, RO_LOGIC_GLITCHLESS), RO(RO_MINIMUM_SKULLTULA_TOKENS, SPIDER_HOUSE_TOKENS_REQUIRED), diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index d9bb50242c..e24650122b 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -2794,6 +2794,7 @@ typedef enum { RO_HINTS_OATH_TO_ORDER, RO_HINTS_PURCHASEABLE, RO_HINTS_SPIDER_HOUSES, + RO_HINTS_TRANSFORMATIONS, RO_TRAP_AMOUNT, RO_LOGIC, RO_MINIMUM_SKULLTULA_TOKENS, From 2a5c0ee31170ad372f2dc9ac36ef13e53b81c6ef Mon Sep 17 00:00:00 2001 From: Caladius Date: Tue, 9 Dec 2025 09:41:50 -0500 Subject: [PATCH 02/15] Deckscrubber Addition --- mm/2s2h/PresetManager/PresetDescriptions.h | 73 +++++++++++ mm/2s2h/PresetManager/PresetManager.cpp | 141 +++++++++++++++++++++ mm/2s2h/PresetManager/PresetManager.h | 2 + mm/2s2h/Rando/ActorBehavior/EnTalk.cpp | 6 +- mm/2s2h/Rando/Menu.cpp | 29 ++++- 5 files changed, 244 insertions(+), 7 deletions(-) create mode 100644 mm/2s2h/PresetManager/PresetDescriptions.h diff --git a/mm/2s2h/PresetManager/PresetDescriptions.h b/mm/2s2h/PresetManager/PresetDescriptions.h new file mode 100644 index 0000000000..712102da5e --- /dev/null +++ b/mm/2s2h/PresetManager/PresetDescriptions.h @@ -0,0 +1,73 @@ +#pragma once +#include "2s2h/BenGui/UIWidgets.hpp" +#include + +#define COLOR_ORANGE UIWidgets::Colors::Orange +#define COLOR_GREEN UIWidgets::Colors::Green +#define TEXT_COLOR(color) UIWidgets::ColorValues.at(COLOR_##color) + +std::vector> deckScrubberReqs = { + { "Dungeon Access:", "Requires Transformation & Song" }, + { "Moon Access Remains:", "4 Boss Remains & Oath to Order" }, + { "Stray Fairies Required:", "5" }, +}; + +std::vector deckScrubberShuffles = { + "Shuffle Songs", "Shuffle Stray Fairies", "Shuffle Owl Statues", "Shuffle Shops", "Shuffle Boss Remains", +}; + +std::vector deckScrubberStarting = { + "Full Wallets", "Maps and Compasses", "Kokiri Sword", "Hero's Shield", + "Ocarina of Time", "Song of Time", "Bunny Hood", +}; + +std::vector> deckScrubberHints = { + { "General Hints", "The Bomb shop 4th Item, Lottery, Great Fairy Fountains, and Mountain Smithy rewards can be " + "hinted by speaking to their respective NPCs." }, + { "Spider House Rewards", "Swamp is hinted within the Spider House.\n" + "Ocean is hinted in South Clock Town day 1 on top of the scaffolding." }, + { "Gossip Stone Static", "Similar to Ship of Harkinian, Gossip Stones will provide hints." }, + { "Boss Remains", "In South Clock Town there is a big poster on the side of the Chest Building, this will tell you " + "where each Boss Remain is located." }, + { "Oath to Order", + "Once you have all 4 Remains, talking to Skull Kid on the Clock Tower Rooftop will hint at the songs location." }, + { "Hookshot", "The Zora near Pirates Fortress will hint the items location." }, + { "Transformation Masks", "In South Clock Town, the sign leaning up against the stall near the Business Scrub will " + "tell you where one of each mask is located." }, +}; + +void DrawDeckScrubberDescription() { + ImGui::SeparatorText("Deckscrubber Settings"); + ImGui::TextColored(TEXT_COLOR(ORANGE), "Requirements"); + if (ImGui::BeginTable("DesckscrubberReq", 2)) { + for (auto& [key, value] : deckScrubberReqs) { + ImGui::TableNextColumn(); + ImGui::TextColored(TEXT_COLOR(GREEN), key.c_str()); + ImGui::TableNextColumn(); + ImGui::Text(value.c_str()); + } + ImGui::EndTable(); + } + ImGui::Separator(); + if (ImGui::BeginTable("DesckscrubberReq", 2)) { + ImGui::TableNextColumn(); + ImGui::TextColored(TEXT_COLOR(ORANGE), "Included Shuffles"); + for (auto& shuffle : deckScrubberShuffles) { + ImGui::Text(shuffle.c_str()); + } + ImGui::TableNextColumn(); + ImGui::TextColored(TEXT_COLOR(ORANGE), "Starting Items"); + for (auto& item : deckScrubberStarting) { + ImGui::Text(item.c_str()); + } + ImGui::EndTable(); + } + ImGui::Separator(); + ImGui::TextColored(TEXT_COLOR(ORANGE), "Hints"); + + for (auto& [key, value] : deckScrubberHints) { + ImGui::TextColored(TEXT_COLOR(GREEN), key.c_str()); + ImGui::TextWrapped(value.c_str()); + ImGui::Separator(); + } +} \ No newline at end of file diff --git a/mm/2s2h/PresetManager/PresetManager.cpp b/mm/2s2h/PresetManager/PresetManager.cpp index 63462833bb..6d89fa4f86 100644 --- a/mm/2s2h/PresetManager/PresetManager.cpp +++ b/mm/2s2h/PresetManager/PresetManager.cpp @@ -256,6 +256,146 @@ nlohmann::json curatedPresetJ = R"( } )"_json; +nlohmann::json deckScrubberJ = R"( +{ + "ClearCVars": [ + "gCheats", + "gCollisionViewer", + "gDeveloperTools", + "gEnhancements", + "gEventLog", + "gFixes", + "gModes", + "gNetwork", + "gNotifications", + "gRando" + ], + "CVars": { + "gCheats": { + "EasyFrameAdvance": 1 + }, + "gEnhancements": { + "Cutscenes": { + "SkipEntranceCutscenes": 1, + "SkipFirstCycle": 1, + "SkipGetItemCutscenes": 1, + "SkipIntroSequence": 1, + "SkipMiscInteractions": 1, + "SkipOnePointCutscenes": 1, + "SkipStoryCutscenes": 1, + "SkipToFileSelect": 1 + }, + "Dialogue": { + "AutoBombersCode": 1, + "FastBankSelection": 1, + "FastText": 1 + }, + "DifficultyOptions": { + "DekuGuardSearchBalls": 1, + "LowerBankRewardThresholds": 1 + }, + "Dpad": { + "DpadEquips": 1 + }, + "Equipment": { + "BetterPictoMessage": 1, + "ChuDrops": 1, + "MagicArrowEquipSpeed": 1, + "TwoHandedSwordSpinAttack": 1 + }, + "Fixes": { + "CompletedHeartContainerAudio": 1, + "ControlCharacters": 1, + "FierceDeityZTargetMovement": 1 + }, + "Masks": { + "FastTransformation": 1, + "FierceDeitysAnywhere": 1, + "NoBlastMaskCooldown": 1, + "PersistentBunnyHood": { + "Enabled": 1 + } + }, + "Minigames": { + "AlwaysWinDoggyRace": 1, + "CuccoShackCuccoCount": 1, + "SwampArcheryScore": 2179, + "TownArcheryScore": 50 + }, + "Playback": { + "NoDropOcarinaInput": 1, + "SkipScarecrowSong": 1 + }, + "Player": { + "ClimbSpeed": 2, + "FasterPushAndPull": 1, + "FierceDeityPutaway": 1, + "InstantPutaway": 1 + }, + "PlayerActions": { + "InstantRecall": 1 + }, + "Restorations": { + "PowerCrouchStab": 2, + "WoodfallMountainAppearance": 1 + }, + "Saving": { + "PauseSave": 1 + }, + "Songs": { + "BetterSongOfDoubleTime": 1, + "FasterSongPlayback": 1, + "ZoraEggCount": 1 + }, + "Timesavers": { + "DampeDiggingSkip": 1, + "FastChests": 1, + "GalleryTwofer": 1, + "MarineLabHP": 1, + "SkipBalladOfWindfish": 1, + "SwampBoatSpeed": 1 + } + }, + "gFixes": { + "FixAmmoCountEnvColor": 1, + "FixEponaStealingSword": 1, + "FixIkanaGreatFairyFountainColor": 1 + }, + "gRando": { + "Enabled": 1, + "InputSeed": "", + "Options": { + "RO_HINTS_BOSS_REMAINS": 1, + "RO_HINTS_GOSSIP_STONES": 1, + "RO_HINTS_HOOKSHOT": 1, + "RO_HINTS_OATH_TO_ORDER": 1, + "RO_HINTS_SPIDER_HOUSES": 1, + "RO_HINTS_TRANSFORMATIONS": 1, + "RO_MINIMUM_STRAY_FAIRIES": 5, + "RO_PLENTIFUL_ITEMS": 1, + "RO_SHUFFLE_BOSS_REMAINS": 1, + "RO_SHUFFLE_GOLD_SKULLTULAS": 0, + "RO_SHUFFLE_OWL_STATUES": 1, + "RO_SHUFFLE_SHOPS": 1, + "RO_SHUFFLE_TRAPS": 1, + "RO_STARTING_MAPS_AND_COMPASSES": 1, + "RO_STARTING_RUPEES": 1, + "RO_TRAP_AMOUNT": 6 + }, + "SpoilerFile": "", + "SpoilerFileIndex": 0, + "StartingItems": "109,126,91,146,66", + "Traps": { + "Freeze": 1, + "Shock": 1 + } + } + }, + "type": "2S2H_PRESET", + "version": 1 +} +)"_json; + std::unordered_map>> presets = {}; const std::filesystem::path presetsFolderPath(Ship::Context::GetPathRelativeToAppDirectory("presets", appShortName)); @@ -264,6 +404,7 @@ void PresetManager_RefreshPresets() { presets.insert( { "Defaults (Everything Off)", { defaultsPresetJ, { "Developer Tools", "Enhancements", "HUD", "Rando" } } }); presets.insert({ "Curated", { curatedPresetJ, { "Developer Tools", "Enhancements", "HUD" } } }); + presets.insert({ "Deckscrubber v3", { deckScrubberJ, { "Developer Tools", "Enhancements", "HUD", "Rando" } } }); // ensure the presets folder exists if (!std::filesystem::exists(presetsFolderPath)) { diff --git a/mm/2s2h/PresetManager/PresetManager.h b/mm/2s2h/PresetManager/PresetManager.h index 7b55c97f71..ce81bb117c 100644 --- a/mm/2s2h/PresetManager/PresetManager.h +++ b/mm/2s2h/PresetManager/PresetManager.h @@ -3,8 +3,10 @@ #define PRESET_MANAGER_H #include +extern nlohmann::json deckScrubberJ; bool PresetManager_HandleFileDropped(const std::string& filePath); +void PresetManager_ApplyPreset(nlohmann::json j); void PresetManager_Draw(); #endif // PRESET_MANAGER_H diff --git a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp index b135686d59..3caa66c293 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp @@ -25,7 +25,7 @@ void ApplyTransformationHints(u16* textId, bool* loadFromMessageTable) { "find and heal them!"; } else { msg = " %g{{mask}}%w:\n" - "Last seen in\n" + "Last seen in\n" "%y{{location}}%w"; switch (transformHintIndex) { @@ -65,7 +65,6 @@ void ApplyTransformationHints(u16* textId, bool* loadFromMessageTable) { CustomMessage::LoadCustomMessageIntoFont(entry); *loadFromMessageTable = false; transformHintIndex++; - } void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { @@ -128,5 +127,6 @@ void Rando::ActorBehavior::InitEnTalkBehavior() { // "Recruiting Soldiers..." Posters around Clock Town COND_ID_HOOK(OnOpenText, 0x1C06, IS_RANDO && RANDO_SAVE_OPTIONS[RO_HINTS_BOSS_REMAINS], ApplyRemainsHint); - COND_ID_HOOK(OnOpenText, 0x1C18, IS_RANDO && RANDO_SAVE_OPTIONS[RO_HINTS_TRANSFORMATIONS], ApplyTransformationHints); + COND_ID_HOOK(OnOpenText, 0x1C18, IS_RANDO && RANDO_SAVE_OPTIONS[RO_HINTS_TRANSFORMATIONS], + ApplyTransformationHints); } diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index bf05350542..5fd41654a8 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -4,6 +4,8 @@ #include "Rando/CheckTracker/CheckTracker.h" #include "build.h" #include "2s2h/BenGui/BenMenu.h" +#include "PresetManager/PresetManager.h" +#include "PresetManager/PresetDescriptions.h" extern "C" { #include "overlays/actors/ovl_En_Sth/z_en_sth.h" @@ -748,11 +750,10 @@ static void DrawHintsTab() { CVarCheckbox("Oath to Order", Rando::StaticData::Options[RO_HINTS_OATH_TO_ORDER].cvar, CheckboxOptions({ { .tooltip = "Once you have the Moon Access Requirements, talking to Skull Kid on " "the Clock Tower Rooftop will hint the location of Oath to Order" } })); - CVarCheckbox( - "Transformation Masks", Rando::StaticData::Options[RO_HINTS_TRANSFORMATIONS].cvar, + CVarCheckbox("Transformation Masks", Rando::StaticData::Options[RO_HINTS_TRANSFORMATIONS].cvar, CheckboxOptions({ { .tooltip = "Checking the sign near the Business Scrub in South Clock Town " "will reveal the location of Transformation Masks.\n" - "Note: This excludes Fierce Deity."} })); + "Note: This excludes Fierce Deity." } })); CVarCheckbox( "Hookshot Location", Rando::StaticData::Options[RO_HINTS_HOOKSHOT].cvar, CheckboxOptions( @@ -761,10 +762,30 @@ static void DrawHintsTab() { ImGui::EndChild(); } +void DrawRacesTab() { + ImGui::BeginChild("randoRacesColumn1", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y)); + ImGui::Text("Apply the Deckscrubber Race Preset and then create your File."); + ImGui::PushID("DeckscrubberSet"); + if (UIWidgets::Button("Apply Preset", { .color = COLOR_GREEN })) { + PresetManager_ApplyPreset(deckScrubberJ); + } + DrawDeckScrubberDescription(); + ImGui::PopID(); + ImGui::EndChild(); +} + void Rando::RegisterMenu() { mBenMenu->AddMenuEntry("Rando", "gSettings.Menu.RandoSidebarSection"); + + // New Race Menu + mBenMenu->AddSidebarEntry("Rando", "Races", 1); + WidgetPath path = { "Rando", "Races", SECTION_COLUMN_1 }; + path.sidebarName = "Races"; + mBenMenu->AddWidget(path, "Races", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { DrawRacesTab(); }); + + // Existing Rando Menu mBenMenu->AddSidebarEntry("Rando", "General", 1); - WidgetPath path = { "Rando", "General", SECTION_COLUMN_1 }; + path = { "Rando", "General", SECTION_COLUMN_1 }; mBenMenu->AddWidget(path, "General", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { DrawGeneralTab(); }); mBenMenu->AddSidebarEntry("Rando", "Logic/Conditions", 1); path.sidebarName = "Logic/Conditions"; From 9c08922eacb86716d5b4cfa5eb84bc4091c9a024 Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 15 Dec 2025 16:02:03 -0500 Subject: [PATCH 03/15] Update Workflow From 81ca2213ef468268bae7ecb0d317918820c071c9 Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 15 Dec 2025 16:02:39 -0500 Subject: [PATCH 04/15] Update PR Artifacts From 4a37ad2af877f7dcd39e77fd0a11599c4066bff1 Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 15 Dec 2025 16:03:35 -0500 Subject: [PATCH 05/15] Update seed settings and add Links Pocket for boss remains --- mm/2s2h/PresetManager/PresetDescriptions.h | 27 +- mm/2s2h/PresetManager/PresetManager.cpp | 5 + mm/2s2h/Rando/Logic/DeckscrubberLogic.cpp | 298 ++++++++++++++++++++ mm/2s2h/Rando/Logic/Logic.h | 1 + mm/2s2h/Rando/Menu.cpp | 1 + mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp | 14 + mm/2s2h/Rando/Types.h | 1 + 7 files changed, 337 insertions(+), 10 deletions(-) create mode 100644 mm/2s2h/Rando/Logic/DeckscrubberLogic.cpp diff --git a/mm/2s2h/PresetManager/PresetDescriptions.h b/mm/2s2h/PresetManager/PresetDescriptions.h index 712102da5e..de3baabc6a 100644 --- a/mm/2s2h/PresetManager/PresetDescriptions.h +++ b/mm/2s2h/PresetManager/PresetDescriptions.h @@ -13,12 +13,12 @@ std::vector> deckScrubberReqs = { }; std::vector deckScrubberShuffles = { - "Shuffle Songs", "Shuffle Stray Fairies", "Shuffle Owl Statues", "Shuffle Shops", "Shuffle Boss Remains", + "Shuffle Boss Remains", "Shuffle Owl Statues", "Shuffle Shops", "Shuffle Songs", "Shuffle Stray Fairies", }; std::vector deckScrubberStarting = { - "Full Wallets", "Maps and Compasses", "Kokiri Sword", "Hero's Shield", - "Ocarina of Time", "Song of Time", "Bunny Hood", + "Bunny Hood", "Full Wallets", "Hero's Shield", "Maps and Compasses", "Kokiri Sword", "Random Boss Remain", + "Ocarina of Time", "", "Song of Time", }; std::vector> deckScrubberHints = { @@ -49,25 +49,32 @@ void DrawDeckScrubberDescription() { ImGui::EndTable(); } ImGui::Separator(); - if (ImGui::BeginTable("DesckscrubberReq", 2)) { + if (ImGui::BeginTable("DeckscrubberSeedSettings", 2)) { ImGui::TableNextColumn(); ImGui::TextColored(TEXT_COLOR(ORANGE), "Included Shuffles"); - for (auto& shuffle : deckScrubberShuffles) { - ImGui::Text(shuffle.c_str()); + if (ImGui::BeginTable("DesckscrubberShuffles", 2)) { + for (auto& shuffle : deckScrubberShuffles) { + ImGui::TableNextColumn(); + ImGui::Text(shuffle.c_str()); + } + ImGui::EndTable(); } ImGui::TableNextColumn(); ImGui::TextColored(TEXT_COLOR(ORANGE), "Starting Items"); - for (auto& item : deckScrubberStarting) { - ImGui::Text(item.c_str()); + if (ImGui::BeginTable("DesckscrubberStarting", 2)) { + for (auto& item : deckScrubberStarting) { + ImGui::TableNextColumn(); + ImGui::Text(item.c_str()); + } + ImGui::EndTable(); } ImGui::EndTable(); } ImGui::Separator(); ImGui::TextColored(TEXT_COLOR(ORANGE), "Hints"); - for (auto& [key, value] : deckScrubberHints) { ImGui::TextColored(TEXT_COLOR(GREEN), key.c_str()); ImGui::TextWrapped(value.c_str()); ImGui::Separator(); } -} \ No newline at end of file +} diff --git a/mm/2s2h/PresetManager/PresetManager.cpp b/mm/2s2h/PresetManager/PresetManager.cpp index 6d89fa4f86..bd441fd619 100644 --- a/mm/2s2h/PresetManager/PresetManager.cpp +++ b/mm/2s2h/PresetManager/PresetManager.cpp @@ -276,6 +276,7 @@ nlohmann::json deckScrubberJ = R"( }, "gEnhancements": { "Cutscenes": { + "SkipEnemyCutscenes": 1, "SkipEntranceCutscenes": 1, "SkipFirstCycle": 1, "SkipGetItemCutscenes": 1, @@ -323,6 +324,7 @@ nlohmann::json deckScrubberJ = R"( "TownArcheryScore": 50 }, "Playback": { + "DpadOcarina": 1, "NoDropOcarinaInput": 1, "SkipScarecrowSong": 1 }, @@ -333,6 +335,7 @@ nlohmann::json deckScrubberJ = R"( "InstantPutaway": 1 }, "PlayerActions": { + "ArrowCycle": 1, "InstantRecall": 1 }, "Restorations": { @@ -350,6 +353,7 @@ nlohmann::json deckScrubberJ = R"( "Timesavers": { "DampeDiggingSkip": 1, "FastChests": 1, + "FasterSceneTransitions": 1, "GalleryTwofer": 1, "MarineLabHP": 1, "SkipBalladOfWindfish": 1, @@ -365,6 +369,7 @@ nlohmann::json deckScrubberJ = R"( "Enabled": 1, "InputSeed": "", "Options": { + "RO_LOGIC": 4, "RO_HINTS_BOSS_REMAINS": 1, "RO_HINTS_GOSSIP_STONES": 1, "RO_HINTS_HOOKSHOT": 1, diff --git a/mm/2s2h/Rando/Logic/DeckscrubberLogic.cpp b/mm/2s2h/Rando/Logic/DeckscrubberLogic.cpp new file mode 100644 index 0000000000..a8dfe0922d --- /dev/null +++ b/mm/2s2h/Rando/Logic/DeckscrubberLogic.cpp @@ -0,0 +1,298 @@ +#include "Logic.h" +#include "2s2h/Rando/Types.h" + +#include +#include +#include + +extern "C" { +#include "variables.h" +#include "ShipUtils.h" +uint64_t GetUnixTimestamp(); +} + +namespace Rando { + +namespace Logic { + +void ApplyDeckscrubberLogicToSaveContext(std::vector& checkPool, std::vector& itemPool) { + + std::map> itemToSceneBlacklist = { + { RI_MASK_DEKU, + { SCENE_MITURIN, SCENE_MITURIN_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, + SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_SONG_SONATA, + { SCENE_MITURIN, SCENE_MITURIN_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, + SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_MASK_ZORA, + { SCENE_SEA, SCENE_SEA_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, + SCENE_LAST_BS } }, + { RI_SONG_NOVA, + { SCENE_SEA, SCENE_SEA_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, + SCENE_LAST_BS } }, + { RI_MASK_GORON, + { SCENE_HAKUGIN, SCENE_HAKUGIN_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, + SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_PROGRESSIVE_LULLABY, + { SCENE_HAKUGIN, SCENE_HAKUGIN_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, + SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_REMAINS_ODOLWA, + { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_REMAINS_GOHT, + { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_REMAINS_GYORG, + { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_REMAINS_TWINMOLD, + { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } }, + { RI_SONG_OATH, + { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } }, + }; + + uint64_t tick = GetUnixTimestamp(); + + SaveContext copiedSaveContext; + memcpy(&copiedSaveContext, &gSaveContext, sizeof(SaveContext)); + + std::set regionsInLogic = { RR_MAX }; + std::map checksInLogic; + std::set>*> eventsInLogic; + + RandoCheckId checkWithJunk = RC_UNKNOWN; + std::set nonJunkItemsThatWeHaveTried; + std::vector checksWithJunk; + std::vector checksWithJunkWeights; + int weight = 1; + + // Initial shuffle + if (itemPool.size() > 1) { + for (size_t i = 0; i < itemPool.size(); i++) { + size_t j = Ship_Random(0, itemPool.size() - 1); + std::swap(itemPool[i], itemPool[j]); + } + } + + auto handleError = [&](std::string message) { + SPDLOG_ERROR("Items/Checks: {}/{}", itemPool.size(), checkPool.size()); + + for (auto& randoCheckId : checkPool) { + SPDLOG_ERROR("Check still in pool: {}", Rando::StaticData::Checks[randoCheckId].name); + } + for (RandoItemId randoItemId : itemPool) { + SPDLOG_ERROR("Item still in pool: {}", Rando::StaticData::Items[randoItemId].spoilerName); + } + + memcpy(&gSaveContext, &copiedSaveContext, sizeof(SaveContext)); + throw std::runtime_error(message); + }; + + // Helper: pick valid item respecting the scene blacklist + RandoItemId currentRandoItemId = RI_UNKNOWN; + auto pickValidItemForScene = [&](std::vector& pool, SceneId scene) -> RandoItemId { + for (auto it = pool.rbegin(); it != pool.rend(); ++it) { + RandoItemId candidate = *it; + currentRandoItemId = candidate; + + auto blIt = itemToSceneBlacklist.find(candidate); + if (blIt != itemToSceneBlacklist.end()) { + const auto& blacklistScenes = blIt->second; + + if (std::find(blacklistScenes.begin(), blacklistScenes.end(), scene) != blacklistScenes.end()) { + // Item forbidden for this scene, skip it + continue; + } + } + + // Valid — remove and return + RandoItemId chosen = candidate; + pool.erase(std::next(it).base()); + return chosen; + } + + handleError("No valid item found for check due to blacklist."); + return RI_NONE; // unreachable + }; + + while (true) { + if (GetUnixTimestamp() - tick > 10000) { + handleError("Logic Generation Timeout"); + } + + bool regionsInLogicChanged = false; + bool eventsInLogicChanged = false; + bool checksInLogicChanged = false; + + // Discover reachable regions + auto prevRegionsInLogicSize = regionsInLogic.size(); + for (RandoRegionId regionId : regionsInLogic) { + FindReachableRegions(regionId, regionsInLogic); + } + if (regionsInLogic.size() != prevRegionsInLogicSize) { + regionsInLogicChanged = true; + } + + for (RandoRegionId regionId : regionsInLogic) { + auto& randoRegion = Regions[regionId]; + + for (auto& randoEvent : randoRegion.events) { + if (!eventsInLogic.contains(&randoEvent) && randoEvent.second()) { + RANDO_EVENTS[randoEvent.first]++; + eventsInLogic.insert(&randoEvent); + eventsInLogicChanged = true; + } + } + + for (auto& [randoCheckId, checkLogic] : randoRegion.checks) { + if (checksInLogic.find(randoCheckId) == checksInLogic.end() && checkLogic.first()) { + + auto it = std::find(checkPool.begin(), checkPool.end(), randoCheckId); + bool isShuffled = it != checkPool.end(); + checksInLogic.insert({ randoCheckId, isShuffled }); + + if (isShuffled) { + checkPool.erase(it); + } + + RandoItemId randoItemId; + + // Determine scene for blacklist check + SceneId checkSceneId = Rando::StaticData::Checks[randoCheckId].sceneId; + + if (RANDO_SAVE_CHECKS[randoCheckId].skipped) { + uint32_t index = 0; + for (auto& item : itemPool) { + if (Rando::StaticData::Items[item].randoItemType == RITYPE_JUNK) { + randoItemId = item; + itemPool.erase(itemPool.begin() + index); + break; + } + index++; + } + } else if (isShuffled) { + // NEW: pick item that is NOT blacklisted for this check's scene + randoItemId = pickValidItemForScene(itemPool, checkSceneId); + + if (Rando::StaticData::Items[randoItemId].randoItemType == RITYPE_JUNK || + Rando::StaticData::Items[randoItemId].randoItemType == RITYPE_HEALTH) { + + checksWithJunk.push_back(randoCheckId); + checksWithJunkWeights.push_back(weight); + } + + SPDLOG_TRACE("Check: {}:{}", Rando::StaticData::Checks[randoCheckId].name, + Rando::StaticData::Items[randoItemId].spoilerName); + } else { + randoItemId = Rando::StaticData::Checks[randoCheckId].randoItemId; + } + + RANDO_SAVE_CHECKS[randoCheckId].randoItemId = randoItemId; + RANDO_SAVE_CHECKS[randoCheckId].shuffled = isShuffled; + GiveItem(ConvertItem(randoItemId)); + checksInLogicChanged = true; + } + } + } + + if (itemPool.empty()) { + break; + } + + // Junk replacement logic — unchanged + if (!regionsInLogicChanged && !checksInLogicChanged && !eventsInLogicChanged) { + + if (checkWithJunk == RC_UNKNOWN) { + if (checksWithJunk.empty()) { + handleError("No checks with junk, not sure what to do"); + } + + if (checksWithJunk.size() == 1) { + checkWithJunk = checksWithJunk[0]; + } else { + std::vector cumulativeWeights(checksWithJunkWeights.size()); + std::partial_sum(checksWithJunkWeights.begin(), checksWithJunkWeights.end(), + cumulativeWeights.begin()); + double random = Ship_Random(0, cumulativeWeights.back()); + auto it = std::lower_bound(cumulativeWeights.begin(), cumulativeWeights.end(), random); + size_t index = std::distance(cumulativeWeights.begin(), it); + + checkWithJunk = checksWithJunk[index]; + + checksWithJunk.erase(checksWithJunk.begin() + index); + checksWithJunkWeights.erase(checksWithJunkWeights.begin() + index); + } + } + + std::vector> nonJunkItemsThatWeHaveNotTried; + bool anyNonJunkItemsLeft = false; + + for (size_t i = 0; i < itemPool.size(); i++) { + if (Rando::StaticData::Items[itemPool[i]].randoItemType != RITYPE_JUNK && + Rando::StaticData::Items[itemPool[i]].randoItemType != RITYPE_HEALTH) { + + anyNonJunkItemsLeft = true; + + if (nonJunkItemsThatWeHaveTried.find(itemPool[i]) == nonJunkItemsThatWeHaveTried.end()) { + nonJunkItemsThatWeHaveNotTried.push_back({ itemPool[i], i }); + } + } + } + + if (!anyNonJunkItemsLeft) { + handleError("No non-junk items left"); + } + + if (nonJunkItemsThatWeHaveNotTried.empty()) { + SPDLOG_TRACE("Already tried all non-junk items, leaving the last non-junk item in place: {}: {}", + Rando::StaticData::Checks[checkWithJunk].name, + Rando::StaticData::Items[RANDO_SAVE_CHECKS[checkWithJunk].randoItemId].spoilerName); + checkWithJunk = RC_UNKNOWN; + nonJunkItemsThatWeHaveTried.clear(); + continue; + } + + RandoItemId oldRandoItemId = RANDO_SAVE_CHECKS[checkWithJunk].randoItemId; + auto& [newRandoItemId, indexInPool] = nonJunkItemsThatWeHaveNotTried[0]; + + RANDO_SAVE_CHECKS[checkWithJunk].randoItemId = newRandoItemId; + + RemoveItem(oldRandoItemId); + GiveItem(ConvertItem(newRandoItemId)); + + itemPool.erase(itemPool.begin() + indexInPool); + itemPool.push_back(oldRandoItemId); + + nonJunkItemsThatWeHaveTried.insert(newRandoItemId); + SPDLOG_TRACE("Attempting to replaced junk item: {}:{}", Rando::StaticData::Checks[checkWithJunk].name, + Rando::StaticData::Items[newRandoItemId].spoilerName); + } else { + weight++; + if (checkWithJunk != RC_UNKNOWN) { + SPDLOG_TRACE("Successfully Replaced junk item with: {}:{}", + Rando::StaticData::Checks[checkWithJunk].name, + Rando::StaticData::Items[RANDO_SAVE_CHECKS[checkWithJunk].randoItemId].spoilerName); + } + checkWithJunk = RC_UNKNOWN; + nonJunkItemsThatWeHaveTried.clear(); + + if (itemPool.size() > 1) { + for (size_t i = 0; i < itemPool.size(); i++) { + size_t j = Ship_Random(0, itemPool.size() - 1); + std::swap(itemPool[i], itemPool[j]); + } + } + } + } + + for (auto& [randoCheckId, isShuffled] : checksInLogic) { + copiedSaveContext.save.shipSaveInfo.rando.randoSaveChecks[randoCheckId].randoItemId = + RANDO_SAVE_CHECKS[randoCheckId].randoItemId; + copiedSaveContext.save.shipSaveInfo.rando.randoSaveChecks[randoCheckId].shuffled = isShuffled; + } + + memcpy(&gSaveContext, &copiedSaveContext, sizeof(SaveContext)); + + SPDLOG_INFO("Successfully placed all items with Glitchless logic"); +} + +} // namespace Logic + +} // namespace Rando diff --git a/mm/2s2h/Rando/Logic/Logic.h b/mm/2s2h/Rando/Logic/Logic.h index 9f4d4b4974..371255453f 100644 --- a/mm/2s2h/Rando/Logic/Logic.h +++ b/mm/2s2h/Rando/Logic/Logic.h @@ -19,6 +19,7 @@ namespace Logic { void FindReachableRegions(RandoRegionId currentRegion, std::set& reachableRegions); RandoRegionId GetRegionIdFromEntrance(s32 entrance); +void ApplyDeckscrubberLogicToSaveContext(std::vector& checkPool, std::vector& itemPool); void ApplyGlitchlessLogicToSaveContext(std::vector& checkPool, std::vector& itemPool); void ApplyNearlyNoLogicToSaveContext(std::vector& checkPool, std::vector& itemPool); void ApplyNoLogicToSaveContext(std::vector& checkPool, std::vector& itemPool); diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index 5fd41654a8..e5da0ffdd1 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -17,6 +17,7 @@ std::unordered_map logicOptions = { { RO_LOGIC_NO_LOGIC, "No Logic" }, { RO_LOGIC_NEARLY_NO_LOGIC, "Nearly No Logic" }, { RO_LOGIC_VANILLA, "Vanilla" }, + { RO_LOGIC_DECKSCRUBBER, "Deckscrubber" }, }; std::unordered_map accessDungeonOptions = { diff --git a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp index ee22858a87..8fade0219a 100644 --- a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp +++ b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp @@ -267,6 +267,18 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { } } + // Link's Pocket for Deckscrubber Logic + if (RANDO_SAVE_OPTIONS[RO_LOGIC] == RO_LOGIC_DECKSCRUBBER) { + RandoItemId remainsRoll = (RandoItemId)((uint16_t)RI_REMAINS_GOHT + (rand() % 4)); + for (int i = 0; i < 2; i++) { + auto it = std::find(itemPool.begin(), itemPool.end(), remainsRoll); + if (it != itemPool.end()) { + itemPool.erase(it); + } + } + startingItems.push_back(remainsRoll); + } + // Shuffle Triforce Pieces into the Pool int piecesShuffled = 0; if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRIFORCE_PIECES] == RO_GENERIC_YES) { @@ -442,6 +454,8 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { Rando::Logic::ApplyNearlyNoLogicToSaveContext(checkPool, itemPool); } else if (RANDO_SAVE_OPTIONS[RO_LOGIC] == RO_LOGIC_GLITCHLESS) { Rando::Logic::ApplyGlitchlessLogicToSaveContext(checkPool, itemPool); + } else if (RANDO_SAVE_OPTIONS[RO_LOGIC] == RO_LOGIC_DECKSCRUBBER) { + Rando::Logic::ApplyDeckscrubberLogicToSaveContext(checkPool, itemPool); } else { throw std::runtime_error("Logic option not implemented: " + std::to_string(RANDO_SAVE_OPTIONS[RO_LOGIC])); diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index e24650122b..0741db42b4 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -2842,6 +2842,7 @@ typedef enum { RO_LOGIC_NO_LOGIC, RO_LOGIC_NEARLY_NO_LOGIC, RO_LOGIC_VANILLA, + RO_LOGIC_DECKSCRUBBER, } RandoOptionLogic; typedef enum { From a698b6cda6a2b9e1c91f79ea8db39d5f6776a2e6 Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 15 Dec 2025 16:04:55 -0500 Subject: [PATCH 06/15] clang --- mm/2s2h/PresetManager/PresetDescriptions.h | 5 +++-- mm/2s2h/Rando/Menu.cpp | 6 ++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mm/2s2h/PresetManager/PresetDescriptions.h b/mm/2s2h/PresetManager/PresetDescriptions.h index de3baabc6a..9a594190bf 100644 --- a/mm/2s2h/PresetManager/PresetDescriptions.h +++ b/mm/2s2h/PresetManager/PresetDescriptions.h @@ -17,8 +17,9 @@ std::vector deckScrubberShuffles = { }; std::vector deckScrubberStarting = { - "Bunny Hood", "Full Wallets", "Hero's Shield", "Maps and Compasses", "Kokiri Sword", "Random Boss Remain", - "Ocarina of Time", "", "Song of Time", + "Bunny Hood", "Full Wallets", "Hero's Shield", "Maps and Compasses", + "Kokiri Sword", "Random Boss Remain", "Ocarina of Time", "", + "Song of Time", }; std::vector> deckScrubberHints = { diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index e5da0ffdd1..e91a0d42f9 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -13,10 +13,8 @@ extern "C" { // TODO: This block should come from elsewhere, tied to data in Rando::StaticData::Options std::unordered_map logicOptions = { - { RO_LOGIC_GLITCHLESS, "Glitchless" }, - { RO_LOGIC_NO_LOGIC, "No Logic" }, - { RO_LOGIC_NEARLY_NO_LOGIC, "Nearly No Logic" }, - { RO_LOGIC_VANILLA, "Vanilla" }, + { RO_LOGIC_GLITCHLESS, "Glitchless" }, { RO_LOGIC_NO_LOGIC, "No Logic" }, + { RO_LOGIC_NEARLY_NO_LOGIC, "Nearly No Logic" }, { RO_LOGIC_VANILLA, "Vanilla" }, { RO_LOGIC_DECKSCRUBBER, "Deckscrubber" }, }; From 9f7870cfd0d6f010f2ec99802528099b7fa6f7d0 Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 15 Dec 2025 19:41:43 -0500 Subject: [PATCH 07/15] Dual Hints for masks. --- mm/2s2h/Rando/ActorBehavior/EnTalk.cpp | 23 ++++++++++++++--------- mm/2s2h/Rando/Rando.cpp | 10 ++++++++++ mm/2s2h/Rando/Rando.h | 1 + 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp index 3caa66c293..caf8b23ee2 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp @@ -24,9 +24,8 @@ void ApplyTransformationHints(u16* textId, bool* loadFromMessageTable) { "The souls of the departed lay restless, " "find and heal them!"; } else { - msg = " %g{{mask}}%w:\n" - "Last seen in\n" - "%y{{location}}%w"; + msg = "%g{{mask}}%w:\n" + "%y{{locations}}%w"; switch (transformHintIndex) { case 1: @@ -46,12 +45,18 @@ void ApplyTransformationHints(u16* textId, bool* loadFromMessageTable) { } icon = Rando::StaticData::GetIconForZMessage(randoItemId); - RandoCheckId randoCheckId = Rando::FindItemPlacement(randoItemId); - if (RANDO_SAVE_CHECKS[randoCheckId].obtained == true) { - CustomMessage::Replace(&msg, "{{location}}", "your %gpocket%w!"); - } else { - CustomMessage::Replace(&msg, "{{location}}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + std::vector itemPlacements = Rando::FindMultiItemPlacement(randoItemId); + std::string locationStr = ""; + if (!itemPlacements.empty()) { + locationStr = RANDO_SAVE_CHECKS[itemPlacements[0]].obtained + ? "your %gpocket%w" : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[0]].sceneId); + if (itemPlacements.size() > 1) { + locationStr += " %w&%y\n"; + locationStr += RANDO_SAVE_CHECKS[itemPlacements[1]].obtained + ? "your %gpocket%w" + : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[1]].sceneId); + CustomMessage::Replace(&msg, "{{locations}}", locationStr); + } msg += "."; } } diff --git a/mm/2s2h/Rando/Rando.cpp b/mm/2s2h/Rando/Rando.cpp index 4140756e49..f4471e00c1 100644 --- a/mm/2s2h/Rando/Rando.cpp +++ b/mm/2s2h/Rando/Rando.cpp @@ -38,3 +38,13 @@ RandoCheckId Rando::FindItemPlacement(RandoItemId randoItemId) { return RC_UNKNOWN; } + +std::vector Rando::FindMultiItemPlacement(RandoItemId randoItemId) { + std::vector itemPlacements; + for (auto& [randocheckId, check] : Rando::StaticData::Checks) { + if (RANDO_SAVE_CHECKS[randocheckId].randoItemId == randoItemId) { + itemPlacements.push_back(randocheckId); + } + } + return itemPlacements; +} diff --git a/mm/2s2h/Rando/Rando.h b/mm/2s2h/Rando/Rando.h index 4f592b0c5f..7f82785663 100644 --- a/mm/2s2h/Rando/Rando.h +++ b/mm/2s2h/Rando/Rando.h @@ -24,6 +24,7 @@ RandoItemId CurrentJunkItem(); bool IsItemObtainable(RandoItemId randoItemId, RandoCheckId randoCheckId = RC_UNKNOWN); RandoItemId ConvertItem(RandoItemId randoItemId, RandoCheckId randoCheckId = RC_UNKNOWN); RandoCheckId FindItemPlacement(RandoItemId randoItemId); +std::vector FindMultiItemPlacement(RandoItemId randoItemId); void RegisterMenu(); } // namespace Rando From 7e31546243bffa8e31373560b3e69a719c58af6a Mon Sep 17 00:00:00 2001 From: Caladius Date: Tue, 16 Dec 2025 09:04:31 -0500 Subject: [PATCH 08/15] adds second hint to boss remains --- mm/2s2h/Rando/ActorBehavior/EnTalk.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp index caf8b23ee2..1d2db07c6b 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp @@ -89,8 +89,8 @@ void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { "local townfolk, will pay good money " "for their remains."; } else { - msg = " %g{{boss}}%w:\n" - "Last seen in near %y{{location}}%w."; + msg = "%g{{boss}}%w was last seen in:\n" + "%y{{locations}}%w."; switch (remainsHintIndex) { case 1: @@ -112,9 +112,22 @@ void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { } icon = Rando::StaticData::GetIconForZMessage(randoItemId); - RandoCheckId randoCheckId = Rando::FindItemPlacement(randoItemId); - CustomMessage::Replace(&msg, "{{location}}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + std::vector itemPlacements = Rando::FindMultiItemPlacement(randoItemId); + std::string locationStr = ""; + if (!itemPlacements.empty()) { + locationStr = RANDO_SAVE_CHECKS[itemPlacements[0]].obtained + ? "your %gpocket%w" + : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[0]].sceneId); + if (itemPlacements.size() > 1) { + locationStr += " %w&%y\n"; + locationStr += RANDO_SAVE_CHECKS[itemPlacements[1]].obtained + ? "your %gpocket%w" + : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[1]].sceneId); + CustomMessage::Replace(&msg, "{{locations}}", locationStr); + } + } else { + CustomMessage::Replace(&msg, "{{locations}}", "%gLinks pocket%w"); + } } CustomMessage::Entry entry = { From 730fc7efdc728dcba80299a505837d01a1dc0996 Mon Sep 17 00:00:00 2001 From: Caladius Date: Tue, 16 Dec 2025 09:33:06 -0500 Subject: [PATCH 09/15] Update hints to check if they are obtained already. --- mm/2s2h/Rando/ActorBehavior/EnTalk.cpp | 56 +++++++++++++++++--------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp index 1d2db07c6b..6b8807d577 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp @@ -25,7 +25,7 @@ void ApplyTransformationHints(u16* textId, bool* loadFromMessageTable) { "find and heal them!"; } else { msg = "%g{{mask}}%w:\n" - "%y{{locations}}%w"; + "%y{{locations}}%w."; switch (transformHintIndex) { case 1: @@ -48,16 +48,26 @@ void ApplyTransformationHints(u16* textId, bool* loadFromMessageTable) { std::vector itemPlacements = Rando::FindMultiItemPlacement(randoItemId); std::string locationStr = ""; if (!itemPlacements.empty()) { - locationStr = RANDO_SAVE_CHECKS[itemPlacements[0]].obtained - ? "your %gpocket%w" : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[0]].sceneId); - if (itemPlacements.size() > 1) { - locationStr += " %w&%y\n"; - locationStr += RANDO_SAVE_CHECKS[itemPlacements[1]].obtained - ? "your %gpocket%w" - : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[1]].sceneId); - CustomMessage::Replace(&msg, "{{locations}}", locationStr); + for (int i = 0; i < itemPlacements.size(); i++) { + if (RANDO_SAVE_CHECKS[itemPlacements[i]].obtained) { + itemPlacements[i] = RC_UNKNOWN; + } } - msg += "."; + for (auto& location : itemPlacements) { + if (location == RC_UNKNOWN) { + locationStr = "%gLinks pocket%w"; + break; + } + + if (locationStr != "") { + locationStr += " %w&%y\n"; + } + + locationStr += Ship_GetSceneName(Rando::StaticData::Checks[location].sceneId); + } + CustomMessage::Replace(&msg, "{{locations}}", locationStr); + } else { + CustomMessage::Replace(&msg, "{{locations}}", "%gLinks pocket%w"); } } @@ -115,16 +125,24 @@ void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { std::vector itemPlacements = Rando::FindMultiItemPlacement(randoItemId); std::string locationStr = ""; if (!itemPlacements.empty()) { - locationStr = RANDO_SAVE_CHECKS[itemPlacements[0]].obtained - ? "your %gpocket%w" - : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[0]].sceneId); - if (itemPlacements.size() > 1) { - locationStr += " %w&%y\n"; - locationStr += RANDO_SAVE_CHECKS[itemPlacements[1]].obtained - ? "your %gpocket%w" - : Ship_GetSceneName(Rando::StaticData::Checks[itemPlacements[1]].sceneId); - CustomMessage::Replace(&msg, "{{locations}}", locationStr); + for (int i = 0; i < itemPlacements.size(); i++) { + if (RANDO_SAVE_CHECKS[itemPlacements[i]].obtained) { + itemPlacements[i] = RC_UNKNOWN; + } + } + for (auto& location : itemPlacements) { + if (location == RC_UNKNOWN) { + locationStr = "%gLinks pocket%w"; + break; + } + + if (locationStr != "") { + locationStr += " %w&%y\n"; + } + + locationStr += Ship_GetSceneName(Rando::StaticData::Checks[location].sceneId); } + CustomMessage::Replace(&msg, "{{locations}}", locationStr); } else { CustomMessage::Replace(&msg, "{{locations}}", "%gLinks pocket%w"); } From 5fb12c2666033172f3f471e285cd9f5e28779f0f Mon Sep 17 00:00:00 2001 From: Caladius Date: Tue, 16 Dec 2025 15:28:16 -0500 Subject: [PATCH 10/15] Implement Settings Reload on File Load --- mm/2s2h/Rando/MiscBehavior/MiscBehavior.cpp | 1 + mm/2s2h/Rando/MiscBehavior/MiscBehavior.h | 1 + mm/2s2h/Rando/MiscBehavior/OnRaceInit.cpp | 22 +++++++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 mm/2s2h/Rando/MiscBehavior/OnRaceInit.cpp diff --git a/mm/2s2h/Rando/MiscBehavior/MiscBehavior.cpp b/mm/2s2h/Rando/MiscBehavior/MiscBehavior.cpp index abef387215..3a5c47e478 100644 --- a/mm/2s2h/Rando/MiscBehavior/MiscBehavior.cpp +++ b/mm/2s2h/Rando/MiscBehavior/MiscBehavior.cpp @@ -16,6 +16,7 @@ void Rando::MiscBehavior::OnFileLoad() { Rando::MiscBehavior::CheckQueueReset(); Rando::MiscBehavior::InitKaleidoItemPage(); Rando::MiscBehavior::InitOfferGetItemBehavior(); + Rando::MiscBehavior::OnRaceFileInit(); COND_HOOK(OnFlagSet, IS_RANDO, Rando::MiscBehavior::OnFlagSet); COND_HOOK(OnSceneFlagSet, IS_RANDO, Rando::MiscBehavior::OnSceneFlagSet); diff --git a/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h b/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h index d0f93f4360..9261396012 100644 --- a/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h +++ b/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h @@ -22,6 +22,7 @@ void OnFlagSet(FlagType flagType, u32 flag); void OnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag); void OnSceneInit(s16 sceneId, s8 spawnNum); void OfferTrapItem(); +void OnRaceFileInit(); } // namespace MiscBehavior diff --git a/mm/2s2h/Rando/MiscBehavior/OnRaceInit.cpp b/mm/2s2h/Rando/MiscBehavior/OnRaceInit.cpp new file mode 100644 index 0000000000..c233f03ec3 --- /dev/null +++ b/mm/2s2h/Rando/MiscBehavior/OnRaceInit.cpp @@ -0,0 +1,22 @@ +#include "MiscBehavior.h" +#include "PresetManager/PresetManager.h" + +extern "C" { +#include "variables.h" +} + +static bool isRaceInitialized = false; + +void Rando::MiscBehavior::OnRaceFileInit() { + COND_HOOK(OnSaveLoad, RANDO_SAVE_OPTIONS[RO_LOGIC] == RO_LOGIC_DECKSCRUBBER, + [](s16 fileNum) { isRaceInitialized = false; }); + + COND_HOOK(OnSceneInit, IS_RANDO, [](s8 sceneId, s8 spawnNum) { + if (!isRaceInitialized) { + if (RANDO_SAVE_OPTIONS[RO_LOGIC] == RO_LOGIC_DECKSCRUBBER) { + PresetManager_ApplyPreset(deckScrubberJ); + } + isRaceInitialized = true; + } + }); +} \ No newline at end of file From 7054c8121cbfd1e10c2e22c02804d3a6383e43da Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 22 Dec 2025 13:42:48 -0500 Subject: [PATCH 11/15] Adds Skipping the Little Beaver Brother races. --- mm/2s2h/BenGui/BenMenu.cpp | 3 ++ mm/2s2h/Enhancements/Minigames/BeaverRace.cpp | 46 ++++++++++++++++--- mm/2s2h/PresetManager/PresetManager.cpp | 2 +- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/mm/2s2h/BenGui/BenMenu.cpp b/mm/2s2h/BenGui/BenMenu.cpp index 610372d9dd..16938d5dfd 100644 --- a/mm/2s2h/BenGui/BenMenu.cpp +++ b/mm/2s2h/BenGui/BenMenu.cpp @@ -1575,6 +1575,9 @@ void BenMenu::AddEnhancements() { .Min(1) .Max(20) .DefaultValue(20)); + AddWidget(path, "Skip Little Beaver Brother Races", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Minigames.SkipLittleBeaver") + .Options(CheckboxOptions().Tooltip("Only Race the Older Beaver.")); AddWidget(path, "Mark Shooting Gallery Octoroks", WIDGET_CVAR_CHECKBOX) .CVar("gEnhancements.Minigames.MarkShootingGalleryOctoroks") .Options(CheckboxOptions().Tooltip("Places markers on the Town Shooting Gallery Octoroks, indicating whether " diff --git a/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp b/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp index 24c03b85f2..4cac7d807d 100644 --- a/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp +++ b/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp @@ -1,6 +1,7 @@ #include #include "2s2h/GameInteractor/GameInteractor.h" #include "2s2h/ShipInit.hpp" +#include "CustomMessage/CustomMessage.h" extern "C" { #include "overlays/actors/ovl_En_Az/z_en_az.h" @@ -9,18 +10,21 @@ void func_80A979DC(EnAz* thisx, PlayState* play); void func_80A97F9C(EnAz* thisx, PlayState* play); } -#define CVAR_NAME "gEnhancements.Minigames.BeaverRaceRingsCollected" -#define CVAR CVarGetInteger(CVAR_NAME, 20) +#define CVAR_RINGS_NAME "gEnhancements.Minigames.BeaverRaceRingsCollected" +#define CVAR_RINGS CVarGetInteger(CVAR_RINGS_NAME, 20) + +#define CVAR_SPEEDUP_NAME "gEnhancements.Minigames.SkipLittleBeaver" +#define CVAR_SPEEDUP CVarGetInteger(CVAR_SPEEDUP_NAME, 0) static bool minigameScoreSet = false; // Flag to track if the score has been set -void RegisterBeaverRace() { - COND_ID_HOOK(ShouldActorUpdate, ACTOR_EN_AZ, CVAR < 20, [](Actor* actor, bool* should) { +void RegisterBeaverRaceRings() { + COND_ID_HOOK(ShouldActorUpdate, ACTOR_EN_AZ, CVAR_RINGS < 20, [](Actor* actor, bool* should) { EnAz* enAz = (EnAz*)actor; Player* player = GET_PLAYER(gPlayState); if (!minigameScoreSet) { - gSaveContext.minigameScore = CVAR; + gSaveContext.minigameScore = CVAR_RINGS; minigameScoreSet = true; // Set the flag after assignment } @@ -60,4 +64,34 @@ void RegisterBeaverRace() { }); } -static RegisterShipInitFunc initFunc(RegisterBeaverRace, { CVAR_NAME }); +void RegisterBeaverRaceSpeedup() { + COND_ID_HOOK(ShouldActorUpdate, ACTOR_EN_AZ, CVAR_SPEEDUP, [](Actor* actor, bool* should) { + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_24_04)) { + SET_WEEKEVENTREG(WEEKEVENTREG_24_04); + } + }); + + COND_ID_HOOK(OnOpenText, 0x10D6, CVAR_SPEEDUP, [](u16* textId, bool* loadFromMessageTable) { + std::string replaceMsg = "My older brother will show you\x11the way, so follow him and\x11" + "don't get separated!"; + auto entry = CustomMessage::LoadVanillaMessageTableEntry(*textId); + entry.msg.replace(entry.msg.begin(), entry.msg.end(), replaceMsg); + + CustomMessage::LoadCustomMessageIntoFont(entry); + *loadFromMessageTable = false; + SPDLOG_INFO("Entry: {}", entry.msg); + }); + + COND_ID_HOOK(OnOpenText, 0x10FA, CVAR_SPEEDUP, [](u16* textId, bool* loadFromMessageTable) { + std::string replaceMsg = "The time limit is %r1:50%w, let's race."; + auto entry = CustomMessage::LoadVanillaMessageTableEntry(*textId); + entry.msg.replace(entry.msg.begin(), entry.msg.end(), replaceMsg); + + CustomMessage::LoadCustomMessageIntoFont(entry); + *loadFromMessageTable = false; + SPDLOG_INFO("Entry: {}", entry.msg); + }); +} + +static RegisterShipInitFunc initRingsFunc(RegisterBeaverRaceRings, { CVAR_RINGS_NAME }); +static RegisterShipInitFunc initSpeedupFunc(RegisterBeaverRaceSpeedup, { CVAR_SPEEDUP_NAME }); diff --git a/mm/2s2h/PresetManager/PresetManager.cpp b/mm/2s2h/PresetManager/PresetManager.cpp index bd441fd619..542909f8e2 100644 --- a/mm/2s2h/PresetManager/PresetManager.cpp +++ b/mm/2s2h/PresetManager/PresetManager.cpp @@ -321,7 +321,7 @@ nlohmann::json deckScrubberJ = R"( "AlwaysWinDoggyRace": 1, "CuccoShackCuccoCount": 1, "SwampArcheryScore": 2179, - "TownArcheryScore": 50 + "SkipLittleBeaver": 1 }, "Playback": { "DpadOcarina": 1, From 695d7c66ebe38f42bbb679df5a134f7d2b44e35f Mon Sep 17 00:00:00 2001 From: Caladius Date: Mon, 22 Dec 2025 15:44:03 -0500 Subject: [PATCH 12/15] Remove SPDLOG --- mm/2s2h/Enhancements/Minigames/BeaverRace.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp b/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp index 4cac7d807d..f2c74b2182 100644 --- a/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp +++ b/mm/2s2h/Enhancements/Minigames/BeaverRace.cpp @@ -79,7 +79,6 @@ void RegisterBeaverRaceSpeedup() { CustomMessage::LoadCustomMessageIntoFont(entry); *loadFromMessageTable = false; - SPDLOG_INFO("Entry: {}", entry.msg); }); COND_ID_HOOK(OnOpenText, 0x10FA, CVAR_SPEEDUP, [](u16* textId, bool* loadFromMessageTable) { @@ -89,7 +88,6 @@ void RegisterBeaverRaceSpeedup() { CustomMessage::LoadCustomMessageIntoFont(entry); *loadFromMessageTable = false; - SPDLOG_INFO("Entry: {}", entry.msg); }); } From 596e5db0ea53c8ef5407d14ff588487170b04657 Mon Sep 17 00:00:00 2001 From: Caladius Date: Tue, 23 Dec 2025 09:07:21 -0500 Subject: [PATCH 13/15] Moves Check Tracker settings to its own sub section. --- mm/2s2h/Rando/CheckTracker/CheckTracker.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp index 48dfddb947..15eb7eff18 100644 --- a/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp +++ b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp @@ -32,14 +32,14 @@ static std::unordered_map betterSceneIndex = { #undef DEFINE_SCENE #undef DEFINE_SCENE_UNSET -#define CVAR_NAME_SHOW_CHECK_TRACKER "gWindows.CheckTracker" -#define CVAR_NAME_SHOW_LOGIC "gRando.CheckTracker.OnlyShowChecksInLogic" -#define CVAR_NAME_HIDE_COLLECTED "gRando.CheckTracker.HideCollectedChecks" -#define CVAR_NAME_HIDE_SKIPPED "gRando.CheckTracker.HideSkippedChecks" -#define CVAR_NAME_SCROLL_TO_SCENE "gRando.CheckTracker.ScrollToCurrentScene" -#define CVAR_NAME_TRACKER_OPACITY "gRando.CheckTracker.Opacity" -#define CVAR_NAME_TRACKER_SCALE "gRando.CheckTracker.Scale" -#define CVAR_NAME_SHOW_CURRENT_SCENE "gRando.CheckTracker.ShowCurrentScene" +#define CVAR_NAME_SHOW_CHECK_TRACKER "gCheckTracker.Enable" +#define CVAR_NAME_SHOW_LOGIC "gCheckTracker.OnlyShowChecksInLogic" +#define CVAR_NAME_HIDE_COLLECTED "gCheckTracker.HideCollectedChecks" +#define CVAR_NAME_HIDE_SKIPPED "gCheckTracker.HideSkippedChecks" +#define CVAR_NAME_SCROLL_TO_SCENE "gCheckTracker.ScrollToCurrentScene" +#define CVAR_NAME_TRACKER_OPACITY "gCheckTracker.Opacity" +#define CVAR_NAME_TRACKER_SCALE "gCheckTracker.Scale" +#define CVAR_NAME_SHOW_CURRENT_SCENE "gCheckTracker.ShowCurrentScene" #define CVAR_SHOW_CHECK_TRACKER CVarGetInteger(CVAR_NAME_SHOW_CHECK_TRACKER, 0) #define CVAR_SHOW_LOGIC CVarGetInteger(CVAR_NAME_SHOW_LOGIC, 0) #define CVAR_HIDE_COLLECTED CVarGetInteger(CVAR_NAME_HIDE_COLLECTED, 0) From f4e631f8bab74ebd95536b5ffef8015d3d8ce88e Mon Sep 17 00:00:00 2001 From: Caladius Date: Tue, 23 Dec 2025 09:28:27 -0500 Subject: [PATCH 14/15] Update Check Tracker buttons for color theme --- mm/2s2h/Rando/Menu.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index e91a0d42f9..8095dbac43 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -155,10 +155,12 @@ static void DrawGeneralTab() { UIWidgets::CVarCheckbox("Container Style Matches Contents", "gRando.CSMC"); UIWidgets::Tooltip("This will make the contents of a container match the container itself. This currently only " "applies to chests and pots."); - UIWidgets::WindowButton("Check Tracker", "gWindows.CheckTracker", BenGui::mRandoCheckTrackerWindow, - { .size = ImVec2((ImGui::GetContentRegionAvail().x - 48.0f), 40.0f) }); + UIWidgets::WindowButton("Check Tracker", "gCheckTracker.Enable", BenGui::mRandoCheckTrackerWindow, + { .size = ImVec2((ImGui::GetContentRegionAvail().x - 48.0f), 40.0f), + .color = BenGui::mBenMenu->GetMenuThemeColor() }); ImGui::SameLine(); - if (UIWidgets::Button(ICON_FA_COG, { .size = ImVec2(40.0f, 40.0f) })) { + if (UIWidgets::Button(ICON_FA_COG, + { .size = ImVec2(40.0f, 40.0f), .color = BenGui::mBenMenu->GetMenuThemeColor() })) { BenGui::mRandoCheckTrackerSettingsWindow->ToggleVisibility(); } ImGui::EndChild(); From 78feeef55f7acbd8ca1d2795ad8f2e414c31660c Mon Sep 17 00:00:00 2001 From: Caladius Date: Tue, 23 Dec 2025 14:47:31 -0500 Subject: [PATCH 15/15] Fix missing Check Tracker change --- mm/2s2h/BenGui/BenGui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/2s2h/BenGui/BenGui.cpp b/mm/2s2h/BenGui/BenGui.cpp index f7c9d8d077..7d27cde49a 100644 --- a/mm/2s2h/BenGui/BenGui.cpp +++ b/mm/2s2h/BenGui/BenGui.cpp @@ -169,7 +169,7 @@ void SetupGuiElements() { mNotificationWindow->Show(); mRandoCheckTrackerWindow = std::make_shared( - "gWindows.CheckTracker", "Check Tracker", ImVec2(375, 460)); + "gCheckTracker.Enable", "Check Tracker", ImVec2(375, 460)); gui->AddGuiWindow(mRandoCheckTrackerWindow); mRandoCheckTrackerSettingsWindow = std::make_shared(