diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 7b514a16c..f094707dd 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -297,7 +297,8 @@ void DrawInfoTab() { Combobox("Z Target Mode", &gSaveContext.zTargetSetting, zTargetMap, comboboxOptionsBase.Tooltip("Z-Targeting behavior")); - if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT)) { + if (IS_RANDO && + (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) != RO_TRIFORCE_HUNT_OFF)) { PushStyleInput(THEME_COLOR); ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U8, &gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); diff --git a/soh/soh/Enhancements/kaleido.cpp b/soh/soh/Enhancements/kaleido.cpp index eef19b495..65a41df9f 100644 --- a/soh/soh/Enhancements/kaleido.cpp +++ b/soh/soh/Enhancements/kaleido.cpp @@ -116,7 +116,7 @@ Kaleido::Kaleido() { gRupeeCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 0xC8, 0xFF, 0x64, 255 }, FlagType::FLAG_RANDOMIZER_INF, static_cast(RAND_INF_GREG_FOUND), 0, yOffset, "Greg")); yOffset += 18; - if (ctx->GetOption(RSK_TRIFORCE_HUNT)) { + if (ctx->GetOption(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) { mEntries.push_back(std::make_shared( gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, 0, yOffset, reinterpret_cast(&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected), diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 3e6588646..7ff49d328 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -515,11 +515,22 @@ void GenerateItemPool() { ctx->possibleIceTrapModels.push_back(RG_LIGHT_MEDALLION); } - if (ctx->GetOption(RSK_TRIFORCE_HUNT)) { + if (ctx->GetOption(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) { ctx->possibleIceTrapModels.push_back(RG_TRIFORCE_PIECE); AddItemToMainPool(RG_TRIFORCE_PIECE, (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() + 1)); - ctx->PlaceItemInLocation(RC_TRIFORCE_COMPLETED, RG_TRIFORCE); // Win condition - ctx->PlaceItemInLocation(RC_GANON, GetJunkItem(), false, true); + + switch (ctx->GetOption(RSK_TRIFORCE_HUNT).Get()) { + case RO_TRIFORCE_HUNT_OFF: + break; + case RO_TRIFORCE_HUNT_WIN: + ctx->PlaceItemInLocation(RC_TRIFORCE_COMPLETED, RG_TRIFORCE); // Win condition + ctx->PlaceItemInLocation(RC_GANON, GetJunkItem(), false, true); + break; + case RO_TRIFORCE_HUNT_GBK: + ctx->PlaceItemInLocation(RC_TRIFORCE_COMPLETED, RG_GANONS_CASTLE_BOSS_KEY); + ctx->PlaceItemInLocation(RC_GANON, RG_TRIFORCE); // Win condition + break; + } } else { ctx->PlaceItemInLocation(RC_GANON, RG_TRIFORCE); // Win condition } @@ -1198,7 +1209,8 @@ void GenerateItemPool() { AddItemToMainPool(RG_SHADOW_TEMPLE_BOSS_KEY); } - if (!ctx->GetOption(RSK_TRIFORCE_HUNT)) { // Don't add GBK to the pool at all for Triforce Hunt. + if (!ctx->GetOption(RSK_TRIFORCE_HUNT) + .IsNot(RO_TRIFORCE_HUNT_OFF)) { // Don't add GBK to the pool at all for Triforce Hunt. if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) { ctx->PlaceItemInLocation(RC_KAK_100_GOLD_SKULLTULA_REWARD, RG_GANONS_CASTLE_BOSS_KEY); } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Get() >= RO_GANON_BOSS_KEY_LACS_VANILLA) { diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index dda37b18a..27443f044 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -54,9 +54,10 @@ void GenerateStartingInventory() { AddItemToInventory(RG_SHADOW_TEMPLE_BOSS_KEY); } - // Add Ganon's Boss key with Triforce Hunt so the game thinks it's obtainable from the start. + // Add Ganon's Boss key with Triforce Hunt's Win setting so the game thinks it's obtainable from the start. // During save init, the boss key isn't actually given and it's instead given when completing the triforce. - if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH) || ctx->GetOption(RSK_TRIFORCE_HUNT)) { + if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH) || + ctx->GetOption(RSK_TRIFORCE_HUNT).Is(RO_TRIFORCE_HUNT_WIN)) { AddItemToInventory(RG_GANONS_CASTLE_BOSS_KEY); } diff --git a/soh/soh/Enhancements/randomizer/hint.cpp b/soh/soh/Enhancements/randomizer/hint.cpp index 27ca4eb2a..9b66dd5e2 100644 --- a/soh/soh/Enhancements/randomizer/hint.cpp +++ b/soh/soh/Enhancements/randomizer/hint.cpp @@ -590,7 +590,7 @@ CustomMessage Hint::GetGanonBossKeyText() { auto ctx = Rando::Context::GetInstance(); CustomMessage ganonBossKeyMessage; - if (ctx->GetOption(RSK_TRIFORCE_HUNT)) { + if (ctx->GetOption(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) { return StaticData::hintTextTable[RHT_GANON_BK_TRIFORCE_HINT].GetHintMessage(); } @@ -710,4 +710,4 @@ void Hint::ResetVariables() { areaNamesChosen = {}; } -} // namespace Rando \ No newline at end of file +} // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 447125ae1..1a17a31e0 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -2261,7 +2261,7 @@ void RandomizerOnPlayerUpdateHandler() { *Rando::StaticData::GetItemTable().at(RG_GANONS_CASTLE_BOSS_KEY).GetGIEntry()); } - if (!GameInteractor::IsGameplayPaused() && RAND_GET_OPTION(RSK_TRIFORCE_HUNT)) { + if (!GameInteractor::IsGameplayPaused() && RAND_GET_OPTION(RSK_TRIFORCE_HUNT) != RO_TRIFORCE_HUNT_OFF) { // Warp to credits if (GameInteractor::State::TriforceHuntCreditsWarpActive) { gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 50d11d9b2..01739425d 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -125,9 +125,9 @@ void Settings::CreateOptionDescriptions() { "set."; mOptionDescriptions[RSK_TRIFORCE_HUNT] = "Pieces of the Triforce of Courage have been scattered across the world. Find them all to finish the game!\n\n" - "When the required amount of pieces have been found, the game is saved and Ganon's Boss key is given " - "to you when you load back into the game if you desire to beat Ganon afterwards.\n\n" - "Keep in mind Ganon might not be logically beatable when \"All Locations Reachable\" is turned off."; + "When the required amount of pieces have been found, Ganon's Boss key is given. If set to Win: the game is " + "saved and you can load back into the game if you desire to beat Ganon afterwards, and " + "keep in mind Ganon might not be logically beatable when \"All Locations Reachable\" is turned off."; mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL] = "The amount of Triforce pieces that will be placed in the world. " "Keep in mind seed generation can fail if more pieces are placed than there are junk items in the item pool."; diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 5f9027d82..20d108888 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -6089,18 +6089,22 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected++; GameInteractor_SetTriforceHuntPieceGiven(true); - // Teleport to credits when goal is reached. + // Give Ganon's Boss Key and teleport to credits if set to Win when goal is reached. if (gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected == (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) + 1)) { - gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] = - static_cast(GAMEPLAYSTAT_TOTAL_TIME); - gSaveContext.ship.stats.gameComplete = 1; Flags_SetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY); - Play_PerformSave(play); - Notification::Emit({ - .message = "Game autosaved", - }); - GameInteractor_SetTriforceHuntCreditsWarpActive(true); + + if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) == + RO_TRIFORCE_HUNT_WIN) { + gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] = + static_cast(GAMEPLAYSTAT_TOTAL_TIME); + gSaveContext.ship.stats.gameComplete = 1; + Play_PerformSave(play); + Notification::Emit({ + .message = "Game autosaved", + }); + GameInteractor_SetTriforceHuntCreditsWarpActive(true); + } } break; diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 15a2fbd37..ac0e37e2a 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -6360,6 +6360,13 @@ typedef enum { RO_MQ_DUNGEONS_SELECTION, } RandoOptionMQDungeons; +// Triforce Hunt settings (off, win, Ganon's Boss Key) +typedef enum { + RO_TRIFORCE_HUNT_OFF, + RO_TRIFORCE_HUNT_WIN, + RO_TRIFORCE_HUNT_GBK, +} RandoOptionTriforceHunt; + typedef enum { RO_LOCATION_INCLUDE, RO_LOCATION_EXCLUDE, diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index fcace1017..e01329f1e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -641,7 +641,8 @@ void DrawItemCount(ItemTrackerItem item, bool hideMax) { ImGui::Text("%s", maxString.c_str()); ImGui::PopStyleColor(); } else if (item.id == RG_TRIFORCE_PIECE && IS_RANDO && - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) && IsValidSaveFile()) { + (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) != RO_TRIFORCE_HUNT_OFF) && + IsValidSaveFile()) { std::string currentString = ""; std::string requiredString = ""; std::string maxString = ""; @@ -760,7 +761,8 @@ void DrawItem(ItemTrackerItem item) { break; case RG_TRIFORCE_PIECE: actualItemId = item.id; - hasItem = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT); + hasItem = IS_RANDO && (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) != + RO_TRIFORCE_HUNT_OFF); itemName = "Triforce Piece"; break; case RG_GOHMA_SOUL: diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 5f8c60ac0..3776d2ec9 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -158,7 +158,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_BOMBCHU_BAG, "Bombchu Bag", CVAR_RANDOMIZER_SETTING("BombchuBag"), mOptionDescriptions[RSK_BOMBCHU_BAG]); OPT_U8(RSK_ENABLE_BOMBCHU_DROPS, "Bombchu Drops", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("EnableBombchuDrops"), mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS], WidgetType::Combobox, RO_AMMO_DROPS_ON); // TODO: AmmoDrops and/or HeartDropRefill, combine with/separate Ammo Drops from Bombchu Drops? - OPT_BOOL(RSK_TRIFORCE_HUNT, "Triforce Hunt", CVAR_RANDOMIZER_SETTING("TriforceHunt"), mOptionDescriptions[RSK_TRIFORCE_HUNT], IMFLAG_NONE); + OPT_U8(RSK_TRIFORCE_HUNT, "Triforce Hunt", {"Off", "Win", "Ganon's Boss Key"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHunt"), mOptionDescriptions[RSK_TRIFORCE_HUNT]); OPT_U8(RSK_TRIFORCE_HUNT_PIECES_TOTAL, "Triforce Hunt Total Pieces", {NumOpts(1, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL], WidgetType::Slider, 29, false, IMFLAG_NONE); OPT_U8(RSK_TRIFORCE_HUNT_PIECES_REQUIRED, "Triforce Hunt Required Pieces", {NumOpts(1, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntRequiredPieces"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED], WidgetType::Slider, 19); OPT_U8(RSK_MQ_DUNGEON_RANDOM, "MQ Dungeon Setting", {"None", "Set Number", "Random", "Selection Only"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeons"), mOptionDescriptions[RSK_MQ_DUNGEON_RANDOM], WidgetType::Combobox, RO_MQ_DUNGEONS_NONE, true, IMFLAG_NONE); @@ -1935,7 +1935,7 @@ void Settings::UpdateOptionProperties() { mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Enable(); mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Enable(); // Remove the pieces required/total sliders and add a separator after Tirforce Hunt if Triforce Hunt is off - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_TRIFORCE_HUNT_OFF) == RO_TRIFORCE_HUNT_OFF) { mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Hide(); mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Hide(); mOptions[RSK_TRIFORCE_HUNT].AddFlag(IMFLAG_SEPARATOR_BOTTOM); @@ -2399,7 +2399,7 @@ void Settings::UpdateOptionProperties() { } else { mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_GENERIC_OFF)) { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_TRIFORCE_HUNT_OFF)) { mOptions[RSK_GANONS_BOSS_KEY].Disable( "This option is disabled because Triforce Hunt is enabled." "Ganon's Boss key\nwill instead be given to you after Triforce Hunt completion.");