Rando: Add Triforce Hunt GBK setting (#5739)

This commit is contained in:
Sirius902
2026-01-02 06:55:34 -08:00
committed by GitHub
parent 31db9e4ade
commit 0bd08d626a
11 changed files with 57 additions and 29 deletions

View File

@@ -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);

View File

@@ -139,7 +139,7 @@ Kaleido::Kaleido() {
gItemIconFishingPoleTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
FlagType::FLAG_RANDOMIZER_INF, static_cast<int>(RAND_INF_FISHING_POLE_FOUND), "Fishing Pole"));
}
if (ctx->GetOption(RSK_TRIFORCE_HUNT)) {
if (ctx->GetOption(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) {
mEntries.push_back(std::make_shared<KaleidoEntryIconCountRequired>(
gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
reinterpret_cast<int*>(&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected),

View File

@@ -502,11 +502,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
}
@@ -1248,7 +1259,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) {

View File

@@ -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);
}

View File

@@ -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
} // namespace Rando

View File

@@ -2500,7 +2500,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;

View File

@@ -124,10 +124,11 @@ void Settings::CreateOptionDescriptions() {
"set to either MQ or Random here, you will have fewer MQ Dungeons than the number you "
"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.";
"Pieces of the Triforce of Courage have been scattered across the world. Find them all to finish the game!\n"
"\n"
"If set to Win: the game is saved and the credits roll, though you can load back in to receive Ganon's "
"Castle Boss Key. Keep in mind that Ganon might not be logically reachable when \"All Locations Reachable\" "
"is disabled.";
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.";

View File

@@ -5728,18 +5728,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<u32>(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<u32>(GAMEPLAYSTAT_TOTAL_TIME);
gSaveContext.ship.stats.gameComplete = 1;
Play_PerformSave(play);
Notification::Emit({
.message = "Game autosaved",
});
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
}
}
break;

View File

@@ -6821,6 +6821,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,

View File

@@ -714,7 +714,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 = "";
@@ -833,7 +834,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_DEATH_MOUNTAIN_CRATER_BEAN_SOUL:

View File

@@ -404,10 +404,10 @@ void Settings::CreateOptions() {
OPT_U8(RSK_BOMBCHU_BAG, "Bombchu Bag", {"None", "Single Bag", "Progressive Bags"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BombchuBag"), mOptionDescriptions[RSK_BOMBCHU_BAG], WIDGET_CVAR_COMBOBOX, RO_BOMBCHU_BAG_NONE);
OPT_U8(RSK_ENABLE_BOMBCHU_DROPS, "Bombchu Drops", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("EnableBombchuDrops"), mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS], WIDGET_CVAR_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_CALLBACK(RSK_TRIFORCE_HUNT, {
// 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) {
// Remove the pieces required/total sliders and add a separator after Triforce Hunt if Triforce Hunt is 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_GANONS_BOSS_KEY].Enable();