This commit is contained in:
Sirius902 2025-08-13 19:09:05 +00:00 committed by GitHub
commit 885fbe1b8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 55 additions and 28 deletions

View file

@ -297,7 +297,8 @@ void DrawInfoTab() {
Combobox("Z Target Mode", &gSaveContext.zTargetSetting, zTargetMap, Combobox("Z Target Mode", &gSaveContext.zTargetSetting, zTargetMap,
comboboxOptionsBase.Tooltip("Z-Targeting behavior")); 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); PushStyleInput(THEME_COLOR);
ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U8, ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U8,
&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); &gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);

View file

@ -116,7 +116,7 @@ Kaleido::Kaleido() {
gRupeeCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 0xC8, 0xFF, 0x64, 255 }, gRupeeCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 0xC8, 0xFF, 0x64, 255 },
FlagType::FLAG_RANDOMIZER_INF, static_cast<int>(RAND_INF_GREG_FOUND), 0, yOffset, "Greg")); FlagType::FLAG_RANDOMIZER_INF, static_cast<int>(RAND_INF_GREG_FOUND), 0, yOffset, "Greg"));
yOffset += 18; 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<KaleidoEntryIconCountRequired>( mEntries.push_back(std::make_shared<KaleidoEntryIconCountRequired>(
gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, 0, yOffset, gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, 0, yOffset,
reinterpret_cast<int*>(&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected), reinterpret_cast<int*>(&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected),

View file

@ -515,11 +515,22 @@ void GenerateItemPool() {
ctx->possibleIceTrapModels.push_back(RG_LIGHT_MEDALLION); 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); ctx->possibleIceTrapModels.push_back(RG_TRIFORCE_PIECE);
AddItemToMainPool(RG_TRIFORCE_PIECE, (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() + 1)); 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 { } else {
ctx->PlaceItemInLocation(RC_GANON, RG_TRIFORCE); // Win condition ctx->PlaceItemInLocation(RC_GANON, RG_TRIFORCE); // Win condition
} }
@ -1198,7 +1209,8 @@ void GenerateItemPool() {
AddItemToMainPool(RG_SHADOW_TEMPLE_BOSS_KEY); 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)) { 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); 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) { } 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); 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. // 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); AddItemToInventory(RG_GANONS_CASTLE_BOSS_KEY);
} }

View file

@ -590,7 +590,7 @@ CustomMessage Hint::GetGanonBossKeyText() {
auto ctx = Rando::Context::GetInstance(); auto ctx = Rando::Context::GetInstance();
CustomMessage ganonBossKeyMessage; 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(); return StaticData::hintTextTable[RHT_GANON_BK_TRIFORCE_HINT].GetHintMessage();
} }

View file

@ -2261,7 +2261,7 @@ void RandomizerOnPlayerUpdateHandler() {
*Rando::StaticData::GetItemTable().at(RG_GANONS_CASTLE_BOSS_KEY).GetGIEntry()); *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 // Warp to credits
if (GameInteractor::State::TriforceHuntCreditsWarpActive) { if (GameInteractor::State::TriforceHuntCreditsWarpActive) {
gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;

View file

@ -125,9 +125,9 @@ void Settings::CreateOptionDescriptions() {
"set."; "set.";
mOptionDescriptions[RSK_TRIFORCE_HUNT] = 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" "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 " "When the required amount of pieces have been found, Ganon's Boss key is given. If set to Win: the game is "
"to you when you load back into the game if you desire to beat Ganon afterwards.\n\n" "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."; "keep in mind Ganon might not be logically beatable when \"All Locations Reachable\" is turned off.";
mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL] = mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL] =
"The amount of Triforce pieces that will be placed in the world. " "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."; "Keep in mind seed generation can fail if more pieces are placed than there are junk items in the item pool.";

View file

@ -6147,18 +6147,22 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected++; gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected++;
GameInteractor_SetTriforceHuntPieceGiven(true); 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 == if (gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected ==
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) + 1)) { (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); Flags_SetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY);
Play_PerformSave(play);
Notification::Emit({ if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) ==
.message = "Game autosaved", RO_TRIFORCE_HUNT_WIN) {
}); gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] =
GameInteractor_SetTriforceHuntCreditsWarpActive(true); static_cast<u32>(GAMEPLAYSTAT_TOTAL_TIME);
gSaveContext.ship.stats.gameComplete = 1;
Play_PerformSave(play);
Notification::Emit({
.message = "Game autosaved",
});
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
}
} }
break; break;

View file

@ -6360,6 +6360,13 @@ typedef enum {
RO_MQ_DUNGEONS_SELECTION, RO_MQ_DUNGEONS_SELECTION,
} RandoOptionMQDungeons; } 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 { typedef enum {
RO_LOCATION_INCLUDE, RO_LOCATION_INCLUDE,
RO_LOCATION_EXCLUDE, RO_LOCATION_EXCLUDE,

View file

@ -663,7 +663,8 @@ void DrawItemCount(ItemTrackerItem item, bool hideMax) {
ImGui::Text("%s", maxString.c_str()); ImGui::Text("%s", maxString.c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} else if (item.id == RG_TRIFORCE_PIECE && IS_RANDO && } 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 currentString = "";
std::string requiredString = ""; std::string requiredString = "";
std::string maxString = ""; std::string maxString = "";
@ -782,7 +783,8 @@ void DrawItem(ItemTrackerItem item) {
break; break;
case RG_TRIFORCE_PIECE: case RG_TRIFORCE_PIECE:
actualItemId = item.id; 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"; itemName = "Triforce Piece";
break; break;
case RG_GOHMA_SOUL: case RG_GOHMA_SOUL:

View file

@ -158,7 +158,7 @@ void Settings::CreateOptions() {
OPT_BOOL(RSK_BOMBCHU_BAG, "Bombchu Bag", CVAR_RANDOMIZER_SETTING("BombchuBag"), mOptionDescriptions[RSK_BOMBCHU_BAG]); 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); 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? // 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_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_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); 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_TOTAL].Enable();
mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].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 // 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_REQUIRED].Hide();
mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Hide(); mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Hide();
mOptions[RSK_TRIFORCE_HUNT].AddFlag(IMFLAG_SEPARATOR_BOTTOM); mOptions[RSK_TRIFORCE_HUNT].AddFlag(IMFLAG_SEPARATOR_BOTTOM);
@ -2399,7 +2399,7 @@ void Settings::UpdateOptionProperties() {
} else { } else {
mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); 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( mOptions[RSK_GANONS_BOSS_KEY].Disable(
"This option is disabled because Triforce Hunt is enabled." "This option is disabled because Triforce Hunt is enabled."
"Ganon's Boss key\nwill instead be given to you after Triforce Hunt completion."); "Ganon's Boss key\nwill instead be given to you after Triforce Hunt completion.");