Decouple GBK from LACS (#5838)

Triforce Pieces can now be tokens for bridge or ganon's soul. & can be tokens for multiple rewards

Wincon can now be arbitrary conditions

Ganon's Soul (removed from existing boss soul options) can now be arbitrary conditions

Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com>
This commit is contained in:
Philip Dubé
2026-07-04 20:04:09 +00:00
committed by GitHub
parent 618b7694b1
commit 015440fdff
55 changed files with 1379 additions and 663 deletions
@@ -24,7 +24,7 @@
"IncludeTycoonWallet": 1,
"KakarikoGate": 1,
"Keysanity": 5,
"LacsRewardCount": 8,
"GbkRewardCount": 8,
"MalonHint": 1,
"MerchantText": 1,
"RainbowBridge": 7,
@@ -27,7 +27,7 @@
"IncludeTycoonWallet": 1,
"KakarikoGate": 1,
"Keysanity": 2,
"LacsRewardCount": 6,
"GbkRewardCount": 6,
"MalonHint": 1,
"MerchantText": 1,
"RainbowBridge": 7,
@@ -18,8 +18,8 @@
"IncludeTycoonWallet": 1,
"KakarikoGate": 1,
"Keysanity": 5,
"LacsRewardCount": 10,
"LacsRewardOptions": 1,
"GbkRewardCount": 10,
"GbkRewardOptions": 1,
"LockOverworldDoors": 1,
"MedallionLockedTrials": 1,
"MixBosses": 1,
@@ -27,7 +27,7 @@
"IncludeTycoonWallet": 1,
"KakarikoGate": 1,
"Keysanity": 5,
"LacsRewardCount": 7,
"GbkRewardCount": 7,
"MalonHint": 1,
"MerchantText": 1,
"RainbowBridge": 7,
-1
View File
@@ -1540,7 +1540,6 @@ u8 CheckStoneCount();
u8 CheckMedallionCount();
u8 CheckDungeonCount();
u8 CheckBridgeRewardCount();
u8 CheckLACSRewardCount();
s32 Play_InCsMode(PlayState* play);
f32 func_800BFCB8(PlayState* play, MtxF* mf, Vec3f* vec);
void* Play_LoadFile(PlayState* play, RomFile* file);
@@ -175,12 +175,9 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
// LACS
bool meetsLACSRequirements =
LINK_IS_ADULT &&
(gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TEMPLE_OF_TIME) &&
if (LINK_IS_ADULT && (gEntranceTable[gSaveContext.entranceIndex].scene == SCENE_TEMPLE_OF_TIME) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) &&
!Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
if (GameInteractor_Should(VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS, meetsLACSRequirements)) {
!Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS)) {
Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
if (GameInteractor_Should(VB_GIVE_ITEM_LIGHT_ARROW, true)) {
Item_Give(gPlayState, ITEM_ARROW_LIGHT);
@@ -35,7 +35,7 @@ static const std::unordered_map<std::string, ItemID> altarIcons = {
{ "l", ITEM_ARROW_LIGHT }, { "b", ITEM_KEY_BOSS }, { "o", ITEM_SWORD_MASTER },
{ "c", ITEM_OCARINA_FAIRY }, { "i", ITEM_OCARINA_TIME }, { "L", ITEM_BOW_ARROW_LIGHT },
{ "k", ITEM_TUNIC_KOKIRI }, { "m", ITEM_DUNGEON_MAP }, { "C", ITEM_COMPASS },
{ "s", ITEM_SKULL_TOKEN }, { "g", ITEM_MASK_GORON },
{ "s", ITEM_SKULL_TOKEN }, { "g", ITEM_MASK_GORON }, { "w", ITEM_CUSTOM },
};
static std::map<std::string, int> pixelWidthTable = {
@@ -406,8 +406,7 @@ void DrawInfoTab() {
Combobox("Z Target Mode", &gSaveContext.zTargetSetting, zTargetMap,
comboboxOptionsBase.Tooltip("Z-Targeting behavior"));
if (IS_RANDO &&
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) != RO_TRIFORCE_HUNT_OFF)) {
if (IS_RANDO && (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_TOTAL) > 0)) {
PushStyleInput(THEME_COLOR);
ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U8,
&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);
@@ -74,8 +74,8 @@ uint8_t GameInteractor_GetRandomWindActive();
uint8_t GameInteractor_GetRandomBonksActive();
uint8_t GameInteractor_GetSlipperyFloorActive();
uint8_t GameInteractor_SecondCollisionUpdate();
void GameInteractor_SetTriforceHuntPieceGiven(uint8_t state);
void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
void GameInteractor_SetTriforceHuntPieceGiven(bool state);
void GameInteractor_SetTriforceHuntCreditsWarpActive(bool state);
#ifdef __cplusplus
}
#endif
@@ -203,8 +203,8 @@ class GameInteractor {
static uint8_t RandomBonksActive;
static uint8_t SlipperyFloorActive;
static uint8_t SecondCollisionUpdate;
static uint8_t TriforceHuntPieceGiven;
static uint8_t TriforceHuntCreditsWarpActive;
static bool TriforceHuntPieceGiven;
static bool TriforceHuntCreditsWarpActive;
static void SetPacifistMode(bool active);
};
@@ -20,8 +20,8 @@ uint8_t GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 0;
uint8_t GameInteractor::State::RandomBonksActive = 0;
uint8_t GameInteractor::State::SlipperyFloorActive = 0;
uint8_t GameInteractor::State::SecondCollisionUpdate = 0;
uint8_t GameInteractor::State::TriforceHuntPieceGiven = 0;
uint8_t GameInteractor::State::TriforceHuntCreditsWarpActive = 0;
bool GameInteractor::State::TriforceHuntPieceGiven = false;
bool GameInteractor::State::TriforceHuntCreditsWarpActive = false;
void GameInteractor::State::SetPacifistMode(bool active) {
PacifistModeActive = active;
@@ -131,11 +131,11 @@ uint8_t GameInteractor_SecondCollisionUpdate() {
}
// MARK: - GameInteractor::State::TriforceHuntPieceGiven
void GameInteractor_SetTriforceHuntPieceGiven(uint8_t state) {
void GameInteractor_SetTriforceHuntPieceGiven(bool state) {
GameInteractor::State::TriforceHuntPieceGiven = state;
}
// MARK: - GameInteractor::State::TriforceHuntCreditsWarpActive
void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state) {
void GameInteractor_SetTriforceHuntCreditsWarpActive(bool state) {
GameInteractor::State::TriforceHuntCreditsWarpActive = state;
}
@@ -2411,6 +2411,14 @@ typedef enum {
// - None
VB_SKIP_TALKING,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - None
VB_SLAY_GANON,
// #### `result`
// ```c
// (collectible >= 0) && (collectible <= 0x19
+3 -4
View File
@@ -144,12 +144,11 @@ 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).IsNot(RO_TRIFORCE_HUNT_OFF)) {
if (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() > 0) {
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),
ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1,
ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() + 1));
ctx->GetOption(RSK_WINCON_TRIFORCE_COUNT).Get(), ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get()));
}
if (ctx->GetOption(RSK_SKELETON_KEY)) {
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
@@ -170,7 +169,7 @@ Kaleido::Kaleido() {
FlagType::FLAG_RANDOMIZER_INF, i, bossSoulNames[i - RAND_INF_GOHMA_SOUL]));
}
}
if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).Is(RO_BOSS_SOULS_ON_PLUS_GANON)) {
if (ctx->GetOption(RSK_GANONS_SOUL).IsNot(RO_GANONS_SOUL_STARTWITH)) {
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
gBossSoulTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
FlagType::FLAG_RANDOMIZER_INF, RAND_INF_GANON_SOUL, "Ganon's Soul"));
@@ -230,16 +230,6 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer
// Get the max number of tokens that can possibly be useful
static int GetMaxGSCount() {
auto ctx = Rando::Context::GetInstance();
// If bridge or LACS is set to tokens, get how many are required
int maxBridge = 0;
int maxLACS = 0;
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS)) {
maxBridge = ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Get();
}
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
maxLACS = ctx->GetOption(RSK_LACS_TOKEN_COUNT).Get();
}
maxBridge = std::max(maxBridge, maxLACS);
// Get the max amount of GS which could be useful from token reward locations
int maxUseful = 0;
// If the highest advancement item is a token, we know it is useless since it won't lead to an otherwise useful item
@@ -262,8 +252,21 @@ static int GetMaxGSCount() {
ctx->GetItemLocation(RC_KAK_10_GOLD_SKULLTULA_REWARD)->GetPlacedItem().GetItemType() != ITEMTYPE_TOKEN) {
maxUseful = 10;
}
// If bridge, GBK, Ganon's Soul, or win condition is set to tokens, get how many are required
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS)) {
maxUseful = std::max(maxUseful, (int)ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Get());
}
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_TOKENS)) {
maxUseful = std::max(maxUseful, (int)ctx->GetOption(RSK_GBK_TOKEN_COUNT).Get());
}
if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_TOKENS)) {
maxUseful = std::max(maxUseful, (int)ctx->GetOption(RSK_GANONS_SOUL_TOKEN_COUNT).Get());
}
if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_TOKENS)) {
maxUseful = std::max(maxUseful, (int)ctx->GetOption(RSK_WINCON_TOKEN_COUNT).Get());
}
// Return max of the two possible reasons tokens could be important, minus the tokens in the starting inventory
return std::max(maxUseful, maxBridge) - ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).Get();
return maxUseful - ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).Get();
}
std::string GetShopItemBaseName(std::string itemName) {
@@ -369,12 +372,12 @@ void AddToPlaythrough(LocationAccess& locPair, GetAccessibleLocationsStruct& gal
if (!exclude) {
gals.itemSphere.push_back(loc);
}
}
// Triforce has been found, seed is beatable, nothing else in this or future spheres matters
else if (location->GetPlacedRandomizerGet() == RG_TRIFORCE) {
gals.itemSphere.clear();
gals.itemSphere.push_back(loc);
ctx->playthroughBeatable = true;
// Triforce has been found, seed is beatable, nothing else in this or future spheres matters
if (location->GetPlacedRandomizerGet() == RG_TRIFORCE) {
gals.itemSphere.clear();
gals.itemSphere.push_back(loc);
ctx->playthroughBeatable = true;
}
}
}
@@ -1159,6 +1162,14 @@ static void RandomizeDungeonItems() {
}
}
if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_ANY_DUNGEON)) {
auto ganonSoul = FilterAndEraseFromPool(itemPool, [](const auto i) { return i == RG_GANON_SOUL; });
AddElementsToPool(anyDungeonItems, ganonSoul);
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_OVERWORLD)) {
auto ganonSoul = FilterAndEraseFromPool(itemPool, [](const auto i) { return i == RG_GANON_SOUL; });
AddElementsToPool(overworldItems, ganonSoul);
}
if (ctx->GetOption(RSK_GERUDO_KEYS).Is(RO_GERUDO_KEYS_ANY_DUNGEON)) {
auto gerudoKeys = FilterAndEraseFromPool(itemPool, [](const auto i) {
return i == RG_GERUDO_FORTRESS_SMALL_KEY || i == RG_GERUDO_FORTRESS_KEY_RING;
@@ -2013,8 +2013,13 @@ void StaticData::HintTable_Init() {
{QM_YELLOW}, {}, TEXTBOX_TYPE_BLUE));
// /*spanish*/$sLos sabios aguardarán a que el héroe obtenga #[[d]] símbolo||s| de skulltula dorada#.^
hintTextTable[RHT_BRIDGE_TRIFORCE_PIECES_HINT] = HintText(CustomMessage("$wThe awakened ones will await for the Hero to collect #[[d]] Triforce Piece||s|#.^",
/*german*/ "$wDie Weisen werden darauf&warten, daß der Held&#[[d]] Triforce-Fragment||e|# sammelt.^",
/*french*/ "$wLes êtres de sagesse attendront le héros muni de #[[d]] Morceau||x| de Triforce#.^",
{QM_YELLOW}, {}, TEXTBOX_TYPE_BLUE));
hintTextTable[RHT_BRIDGE_GREG_HINT] = HintText(CustomMessage("$gThe awakened ones will await for the Hero to find #Greg#.^",
/*german*/ "$gDie Weisen werden darauf&warten, daß der Held&#Greg# findet.^",
/*german*/ "$gDie Weisen werden darauf&warten, daß der Held&#Greg# findet.^",
/*french*/ "$gLes êtres de sagesse attendront le héros muni de #Greg#.^",
{QM_GREEN}, {}, TEXTBOX_TYPE_BLUE));
@@ -2059,56 +2064,118 @@ void StaticData::HintTable_Init() {
{QM_PINK, QM_BLUE}));
// /*spanish*/$bY la llave del #señor del mal# aguardará en #cualquier lugar de Hyrule#.^
hintTextTable[RHT_GANON_BK_TRIFORCE_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be given to the Hero once the #Triforce## is completed.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird verliehen, sobald das #Triforce# vervollständigt wurde.^",
/*french*/ "$bAussi, la #clé du Malin# se&révèlera une fois la #Triforce#&assemblée.^",
{QM_PINK, QM_YELLOW}));
// /*spanish*/$bY el héroe recibirá la llave del #señor del mal# cuando haya completado la #Trifuerza#.^
hintTextTable[RHT_GANON_BK_SKULLTULA_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided by the cursed rich man once #100 Gold Skulltula Tokens# are retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird von einem verfluchten reichen Mann verliehen, sobald #100 Skulltula-Symbole# gesammelt wurden.^",
/*french*/ "$bAussi, la #clé du Malin# sera&donnée par l'homme maudit une fois que #100 Symboles de Skulltula d'or# auront été trouvés.^",
{QM_PINK, QM_YELLOW}));
// /*spanish*/$bY el rico maldito entregará la llave&del #señor de mal# tras obtener&100 símbolos de skulltula dorada#.^
hintTextTable[RHT_LACS_VANILLA_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided by #Zelda# once the #Shadow and Spirit Medallions# are retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird von #Zelda# verliehen, sobald #die Amulette des Schattens und der Geister# geborgen wurden.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie par #Zelda# une fois que les #Médaillons de l'Ombre et de l'Esprit# seront récupérés.^",
{QM_PINK, QM_YELLOW, QM_RED}));
// /*spanish*/$bY #Zelda# entregará la llave del #señor del mal# tras obtener #el medallón de las sombras y del espíritu#.^
hintTextTable[RHT_LACS_MEDALLIONS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided by #Zelda# once #[[d]] Medallion|# is|s# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird von #Zelda# verliehen, sobald #[[d]] Amulett|# geborgen wurde|e# geborgen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie par #Zelda# une fois |qu' #[[d]] Médaillon# aura été récupéré|que #[[d]] Médaillons# auront été récupérés|.^",
hintTextTable[RHT_GBK_MEDALLIONS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided once #[[d]] Medallion|# is|s# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird verliehen, sobald #[[d]] Amulett|# geborgen wurde|e# geborgen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie une fois |qu' #[[d]] Médaillon# aura été récupéré|que #[[d]] Médaillons# auront été récupérés|.^",
{QM_PINK, QM_YELLOW, QM_RED}));
// /*spanish*/$bY #Zelda# entregará la llave&del #señor del mal# tras obtener #[[d]] |medallón|medallones|#.^
hintTextTable[RHT_LACS_STONES_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided by #Zelda# once #[[d]] Spiritual Stone|# is|s# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird von #Zelda# verliehen, sobald #[[d]] Heilige|r Stein# geborgen wurde| Steine# geborgen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie par #Zelda# une fois |qu' #[[d]] Pierre Ancestrale# aura été&récupérée|que #[[d]] Pierres Ancestrales# auront été récupérées|.^",
hintTextTable[RHT_GBK_STONES_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided once #[[d]] Spiritual Stone|# is|s# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird verliehen, sobald #[[d]] Heilige|r Stein# geborgen wurde| Steine# geborgen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie une fois |qu' #[[d]] Pierre Ancestrale# aura été&récupérée|que #[[d]] Pierres Ancestrales# auront été récupérées|.^",
{QM_PINK, QM_YELLOW, QM_BLUE}));
// /*spanish*/$bY #Zelda# entregará la llave del #señor del mal# tras obtener #[[d]] piedra| espiritual|s espirituales|#.^
hintTextTable[RHT_LACS_REWARDS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided by #Zelda# once #[[d]]# #Spiritual Stone|# or #Medallion# is|s# and #Medallions# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird von #Zelda# verliehen, sobald #[[d]]# #Heilige|r Stein# oder #Amulett#&geborgen wurde| Steine# oder #Amulette#&geborgen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie par #Zelda# une fois qu|' #[[d]]# #Pierre Ancestrale# ou #[[d]] Médaillon# sera récupéré|e&#[[d]]# #Pierres Ancestrales# et&#Médaillons# seront récupérés|.^",
hintTextTable[RHT_GBK_REWARDS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided once #[[d]]# #Spiritual Stone|# or #Medallion# is|s# and #Medallions# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird verliehen, sobald #[[d]]# #Heilige|r Stein# oder #Amulett#&geborgen wurde| Steine# oder #Amulette#&geborgen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie une fois qu|' #[[d]]# #Pierre Ancestrale# ou #[[d]] Médaillon# sera récupéré|e&#[[d]]# #Pierres Ancestrales# et&#Médaillons# seront récupérés|.^",
{QM_PINK, QM_YELLOW, QM_YELLOW, QM_BLUE, QM_RED}));
// /*spanish*/$bY #Zelda# entregará la llave del #señor del mal# tras obtener #[[d]]# piedra| espiritual o medallón|s espirituales o medallones|#.^
hintTextTable[RHT_LACS_DUNGEONS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided by #Zelda# once #[[d]] Dungeon|# is|s# are| conquered.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird von #Zelda# verliehen, sobald #[[d]] Labyrinth|# abgeschloßen wurde|e# abgeschloßen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie par #Zelda# une fois qu|' #[[d]] donjon #sera conquis|e #[[d]] donjons# seront conquis|.^",
hintTextTable[RHT_GBK_DUNGEONS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided once #[[d]] Dungeon|# is|s# are| conquered.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird verliehen, sobald #[[d]] Labyrinth|# abgeschloßen wurde|e# abgeschloßen wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie une fois qu|' #[[d]] donjon #sera conquis|e #[[d]] donjons# seront conquis|.^",
{QM_PINK, QM_YELLOW, QM_PINK}));
// /*spanish*/$bY #Zelda# entregará la llave del #señor del mal# tras completar #[[d]] mazmorra||s|#.^
hintTextTable[RHT_LACS_TOKENS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided by #Zelda# once #[[d]] Gold Skulltula Token|# is|s# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird von #Zelda# verliehen, sobald #[[d]] Skulltula-Symbol|# gesammelt wurde|e# gesammelt wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie par #Zelda# une fois |qu' #[[d]] symbole de Skulltula d'or #sera récupuéré"
hintTextTable[RHT_GBK_TOKENS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided once #[[d]] Gold Skulltula Token|# is|s# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird verliehen, sobald #[[d]] Skulltula-Symbol|# gesammelt wurde|e# gesammelt wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie une fois |qu' #[[d]] symbole de Skulltula d'or #sera récupuéré"
"|que &#[[d]] symboles de Skulltula d'or&#seront recupérés|.^",
{QM_PINK, QM_YELLOW, QM_YELLOW}));
// /*spanish*/$bY #Zelda# entregará la llave del #señor del mal# tras obtener #[[d]] símbolo
// ||s| de skulltula dorada#.^
hintTextTable[RHT_GBK_TRIFORCE_PIECES_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s key will be provided once #[[d]] Triforce Piece|# is|s# are| retrieved.^",
/*german*/ "$bUnd der #Schlüssel des Bösen# wird verliehen, sobald #[[d]] Triforce-Fragment|# gesammelt wurde|e# gesammelt wurden|.^",
/*french*/ "$bAussi, la #clé du Malin# sera fournie une fois |qu' #[[d]] Morceau de Triforce# aura été récupéré|que #[[d]] Morceaux de Triforce# auront été récupérés|.^",
{QM_PINK, QM_YELLOW, QM_YELLOW}));
/*--------------------------
| GANON'S SOUL HINT TEXT |
---------------------------*/
hintTextTable[RHT_GANONS_SOUL_MEDALLIONS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s soul will be provided once #[[d]] Medallion|# is|s# are| retrieved.^",
/*german*/ "$bUnd die #Seele des Bösen# wird verliehen, sobald #[[d]] Amulett|# geborgen wurde|e# geborgen wurden|.^",
/*french*/ "$bAussi, l'#âme du Malin# sera fournie une fois |qu' #[[d]] Médaillon# aura été récupéré|que #[[d]] Médaillons# auront été récupérés|.^",
{QM_PINK, QM_YELLOW, QM_RED}));
hintTextTable[RHT_GANONS_SOUL_STONES_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s soul will be provided once #[[d]] Spiritual Stone|# is|s# are| retrieved.^",
/*german*/ "$bUnd die #Seele des Bösen# wird verliehen, sobald #[[d]] Heilige|r Stein# geborgen wurde| Steine# geborgen wurden|.^",
/*french*/ "$bAussi, l'#âme du Malin# sera fournie une fois |qu' #[[d]] Pierre Ancestrale# aura été&récupérée|que #[[d]] Pierres Ancestrales# auront été récupérées|.^",
{QM_PINK, QM_YELLOW, QM_BLUE}));
hintTextTable[RHT_GANONS_SOUL_REWARDS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s soul will be provided once #[[d]]# #Spiritual Stone|# or #Medallion# is|s# and #Medallions# are| retrieved.^",
/*german*/ "$bUnd die #Seele des Bösen# wird verliehen, sobald #[[d]]# #Heilige|r Stein# oder #Amulett#&geborgen wurde| Steine# oder #Amulette#&geborgen wurden|.^",
/*french*/ "$bAussi, l'#âme du Malin# sera fournie une fois qu|' #[[d]]# #Pierre Ancestrale# ou #[[d]] Médaillon# sera récupéré|e&#[[d]]# #Pierres Ancestrales# et&#Médaillons# seront récupérés|.^",
{QM_PINK, QM_YELLOW, QM_YELLOW, QM_BLUE, QM_RED}));
hintTextTable[RHT_GANONS_SOUL_DUNGEONS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s soul will be provided once #[[d]] Dungeon|# is|s# are| conquered.^",
/*german*/ "$bUnd die #Seele des Bösen# wird verliehen, sobald #[[d]] Labyrinth|# abgeschloßen wurde|e# abgeschloßen wurden|.^",
/*french*/ "$bAussi, l'#âme du Malin# sera fournie une fois qu|' #[[d]] donjon #sera conquis|e #[[d]] donjons# seront conquis|.^",
{QM_PINK, QM_YELLOW, QM_PINK}));
hintTextTable[RHT_GANONS_SOUL_TOKENS_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s soul will be provided once #[[d]] Gold Skulltula Token|# is|s# are| retrieved.^",
/*german*/ "$bUnd die #Seele des Bösen# wird verliehen, sobald #[[d]] Skulltula-Symbol|# gesammelt wurde|e# gesammelt wurden|.^",
/*french*/ "$bAussi, l'#âme du Malin# sera fournie une fois |qu' #[[d]] symbole de Skulltula d'or #sera récupuéré"
"|que &#[[d]] symboles de Skulltula d'or&#seront recupérés|.^",
{QM_PINK, QM_YELLOW, QM_YELLOW}));
hintTextTable[RHT_GANONS_SOUL_TRIFORCE_PIECES_HINT] = HintText(CustomMessage("$bAnd the #evil one#'s soul will be provided once #[[d]] Triforce Piece|# is|s# are| retrieved.^",
/*german*/ "$bUnd die #Seele des Bösen# wird verliehen, sobald #[[d]] Triforce-Fragment|# gesammelt wurde|e# gesammelt wurden|.^",
/*french*/ "$bAussi, l'#âme du Malin# sera fournie une fois |qu' #[[d]] Morceau de Triforce# aura été récupéré|que #[[d]] Morceaux de Triforce# auront été récupérés|.^",
{QM_PINK, QM_YELLOW, QM_YELLOW}));
/*--------------------------
| WINCON HINT TEXT |
---------------------------*/
hintTextTable[RHT_WINCON_ANYWHERE_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be hidden somewhere&#in Hyrule#.^",
/*german*/ "$wUnd das #Triforce# wird irgendwo #in Hyrule# zu finden sein.^",
/*french*/ "$wAussi, la #Triforce# se trouve quelque part #dans Hyrule#.^",
{QM_YELLOW, QM_BLUE}));
hintTextTable[RHT_WINCON_MEDALLIONS_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be granted once #[[d]] Medallion|# is|s# are| retrieved.^",
/*german*/ "$wUnd das #Triforce# wird gewährt, sobald #[[d]] Amulett|# geborgen wurde|e# geborgen wurden|.^",
/*french*/ "$wAussi, la #Triforce# sera accordée une fois |qu' #[[d]] Médaillon# aura été récupéré|que #[[d]] Médaillons# auront été récupérés|.^",
{QM_YELLOW, QM_YELLOW, QM_RED}));
hintTextTable[RHT_WINCON_STONES_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be granted once #[[d]] Spiritual Stone|# is|s# are| retrieved.^",
/*german*/ "$wUnd das #Triforce# wird gewährt, sobald #[[d]] Heilige|r Stein# geborgen wurde| Steine# geborgen wurden|.^",
/*french*/ "$wAussi, la #Triforce# sera accordée une fois |qu' #[[d]] Pierre Ancestrale# aura été&récupérée|que #[[d]] Pierres Ancestrales# auront été récupérées|.^",
{QM_YELLOW, QM_YELLOW, QM_BLUE}));
hintTextTable[RHT_WINCON_REWARDS_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be granted once #[[d]]# #Spiritual Stone|# or #Medallion# is|s# and #Medallions# are| retrieved.^",
/*german*/ "$wUnd das #Triforce# wird gewährt, sobald #[[d]]# #Heilige|r Stein# oder #Amulett#&geborgen wurde| Steine# oder #Amulette#&geborgen wurden|.^",
/*french*/ "$wAussi, la #Triforce# sera accordée une fois qu|' #[[d]]# #Pierre Ancestrale# ou #[[d]] Médaillon# sera récupéré|e&#[[d]]# #Pierres Ancestrales# et&#Médaillons# seront récupérés|.^",
{QM_YELLOW, QM_YELLOW, QM_YELLOW, QM_BLUE, QM_RED}));
hintTextTable[RHT_WINCON_DUNGEONS_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be granted once #[[d]] Dungeon|# is|s# are| conquered.^",
/*german*/ "$wUnd das #Triforce# wird gewährt, sobald #[[d]] Labyrinth|# abgeschloßen wurde|e# abgeschloßen wurden|.^",
/*french*/ "$wAussi, la #Triforce# sera accordée une fois qu|' #[[d]] donjon #sera conquis|e #[[d]] donjons# seront conquis|.^",
{QM_YELLOW, QM_YELLOW, QM_PINK}));
hintTextTable[RHT_WINCON_TOKENS_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be granted once #[[d]] Gold Skulltula Token|# is|s# are| retrieved.^",
/*german*/ "$wUnd das #Triforce# wird gewährt, sobald #[[d]] Skulltula-Symbol|# gesammelt wurde|e# gesammelt wurden|.^",
/*french*/ "$wAussi, la #Triforce# sera accordée une fois |qu' #[[d]] symbole de Skulltula d'or #sera récupuéré"
"|que &#[[d]] symboles de Skulltula d'or&#seront recupérés|.^",
{QM_YELLOW, QM_YELLOW, QM_YELLOW}));
hintTextTable[RHT_WINCON_TRIFORCE_PIECES_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be granted once #[[d]] Triforce Piece|# is|s# are| retrieved.^",
/*german*/ "$wUnd das #Triforce# wird gewährt, sobald #[[d]] Triforce-Fragment|# gesammelt wurde|e# gesammelt wurden|.^",
/*french*/ "$wAussi, la #Triforce# sera accordée une fois |qu' #[[d]] Morceau de Triforce# aura été récupéré|que #[[d]] Morceaux de Triforce# auront été récupérés|.^",
{QM_YELLOW, QM_YELLOW, QM_YELLOW}));
/*--------------------------
| TRIAL HINT TEXT |
---------------------------*/
@@ -211,39 +211,72 @@ const std::array<HintSetting, 4> hintSettingTable{{
}};
struct BridgeReqConfig {
RandomizerSettingKey bridgeDirectKey;
RandomizerSettingKey lacsDirectKey;
RandomizerSettingKey bridgeKey;
RandomizerSettingKey gbkKey;
RandomizerSettingKey soulKey;
RandomizerSettingKey winKey;
RandoOptionRainbowBridge bridgeEnum;
RandoOptionGanonsBossKey lacsEnum;
RandoOptionGanonsBossKey gbkEnum;
RandoOptionGanonsSoul soulEnum;
RandoOptionWincon winEnum;
uint8_t offset;
};
static constexpr BridgeReqConfig StonesConfig{ RSK_RAINBOW_BRIDGE_STONE_COUNT, RSK_LACS_STONE_COUNT, RO_BRIDGE_STONES,
RO_GANON_BOSS_KEY_LACS_STONES, 6 };
static constexpr BridgeReqConfig MedallionsConfig{ RSK_RAINBOW_BRIDGE_MEDALLION_COUNT, RSK_LACS_MEDALLION_COUNT,
RO_BRIDGE_MEDALLIONS, RO_GANON_BOSS_KEY_LACS_MEDALLIONS, 3 };
static constexpr BridgeReqConfig TokensConfig{ RSK_RAINBOW_BRIDGE_TOKEN_COUNT, RSK_LACS_TOKEN_COUNT, RO_BRIDGE_TOKENS,
RO_GANON_BOSS_KEY_LACS_TOKENS, 0 };
static constexpr BridgeReqConfig StonesConfig{
RSK_RAINBOW_BRIDGE_STONE_COUNT, RSK_GBK_STONE_COUNT, RSK_GANONS_SOUL_STONE_COUNT,
RSK_WINCON_STONE_COUNT, RO_BRIDGE_STONES, RO_GANON_BOSS_KEY_STONES,
RO_GANONS_SOUL_STONES, RO_WINCON_STONES, 6
};
static constexpr BridgeReqConfig MedallionsConfig{
RSK_RAINBOW_BRIDGE_MEDALLION_COUNT, RSK_GBK_MEDALLION_COUNT, RSK_GANONS_SOUL_MEDALLION_COUNT,
RSK_WINCON_MEDALLION_COUNT, RO_BRIDGE_MEDALLIONS, RO_GANON_BOSS_KEY_MEDALLIONS,
RO_GANONS_SOUL_MEDALLIONS, RO_WINCON_MEDALLIONS, 3
};
static constexpr BridgeReqConfig TokensConfig{
RSK_RAINBOW_BRIDGE_TOKEN_COUNT, RSK_GBK_TOKEN_COUNT, RSK_GANONS_SOUL_TOKEN_COUNT,
RSK_WINCON_TOKEN_COUNT, RO_BRIDGE_TOKENS, RO_GANON_BOSS_KEY_TOKENS,
RO_GANONS_SOUL_TOKENS, RO_WINCON_TOKENS, 0
};
static uint8_t RequiredBySettings(const BridgeReqConfig& cfg) {
auto ctx = Rando::Context::GetInstance();
uint8_t count = 0;
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(cfg.bridgeEnum)) {
count = ctx->GetOption(cfg.bridgeDirectKey).Get();
count = ctx->GetOption(cfg.bridgeKey).Get();
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEON_REWARDS)) {
count = ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Get() - cfg.offset;
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEONS) &&
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
count = ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Get() - cfg.offset;
}
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(cfg.lacsEnum)) {
count = std::max<uint8_t>(count, ctx->GetOption(cfg.lacsDirectKey).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).Get() - cfg.offset));
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS) &&
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(cfg.gbkEnum)) {
count = std::max<uint8_t>(count, ctx->GetOption(cfg.gbkKey).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_REWARDS)) {
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_GBK_REWARD_COUNT).Get() - cfg.offset));
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_DUNGEONS) &&
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Get() - cfg.offset));
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_GBK_DUNGEON_COUNT).Get() - cfg.offset));
}
if (ctx->GetOption(RSK_GANONS_SOUL).Is(cfg.soulEnum)) {
count = std::max<uint8_t>(count, ctx->GetOption(cfg.soulKey).Get());
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_REWARDS)) {
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_GANONS_SOUL_REWARD_COUNT).Get() - cfg.offset));
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_DUNGEONS) &&
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_GANONS_SOUL_DUNGEON_COUNT).Get() - cfg.offset));
}
if (ctx->GetOption(RSK_WINCON).Is(cfg.winEnum)) {
count = std::max<uint8_t>(count, ctx->GetOption(cfg.winKey).Get());
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_REWARDS)) {
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_WINCON_REWARD_COUNT).Get() - cfg.offset));
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_DUNGEONS) &&
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
count = std::max<uint8_t>(count, (uint8_t)(ctx->GetOption(RSK_WINCON_DUNGEON_COUNT).Get() - cfg.offset));
}
return count;
}
@@ -326,27 +326,27 @@ void GenerateItemPool() {
AddFixedItemToPool(RG_SHADOW_MEDALLION, 1, rewardIceTraps);
AddFixedItemToPool(RG_LIGHT_MEDALLION, 1, rewardIceTraps);
if (ctx->GetOption(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) {
AddFixedItemToPool(RG_TRIFORCE_PIECE, ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() + 1, false);
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, RG_BLUE_RUPEE, 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
if (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() > 0) {
AddFixedItemToPool(RG_TRIFORCE_PIECE, ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get(), false);
}
// Fixed item locations
ctx->PlaceItemInLocation(RC_HC_ZELDAS_LETTER, RG_ZELDAS_LETTER);
ctx->PlaceItemInLocation(RC_GANONS_BOSS_KEY, RG_BLUE_RUPEE); // placeholder, filled by setting
ctx->PlaceItemInLocation(RC_GANON_SOUL, RG_BLUE_RUPEE); // placeholder, filled by setting
ctx->PlaceItemInLocation(RC_WINCON, RG_BLUE_RUPEE); // placeholder, filled by setting
if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_DEFEAT_GANON)) {
ctx->PlaceItemInLocation(RC_GANON, RG_TRIFORCE); // Win condition
} else {
// Ganon isn't the win condition, so slaying him is optional and just hands out a junk reward.
ctx->PlaceItemInLocation(RC_GANON, RG_BLUE_RUPEE, false, true);
if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_ANYWHERE)) {
AddFixedItemToPool(RG_TRIFORCE, 1);
} else {
ctx->PlaceItemInLocation(RC_WINCON, RG_TRIFORCE);
}
}
if (!ctx->GetOption(RSK_STARTING_KOKIRI_SWORD)) {
if (ctx->GetOption(RSK_SHUFFLE_KOKIRI_SWORD)) {
@@ -625,9 +625,6 @@ void GenerateItemPool() {
AddItemToPool(RG_MORPHA_SOUL, 2, 1, 1, 1);
AddItemToPool(RG_BONGO_BONGO_SOUL, 2, 1, 1, 1);
AddItemToPool(RG_TWINROVA_SOUL, 2, 1, 1, 1);
if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).Is(RO_BOSS_SOULS_ON_PLUS_GANON)) {
AddItemToPool(RG_GANON_SOUL, 2, 1, 1, 1);
}
}
// Gerudo Fortress
@@ -727,12 +724,9 @@ void GenerateItemPool() {
AddItemToPool(RG_SHADOW_TEMPLE_BOSS_KEY, 2, 1, 1, 1);
}
// Don't add GBK to the pool at all for Triforce Hunt or if we start with it.
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_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) {
ctx->PlaceItemInLocation(RC_TOT_LIGHT_ARROWS_CUTSCENE, RG_GANONS_CASTLE_BOSS_KEY);
if (!(ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH))) {
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Get() >= RO_GANON_BOSS_KEY_STONES) {
ctx->PlaceItemInLocation(RC_GANONS_BOSS_KEY, RG_GANONS_CASTLE_BOSS_KEY);
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_VANILLA)) {
ctx->PlaceItemInLocation(RC_GANONS_TOWER_BOSS_KEY_CHEST, RG_GANONS_CASTLE_BOSS_KEY);
} else {
@@ -740,6 +734,14 @@ void GenerateItemPool() {
}
}
if (!(ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_STARTWITH))) {
if (ctx->GetOption(RSK_GANONS_SOUL).Get() >= RO_GANONS_SOUL_STONES) {
ctx->PlaceItemInLocation(RC_GANON_SOUL, RG_GANON_SOUL);
} else {
AddItemToPool(RG_GANON_SOUL, 2, 1, 1, 1);
}
}
// Shopsanity
if (ctx->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_OFF) ||
(ctx->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_SPECIFIC_COUNT) &&
@@ -43,7 +43,7 @@ bool GenerateRandomizer(std::set<RandomizerCheck> excludedLocations, std::set<Ra
ctx->ClearItemLocations();
int ret = Playthrough::Playthrough_Init(ctx->GetSeed(), excludedLocations, enabledTricks);
if (ret < 0) {
if (ret == -1) { // Failed to generate after 5 tries
if (ret == -1) {
SPDLOG_ERROR("Failed to generate after 5 tries.");
return false;
} else {
@@ -53,10 +53,7 @@ void GenerateStartingInventory() {
AddItemToInventory(RG_SHADOW_TEMPLE_BOSS_KEY);
}
// 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).Is(RO_TRIFORCE_HUNT_WIN)) {
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH)) {
AddItemToInventory(RG_GANONS_CASTLE_BOSS_KEY);
}
@@ -15,6 +15,7 @@
#include <soh/ResourceManagerHelpers.h>
#include <cstdarg>
#include <algorithm>
extern "C" {
#include <variables.h>
@@ -24,47 +25,111 @@ extern PlayState* gPlayState;
}
void BuildTriforcePieceMessage(CustomMessage& msg) {
auto rando = OTRGlobals::Instance->gRandomizer;
uint8_t current = gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected + 1;
uint8_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) + 1;
uint8_t remaining = required - current;
float percentageCollected = (float)current / (float)required;
// if any settings are off, 0 them out here as a precaution
uint8_t bridge = rando->GetRandoSettingValue(RSK_RAINBOW_BRIDGE) == RO_BRIDGE_TRIFORCE_PIECES
? rando->GetRandoSettingValue(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT)
: 0;
uint8_t wincon = rando->GetRandoSettingValue(RSK_WINCON) == RO_WINCON_TRIFORCE_PIECES
? rando->GetRandoSettingValue(RSK_WINCON_TRIFORCE_COUNT)
: 0;
uint8_t GBK = rando->GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_TRIFORCE_PIECES
? rando->GetRandoSettingValue(RSK_GBK_TRIFORCE_COUNT)
: 0;
uint8_t soul = rando->GetRandoSettingValue(RSK_GANONS_SOUL) == RO_GANONS_SOUL_TRIFORCE_PIECES
? rando->GetRandoSettingValue(RSK_GANONS_SOUL_TRIFORCE_COUNT)
: 0;
if (percentageCollected <= 0.25) {
msg = { "You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. It's a start!",
"Ein %yTriforce-Splitter%w! Du hast&%g[[current]]%w von %c[[required]]%w gefunden. Es ist ein&Anfang!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste "
"%c[[remaining]]%w à trouver. C'est un début!" };
} else if (percentageCollected <= 0.5) {
msg = { "You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. Progress!",
"Ein %yTriforce-Splitter%w! Du hast&%g[[current]]%w von %c[[required]]%w gefunden. Es geht voran!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste "
"%c[[remaining]]%w à trouver. Ça avance!" };
} else if (percentageCollected <= 0.75) {
msg = { "You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. Over half-way&there!",
"Ein %yTriforce-Splitter%w! Du hast&schon %g[[current]]%w von %c[[required]]%w gefunden. Schon&über "
"die Hälfte!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste "
"%c[[remaining]]%w à trouver. Il en reste un&peu moins que la moitié!" };
} else if (percentageCollected < 1.0) {
msg = {
"You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. Almost done!",
"Ein %yTriforce-Splitter%w! Du hast&schon %g[[current]]%w von %c[[required]]%w gefunden. Fast&geschafft!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste %c[[remaining]]%w "
"à trouver. C'est presque&terminé!"
};
} else if (current == required) {
msg = { "You completed the %yTriforce of&Courage%w! %gGG%w!",
"Das %yTriforce des Mutes%w! Du hast&alle Splitter gefunden. %gGut gemacht%w!",
"Vous avez complété la %yTriforce&du Courage%w! %gFélicitations%w!" };
// If we reach wincon, we win!
if (current == wincon) {
msg = { "You completed the %yTriforce of Courage%w! %gGG%w!",
"Das %yTriforce des Mutes%w! Du hast alle Splitter gefunden. %gGut gemacht%w!",
"Vous avez complété la %yTriforce du Courage%w! %gFélicitations%w!" };
// otherwise prioritise the different triggers
} else if (current == bridge) {
msg = { "You made your wish to the %yTriforce%w! %rTh%ye R%gai%cnb%bow %pBr%rid%yge %gha%cs r%bai%psed%w!",
TODO_TRANSLATE, TODO_TRANSLATE };
} else if (current == GBK) {
msg = { "You completed the %yTriforce of Power%w! %rThe Key to Evil is yours%w!", TODO_TRANSLATE,
TODO_TRANSLATE };
} else if (current == soul) {
msg = { "You completed the %yTriforce of Wisdom%w! %bGanon's soul is reclaimed%w!", TODO_TRANSLATE,
TODO_TRANSLATE };
// if everything is zero, then there's no goal...
} else if (bridge + wincon + GBK + soul == 0) {
msg = { "You found a %yTriforce Piece%w! But it's %puseless%w...", TODO_TRANSLATE, TODO_TRANSLATE };
} else {
msg = { "You found a spare %yTriforce Piece%w!&You only needed %c[[required]]%w, but you have %g[[current]]%w!",
"Ein übriger %yTriforce-Splitter%w! Du&hast nun %g[[current]]%w von %c[[required]]%w nötigen gefunden.",
"Vous avez trouvé un %yFragment de&Triforce%w en plus! Vous n'aviez besoin&que de %c[[required]]%w, "
"mais vous en avez %g[[current]]%w en&tout!" };
// if nothing is complete, we need to check is we have more than we need
uint8_t highest = std::max({ current, bridge, wincon, GBK, soul });
if (highest == current) {
// RANDOTODO TODO_TRANSLATE you could maybe make this sound cleaner because InsertNumber allows for dynamic
// plurals
msg = { "You found a spare %yTriforce Piece%w! You only needed %c[[d]]%w, but you have %g[[current]]%w!",
"Ein übriger %yTriforce-Splitter%w! Du hast nun %g[[current]]%w von %c[[d]]%w nötigen gefunden.",
"Vous avez trouvé un %yFragment de Triforce%w en plus! Vous n'aviez besoin que de %c[[d]]%w, "
"mais vous en avez %g[[current]]%w en tout!" };
msg.InsertNumber(std::max({ bridge, wincon, GBK, soul }));
} else {
// find the next goal by setting everything below current (including failed conditions set to 0 before)
// to a high number, then looking for the lowest.
// if we have the exact amount, it will be caught by the first check, so no worries there
if (bridge < current) {
bridge = 255;
}
if (GBK < current) {
GBK = 255;
}
if (soul < current) {
soul = 255;
}
if (wincon < current) {
wincon = 255;
}
uint8_t next = std::min({ bridge, GBK, soul, wincon });
uint8_t remaining = next - current;
float percentageCollected = (float)current / (float)next;
if (percentageCollected <= 0.25) {
msg = { "You found a %yTriforce Piece%w! %g[[current]]%w down, %c[[d]]%w more and you [[condition]]! "
"It's a start!",
TODO_TRANSLATE, TODO_TRANSLATE };
} else if (percentageCollected <= 0.5) {
msg = { "You found a %yTriforce Piece%w! that makes %g[[current]]%w, %c[[d]]%w to go until you "
"[[condition]]! Progress!",
TODO_TRANSLATE, TODO_TRANSLATE };
} else if (percentageCollected <= 0.75) {
msg = { "You found a %yTriforce Piece%w! You have %g[[current]]%w and need %c[[d]]%w more and you "
"[[condition]]! Over half-way there!",
TODO_TRANSLATE, TODO_TRANSLATE };
} else if (percentageCollected < 1.0) {
msg = { "You found a %yTriforce Piece%w! %g[[current]]%w down, %c[[d]]%w left until you [[condition]]! "
"Almost done!",
TODO_TRANSLATE, TODO_TRANSLATE };
}
// default condition is soul
CustomMessage condition = { "%brelease Ganons Soul%w", TODO_TRANSLATE, TODO_TRANSLATE };
if (next == wincon) {
condition = { "%gWin the game%w", TODO_TRANSLATE, TODO_TRANSLATE };
} else if (next == bridge) {
condition = { "%csummon the Rainbow Bridge%w", TODO_TRANSLATE, TODO_TRANSLATE };
} else if (next == GBK) {
condition = { "%rfind the key to Ganondorf's Lair%w", TODO_TRANSLATE, TODO_TRANSLATE };
}
msg.Replace("[[condition]]", condition);
msg.InsertNumber(remaining);
}
}
msg.Replace("[[current]]", std::to_string(current));
msg.Replace("[[remaining]]", std::to_string(remaining));
msg.Replace("[[required]]", std::to_string(required));
msg.AutoFormat(ITEM_CUSTOM);
}
void BuildTriforceMessage(CustomMessage& msg) {
msg = { "You completed the %yTriforce of&Courage%w! %gGG%w!",
"Das %yTriforce des Mutes%w! Du hast&alle Splitter gefunden. %gGut gemacht%w!",
"Vous avez complété la %yTriforce&du Courage%w! %gFélicitations%w!" };
msg.Format(ITEM_CUSTOM);
}
@@ -100,12 +165,16 @@ void LoadCustomItemIcon(bool displayAsEnglish) {
RandomizerGet rgid = static_cast<RandomizerGet>(player->getItemEntry.getItemId);
customIcon = Rando::StaticData::RetrieveItem(rgid).GetCustomIcon();
iconSize = Rando::StaticData::RetrieveItem(rgid).GetCustomIconSize();
} else {
// if we're seeing an icon and we don't have a GI, assume we're in the alter text showing a triforce piece
customIcon = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetCustomIcon();
iconSize = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetCustomIconSize();
}
if (customIcon != nullptr) {
static int16_t sIconItem32XOffsets[] = { 74, 74, 74, 54 };
static int16_t sIconItem24XOffsets[] = { 72, 72, 72, 50 };
MessageContext* msgCtx = &gPlayState->msgCtx;
uint8_t language = displayAsEnglish ? LANGUAGE_ENG : gSaveContext.language;
uint8_t language = displayAsEnglish ? LANGUAGE_ENG : (Language)gSaveContext.language;
if (iconSize == ICON_SIZE_32) {
R_TEXTBOX_ICON_XPOS = R_TEXT_INIT_XPOS - sIconItem32XOffsets[language];
R_TEXTBOX_ICON_YPOS = (R_TEXTBOX_Y + 10) + 6;
@@ -150,6 +219,8 @@ void BuildItemMessage(u16* textId, bool* loadFromMessageTable) {
Rando::Traps::BuildIceTrapMessage(msg, player->getItemEntry);
} else if (player->getItemEntry.getItemId == RG_TRIFORCE_PIECE) {
BuildTriforcePieceMessage(msg);
} else if (player->getItemEntry.getItemId == RG_TRIFORCE) {
BuildTriforceMessage(msg);
} else {
BuildCustomItemMessage(player, msg);
}
@@ -255,6 +255,7 @@ std::unordered_map<RandomizerGet, std::string> itemImageMap = {
{ RG_FISHING_POLE, "ITEM_FISHING_POLE" },
{ RG_SOLD_OUT, "ITEM_SOLD_OUT" },
{ RG_TRIFORCE_PIECE, "TRIFORCE_PIECE" },
{ RG_TRIFORCE, "TRIFORCE" },
{ RG_SKELETON_KEY, "ITEM_KEY_SMALL" }
};
@@ -1207,4 +1208,6 @@ void PlandomizerWindow::InitElement() {
->LoadGuiTexture("BOSS_SOUL", gBossSoulTex, ImVec4(1, 1, 1, 1));
std::dynamic_pointer_cast<Fast::Fast3dGui>(Ship::Context::GetRawInstance()->GetWindow()->GetGui())
->LoadGuiTexture("TRIFORCE_PIECE", gTriforcePieceTex, ImVec4(1, 1, 1, 1));
std::dynamic_pointer_cast<Fast::Fast3dGui>(Ship::Context::GetRawInstance()->GetWindow()->GetGui())
->LoadGuiTexture("TRIFORCE", gTriforcePieceTex, ImVec4(1, 1, 1, 1));
}
@@ -152,6 +152,10 @@ bool Context::IsQuestOfLocationActive(RandomizerCheck rc) {
void Context::GenerateLocationPool() {
allLocations.clear();
overworldLocations.clear();
// add wincon here so it is properly logged
if (ctx->GetOption(RSK_WINCON).Get() > RO_WINCON_ANYWHERE) {
AddLocation(RC_WINCON);
}
for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) {
dungeon->locations.clear();
}
@@ -159,7 +163,9 @@ void Context::GenerateLocationPool() {
// skip RCs that shouldn't be in the pool for any reason (i.e. settings, unsupported check type, etc.)
// TODO: Exclude checks for some of the older shuffles from the pool too i.e. Frog Songs, Scrubs, etc.)
if (location.GetRandomizerCheck() == RC_UNKNOWN_CHECK ||
location.GetRandomizerCheck() == RC_TRIFORCE_COMPLETED || // already in pool
location.GetRandomizerCheck() == RC_WINCON || // already in pool
(location.GetRandomizerCheck() == RC_GANONS_BOSS_KEY && mGBKCondition == RO_CHECK_TRIGGER_NONE) ||
(location.GetRandomizerCheck() == RC_GANON_SOUL && mGanonsSoulCondition == RO_CHECK_TRIGGER_NONE) ||
(location.GetRandomizerCheck() == RC_TOT_MASTER_SWORD &&
mOptions[RSK_SHUFFLE_MASTER_SWORD].Is(RO_GENERIC_OFF)) ||
(location.GetRandomizerCheck() == RC_KAK_100_GOLD_SKULLTULA_REWARD &&
@@ -264,7 +270,7 @@ void Context::GenerateLocationPool() {
void Context::AddExcludedOptions() {
for (auto& loc : StaticData::GetLocationTable()) {
// Checks of these types don't have items, skip them.
if (loc.GetRandomizerCheck() == RC_UNKNOWN_CHECK || loc.GetRandomizerCheck() == RC_TRIFORCE_COMPLETED ||
if (loc.GetRandomizerCheck() == RC_UNKNOWN_CHECK || loc.GetRandomizerCheck() == RC_WINCON ||
loc.GetRCType() == RCTYPE_CHEST_GAME || loc.GetRCType() == RCTYPE_STATIC_HINT ||
loc.GetRCType() == RCTYPE_GOSSIP_STONE) {
continue;
@@ -533,12 +539,28 @@ OptionValue& Context::GetLocationOption(const RandomizerCheck key) {
return itemLocationTable[key].GetExcludedOption();
}
RandoOptionLACSCondition Context::LACSCondition() const {
return mLACSCondition;
RandoOptionCheckTrigger Context::GBKCondition() const {
return mGBKCondition;
}
void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) {
mLACSCondition = lacsCondition;
void Context::GBKCondition(RandoOptionCheckTrigger condition) {
mGBKCondition = condition;
}
RandoOptionCheckTrigger Context::GanonsSoulCondition() const {
return mGanonsSoulCondition;
}
void Context::GanonsSoulCondition(RandoOptionCheckTrigger condition) {
mGanonsSoulCondition = condition;
}
RandoOptionWincon Context::WinCondition() const {
return mWinCondition;
}
void Context::WinCondition(RandoOptionWincon condition) {
mWinCondition = condition;
}
std::shared_ptr<Kaleido> Context::GetKaleido() {
+13 -7
View File
@@ -100,20 +100,24 @@ class Context {
OptionValue& GetLocationOption(RandomizerCheck key);
/**
* @brief Gets the resolved Light Arrow CutScene check condition.
* @brief Gets the resolved GBK check condition.
* There is no direct option for this, it is inferred based on the value of a few other options.
*
* @return RandoOptionLACSCondition
* @return RandoOptionCheckTriggerCondition
*/
RandoOptionLACSCondition LACSCondition() const;
RandoOptionCheckTrigger GBKCondition() const;
RandoOptionCheckTrigger GanonsSoulCondition() const;
RandoOptionWincon WinCondition() const;
/**
* @brief Sets the resolved Light Arrow CutScene check condition.
* @brief Sets the resolved GBK check condition.
* There is no direct option for this, it is inferred based on the value of a few other options.
*
* @param lacsCondition
* @param condition
*/
void LACSCondition(RandoOptionLACSCondition lacsCondition);
void GBKCondition(RandoOptionCheckTrigger condition);
void GanonsSoulCondition(RandoOptionCheckTrigger condition);
void WinCondition(RandoOptionWincon condition);
GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE);
void ParseSpoiler(const char* spoilerFileName);
@@ -183,7 +187,9 @@ class Context {
std::array<ItemLocation, RC_MAX> itemLocationTable = {};
std::array<OptionValue, RSK_MAX> mOptions;
std::array<OptionValue, RT_MAX> mTrickOptions;
RandoOptionLACSCondition mLACSCondition = RO_LACS_VANILLA;
RandoOptionCheckTrigger mGBKCondition = RO_CHECK_TRIGGER_NONE;
RandoOptionCheckTrigger mGanonsSoulCondition = RO_CHECK_TRIGGER_NONE;
RandoOptionWincon mWinCondition = RO_WINCON_DEFEAT_GANON;
std::shared_ptr<EntranceShuffler> mEntranceShuffler;
std::shared_ptr<Dungeons> mDungeons;
std::shared_ptr<Logic> mLogic;
@@ -661,6 +661,12 @@ static void InitTrickNames() {
Text{ "Triforce Shard", "Éclat de Triforce", "Triforce-Fragment" }, // "Triforce Shard"
Text{ "Shiny Rock", "Caillou Brillant", "glänzender Stein" }, // "Shiny Rock"
};
trickNameTable[RG_TRIFORCE] = {
// TODO_TRANSLATE
Text{ "Cheese Triangle" },
Text{ "Triumph Fork" },
Text{ "Force Gem" },
};
trickNameTable[RG_ROCS_FEATHER] = {
Text{ "Chicken Wing", "Chicken Wing", "Chicken Wing" }, // "Chicken Wing"
Text{ "Roc's Leg", "Roc's Leg", "Roc's Leg" }, // "Roc's Leg"
+17 -4
View File
@@ -408,8 +408,21 @@ extern "C" void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry get
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
auto rando = OTRGlobals::Instance->gRandomizer;
uint8_t current = gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected;
uint8_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) + 1;
bool fullTriforce = false;
if (rando->GetRandoSettingValue(RSK_RAINBOW_BRIDGE) == RO_BRIDGE_TRIFORCE_PIECES) {
fullTriforce = rando->GetRandoSettingValue(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT) == current;
}
if (rando->GetRandoSettingValue(RSK_WINCON) == RO_WINCON_TRIFORCE_PIECES) {
fullTriforce = fullTriforce || (rando->GetRandoSettingValue(RSK_WINCON_TRIFORCE_COUNT) == current);
}
if (rando->GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_TRIFORCE_PIECES) {
fullTriforce = fullTriforce || (rando->GetRandoSettingValue(RSK_GBK_TRIFORCE_COUNT) == current);
}
if (rando->GetRandoSettingValue(RSK_GANONS_SOUL) == RO_GANONS_SOUL_TRIFORCE_PIECES) {
fullTriforce = fullTriforce || (rando->GetRandoSettingValue(RSK_GANONS_SOUL_TRIFORCE_COUNT) == current);
}
Matrix_Scale(triforcePieceScale, triforcePieceScale, triforcePieceScale, MTXMODE_APPLY);
@@ -421,7 +434,7 @@ extern "C" void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry get
// Animation. When not the completed triforce, create delay before showing the piece to bypass interpolation.
// If the completed triforce, make it grow slowly.
if (current != required) {
if (!fullTriforce) {
if (triforcePieceScale > 0.00008f && triforcePieceScale < 0.034f) {
triforcePieceScale = 0.034f;
} else if (triforcePieceScale < 0.035f) {
@@ -436,13 +449,13 @@ extern "C" void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry get
// Show piece when not currently completing the triforce. Use the scale to create a delay so interpolation doesn't
// make the triforce twitch when the size is set to a higher value.
if (current != required && triforcePieceScale > 0.035f) {
if (!fullTriforce && triforcePieceScale > 0.035f) {
// Get shard DL. Remove one before division to account for triforce piece given in the textbox
// to match up the shard from the overworld model.
Gfx* triforcePieceDL = Randomizer_GetTriforcePieceDL((current - 1) % 3);
gSPDisplayList(POLY_XLU_DISP++, triforcePieceDL);
} else if (current == required && triforcePieceScale > 0.00008f) {
} else if (fullTriforce && triforcePieceScale > 0.00008f) {
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gTriforcePieceCompletedDL);
}
+76 -24
View File
@@ -306,7 +306,7 @@ const CustomMessage Hint::GetHintMessage(MessageFormat format, size_t id) const
} else {
hintText.SetTextBoxType(TEXTBOX_TYPE_BLUE);
}
hintText += GetBridgeReqsText() + GetGanonBossKeyText() +
hintText += GetBridgeReqsText() + GetGanonBossKeyText() + GetGanonsSoulText() + GetWinconText() +
StaticData::hintTextTable[RHT_ADULT_ALTAR_TEXT_END].GetHintMessage();
} else {
hintText = GetHintText(id).GetHintMessage(chosenMessage);
@@ -570,6 +570,9 @@ CustomMessage Hint::GetBridgeReqsText() {
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS)) {
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_TOKENS_HINT].GetHintMessage();
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Get());
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TRIFORCE_PIECES)) {
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_TRIFORCE_PIECES_HINT].GetHintMessage();
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT).Get());
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_GREG)) {
return StaticData::hintTextTable[RHT_BRIDGE_GREG_HINT].GetHintMessage();
}
@@ -580,10 +583,6 @@ CustomMessage Hint::GetGanonBossKeyText() {
auto ctx = Rando::Context::GetInstance();
CustomMessage ganonBossKeyMessage;
if (ctx->GetOption(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) {
return StaticData::hintTextTable[RHT_GANON_BK_TRIFORCE_HINT].GetHintMessage();
}
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH)) {
return StaticData::hintTextTable[RHT_GANON_BK_START_WITH_HINT].GetHintMessage();
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_VANILLA)) {
@@ -596,29 +595,82 @@ CustomMessage Hint::GetGanonBossKeyText() {
return StaticData::hintTextTable[RHT_GANON_BK_OVERWORLD_HINT].GetHintMessage();
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_ANYWHERE)) {
return StaticData::hintTextTable[RHT_GANON_BK_ANYWHERE_HINT].GetHintMessage();
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) {
return StaticData::hintTextTable[RHT_GANON_BK_SKULLTULA_HINT].GetHintMessage();
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_VANILLA)) {
return StaticData::hintTextTable[RHT_LACS_VANILLA_HINT].GetHintMessage();
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_STONES_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_STONE_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_MEDALLIONS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_REWARDS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_REWARD_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_DUNGEONS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_TOKENS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_TOKEN_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STONES)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_GBK_STONES_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_GBK_STONE_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_MEDALLIONS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_GBK_MEDALLIONS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_GBK_MEDALLION_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_REWARDS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_GBK_REWARDS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_GBK_REWARD_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_DUNGEONS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_GBK_DUNGEONS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_GBK_DUNGEON_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_TOKENS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_GBK_TOKENS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_GBK_TOKEN_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_TRIFORCE_PIECES)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_GBK_TRIFORCE_PIECES_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_GBK_TRIFORCE_COUNT).Get());
}
return ganonBossKeyMessage;
}
CustomMessage Hint::GetGanonsSoulText() {
auto ctx = Rando::Context::GetInstance();
CustomMessage ganonsSoulMessage;
if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_STONES)) {
ganonsSoulMessage = StaticData::hintTextTable[RHT_GANONS_SOUL_STONES_HINT].GetHintMessage();
ganonsSoulMessage.InsertNumber(ctx->GetOption(RSK_GANONS_SOUL_STONE_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_MEDALLIONS)) {
ganonsSoulMessage = StaticData::hintTextTable[RHT_GANONS_SOUL_MEDALLIONS_HINT].GetHintMessage();
ganonsSoulMessage.InsertNumber(ctx->GetOption(RSK_GANONS_SOUL_MEDALLION_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_REWARDS)) {
ganonsSoulMessage = StaticData::hintTextTable[RHT_GANONS_SOUL_REWARDS_HINT].GetHintMessage();
ganonsSoulMessage.InsertNumber(ctx->GetOption(RSK_GANONS_SOUL_REWARD_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_DUNGEONS)) {
ganonsSoulMessage = StaticData::hintTextTable[RHT_GANONS_SOUL_DUNGEONS_HINT].GetHintMessage();
ganonsSoulMessage.InsertNumber(ctx->GetOption(RSK_GANONS_SOUL_DUNGEON_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_TOKENS)) {
ganonsSoulMessage = StaticData::hintTextTable[RHT_GANONS_SOUL_TOKENS_HINT].GetHintMessage();
ganonsSoulMessage.InsertNumber(ctx->GetOption(RSK_GANONS_SOUL_TOKEN_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_TRIFORCE_PIECES)) {
ganonsSoulMessage = StaticData::hintTextTable[RHT_GANONS_SOUL_TRIFORCE_PIECES_HINT].GetHintMessage();
ganonsSoulMessage.InsertNumber(ctx->GetOption(RSK_GANONS_SOUL_TRIFORCE_COUNT).Get());
}
return ganonsSoulMessage;
}
CustomMessage Hint::GetWinconText() {
auto ctx = Rando::Context::GetInstance();
CustomMessage winconMessage;
if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_ANYWHERE)) {
return StaticData::hintTextTable[RHT_WINCON_ANYWHERE_HINT].GetHintMessage();
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_STONES)) {
winconMessage = StaticData::hintTextTable[RHT_WINCON_STONES_HINT].GetHintMessage();
winconMessage.InsertNumber(ctx->GetOption(RSK_WINCON_STONE_COUNT).Get());
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_MEDALLIONS)) {
winconMessage = StaticData::hintTextTable[RHT_WINCON_MEDALLIONS_HINT].GetHintMessage();
winconMessage.InsertNumber(ctx->GetOption(RSK_WINCON_MEDALLION_COUNT).Get());
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_REWARDS)) {
winconMessage = StaticData::hintTextTable[RHT_WINCON_REWARDS_HINT].GetHintMessage();
winconMessage.InsertNumber(ctx->GetOption(RSK_WINCON_REWARD_COUNT).Get());
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_DUNGEONS)) {
winconMessage = StaticData::hintTextTable[RHT_WINCON_DUNGEONS_HINT].GetHintMessage();
winconMessage.InsertNumber(ctx->GetOption(RSK_WINCON_DUNGEON_COUNT).Get());
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_TOKENS)) {
winconMessage = StaticData::hintTextTable[RHT_WINCON_TOKENS_HINT].GetHintMessage();
winconMessage.InsertNumber(ctx->GetOption(RSK_WINCON_TOKEN_COUNT).Get());
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_TRIFORCE_PIECES)) {
winconMessage = StaticData::hintTextTable[RHT_WINCON_TRIFORCE_PIECES_HINT].GetHintMessage();
winconMessage.InsertNumber(ctx->GetOption(RSK_WINCON_TRIFORCE_COUNT).Get());
}
return winconMessage;
}
void Hint::AddHintedLocation(RandomizerCheck location) {
locations.push_back(location);
}
+2
View File
@@ -34,6 +34,8 @@ class Hint {
const CustomMessage GetAreaName(uint8_t slot) const;
static CustomMessage GetBridgeReqsText();
static CustomMessage GetGanonBossKeyText();
static CustomMessage GetGanonsSoulText();
static CustomMessage GetWinconText();
void AddHintedLocation(RandomizerCheck location);
std::vector<RandomizerCheck> GetHintedLocations() const;
void SetHintType(HintType type);
+181 -124
View File
@@ -123,42 +123,97 @@ RandomizerCheck GetRandomizerCheckFromSceneFlag(int16_t sceneNum, int16_t flagTy
return RC_UNKNOWN_CHECK;
}
bool MeetsLACSRequirements() {
switch (RAND_GET_OPTION(RSK_GANONS_BOSS_KEY).Get()) {
case RO_GANON_BOSS_KEY_LACS_STONES:
if ((CheckStoneCount() + CheckLACSRewardCount()) >= RAND_GET_OPTION(RSK_LACS_STONE_COUNT).Get()) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
if ((CheckMedallionCount() + CheckLACSRewardCount()) >= RAND_GET_OPTION(RSK_LACS_MEDALLION_COUNT).Get()) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_REWARDS:
if ((CheckMedallionCount() + CheckStoneCount() + CheckLACSRewardCount()) >=
RAND_GET_OPTION(RSK_LACS_REWARD_COUNT).Get()) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
if ((CheckDungeonCount() + CheckLACSRewardCount()) >= RAND_GET_OPTION(RSK_LACS_DUNGEON_COUNT).Get()) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_TOKENS:
if (gSaveContext.inventory.gsTokens >= RAND_GET_OPTION(RSK_LACS_TOKEN_COUNT).Get()) {
return true;
}
break;
default:
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) {
return true;
bool MeetsGBKRequirements() {
u8 bonusRewardCount = 0;
switch (RAND_GET_OPTION(RSK_GBK_OPTIONS).Get()) {
case RO_CHECK_TRIGGER_WILDCARD_REWARD:
case RO_CHECK_TRIGGER_GREG_REWARD:
if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) {
bonusRewardCount = 1;
}
break;
}
return false;
switch (RAND_GET_OPTION(RSK_GANONS_BOSS_KEY).Get()) {
case RO_GANON_BOSS_KEY_STONES:
return (CheckStoneCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_GBK_STONE_COUNT).Get();
case RO_GANON_BOSS_KEY_MEDALLIONS:
return (CheckMedallionCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_GBK_MEDALLION_COUNT).Get();
case RO_GANON_BOSS_KEY_REWARDS:
return (CheckMedallionCount() + CheckStoneCount() + bonusRewardCount) >=
RAND_GET_OPTION(RSK_GBK_REWARD_COUNT).Get();
case RO_GANON_BOSS_KEY_DUNGEONS:
return (CheckDungeonCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_GBK_DUNGEON_COUNT).Get();
case RO_GANON_BOSS_KEY_TOKENS:
return gSaveContext.inventory.gsTokens >= RAND_GET_OPTION(RSK_GBK_TOKEN_COUNT).Get();
case RO_GANON_BOSS_KEY_TRIFORCE_PIECES:
return gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected >=
RAND_GET_OPTION(RSK_GBK_TRIFORCE_COUNT).Get();
default:
return false;
}
}
bool MeetsGanonsSoulRequirements() {
u8 bonusRewardCount = 0;
switch (RAND_GET_OPTION(RSK_GANONS_SOUL_OPTIONS).Get()) {
case RO_CHECK_TRIGGER_WILDCARD_REWARD:
case RO_CHECK_TRIGGER_GREG_REWARD:
if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) {
bonusRewardCount = 1;
}
break;
}
switch (RAND_GET_OPTION(RSK_GANONS_SOUL).Get()) {
case RO_GANONS_SOUL_STONES:
return (CheckStoneCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_GANONS_SOUL_STONE_COUNT).Get();
case RO_GANONS_SOUL_MEDALLIONS:
return (CheckMedallionCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_GANONS_SOUL_MEDALLION_COUNT).Get();
case RO_GANONS_SOUL_REWARDS:
return (CheckMedallionCount() + CheckStoneCount() + bonusRewardCount) >=
RAND_GET_OPTION(RSK_GANONS_SOUL_REWARD_COUNT).Get();
case RO_GANONS_SOUL_DUNGEONS:
return (CheckDungeonCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_GANONS_SOUL_DUNGEON_COUNT).Get();
case RO_GANONS_SOUL_TOKENS:
return gSaveContext.inventory.gsTokens >= RAND_GET_OPTION(RSK_GANONS_SOUL_TOKEN_COUNT).Get();
case RO_GANONS_SOUL_TRIFORCE_PIECES:
return gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected >=
RAND_GET_OPTION(RSK_GANONS_SOUL_TRIFORCE_COUNT).Get();
default:
return false;
}
}
bool MeetsWinconRequirements() {
u8 bonusRewardCount = 0;
switch (RAND_GET_OPTION(RSK_WINCON_OPTIONS).Get()) {
case RO_CHECK_TRIGGER_WILDCARD_REWARD:
case RO_CHECK_TRIGGER_GREG_REWARD:
if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) {
bonusRewardCount = 1;
}
break;
}
switch (RAND_GET_OPTION(RSK_WINCON).Get()) {
case RO_WINCON_STONES:
return (CheckStoneCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_WINCON_STONE_COUNT).Get();
case RO_WINCON_MEDALLIONS:
return (CheckMedallionCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_WINCON_MEDALLION_COUNT).Get();
case RO_WINCON_REWARDS:
return (CheckMedallionCount() + CheckStoneCount() + bonusRewardCount) >=
RAND_GET_OPTION(RSK_WINCON_REWARD_COUNT).Get();
case RO_WINCON_DUNGEONS:
return (CheckDungeonCount() + bonusRewardCount) >= RAND_GET_OPTION(RSK_WINCON_DUNGEON_COUNT).Get();
case RO_WINCON_TOKENS:
return gSaveContext.inventory.gsTokens >= RAND_GET_OPTION(RSK_WINCON_TOKEN_COUNT).Get();
case RO_WINCON_TRIFORCE_PIECES:
return gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected >=
RAND_GET_OPTION(RSK_WINCON_TRIFORCE_COUNT).Get();
default:
return false;
}
}
bool CompletedAllTrials() {
@@ -172,59 +227,33 @@ bool CompletedAllTrials() {
bool MeetsRainbowBridgeRequirements() {
switch (RAND_GET_OPTION(RSK_RAINBOW_BRIDGE).Get()) {
case RO_BRIDGE_VANILLA: {
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) &&
(INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT)) {
return true;
}
break;
}
case RO_BRIDGE_STONES: {
if ((CheckStoneCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_STONE_COUNT).Get()) {
return true;
}
break;
}
case RO_BRIDGE_MEDALLIONS: {
if ((CheckMedallionCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).Get()) {
return true;
}
break;
}
case RO_BRIDGE_DUNGEON_REWARDS: {
if ((CheckMedallionCount() + CheckStoneCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Get()) {
return true;
}
break;
}
case RO_BRIDGE_DUNGEONS: {
if ((CheckDungeonCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Get()) {
return true;
}
break;
}
case RO_BRIDGE_TOKENS: {
if (gSaveContext.inventory.gsTokens >= RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Get()) {
return true;
}
break;
}
case RO_BRIDGE_GREG: {
if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) {
return true;
}
break;
}
case RO_BRIDGE_ALWAYS_OPEN: {
case RO_BRIDGE_VANILLA:
return CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) &&
INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT;
case RO_BRIDGE_STONES:
return (CheckStoneCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_STONE_COUNT).Get();
case RO_BRIDGE_MEDALLIONS:
return (CheckMedallionCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).Get();
case RO_BRIDGE_DUNGEON_REWARDS:
return (CheckMedallionCount() + CheckStoneCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Get();
case RO_BRIDGE_DUNGEONS:
return (CheckDungeonCount() + CheckBridgeRewardCount()) >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Get();
case RO_BRIDGE_TOKENS:
return gSaveContext.inventory.gsTokens >= RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Get();
case RO_BRIDGE_TRIFORCE_PIECES:
return gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected >=
RAND_GET_OPTION(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT).Get();
case RO_BRIDGE_GREG:
return Flags_GetRandomizerInf(RAND_INF_GREG_FOUND);
case RO_BRIDGE_ALWAYS_OPEN:
return true;
}
default:
return false;
}
return false;
}
// Todo Move this to randomizer context, clear it out on save load etc
@@ -232,9 +261,26 @@ static std::queue<RandomizerCheck> randomizerQueuedChecks;
static RandomizerCheck randomizerQueuedCheck = RC_UNKNOWN_CHECK;
static GetItemEntry randomizerQueuedItemEntry = GET_ITEM_NONE;
void CheckTriggers() {
if (!(gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER] & 1) && MeetsGBKRequirements()) {
SPDLOG_INFO("Queuing RC: RC_GANONS_BOSS_KEY");
randomizerQueuedChecks.push(RC_GANONS_BOSS_KEY);
}
if (!Flags_GetRandomizerInf(RAND_INF_GANON_SOUL) && MeetsGanonsSoulRequirements()) {
SPDLOG_INFO("Queuing RC: RC_GANON_SOUL");
randomizerQueuedChecks.push(RC_GANON_SOUL);
}
if (MeetsWinconRequirements()) {
SPDLOG_INFO("Queuing RC: RC_WINCON");
randomizerQueuedChecks.push(RC_WINCON);
}
}
void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) {
// Consume adult trade items
if (RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) && flagType == FLAG_RANDOMIZER_INF) {
if (RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE).Get() && flagType == FLAG_RANDOMIZER_INF) {
switch (flag) {
case RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD:
Flags_UnsetRandomizerInf(RAND_INF_ADULT_TRADES_HAS_SWORD_BROKEN);
@@ -550,6 +596,8 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) {
Flags_SetEventChkInf(EVENTCHKINF_NABOORU_CAPTURED_BY_TWINROVA);
}
}
CheckTriggers();
}
void EnExItem_DrawRandomizedItem(EnExItem* enExItem, PlayState* play) {
@@ -1098,14 +1146,9 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
case VB_BE_ELIGIBLE_FOR_DARUNIAS_JOY_REWARD:
*should = !Flags_GetRandomizerInf(RAND_INF_DARUNIAS_JOY);
break;
case VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS:
*should = LINK_IS_ADULT && (gEntranceTable[gSaveContext.entranceIndex].scene == SCENE_TEMPLE_OF_TIME) &&
!Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS) &&
MeetsLACSRequirements();
break;
case VB_BE_ELIGIBLE_FOR_NOCTURNE_OF_SHADOW:
*should = !Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL) && LINK_IS_ADULT &&
gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_KAKARIKO_VILLAGE &&
gEntranceTable[gSaveContext.entranceIndex].scene == SCENE_KAKARIKO_VILLAGE &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER) && gSaveContext.cutsceneIndex < 0xFFF0;
break;
@@ -1821,6 +1864,18 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
}
break;
}
case VB_SLAY_GANON:
if (RAND_GET_OPTION(RSK_WINCON).IsNot(RO_WINCON_DEFEAT_GANON)) {
*should = false;
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_GANONS_TOWER);
randomizerQueuedChecks.push(RC_GANON);
CheckTriggers();
gPlayState->nextEntranceIndex = ENTR_OUTSIDE_GANONS_CASTLE_1_2;
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE_SLOW;
}
break;
case VB_DRAW_AMMO_COUNT: {
s16 item = *va_arg(args, s16*);
// don't draw ammo count if you have the infinite upgrade
@@ -1871,8 +1926,8 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
}
case VB_SKIP_SCARECROWS_SONG: {
int ocarinaButtonCount = 0;
for (int i = VB_HAVE_OCARINA_NOTE_A4; i <= VB_HAVE_OCARINA_NOTE_F4; i++) {
if (GameInteractor_Should((GIVanillaBehavior)i, true)) {
for (int i = RAND_INF_HAS_OCARINA_A; i <= RAND_INF_HAS_OCARINA_C_RIGHT; i++) {
if (Flags_GetRandomizerInf((RandomizerInf)i)) {
ocarinaButtonCount++;
}
}
@@ -2022,6 +2077,8 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
break;
}
case VB_FREEZE_ON_SKULL_TOKEN:
CheckTriggers();
[[fallthrough]];
case VB_TRADE_TIMER_ODD_MUSHROOM:
case VB_TRADE_TIMER_FROG:
case VB_GIVE_ITEM_FROM_TARGET_IN_WOODS:
@@ -2124,6 +2181,9 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) {
Entrance_OverrideSpawnScene(sceneNum, gPlayState->curSpawn);
}
// Check here in case queued item got lost by poorly timed save & quit
CheckTriggers();
// LACS & Prelude checks
static uint32_t updateHook = 0;
@@ -2146,7 +2206,9 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) {
}
// We're always in rando here, and rando always overrides this should so we can just pass false
if (GameInteractor_Should(VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS, false)) {
if (LINK_IS_ADULT && (gEntranceTable[gSaveContext.entranceIndex].scene == SCENE_TEMPLE_OF_TIME) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) &&
!Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS)) {
Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
}
@@ -2424,9 +2486,9 @@ void RandomizerOnActorInitHandler(void* actorRef) {
Actor_Kill(actor);
}
RandomizerInf currentBossSoulRandInf = RAND_INF_MAX;
if (RAND_GET_OPTION(RSK_SHUFFLE_BOSS_SOULS)) {
// Boss souls require an additional item (represented by a RAND_INF) to spawn a boss in a particular lair
RandomizerInf currentBossSoulRandInf = RAND_INF_MAX;
switch (gPlayState->sceneNum) {
case SCENE_DEKU_TREE_BOSS:
currentBossSoulRandInf = RAND_INF_GOHMA_SOUL;
@@ -2452,30 +2514,33 @@ void RandomizerOnActorInitHandler(void* actorRef) {
case SCENE_SPIRIT_TEMPLE_BOSS:
currentBossSoulRandInf = RAND_INF_TWINROVA_SOUL;
break;
case SCENE_GANONDORF_BOSS:
case SCENE_GANON_BOSS:
if (RAND_GET_OPTION(RSK_SHUFFLE_BOSS_SOULS).Is(RO_BOSS_SOULS_ON_PLUS_GANON)) {
currentBossSoulRandInf = RAND_INF_GANON_SOUL;
}
break;
default:
break;
}
}
// Deletes all actors in the boss category if the soul isn't found.
// Some actors, like Dark Link, Arwings, and Zora's Sapphire...?, are in this category despite not being actual
// bosses, so ignore any "boss" if `currentBossSoulRandInf` doesn't change from RAND_INF_MAX. Iron Knuckle
// (Nabooru) in Twinrova's room is a special exception, so exclude knuckles too.
if (currentBossSoulRandInf != RAND_INF_MAX) {
if (!Flags_GetRandomizerInf(currentBossSoulRandInf) && actor->category == ACTORCAT_BOSS &&
actor->id != ACTOR_EN_IK) {
Actor_Delete(&gPlayState->actorCtx, actor, gPlayState);
}
// Special case for Phantom Ganon's horse (and fake), as they're considered "background actors",
// but still control the boss fight flow.
if (!Flags_GetRandomizerInf(RAND_INF_PHANTOM_GANON_SOUL) && actor->id == ACTOR_EN_FHG) {
Actor_Delete(&gPlayState->actorCtx, actor, gPlayState);
}
if (RAND_GET_OPTION(RSK_GANONS_SOUL).IsNot(RO_GANONS_SOUL_STARTWITH)) {
switch (gPlayState->sceneNum) {
case SCENE_GANONDORF_BOSS:
case SCENE_GANON_BOSS:
currentBossSoulRandInf = RAND_INF_GANON_SOUL;
break;
}
}
// Deletes all actors in the boss category if the soul isn't found.
// Some actors, like Dark Link, Arwings, and Zora's Sapphire...?, are in this category despite not being actual
// bosses, so ignore any "boss" if `currentBossSoulRandInf` doesn't change from RAND_INF_MAX. Iron Knuckle
// (Nabooru) in Twinrova's room is a special exception, so exclude knuckles too.
if (currentBossSoulRandInf != RAND_INF_MAX) {
if (!Flags_GetRandomizerInf(currentBossSoulRandInf) && actor->category == ACTORCAT_BOSS &&
actor->id != ACTOR_EN_IK) {
Actor_Delete(&gPlayState->actorCtx, actor, gPlayState);
}
// Special case for Phantom Ganon's horse (and fake), as they're considered "background actors",
// but still control the boss fight flow.
if (!Flags_GetRandomizerInf(RAND_INF_PHANTOM_GANON_SOUL) && actor->id == ACTOR_EN_FHG) {
Actor_Delete(&gPlayState->actorCtx, actor, gPlayState);
}
}
@@ -2744,15 +2809,7 @@ void RandomizerOnPlayerUpdateHandler() {
}
}
// Triforce Hunt needs the check if the player isn't being teleported to the credits scene.
if (!GameInteractor::IsGameplayPaused() && Flags_GetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY) &&
gPlayState->transitionTrigger != TRANS_TRIGGER_START &&
(1 << 0 & gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER]) == 0) {
GiveItemEntryWithoutActor(gPlayState,
*Rando::StaticData::GetItemTable().at(RG_GANONS_CASTLE_BOSS_KEY).GetGIEntry());
}
if (!GameInteractor::IsGameplayPaused() && RAND_GET_OPTION(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) {
if (!GameInteractor::IsGameplayPaused()) {
// Warp to credits once item queue has drained to avoid losing queued items
if (GameInteractor::State::TriforceHuntCreditsWarpActive && randomizerQueuedChecks.empty() &&
randomizerQueuedCheck == RC_UNKNOWN_CHECK) {
@@ -2775,7 +2832,7 @@ void RandomizerOnPlayerUpdateHandler() {
// to ensure it's done at that point in time specifically.
if (GameInteractor::State::TriforceHuntPieceGiven) {
triforcePieceScale = 0.0f;
GameInteractor::State::TriforceHuntPieceGiven = 0;
GameInteractor::State::TriforceHuntPieceGiven = false;
}
}
}
+2 -1
View File
@@ -433,7 +433,8 @@ bool Item::IsBottleItem() const {
bool Item::IsMajorItem() const {
const auto ctx = Context::GetInstance();
if (type == ITEMTYPE_TOKEN) {
return ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS) || ctx->LACSCondition() == RO_LACS_TOKENS;
return ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS) ||
ctx->GBKCondition() == RO_CHECK_TRIGGER_TOKENS;
}
if (type == ITEMTYPE_DROP || type == ITEMTYPE_EVENT || type == ITEMTYPE_SHOP || type == ITEMTYPE_MAP ||
@@ -422,7 +422,7 @@ void Rando::StaticData::InitItemTable() {
itemTable[RG_DEKU_NUT_BAG] = Item(RG_DEKU_NUT_BAG, Text{ "Deku Nut Bag", "Sac de Noix Mojo", "Deku-Nuß-Tasche" }, ITEMTYPE_ITEM, GI_NUT_UPGRADE_30, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_NONE, RG_DEKU_NUT_BAG, OBJECT_GI_NUTS, GID_NUTS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"a ", "un ", "eine "}).CustomIcon(gItemIconDekuNutTex);
itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_EVENT, RG_TRIFORCE, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_MAJOR, {"the ", "la ", "die "});
itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_ITEM, RG_TRIFORCE, true, LOGIC_NONE, RHT_NONE, RG_TRIFORCE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "la ", "die "}).CustomIcon(gTriforcePieceTex);;
itemTable[RG_HINT] = Item(RG_HINT, Text{ "Hint", "Indice", "Hinweis" }, ITEMTYPE_EVENT, RG_HINT, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_LESSER);
// Individual stages of progressive items (only here for GetItemEntry purposes, not for use in seed gen)
itemTable[RG_HOOKSHOT] = Item(RG_HOOKSHOT, Text{ "Hookshot", "Grappin", "Fanghaken" }, ITEMTYPE_ITEM, GI_HOOKSHOT, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_HOOKSHOT, ITEM_HOOKSHOT, OBJECT_GI_HOOKSHOT, GID_HOOKSHOT, 0x36, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, {"the ", "le ", "den "});
@@ -453,7 +453,7 @@ void Rando::StaticData::InitItemTable() {
itemTable[RG_DEKU_STICK_CAPACITY_30] = Item(RG_DEKU_STICK_CAPACITY_30, Text{ "Deku Stick Capacity (30)", "Capacité de Bâtons Mojo (30)", "Deku-Stab-Kapazität (30)" }, ITEMTYPE_ITEM, GI_STICK_UPGRADE_30, true, LOGIC_PROGRESSIVE_STICK_BAG, RHT_DEKU_STICK_CAPACITY_30, ITEM_STICK_UPGRADE_30, OBJECT_GI_STICK, GID_STICK, 0x91, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE).CustomIcon(gItemIconDekuStickTex);
itemTable[RG_MAGIC_SINGLE] = Item(RG_MAGIC_SINGLE, Text{ "Magic Meter", "Jauge de Magie", "Magisches Maß" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_MAGIC_SINGLE, RG_MAGIC_SINGLE, OBJECT_GI_MAGICPOT, GID_MAGIC_SMALL, 0xE4, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "la ", "das "}).CustomIcon(gQuestIconMagicJarSmallTex, ICON_SIZE_24);
itemTable[RG_MAGIC_DOUBLE] = Item(RG_MAGIC_DOUBLE, Text{ "Enhanced Magic Meter", "Jauge de Magie améliorée", "Verbessertes Magisches Maß" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_MAGIC_DOUBLE, RG_MAGIC_DOUBLE, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, {"the ", "la ", "das "}).CustomIcon(gQuestIconMagicJarBigTex, ICON_SIZE_24);
itemTable[RG_TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{ "Triforce Piece", "Morceau de Triforce", "Triforce-Fragment" }, ITEMTYPE_ITEM, 0xDF, true, LOGIC_TRIFORCE_PIECES, RHT_TRIFORCE_PIECE, RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"a ", "un ", "ein "}).CustomIcon(gTriforcePieceTex);
itemTable[RG_TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{ "Triforce Piece", "Morceau de Triforce", "Triforce-Fragment" }, ITEMTYPE_ITEM, 0xDF, true, LOGIC_NONE, RHT_TRIFORCE_PIECE, RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"a ", "un ", "ein "}).CustomIcon(gTriforcePieceTex);
itemTable[RG_ROCS_FEATHER] = Item(RG_ROCS_FEATHER, Text{ "Roc's Feather", "Plume de Roc", "Grefenfeider" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_ROCS_FEATHER, RHT_ROCS_FEATHER, RG_ROCS_FEATHER, OBJECT_GI_BOMB_2, GID_STONE_OF_AGONY, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"a ", "la ", "ein "}).CustomIcon(gRocsFeatherTex);
itemTable[RG_ROCS_FEATHER].SetCustomDrawFunc(Randomizer_DrawRocsFeather);
@@ -30,7 +30,7 @@ void RegionTable_Init_TempleOfTime() {
areaTable[RR_TEMPLE_OF_TIME] = Region("Temple of Time", SCENE_TEMPLE_OF_TIME, {}, {
//Locations
LOCATION(RC_TOT_LIGHT_ARROWS_CUTSCENE, logic->IsAdult && logic->CanTriggerLACS()),
LOCATION(RC_TOT_LIGHT_ARROWS_CUTSCENE, logic->IsAdult && logic->HasItem(RG_SHADOW_MEDALLION) && logic->HasItem(RG_SPIRIT_MEDALLION)),
LOCATION(RC_ALTAR_HINT_CHILD, logic->IsChild),
LOCATION(RC_ALTAR_HINT_ADULT, logic->IsAdult),
LOCATION(RC_TOT_SHEIK_HINT, logic->IsAdult && logic->HasItem(RG_SPEAK_HYLIAN)),
@@ -45,7 +45,7 @@ void RegionTable_Init_TempleOfTime() {
//Locations
LOCATION(RC_TOT_MASTER_SWORD, logic->IsAdult),
LOCATION(RC_GIFT_FROM_RAURU, logic->IsAdult),
LOCATION(RC_SHEIK_AT_TEMPLE, logic->HasItem(RG_FOREST_MEDALLION) && logic->IsAdult),
LOCATION(RC_SHEIK_AT_TEMPLE, logic->IsAdult && logic->HasItem(RG_FOREST_MEDALLION)),
}, {
//Exits
ENTRANCE(RR_TEMPLE_OF_TIME, true),
@@ -18,7 +18,9 @@ void RegionTable_Init_Root() {
}, {
//Locations
LOCATION(RC_LINKS_POCKET, true),
LOCATION(RC_TRIFORCE_COMPLETED, logic->GetSaveContext()->ship.quest.data.randomizer.triforcePiecesCollected >= ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1;),
LOCATION(RC_GANONS_BOSS_KEY, logic->CanTriggerGBK()),
LOCATION(RC_GANON_SOUL, logic->CanTriggerGanonsSoul()),
LOCATION(RC_WINCON, logic->CanTriggerWincon()),
LOCATION(RC_SARIA_SONG_HINT, logic->CanUse(RG_SARIAS_SONG)),
LOCATION(RC_SONG_FROM_IMPA, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)),
LOCATION(RC_HC_MALON_EGG, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)),
@@ -1001,7 +1001,10 @@ void Rando::StaticData::InitLocationTable() {
locationTable[RC_BIGGORON_HINT] = Location::OtherHint(RC_BIGGORON_HINT, RCQUEST_BOTH, ACTOR_EN_GO2, SCENE_DEATH_MOUNTAIN_TRAIL, "Biggoron Hint");
locationTable[RC_MASK_SHOP_HINT] = Location::OtherHint(RC_MASK_SHOP_HINT, RCQUEST_BOTH, ACTOR_ID_MAX, SCENE_HAPPY_MASK_SHOP, "Mask Shop Hint");
locationTable[RC_TRIFORCE_COMPLETED] = Location::Base(RC_TRIFORCE_COMPLETED, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_MARKET, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Completed Triforce", "Completed Triforce", RHT_NONE, RG_NONE);
// Conditional
locationTable[RC_WINCON] = Location::Base(RC_WINCON, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_MARKET, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Win Condition", "Win Condition", RHT_NONE, RG_NONE);
locationTable[RC_GANONS_BOSS_KEY] = Location::Base(RC_GANONS_BOSS_KEY, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_GANONS_CASTLE, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Ganon's Boss Key", "Ganon's Boss Key", RHT_NONE, RG_NONE);
locationTable[RC_GANON_SOUL] = Location::Base(RC_GANON_SOUL, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_GANONS_CASTLE, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Ganon's Soul", "Ganon's Soul", RHT_NONE, RG_NONE);
// clang-format on
// Init locationNameToEnum
+88 -17
View File
@@ -199,7 +199,7 @@ bool Logic::HasItem(RandomizerGet itemName) {
case RG_TWINROVA_SOUL:
return !ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS) || CheckRandoInf(StaticData::RandoGetToRandInf.at(itemName));
case RG_GANON_SOUL:
return !ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).Is(RO_BOSS_SOULS_ON_PLUS_GANON) ||
return ctx->GetOption(RSK_GANONS_SOUL).Is(RO_GANONS_SOUL_STARTWITH) ||
CheckRandoInf(StaticData::RandoGetToRandInf.at(itemName));
// Overworld Keys
case RG_GUARD_HOUSE_KEY:
@@ -1704,25 +1704,92 @@ bool Logic::CanBuildRainbowBridge() {
ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Get()) ||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS) &&
GetGSCount() >= ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Get()) ||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TRIFORCE_PIECES) &&
GetTriforcePieceCount() >= ctx->GetOption(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT).Get()) ||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_GREG) && HasItem(RG_GREG_RUPEE));
}
bool Logic::CanTriggerLACS() {
return (ctx->LACSCondition() == RO_LACS_VANILLA && HasItem(RG_SHADOW_MEDALLION) && HasItem(RG_SPIRIT_MEDALLION)) ||
(ctx->LACSCondition() == RO_LACS_STONES &&
StoneCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >=
ctx->GetOption(RSK_LACS_STONE_COUNT).Get()) ||
(ctx->LACSCondition() == RO_LACS_MEDALLIONS &&
MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >=
ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Get()) ||
(ctx->LACSCondition() == RO_LACS_REWARDS &&
StoneCount() + MedallionCount() +
(HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >=
ctx->GetOption(RSK_LACS_REWARD_COUNT).Get()) ||
(ctx->LACSCondition() == RO_LACS_DUNGEONS &&
DungeonCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >=
ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Get()) ||
(ctx->LACSCondition() == RO_LACS_TOKENS && GetGSCount() >= ctx->GetOption(RSK_LACS_TOKEN_COUNT).Get());
bool Logic::CanTriggerGBK() {
switch (ctx->GBKCondition()) {
case RO_CHECK_TRIGGER_STONES:
return StoneCount() +
(HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_GBK_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GBK_STONE_COUNT).Get();
case RO_CHECK_TRIGGER_MEDALLIONS:
return MedallionCount() +
(HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_GBK_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GBK_MEDALLION_COUNT).Get();
case RO_CHECK_TRIGGER_REWARDS:
return StoneCount() + MedallionCount() +
(HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_GBK_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GBK_REWARD_COUNT).Get();
case RO_CHECK_TRIGGER_DUNGEONS:
return DungeonCount() +
(HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_GBK_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GBK_DUNGEON_COUNT).Get();
case RO_CHECK_TRIGGER_TOKENS:
return GetGSCount() >= ctx->GetOption(RSK_GBK_TOKEN_COUNT).Get();
case RO_CHECK_TRIGGER_TRIFORCE_PIECES:
return GetTriforcePieceCount() >= ctx->GetOption(RSK_GBK_TRIFORCE_COUNT).Get();
default:
return false;
}
}
bool Logic::CanTriggerGanonsSoul() {
switch (ctx->GanonsSoulCondition()) {
case RO_CHECK_TRIGGER_STONES:
return StoneCount() + (HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_GANONS_SOUL_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GANONS_SOUL_STONE_COUNT).Get();
case RO_CHECK_TRIGGER_MEDALLIONS:
return MedallionCount() + (HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_GANONS_SOUL_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GANONS_SOUL_MEDALLION_COUNT).Get();
case RO_CHECK_TRIGGER_REWARDS:
return StoneCount() + MedallionCount() +
(HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_GANONS_SOUL_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GANONS_SOUL_REWARD_COUNT).Get();
case RO_CHECK_TRIGGER_DUNGEONS:
return DungeonCount() + (HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_GANONS_SOUL_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_GANONS_SOUL_DUNGEON_COUNT).Get();
case RO_CHECK_TRIGGER_TOKENS:
return GetGSCount() >= ctx->GetOption(RSK_GANONS_SOUL_TOKEN_COUNT).Get();
case RO_CHECK_TRIGGER_TRIFORCE_PIECES:
return GetTriforcePieceCount() >= ctx->GetOption(RSK_GANONS_SOUL_TRIFORCE_COUNT).Get();
default:
return false;
}
}
bool Logic::CanTriggerWincon() {
switch (ctx->WinCondition()) {
case RO_WINCON_STONES:
return StoneCount() + (HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_WINCON_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_WINCON_STONE_COUNT).Get();
case RO_WINCON_MEDALLIONS:
return MedallionCount() + (HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_WINCON_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_WINCON_MEDALLION_COUNT).Get();
case RO_WINCON_REWARDS:
return StoneCount() + MedallionCount() +
(HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_WINCON_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_WINCON_REWARD_COUNT).Get();
case RO_WINCON_DUNGEONS:
return DungeonCount() + (HasItem(RG_GREG_RUPEE) &&
ctx->GetOption(RSK_WINCON_OPTIONS).Is(RO_CHECK_TRIGGER_GREG_REWARD)) >=
ctx->GetOption(RSK_WINCON_DUNGEON_COUNT).Get();
case RO_WINCON_TOKENS:
return GetGSCount() >= ctx->GetOption(RSK_WINCON_TOKEN_COUNT).Get();
case RO_WINCON_TRIFORCE_PIECES:
return GetTriforcePieceCount() >= ctx->GetOption(RSK_WINCON_TRIFORCE_COUNT).Get();
default:
return false;
}
}
bool Logic::SmallKeys(SceneID scene, uint8_t requiredAmount) {
@@ -2667,6 +2734,10 @@ uint8_t Logic::GetGSCount() {
return static_cast<uint8_t>(mSaveContext->inventory.gsTokens);
}
uint8_t Logic::GetTriforcePieceCount() {
return mSaveContext->ship.quest.data.randomizer.triforcePiecesCollected;
}
uint8_t Logic::GetAmmo(uint32_t item) {
return mSaveContext->inventory.ammo[gItemSlots[item]];
}
+4 -1
View File
@@ -114,7 +114,9 @@ class Logic {
bool CanShield();
bool CanUseProjectile();
bool CanBuildRainbowBridge();
bool CanTriggerLACS();
bool CanTriggerGBK();
bool CanTriggerGanonsSoul();
bool CanTriggerWincon();
bool IsFireLoopLocked();
bool ReachScarecrow();
bool ReachDistantScarecrow();
@@ -143,6 +145,7 @@ class Logic {
void SetRandoInf(uint32_t flag, bool state);
bool CheckEventChkInf(int32_t flag);
uint8_t GetGSCount();
uint8_t GetTriforcePieceCount();
void SetEventChkInf(int32_t flag, bool state);
uint8_t GetAmmo(uint32_t item);
void SetAmmo(uint32_t item, uint8_t count);
@@ -124,16 +124,13 @@ void Settings::CreateOptionDescriptions() {
"here will be guaranteed to be Vanilla. If Set Number is higher than the amount of dungeons "
"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"
"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.";
mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED] = "The amount of Triforce pieces required to win the game.";
"The amount of Triforce pieces that will be placed in the world. Set to 0 to disable Triforce Hunt.\n"
"\n"
"Triforce Pieces can be used as a requirement for the Rainbow Bridge, Ganon's Boss Key, Ganon's Soul, or the "
"win condition. Keep in mind seed generation can fail if more pieces are placed than there are junk items in "
"the item pool.";
mOptionDescriptions[RSK_WINCON_TRIFORCE_COUNT] = "The amount of Triforce pieces required to win the game.";
mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION] =
"Any dungeon - Triforce pieces can only appear inside of any dungeon.\n"
"\n"
@@ -657,19 +654,15 @@ void Settings::CreateOptionDescriptions() {
"\n"
"Anywhere - Ganon's Boss Key Key can appear anywhere in the world.\n"
"\n"
"LACS - These settings put the boss key on the Light Arrow Cutscene location, from Zelda in Temple of Time as "
"adult, with differing requirements:\n"
"- Vanilla: Obtain the Shadow Medallion and Spirit Medallion\n"
"Trigger - These settings put the boss key on a trigger, "
"granting key once requirements met:\n"
"- Stones: Obtain the specified amount of Spiritual Stones.\n"
"- Medallions: Obtain the specified amount of medallions.\n"
"- Dungeon rewards: Obtain the specified total sum of Spiritual Stones or medallions.\n"
"- Dungeons: Complete the specified amount of dungeons. Dungeons are considered complete after stepping in to "
"the blue warp after the boss.\n"
"- Tokens: Obtain the specified amount of Skulltula tokens.\n"
"\n"
"100 GS Reward - Ganon's Boss Key will be awarded by the cursed rich man after you collect 100 Gold Skulltula "
"Tokens.";
mOptionDescriptions[RSK_LACS_OPTIONS] =
"- Tokens: Obtain the specified amount of Skulltula tokens.";
mOptionDescriptions[RSK_GBK_OPTIONS] =
"Standard Rewards - Greg does not change logic, Greg does not help obtain GBK, max "
"number of rewards on slider does not change.\n"
"\n"
@@ -679,6 +672,16 @@ void Settings::CreateOptionDescriptions() {
"\n"
"Greg as Wildcard - Greg does not change logic, Greg helps obtain GBK, max number of "
"rewards on slider does not change.";
mOptionDescriptions[RSK_GANONS_SOUL_OPTIONS] =
"Standard Rewards - Greg does not change logic, Greg does not help obtain Ganon's Soul, max "
"number of rewards on slider does not change.\n"
"\n"
"Greg as Reward - Greg does change logic (can be part of expected path for obtaining "
"Ganon's Soul), Greg helps obtain Ganon's Soul, max number of rewards on slider increases by 1 to "
"account for Greg. \n"
"\n"
"Greg as Wildcard - Greg does not change logic, Greg helps obtain Ganon's Soul, max number of "
"rewards on slider does not change.";
mOptionDescriptions[RSK_BIG_POE_COUNT] = "The Poe collector will give a reward for turning in this many Big Poes.";
mOptionDescriptions[RSK_SKIP_CHILD_STEALTH] =
"The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards.";
@@ -866,7 +869,6 @@ void Settings::CreateOptionDescriptions() {
"Shuffle 10 bean souls which must be found to spawn corresponding soil / plant.";
mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS] =
"Shuffles 8 boss souls (one for each blue warp dungeon). A boss will not appear until you collect its "
"respective soul."
"\n\"On + Ganon\" will also hide Ganon and Ganondorf behind a boss soul.";
"respective soul.";
}
} // namespace Rando
+23 -14
View File
@@ -261,7 +261,6 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGe
switch (randoGet) {
case RG_NONE:
case RG_TRIFORCE:
case RG_HINT:
case RG_MAX:
case RG_SOLD_OUT:
@@ -668,6 +667,7 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGe
case RG_TREASURE_GAME_GREEN_RUPEE:
case RG_BUY_HEART:
case RG_TRIFORCE_PIECE:
case RG_TRIFORCE:
default:
return CAN_OBTAIN;
}
@@ -862,6 +862,23 @@ u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
return Rando::Context::GetInstance()->GetOption(randoSettingKey).Get();
}
u8 Randomizer::GetTriforcePiecesRequired() {
u8 required = 0;
if (GetRandoSettingValue(RSK_RAINBOW_BRIDGE) == RO_BRIDGE_TRIFORCE_PIECES) {
required = std::max(required, GetRandoSettingValue(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT));
}
if (GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_TRIFORCE_PIECES) {
required = std::max(required, GetRandoSettingValue(RSK_GBK_TRIFORCE_COUNT));
}
if (GetRandoSettingValue(RSK_GANONS_SOUL) == RO_GANONS_SOUL_TRIFORCE_PIECES) {
required = std::max(required, GetRandoSettingValue(RSK_GANONS_SOUL_TRIFORCE_COUNT));
}
if (GetRandoSettingValue(RSK_WINCON) == RO_WINCON_TRIFORCE_PIECES) {
required = std::max(required, GetRandoSettingValue(RSK_WINCON_TRIFORCE_COUNT));
}
return required;
}
GetItemEntry Randomizer::GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId,
bool checkObtainability) {
return Rando::Context::GetInstance()->GetFinalGIEntry(randomizerCheck, checkObtainability);
@@ -1291,22 +1308,14 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
Rupees_ChangeBy(999);
}
break;
case RG_TRIFORCE:
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
break;
case RG_TRIFORCE_PIECE:
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected++;
GameInteractor_SetTriforceHuntPieceGiven(true);
// 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)) {
Flags_SetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY);
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) ==
RO_TRIFORCE_HUNT_WIN) {
// Save and warp are deferred until item queue drains
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
}
}
// Reward/win triggers (Ganon's Boss Key, Ganon's Soul, win condition) are evaluated by
// CheckTriggers() on item receive, so Triforce Piece thresholds are handled there.
break;
case RG_PROGRESSIVE_BOMBCHU_BAG:
OTRGlobals::Instance->gRandoContext->HandleGetBombchuBag();
@@ -41,6 +41,7 @@ class Randomizer {
bool SpoilerFileExists(const char* spoilerFileName);
bool IsTrialRequired(s32 trialFlag);
u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey);
u8 GetTriforcePiecesRequired();
RandomizerCheck GetCheckFromRandomizerInf(RandomizerInf randomizerInf);
RandomizerInf GetRandomizerInfFromCheck(RandomizerCheck rc);
Rando::Location* GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams);
@@ -177,7 +177,6 @@ RANDO_ENUM_ITEM(LOGIC_OCARINA_C_UP_BUTTON)
RANDO_ENUM_ITEM(LOGIC_OCARINA_C_DOWN_BUTTON)
RANDO_ENUM_ITEM(LOGIC_OCARINA_C_LEFT_BUTTON)
RANDO_ENUM_ITEM(LOGIC_OCARINA_C_RIGHT_BUTTON)
RANDO_ENUM_ITEM(LOGIC_TRIFORCE_PIECES)
RANDO_ENUM_ITEM(LOGIC_ROCS_FEATHER)
RANDO_ENUM_ITEM(LOGIC_CAN_BORROW_MASKS)
RANDO_ENUM_ITEM(LOGIC_BORROW_SKULL_MASK)
@@ -14,6 +14,8 @@
RANDO_ENUM_BEGIN(RandomizerCheck)
RANDO_ENUM_ITEM(RC_UNKNOWN_CHECK)
RANDO_ENUM_ITEM(RC_LINKS_POCKET)
RANDO_ENUM_ITEM(RC_GANONS_BOSS_KEY)
RANDO_ENUM_ITEM(RC_GANON_SOUL)
RANDO_ENUM_ITEM(RC_QUEEN_GOHMA)
RANDO_ENUM_ITEM(RC_KING_DODONGO)
RANDO_ENUM_ITEM(RC_BARINADE)
@@ -2063,7 +2065,7 @@ RANDO_ENUM_ITEM(RC_COLOSSUS_DEKU_SCRUB_GROTTO_BEEHIVE)
RANDO_ENUM_ITEM(RC_GANONDORF_HINT)
RANDO_ENUM_ITEM(RC_SHEIK_HINT_GC)
RANDO_ENUM_ITEM(RC_SHEIK_HINT_MQ_GC)
RANDO_ENUM_ITEM(RC_TRIFORCE_COMPLETED)
RANDO_ENUM_ITEM(RC_WINCON)
RANDO_ENUM_ITEM(RC_DAMPE_HINT)
RANDO_ENUM_ITEM(RC_GREG_HINT)
RANDO_ENUM_ITEM(RC_SARIA_SONG_HINT)
@@ -1223,6 +1223,7 @@ RANDO_ENUM_ITEM(RHT_BRIDGE_MEDALLIONS_HINT)
RANDO_ENUM_ITEM(RHT_BRIDGE_REWARDS_HINT)
RANDO_ENUM_ITEM(RHT_BRIDGE_DUNGEONS_HINT)
RANDO_ENUM_ITEM(RHT_BRIDGE_TOKENS_HINT)
RANDO_ENUM_ITEM(RHT_BRIDGE_TRIFORCE_PIECES_HINT)
RANDO_ENUM_ITEM(RHT_BRIDGE_GREG_HINT)
// Ganon Boss Key
RANDO_ENUM_ITEM(RHT_GANON_BK_START_WITH_HINT)
@@ -1231,15 +1232,28 @@ RANDO_ENUM_ITEM(RHT_GANON_BK_OWN_DUNGEON_HINT)
RANDO_ENUM_ITEM(RHT_GANON_BK_OVERWORLD_HINT)
RANDO_ENUM_ITEM(RHT_GANON_BK_ANY_DUNGEON_HINT)
RANDO_ENUM_ITEM(RHT_GANON_BK_ANYWHERE_HINT)
RANDO_ENUM_ITEM(RHT_GANON_BK_TRIFORCE_HINT)
RANDO_ENUM_ITEM(RHT_GANON_BK_SKULLTULA_HINT)
// LACS
RANDO_ENUM_ITEM(RHT_LACS_VANILLA_HINT)
RANDO_ENUM_ITEM(RHT_LACS_MEDALLIONS_HINT)
RANDO_ENUM_ITEM(RHT_LACS_STONES_HINT)
RANDO_ENUM_ITEM(RHT_LACS_REWARDS_HINT)
RANDO_ENUM_ITEM(RHT_LACS_DUNGEONS_HINT)
RANDO_ENUM_ITEM(RHT_LACS_TOKENS_HINT)
// GBK
RANDO_ENUM_ITEM(RHT_GBK_MEDALLIONS_HINT)
RANDO_ENUM_ITEM(RHT_GBK_STONES_HINT)
RANDO_ENUM_ITEM(RHT_GBK_REWARDS_HINT)
RANDO_ENUM_ITEM(RHT_GBK_DUNGEONS_HINT)
RANDO_ENUM_ITEM(RHT_GBK_TOKENS_HINT)
RANDO_ENUM_ITEM(RHT_GBK_TRIFORCE_PIECES_HINT)
// Ganon's Soul
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_MEDALLIONS_HINT)
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_STONES_HINT)
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_REWARDS_HINT)
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_DUNGEONS_HINT)
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_TOKENS_HINT)
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_TRIFORCE_PIECES_HINT)
// Wincon
RANDO_ENUM_ITEM(RHT_WINCON_ANYWHERE_HINT)
RANDO_ENUM_ITEM(RHT_WINCON_STONES_HINT)
RANDO_ENUM_ITEM(RHT_WINCON_MEDALLIONS_HINT)
RANDO_ENUM_ITEM(RHT_WINCON_REWARDS_HINT)
RANDO_ENUM_ITEM(RHT_WINCON_DUNGEONS_HINT)
RANDO_ENUM_ITEM(RHT_WINCON_TOKENS_HINT)
RANDO_ENUM_ITEM(RHT_WINCON_TRIFORCE_PIECES_HINT)
// Trials
RANDO_ENUM_ITEM(RHT_SIX_TRIALS)
RANDO_ENUM_ITEM(RHT_ZERO_TRIALS)
@@ -15,6 +15,7 @@ RANDO_ENUM_BEGIN(RandomizerInf)
RANDO_ENUM_ITEM(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE)
RANDO_ENUM_ITEM(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE)
RANDO_ENUM_ITEM(RAND_INF_DUNGEONS_DONE_GANONS_TOWER)
RANDO_ENUM_ITEM(RAND_INF_COWS_MILKED_KF_LINKS_HOUSE_COW)
RANDO_ENUM_ITEM(RAND_INF_COWS_MILKED_HF_COW_GROTTO_COW)
@@ -195,7 +196,6 @@ RANDO_ENUM_ITEM(RAND_INF_ADULT_FISH_15)
RANDO_ENUM_ITEM(RAND_INF_ADULT_LOACH)
RANDO_ENUM_ITEM(RAND_INF_10_BIG_POES)
RANDO_ENUM_ITEM(RAND_INF_GRANT_GANONS_BOSSKEY)
RANDO_ENUM_ITEM(RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL)
RANDO_ENUM_ITEM(RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL)
@@ -92,6 +92,7 @@ RANDO_ENUM_ITEM(RO_BRIDGE_MEDALLIONS)
RANDO_ENUM_ITEM(RO_BRIDGE_DUNGEON_REWARDS)
RANDO_ENUM_ITEM(RO_BRIDGE_DUNGEONS)
RANDO_ENUM_ITEM(RO_BRIDGE_TOKENS)
RANDO_ENUM_ITEM(RO_BRIDGE_TRIFORCE_PIECES)
RANDO_ENUM_ITEM(RO_BRIDGE_GREG)
RANDO_ENUM_END(RandoOptionRainbowBridge)
@@ -155,7 +156,6 @@ RANDO_ENUM_END(RandoOptionBombchuBag)
RANDO_ENUM_BEGIN(RandoOptionBossSouls)
RANDO_ENUM_ITEM(RO_BOSS_SOULS_OFF)
RANDO_ENUM_ITEM(RO_BOSS_SOULS_ON)
RANDO_ENUM_ITEM(RO_BOSS_SOULS_ON_PLUS_GANON)
RANDO_ENUM_END(RandoOptionBossSouls)
// Fishsanity settings (off, loach only, pond only, grottos only, both)
@@ -209,8 +209,7 @@ RANDO_ENUM_ITEM(RO_KEYRING_FOR_DUNGEON_RANDOM)
RANDO_ENUM_ITEM(RO_KEYRING_FOR_DUNGEON_ON)
RANDO_ENUM_END(RandoOptionKeyringForDungeon)
// Ganon's Boss Key Settings (vanilla, own dungeon, start with,
// overworld, anywhere, 100 GS reward)
// Ganon's Boss Key Settings
RANDO_ENUM_BEGIN(RandoOptionGanonsBossKey)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_VANILLA)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_OWN_DUNGEON)
@@ -218,30 +217,57 @@ RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_STARTWITH)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_ANY_DUNGEON)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_OVERWORLD)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_ANYWHERE)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_LACS_VANILLA)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_LACS_STONES)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_LACS_REWARDS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_LACS_DUNGEONS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_LACS_TOKENS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_KAK_TOKENS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_STONES)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_MEDALLIONS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_REWARDS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_DUNGEONS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_TOKENS)
RANDO_ENUM_ITEM(RO_GANON_BOSS_KEY_TRIFORCE_PIECES)
RANDO_ENUM_END(RandoOptionGanonsBossKey)
RANDO_ENUM_BEGIN(RandoOptionLACSCondition)
RANDO_ENUM_ITEM(RO_LACS_VANILLA)
RANDO_ENUM_ITEM(RO_LACS_STONES)
RANDO_ENUM_ITEM(RO_LACS_MEDALLIONS)
RANDO_ENUM_ITEM(RO_LACS_REWARDS)
RANDO_ENUM_ITEM(RO_LACS_DUNGEONS)
RANDO_ENUM_ITEM(RO_LACS_TOKENS)
RANDO_ENUM_END(RandoOptionLACSCondition)
// Ganon's Soul Settings
RANDO_ENUM_BEGIN(RandoOptionGanonsSoul)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_STARTWITH)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_ANY_DUNGEON)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_OVERWORLD)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_ANYWHERE)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_STONES)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_MEDALLIONS)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_REWARDS)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_DUNGEONS)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_TOKENS)
RANDO_ENUM_ITEM(RO_GANONS_SOUL_TRIFORCE_PIECES)
RANDO_ENUM_END(RandoOptionGanonsSoul)
// LACS Reward Options settings (Standard rewards, Greg as reward, Greg as wildcard)
RANDO_ENUM_BEGIN(RandoOptionLACSRewards)
RANDO_ENUM_ITEM(RO_LACS_STANDARD_REWARD)
RANDO_ENUM_ITEM(RO_LACS_GREG_REWARD)
RANDO_ENUM_ITEM(RO_LACS_WILDCARD_REWARD)
RANDO_ENUM_END(RandoOptionLACSRewards)
// Wincon Triggers
RANDO_ENUM_BEGIN(RandoOptionWincon)
RANDO_ENUM_ITEM(RO_WINCON_DEFEAT_GANON)
RANDO_ENUM_ITEM(RO_WINCON_ANYWHERE)
RANDO_ENUM_ITEM(RO_WINCON_STONES)
RANDO_ENUM_ITEM(RO_WINCON_MEDALLIONS)
RANDO_ENUM_ITEM(RO_WINCON_REWARDS)
RANDO_ENUM_ITEM(RO_WINCON_DUNGEONS)
RANDO_ENUM_ITEM(RO_WINCON_TOKENS)
RANDO_ENUM_ITEM(RO_WINCON_TRIFORCE_PIECES)
RANDO_ENUM_END(RandoOptionWincon)
// Reward Triggers
RANDO_ENUM_BEGIN(RandoOptionCheckTrigger)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_NONE)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_STONES)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_MEDALLIONS)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_REWARDS)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_DUNGEONS)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_TOKENS)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_TRIFORCE_PIECES)
RANDO_ENUM_END(RandoOptionCheckTrigger)
// Reward Options settings (Standard rewards, Greg as reward, Greg as wildcard)
RANDO_ENUM_BEGIN(RandoOptionCheckTriggerRewards)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_STANDARD_REWARD)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_GREG_REWARD)
RANDO_ENUM_ITEM(RO_CHECK_TRIGGER_WILDCARD_REWARD)
RANDO_ENUM_END(RandoOptionCheckTriggerRewards)
// Ganon's Trials
RANDO_ENUM_BEGIN(RandoOptionGanonsTrials)
@@ -470,13 +496,6 @@ RANDO_ENUM_ITEM(RO_MQ_DUNGEONS_RANDOM_NUMBER)
RANDO_ENUM_ITEM(RO_MQ_DUNGEONS_SELECTION)
RANDO_ENUM_END(RandoOptionMQDungeons)
// Triforce Hunt settings (off, win, Ganon's Boss Key)
RANDO_ENUM_BEGIN(RandoOptionTriforceHunt)
RANDO_ENUM_ITEM(RO_TRIFORCE_HUNT_OFF)
RANDO_ENUM_ITEM(RO_TRIFORCE_HUNT_WIN)
RANDO_ENUM_ITEM(RO_TRIFORCE_HUNT_GBK)
RANDO_ENUM_END(RandoOptionTriforceHunt)
// Trifoce Hunt location
RANDO_ENUM_BEGIN(RandoOptionTriforceHuntLocation)
RANDO_ENUM_ITEM(RO_TRIFORCE_HUNT_LOCATION_ANY_DUNGEON)
@@ -29,6 +29,7 @@ RANDO_ENUM_ITEM(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT)
RANDO_ENUM_ITEM(RSK_RAINBOW_BRIDGE_REWARD_COUNT)
RANDO_ENUM_ITEM(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT)
RANDO_ENUM_ITEM(RSK_RAINBOW_BRIDGE_TOKEN_COUNT)
RANDO_ENUM_ITEM(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT)
RANDO_ENUM_ITEM(RSK_BRIDGE_OPTIONS)
RANDO_ENUM_ITEM(RSK_GANONS_TRIALS)
RANDO_ENUM_ITEM(RSK_TRIAL_COUNT)
@@ -139,6 +140,7 @@ RANDO_ENUM_ITEM(RSK_KEYSANITY)
RANDO_ENUM_ITEM(RSK_GERUDO_KEYS)
RANDO_ENUM_ITEM(RSK_BOSS_KEYSANITY)
RANDO_ENUM_ITEM(RSK_GANONS_BOSS_KEY)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL)
RANDO_ENUM_ITEM(RSK_SKIP_CHILD_STEALTH)
RANDO_ENUM_ITEM(RSK_SKIP_CHILD_ZELDA)
RANDO_ENUM_ITEM(RSK_STARTING_STICKS)
@@ -221,12 +223,20 @@ RANDO_ENUM_ITEM(RSK_MQ_BOTTOM_OF_THE_WELL)
RANDO_ENUM_ITEM(RSK_MQ_ICE_CAVERN)
RANDO_ENUM_ITEM(RSK_MQ_GTG)
RANDO_ENUM_ITEM(RSK_MQ_GANONS_CASTLE)
RANDO_ENUM_ITEM(RSK_LACS_STONE_COUNT)
RANDO_ENUM_ITEM(RSK_LACS_MEDALLION_COUNT)
RANDO_ENUM_ITEM(RSK_LACS_REWARD_COUNT)
RANDO_ENUM_ITEM(RSK_LACS_DUNGEON_COUNT)
RANDO_ENUM_ITEM(RSK_LACS_TOKEN_COUNT)
RANDO_ENUM_ITEM(RSK_LACS_OPTIONS)
RANDO_ENUM_ITEM(RSK_GBK_STONE_COUNT)
RANDO_ENUM_ITEM(RSK_GBK_MEDALLION_COUNT)
RANDO_ENUM_ITEM(RSK_GBK_REWARD_COUNT)
RANDO_ENUM_ITEM(RSK_GBK_DUNGEON_COUNT)
RANDO_ENUM_ITEM(RSK_GBK_TOKEN_COUNT)
RANDO_ENUM_ITEM(RSK_GBK_TRIFORCE_COUNT)
RANDO_ENUM_ITEM(RSK_GBK_OPTIONS)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL_STONE_COUNT)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL_MEDALLION_COUNT)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL_REWARD_COUNT)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL_DUNGEON_COUNT)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL_TOKEN_COUNT)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL_TRIFORCE_COUNT)
RANDO_ENUM_ITEM(RSK_GANONS_SOUL_OPTIONS)
RANDO_ENUM_ITEM(RSK_KEYRINGS)
RANDO_ENUM_ITEM(RSK_KEYRINGS_RANDOM_COUNT)
RANDO_ENUM_ITEM(RSK_KEYRINGS_GERUDO_FORTRESS)
@@ -262,9 +272,15 @@ RANDO_ENUM_ITEM(RSK_ALL_LOCATIONS_REACHABLE)
RANDO_ENUM_ITEM(RSK_SHUFFLE_BOSS_ENTRANCES)
RANDO_ENUM_ITEM(RSK_SHUFFLE_GANONS_TOWER_ENTRANCE)
RANDO_ENUM_ITEM(RSK_SHUFFLE_100_GS_REWARD)
RANDO_ENUM_ITEM(RSK_TRIFORCE_HUNT)
RANDO_ENUM_ITEM(RSK_TRIFORCE_HUNT_PIECES_TOTAL)
RANDO_ENUM_ITEM(RSK_TRIFORCE_HUNT_PIECES_REQUIRED)
RANDO_ENUM_ITEM(RSK_WINCON)
RANDO_ENUM_ITEM(RSK_WINCON_STONE_COUNT)
RANDO_ENUM_ITEM(RSK_WINCON_MEDALLION_COUNT)
RANDO_ENUM_ITEM(RSK_WINCON_REWARD_COUNT)
RANDO_ENUM_ITEM(RSK_WINCON_DUNGEON_COUNT)
RANDO_ENUM_ITEM(RSK_WINCON_TOKEN_COUNT)
RANDO_ENUM_ITEM(RSK_WINCON_TRIFORCE_COUNT)
RANDO_ENUM_ITEM(RSK_WINCON_OPTIONS)
RANDO_ENUM_ITEM(RSK_TRIFORCE_HUNT_PIECES_LOCATION)
RANDO_ENUM_ITEM(RSK_SHUFFLE_BEAN_SOULS)
RANDO_ENUM_ITEM(RSK_SHUFFLE_BOSS_SOULS)
@@ -239,24 +239,33 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() {
RO_DUNGEON_ITEM_LOC_VANILLA) &&
(location.GetRCType() != RCTYPE_GANON_BOSS_KEY ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_VANILLA ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), 0)) &&
(location.GetRandomizerCheck() != RC_TOT_LIGHT_ARROWS_CUTSCENE ||
RO_GANON_BOSS_KEY_VANILLA) && // vanilla ganon boss key
(location.GetRandomizerCheck() != RC_GANONS_BOSS_KEY ||
(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_LACS_DUNGEONS &&
RO_GANON_BOSS_KEY_DUNGEONS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_LACS_MEDALLIONS &&
RO_GANON_BOSS_KEY_MEDALLIONS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_LACS_REWARDS &&
RO_GANON_BOSS_KEY_REWARDS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_LACS_STONES &&
RO_GANON_BOSS_KEY_STONES &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_LACS_TOKENS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_LACS_VANILLA)) && // LACS ganon boss key
(location.GetRandomizerCheck() != RC_KAK_100_GOLD_SKULLTULA_REWARD ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_KAK_TOKENS) && // 100 skull reward ganon boss key
RO_GANON_BOSS_KEY_TOKENS) &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) !=
RO_GANON_BOSS_KEY_TRIFORCE_PIECES) && // ganon boss key condition
(location.GetRandomizerCheck() != RC_GANON_SOUL ||
(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), RO_GANONS_SOUL_STARTWITH) !=
RO_GANONS_SOUL_DUNGEONS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), RO_GANONS_SOUL_STARTWITH) !=
RO_GANONS_SOUL_MEDALLIONS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), RO_GANONS_SOUL_STARTWITH) !=
RO_GANONS_SOUL_REWARDS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), RO_GANONS_SOUL_STARTWITH) !=
RO_GANONS_SOUL_STONES &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), RO_GANONS_SOUL_STARTWITH) !=
RO_GANONS_SOUL_TOKENS) &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), RO_GANONS_SOUL_STARTWITH) !=
RO_GANONS_SOUL_TRIFORCE_PIECES) && // ganon's soul condition
(location.GetRCType() != RCTYPE_GF_KEY && location.GetRandomizerCheck() != RC_TH_FREED_CARPENTERS ||
(CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) ==
RO_GF_CARPENTERS_FREE &&
@@ -1633,23 +1633,23 @@ void LoadSettings() {
}
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY)) {
case RO_GANON_BOSS_KEY_LACS_STONES:
Rando::Context::GetInstance()->LACSCondition(RO_LACS_STONES);
case RO_GANON_BOSS_KEY_STONES:
Rando::Context::GetInstance()->GBKCondition(RO_CHECK_TRIGGER_STONES);
break;
case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
Rando::Context::GetInstance()->LACSCondition(RO_LACS_MEDALLIONS);
case RO_GANON_BOSS_KEY_MEDALLIONS:
Rando::Context::GetInstance()->GBKCondition(RO_CHECK_TRIGGER_MEDALLIONS);
break;
case RO_GANON_BOSS_KEY_LACS_REWARDS:
Rando::Context::GetInstance()->LACSCondition(RO_LACS_REWARDS);
case RO_GANON_BOSS_KEY_REWARDS:
Rando::Context::GetInstance()->GBKCondition(RO_CHECK_TRIGGER_REWARDS);
break;
case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
Rando::Context::GetInstance()->LACSCondition(RO_LACS_DUNGEONS);
case RO_GANON_BOSS_KEY_DUNGEONS:
Rando::Context::GetInstance()->GBKCondition(RO_CHECK_TRIGGER_DUNGEONS);
break;
case RO_GANON_BOSS_KEY_LACS_TOKENS:
Rando::Context::GetInstance()->LACSCondition(RO_LACS_TOKENS);
case RO_GANON_BOSS_KEY_TOKENS:
Rando::Context::GetInstance()->GBKCondition(RO_CHECK_TRIGGER_TOKENS);
break;
default:
Rando::Context::GetInstance()->LACSCondition(RO_LACS_VANILLA);
Rando::Context::GetInstance()->GBKCondition(RO_CHECK_TRIGGER_NONE);
break;
}
}
@@ -1672,7 +1672,7 @@ bool IsCheckShuffled(RandomizerCheck rc) {
(showShops &&
OTRGlobals::Instance->gRandomizer->IdentifyShopItem(loc->GetScene(), loc->GetActorParams() + 1)
.enGirlAShopItem == 50)) &&
(rc != RC_TRIFORCE_COMPLETED) && (rc != RC_GANON) &&
(rc != RC_WINCON) && (rc != RC_GANON) &&
(loc->GetRCType() != RCTYPE_SCRUB || showScrubs ||
(showMajorScrubs && (rc == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || // The 3 scrubs that are always randomized
rc == RC_HF_DEKU_SCRUB_GROTTO || rc == RC_LW_DEKU_SCRUB_GROTTO_FRONT))) &&
@@ -756,15 +756,17 @@ 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) != RO_TRIFORCE_HUNT_OFF) &&
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_TOTAL) > 0) &&
IsValidSaveFile()) {
std::string currentString = "";
std::string requiredString = "";
std::string maxString = "";
uint8_t piecesRequired =
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) + 1);
uint8_t piecesTotal =
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_TOTAL) + 1);
uint8_t piecesTotal = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_TOTAL);
uint8_t piecesRequired = OTRGlobals::Instance->gRandomizer->GetTriforcePiecesRequired();
// If no trigger uses Triforce Pieces they're just filler; gauge progress against the whole pool.
if (piecesRequired == 0) {
piecesRequired = piecesTotal;
}
ImU32 currentColor = gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected >= piecesRequired
? IM_COL_GREEN
: IM_COL_WHITE;
@@ -827,11 +829,15 @@ void DrawQuest(ItemTrackerItem item) {
};
bool HasBossSoul(RandomizerInf bossSoul) {
uint8_t soulSetting = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS);
bool isSoulRandomized = IS_RANDO && (soulSetting == RO_BOSS_SOULS_ON_PLUS_GANON ||
(soulSetting == RO_BOSS_SOULS_ON && bossSoul != RAND_INF_GANON_SOUL));
return isSoulRandomized ? Flags_GetRandomizerInf(bossSoul) : true;
if (!IS_RANDO) {
return false;
} else if (bossSoul == RAND_INF_GANON_SOUL) {
return OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_SOUL) == RO_GANONS_SOUL_STARTWITH ||
Flags_GetRandomizerInf(RAND_INF_GANON_SOUL);
} else {
return OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS) &&
Flags_GetRandomizerInf(bossSoul);
}
}
void DrawItem(ItemTrackerItem item) {
@@ -885,8 +891,8 @@ void DrawItem(ItemTrackerItem item) {
break;
case RG_TRIFORCE_PIECE:
actualItemId = item.id;
hasItem = IS_RANDO && (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) !=
RO_TRIFORCE_HUNT_OFF);
hasItem = IS_RANDO &&
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_TOTAL) > 0);
itemName = "Triforce Piece";
break;
case ITEM_NAYRUS_LOVE:
+290 -148
View File
@@ -196,7 +196,7 @@ void Settings::CreateOptions() {
mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable();
}
});
OPT_U8(RSK_RAINBOW_BRIDGE, "Rainbow Bridge", {"Vanilla", "Always open", "Stones", "Medallions", "Dungeon rewards", "Dungeons", "Tokens", "Greg"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RainbowBridge"), mOptionDescriptions[RSK_RAINBOW_BRIDGE], WIDGET_CVAR_COMBOBOX, RO_BRIDGE_VANILLA, false, nullptr, IMFLAG_NONE);
OPT_U8(RSK_RAINBOW_BRIDGE, "Rainbow Bridge", {"Vanilla", "Always open", "Stones", "Medallions", "Dungeon rewards", "Dungeons", "Tokens", "Triforce Pieces", "Greg"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RainbowBridge"), mOptionDescriptions[RSK_RAINBOW_BRIDGE], WIDGET_CVAR_COMBOBOX, RO_BRIDGE_VANILLA, false, nullptr, IMFLAG_NONE);
OPT_CALLBACK(RSK_RAINBOW_BRIDGE, {
mOptions[RSK_BRIDGE_OPTIONS].Hide();
mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].Hide();
@@ -204,37 +204,38 @@ void Settings::CreateOptions() {
mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].Hide();
mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].Hide();
mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT].Hide();
mOptions[RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT].Hide();
switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("RainbowBridge"), RO_BRIDGE_VANILLA)) {
case RO_BRIDGE_STONES:
// Show Bridge Options and Stone Count slider
mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_BRIDGE_OPTIONS].Unhide();
mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].Unhide();
break;
case RO_BRIDGE_MEDALLIONS:
// Show Bridge Options and Medallion Count Slider
mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_BRIDGE_OPTIONS].Unhide();
mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].Unhide();
break;
case RO_BRIDGE_DUNGEON_REWARDS:
// Show Bridge Options and Dungeon Reward Count Slider
mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_BRIDGE_OPTIONS].Unhide();
mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].Unhide();
break;
case RO_BRIDGE_DUNGEONS:
// Show Bridge Options and Dungeon Count Slider
mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_BRIDGE_OPTIONS].Unhide();
mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].Unhide();
break;
case RO_BRIDGE_TOKENS:
// Show token count slider (not bridge options)
mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_BRIDGE_OPTIONS].Hide();
mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT].Unhide();
break;
case RO_BRIDGE_TRIFORCE_PIECES:
mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
mOptions[RSK_BRIDGE_OPTIONS].Hide();
mOptions[RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT].Unhide();
break;
default:
break;
}
@@ -244,35 +245,20 @@ void Settings::CreateOptions() {
OPT_U8(RSK_RAINBOW_BRIDGE_REWARD_COUNT, "Bridge Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true);
OPT_U8(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT, "Bridge Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true);
OPT_U8(RSK_RAINBOW_BRIDGE_TOKEN_COUNT, "Bridge Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT, "Bridge Triforce Piece Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforcePieceCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_BRIDGE_OPTIONS, "Bridge Reward Options", {"Standard Rewards", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), mOptionDescriptions[RSK_BRIDGE_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_BRIDGE_STANDARD_REWARD, false, nullptr, IMFLAG_NONE);
OPT_CALLBACK(RSK_BRIDGE_OPTIONS, {
const uint8_t bridgeOpt = CVarGetInteger(CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), RO_BRIDGE_STANDARD_REWARD);
if (bridgeOpt == RO_BRIDGE_GREG_REWARD) {
if (mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].GetOptionCount() == 4) {
mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 4));
}
if (mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].GetOptionCount() == 7) {
mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7));
}
if (mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].GetOptionCount() == 10) {
mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 10));
}
if (mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].GetOptionCount() == 9) {
mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9));
}
mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 4));
mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7));
mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 10));
mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9));
} else {
if (mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].GetOptionCount() == 5) {
mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 3));
}
if (mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].GetOptionCount() == 8) {
mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6));
}
if (mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].GetOptionCount() == 11) {
mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 9));
}
if (mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].GetOptionCount() == 10) {
mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8));
}
mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 3));
mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6));
mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 9));
mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8));
}
});
OPT_U8(RSK_GANONS_TRIALS, "Ganon's Trials", {"Skip", "Set Number", "Random Number"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonTrial"), mOptionDescriptions[RSK_GANONS_TRIALS], WIDGET_CVAR_COMBOBOX, RO_GANONS_TRIALS_SET_NUMBER);
@@ -431,32 +417,29 @@ 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_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 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_TRIFORCE_HUNT_PIECES_LOCATION].Hide();
mOptions[RSK_GANONS_BOSS_KEY].Enable();
} else {
mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Unhide();
mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Unhide();
mOptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION].Unhide();
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.");
}
});
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], WIDGET_CVAR_SLIDER_INT, 29, false, nullptr, IMFLAG_NONE);
// Triforce Hunt: the total piece count is the on/off control. Zero disables the hunt entirely; any
// positive value adds that many Triforce Pieces to the pool and unlocks the pieces-location option.
OPT_U8(RSK_TRIFORCE_HUNT_PIECES_TOTAL, "Triforce Hunt Total Pieces", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL], WIDGET_CVAR_SLIDER_INT, 0, false, nullptr, IMFLAG_NONE);
OPT_CALLBACK(RSK_TRIFORCE_HUNT_PIECES_TOTAL, {
// Update triforce pieces required to be capped at the current value for pieces total.
const uint8_t triforceTotal = CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), 30);
if (mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].GetOptionCount() != triforceTotal + 1) {
mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].ChangeOptions(NumOpts(1, triforceTotal + 1));
const uint8_t triforceTotal = CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), 0);
if (triforceTotal == 0) {
mOptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION].Hide();
} else {
mOptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION].Unhide();
}
if (mOptions[RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT].GetOptionCount() != triforceTotal + 1) {
mOptions[RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT].ChangeOptions(NumOpts(0, triforceTotal));
}
if (mOptions[RSK_GBK_TRIFORCE_COUNT].GetOptionCount() != triforceTotal + 1) {
mOptions[RSK_GBK_TRIFORCE_COUNT].ChangeOptions(NumOpts(0, triforceTotal));
}
if (mOptions[RSK_GANONS_SOUL_TRIFORCE_COUNT].GetOptionCount() != triforceTotal + 1) {
mOptions[RSK_GANONS_SOUL_TRIFORCE_COUNT].ChangeOptions(NumOpts(0, triforceTotal));
}
if (mOptions[RSK_WINCON_TRIFORCE_COUNT].GetOptionCount() != triforceTotal + 1) {
mOptions[RSK_WINCON_TRIFORCE_COUNT].ChangeOptions(NumOpts(0, triforceTotal));
}
});
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], WIDGET_CVAR_SLIDER_INT, 19);
OPT_U8(RSK_TRIFORCE_HUNT_PIECES_LOCATION, "Triforce Hunt Pieces Location", {"Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntPiecesLocation"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION], WIDGET_CVAR_COMBOBOX, RO_TRIFORCE_HUNT_LOCATION_ANYWHERE);
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], WIDGET_CVAR_COMBOBOX, RO_MQ_DUNGEONS_NONE, false, nullptr, IMFLAG_NONE);
OPT_CALLBACK(RSK_MQ_DUNGEON_RANDOM, {
@@ -1069,7 +1052,7 @@ void Settings::CreateOptions() {
}
});
OPT_BOOL(RSK_SHUFFLE_BEAN_SOULS, "Shuffle Bean Souls", CVAR_RANDOMIZER_SETTING("ShuffleBeanSouls"), mOptionDescriptions[RSK_SHUFFLE_BEAN_SOULS], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF);
OPT_U8(RSK_SHUFFLE_BOSS_SOULS, "Shuffle Boss Souls", {"Off", "On", "On + Ganon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBossSouls"), mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS], WIDGET_CVAR_COMBOBOX);
OPT_U8(RSK_SHUFFLE_BOSS_SOULS, "Shuffle Boss Souls", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBossSouls"), mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS], WIDGET_CVAR_COMBOBOX);
OPT_BOOL(RSK_SHUFFLE_DEKU_STICK_BAG, "Shuffle Deku Stick Bag", CVAR_RANDOMIZER_SETTING("ShuffleDekuStickBag"), mOptionDescriptions[RSK_SHUFFLE_DEKU_STICK_BAG], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF);
OPT_CALLBACK(RSK_SHUFFLE_DEKU_STICK_BAG, {
if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDekuStickBag"), 0)) {
@@ -1141,78 +1124,169 @@ void Settings::CreateOptions() {
}
});
OPT_U8(RSK_BOSS_KEYSANITY, "Boss Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BossKeysanity"), mOptionDescriptions[RSK_BOSS_KEYSANITY], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON);
OPT_U8(RSK_GANONS_BOSS_KEY, "Ganon's Boss Key", {"Vanilla", "Own Dungeon", "Start With", "Any Dungeon", "Overworld", "Anywhere", "LACS-Vanilla", "LACS-Stones", "LACS-Medallions", "LACS-Rewards", "LACS-Dungeons", "LACS-Tokens", "100 GS Reward"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), mOptionDescriptions[RSK_GANONS_BOSS_KEY], WIDGET_CVAR_COMBOBOX, RO_GANON_BOSS_KEY_VANILLA);
OPT_U8(RSK_GANONS_BOSS_KEY, "Ganon's Boss Key", {"Vanilla", "Own Dungeon", "Start With", "Any Dungeon", "Overworld", "Anywhere", "Trigger-Stones", "Trigger-Medallions", "Trigger-Rewards", "Trigger-Dungeons", "Trigger-Tokens", "Trigger-Triforce Pieces"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), mOptionDescriptions[RSK_GANONS_BOSS_KEY], WIDGET_CVAR_COMBOBOX, RO_GANON_BOSS_KEY_VANILLA);
OPT_CALLBACK(RSK_GANONS_BOSS_KEY, {
// Shuffle 100 GS Reward - Force-Enabled if Ganon's Boss Key is on the 100 GS Reward
if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) ==
RO_GANON_BOSS_KEY_KAK_TOKENS) {
mOptions[RSK_SHUFFLE_100_GS_REWARD].Disable(
"This option is force-enabled because \"Ganon's Boss Key\" is set to \"100 GS Reward\".");
} else {
mOptions[RSK_SHUFFLE_100_GS_REWARD].Enable();
}
mOptions[RSK_LACS_OPTIONS].Hide();
mOptions[RSK_LACS_STONE_COUNT].Hide();
mOptions[RSK_LACS_MEDALLION_COUNT].Hide();
mOptions[RSK_LACS_REWARD_COUNT].Hide();
mOptions[RSK_LACS_DUNGEON_COUNT].Hide();
mOptions[RSK_LACS_TOKEN_COUNT].Hide();
mOptions[RSK_GBK_OPTIONS].Hide();
mOptions[RSK_GBK_STONE_COUNT].Hide();
mOptions[RSK_GBK_MEDALLION_COUNT].Hide();
mOptions[RSK_GBK_REWARD_COUNT].Hide();
mOptions[RSK_GBK_DUNGEON_COUNT].Hide();
mOptions[RSK_GBK_TOKEN_COUNT].Hide();
mOptions[RSK_GBK_TRIFORCE_COUNT].Hide();
switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA)) {
case RO_GANON_BOSS_KEY_LACS_STONES:
mOptions[RSK_LACS_OPTIONS].Unhide();
mOptions[RSK_LACS_STONE_COUNT].Unhide();
case RO_GANON_BOSS_KEY_STONES:
mOptions[RSK_GBK_OPTIONS].Unhide();
mOptions[RSK_GBK_STONE_COUNT].Unhide();
break;
case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
mOptions[RSK_LACS_OPTIONS].Unhide();
mOptions[RSK_LACS_MEDALLION_COUNT].Unhide();
case RO_GANON_BOSS_KEY_MEDALLIONS:
mOptions[RSK_GBK_OPTIONS].Unhide();
mOptions[RSK_GBK_MEDALLION_COUNT].Unhide();
break;
case RO_GANON_BOSS_KEY_LACS_REWARDS:
mOptions[RSK_LACS_OPTIONS].Unhide();
mOptions[RSK_LACS_REWARD_COUNT].Unhide();
case RO_GANON_BOSS_KEY_REWARDS:
mOptions[RSK_GBK_OPTIONS].Unhide();
mOptions[RSK_GBK_REWARD_COUNT].Unhide();
break;
case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
mOptions[RSK_LACS_OPTIONS].Unhide();
mOptions[RSK_LACS_DUNGEON_COUNT].Unhide();
case RO_GANON_BOSS_KEY_DUNGEONS:
mOptions[RSK_GBK_OPTIONS].Unhide();
mOptions[RSK_GBK_DUNGEON_COUNT].Unhide();
break;
case RO_GANON_BOSS_KEY_LACS_TOKENS:
mOptions[RSK_LACS_TOKEN_COUNT].Unhide();
case RO_GANON_BOSS_KEY_TOKENS:
mOptions[RSK_GBK_TOKEN_COUNT].Unhide();
break;
case RO_GANON_BOSS_KEY_TRIFORCE_PIECES:
mOptions[RSK_GBK_TRIFORCE_COUNT].Unhide();
break;
}
});
OPT_U8(RSK_LACS_STONE_COUNT, "GCBK Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsStoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true);
OPT_U8(RSK_LACS_MEDALLION_COUNT, "GCBK Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsMedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true);
OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true);
OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true);
OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), mOptionDescriptions[RSK_LACS_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_LACS_STANDARD_REWARD);
OPT_CALLBACK(RSK_LACS_OPTIONS, {
const uint8_t lacsOpts = CVarGetInteger(CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), RO_LACS_STANDARD_REWARD);
if (lacsOpts == RO_LACS_GREG_REWARD) {
if (mOptions[RSK_LACS_STONE_COUNT].GetOptionCount() == 4) {
mOptions[RSK_LACS_STONE_COUNT].ChangeOptions(NumOpts(0, 4));
}
if (mOptions[RSK_LACS_MEDALLION_COUNT].GetOptionCount() == 7) {
mOptions[RSK_LACS_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7));
}
if (mOptions[RSK_LACS_REWARD_COUNT].GetOptionCount() == 10) {
mOptions[RSK_LACS_REWARD_COUNT].ChangeOptions(NumOpts(0, 10));
}
if (mOptions[RSK_LACS_DUNGEON_COUNT].GetOptionCount() == 9) {
mOptions[RSK_LACS_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9));
}
OPT_U8(RSK_GBK_STONE_COUNT, "GBK Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GbkStoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true);
OPT_U8(RSK_GBK_MEDALLION_COUNT, "GBK Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GbkMedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true);
OPT_U8(RSK_GBK_REWARD_COUNT, "GBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GbkRewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true);
OPT_U8(RSK_GBK_DUNGEON_COUNT, "GBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GbkDungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true);
OPT_U8(RSK_GBK_TOKEN_COUNT, "GBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GbkTokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_GBK_TRIFORCE_COUNT, "GBK Triforce Piece Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GbkTriforceCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_GBK_OPTIONS, "GBK Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GbkRewardOptions"), mOptionDescriptions[RSK_GBK_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_CHECK_TRIGGER_STANDARD_REWARD);
OPT_CALLBACK(RSK_GBK_OPTIONS, {
const uint8_t gbkOpts = CVarGetInteger(CVAR_RANDOMIZER_SETTING("GbkRewardOptions"), RO_CHECK_TRIGGER_STANDARD_REWARD);
if (gbkOpts == RO_CHECK_TRIGGER_GREG_REWARD) {
mOptions[RSK_GBK_STONE_COUNT].ChangeOptions(NumOpts(0, 4));
mOptions[RSK_GBK_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7));
mOptions[RSK_GBK_REWARD_COUNT].ChangeOptions(NumOpts(0, 10));
mOptions[RSK_GBK_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9));
} else {
if (mOptions[RSK_LACS_STONE_COUNT].GetOptionCount() == 5) {
mOptions[RSK_LACS_STONE_COUNT].ChangeOptions(NumOpts(0, 3));
}
if (mOptions[RSK_LACS_MEDALLION_COUNT].GetOptionCount() == 8) {
mOptions[RSK_LACS_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6));
}
if (mOptions[RSK_LACS_REWARD_COUNT].GetOptionCount() == 11) {
mOptions[RSK_LACS_REWARD_COUNT].ChangeOptions(NumOpts(0, 9));
}
if (mOptions[RSK_LACS_DUNGEON_COUNT].GetOptionCount() == 10) {
mOptions[RSK_LACS_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8));
}
mOptions[RSK_GBK_STONE_COUNT].ChangeOptions(NumOpts(0, 3));
mOptions[RSK_GBK_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6));
mOptions[RSK_GBK_REWARD_COUNT].ChangeOptions(NumOpts(0, 9));
mOptions[RSK_GBK_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8));
}
});
OPT_U8(RSK_GANONS_SOUL, "Ganon's Soul", {"Start With", "Any Dungeon", "Overworld", "Anywhere", "Trigger-Stones", "Trigger-Medallions", "Trigger-Rewards", "Trigger-Dungeons", "Trigger-Tokens", "Trigger-Triforce Pieces"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), mOptionDescriptions[RSK_GANONS_SOUL], WIDGET_CVAR_COMBOBOX, RO_GANONS_SOUL_STARTWITH);
OPT_CALLBACK(RSK_GANONS_SOUL, {
mOptions[RSK_GANONS_SOUL_OPTIONS].Hide();
mOptions[RSK_GANONS_SOUL_STONE_COUNT].Hide();
mOptions[RSK_GANONS_SOUL_MEDALLION_COUNT].Hide();
mOptions[RSK_GANONS_SOUL_REWARD_COUNT].Hide();
mOptions[RSK_GANONS_SOUL_DUNGEON_COUNT].Hide();
mOptions[RSK_GANONS_SOUL_TOKEN_COUNT].Hide();
mOptions[RSK_GANONS_SOUL_TRIFORCE_COUNT].Hide();
switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonsSoul"), RO_GANONS_SOUL_STARTWITH)) {
case RO_GANONS_SOUL_STONES:
mOptions[RSK_GANONS_SOUL_OPTIONS].Unhide();
mOptions[RSK_GANONS_SOUL_STONE_COUNT].Unhide();
break;
case RO_GANONS_SOUL_MEDALLIONS:
mOptions[RSK_GANONS_SOUL_OPTIONS].Unhide();
mOptions[RSK_GANONS_SOUL_MEDALLION_COUNT].Unhide();
break;
case RO_GANONS_SOUL_REWARDS:
mOptions[RSK_GANONS_SOUL_OPTIONS].Unhide();
mOptions[RSK_GANONS_SOUL_REWARD_COUNT].Unhide();
break;
case RO_GANONS_SOUL_DUNGEONS:
mOptions[RSK_GANONS_SOUL_OPTIONS].Unhide();
mOptions[RSK_GANONS_SOUL_DUNGEON_COUNT].Unhide();
break;
case RO_GANONS_SOUL_TOKENS:
mOptions[RSK_GANONS_SOUL_TOKEN_COUNT].Unhide();
break;
case RO_GANONS_SOUL_TRIFORCE_PIECES:
mOptions[RSK_GANONS_SOUL_TRIFORCE_COUNT].Unhide();
break;
}
});
OPT_U8(RSK_GANONS_SOUL_STONE_COUNT, "Ganon's Soul Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonsSoulStoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true);
OPT_U8(RSK_GANONS_SOUL_MEDALLION_COUNT, "Ganon's Soul Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonsSoulMedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true);
OPT_U8(RSK_GANONS_SOUL_REWARD_COUNT, "Ganon's Soul Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonsSoulRewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true);
OPT_U8(RSK_GANONS_SOUL_DUNGEON_COUNT, "Ganon's Soul Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonsSoulDungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true);
OPT_U8(RSK_GANONS_SOUL_TOKEN_COUNT, "Ganon's Soul Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonsSoulTokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_GANONS_SOUL_TRIFORCE_COUNT, "Ganon's Soul Triforce Piece Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonsSoulTriforceCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_GANONS_SOUL_OPTIONS, "Ganon's Soul Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonsSoulRewardOptions"), mOptionDescriptions[RSK_GANONS_SOUL_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_CHECK_TRIGGER_STANDARD_REWARD);
OPT_CALLBACK(RSK_GANONS_SOUL_OPTIONS, {
const uint8_t soulOpts = CVarGetInteger(CVAR_RANDOMIZER_SETTING("GanonsSoulRewardOptions"), RO_CHECK_TRIGGER_STANDARD_REWARD);
if (soulOpts == RO_CHECK_TRIGGER_GREG_REWARD) {
mOptions[RSK_GANONS_SOUL_STONE_COUNT].ChangeOptions(NumOpts(0, 4));
mOptions[RSK_GANONS_SOUL_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7));
mOptions[RSK_GANONS_SOUL_REWARD_COUNT].ChangeOptions(NumOpts(0, 10));
mOptions[RSK_GANONS_SOUL_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9));
} else {
mOptions[RSK_GANONS_SOUL_STONE_COUNT].ChangeOptions(NumOpts(0, 3));
mOptions[RSK_GANONS_SOUL_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6));
mOptions[RSK_GANONS_SOUL_REWARD_COUNT].ChangeOptions(NumOpts(0, 9));
mOptions[RSK_GANONS_SOUL_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8));
}
});
OPT_U8(RSK_WINCON, "Win Condition", {"Defeat Ganon", "Anywhere", "Trigger-Stones", "Trigger-Medallions", "Trigger-Rewards", "Trigger-Dungeons", "Trigger-Tokens", "Trigger-Triforce Pieces"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleWincon"), mOptionDescriptions[RSK_WINCON], WIDGET_CVAR_COMBOBOX, RO_WINCON_DEFEAT_GANON);
OPT_CALLBACK(RSK_WINCON, {
mOptions[RSK_WINCON_OPTIONS].Hide();
mOptions[RSK_WINCON_STONE_COUNT].Hide();
mOptions[RSK_WINCON_MEDALLION_COUNT].Hide();
mOptions[RSK_WINCON_REWARD_COUNT].Hide();
mOptions[RSK_WINCON_DUNGEON_COUNT].Hide();
mOptions[RSK_WINCON_TOKEN_COUNT].Hide();
mOptions[RSK_WINCON_TRIFORCE_COUNT].Hide();
switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleWincon"), RO_WINCON_DEFEAT_GANON)) {
case RO_WINCON_STONES:
mOptions[RSK_WINCON_OPTIONS].Unhide();
mOptions[RSK_WINCON_STONE_COUNT].Unhide();
break;
case RO_WINCON_MEDALLIONS:
mOptions[RSK_WINCON_OPTIONS].Unhide();
mOptions[RSK_WINCON_MEDALLION_COUNT].Unhide();
break;
case RO_WINCON_REWARDS:
mOptions[RSK_WINCON_OPTIONS].Unhide();
mOptions[RSK_WINCON_REWARD_COUNT].Unhide();
break;
case RO_WINCON_DUNGEONS:
mOptions[RSK_WINCON_OPTIONS].Unhide();
mOptions[RSK_WINCON_DUNGEON_COUNT].Unhide();
break;
case RO_WINCON_TOKENS:
mOptions[RSK_WINCON_TOKEN_COUNT].Unhide();
break;
case RO_WINCON_TRIFORCE_PIECES:
mOptions[RSK_WINCON_TRIFORCE_COUNT].Unhide();
break;
}
});
OPT_U8(RSK_WINCON_STONE_COUNT, "Win Condition Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("WinconStoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true);
OPT_U8(RSK_WINCON_MEDALLION_COUNT, "Win Condition Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("WinconMedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true);
OPT_U8(RSK_WINCON_REWARD_COUNT, "Win Condition Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("WinconRewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true);
OPT_U8(RSK_WINCON_DUNGEON_COUNT, "Win Condition Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("WinconDungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true);
OPT_U8(RSK_WINCON_TOKEN_COUNT, "Win Condition Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("WinconTokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_WINCON_TRIFORCE_COUNT, "Win Condition Triforce Piece Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("WinconTriforceCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true);
OPT_U8(RSK_WINCON_OPTIONS, "Win Condition Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("WinconRewardOptions"), mOptionDescriptions[RSK_WINCON_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_CHECK_TRIGGER_STANDARD_REWARD);
OPT_CALLBACK(RSK_WINCON_OPTIONS, {
const uint8_t winconOpts = CVarGetInteger(CVAR_RANDOMIZER_SETTING("WinconRewardOptions"), RO_CHECK_TRIGGER_STANDARD_REWARD);
if (winconOpts == RO_CHECK_TRIGGER_GREG_REWARD) {
mOptions[RSK_WINCON_STONE_COUNT].ChangeOptions(NumOpts(0, 4));
mOptions[RSK_WINCON_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7));
mOptions[RSK_WINCON_REWARD_COUNT].ChangeOptions(NumOpts(0, 10));
mOptions[RSK_WINCON_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9));
} else {
mOptions[RSK_WINCON_STONE_COUNT].ChangeOptions(NumOpts(0, 3));
mOptions[RSK_WINCON_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6));
mOptions[RSK_WINCON_REWARD_COUNT].ChangeOptions(NumOpts(0, 9));
mOptions[RSK_WINCON_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8));
}
});
OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WIDGET_CVAR_COMBOBOX, RO_KEYRINGS_OFF);
@@ -1822,14 +1896,34 @@ void Settings::CreateOptions() {
&mOptions[RSK_SKIP_SCARECROWS_SONG],
},
WidgetContainerType::SECTION);
mOptionGroups[RSG_MENU_SECTION_WINCON] = OptionGroup::SubGroup(
"Win Condition",
{ &mOptions[RSK_TRIFORCE_HUNT], &mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL],
&mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED], &mOptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION],
&mOptions[RSK_GANONS_BOSS_KEY], &mOptions[RSK_LACS_OPTIONS], &mOptions[RSK_LACS_MEDALLION_COUNT],
&mOptions[RSK_LACS_STONE_COUNT], &mOptions[RSK_LACS_DUNGEON_COUNT], &mOptions[RSK_LACS_REWARD_COUNT],
&mOptions[RSK_LACS_TOKEN_COUNT] },
WidgetContainerType::SECTION);
mOptionGroups[RSG_MENU_SECTION_WINCON] = OptionGroup::SubGroup("Win Condition",
{ &mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL],
&mOptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION],
&mOptions[RSK_GANONS_BOSS_KEY],
&mOptions[RSK_GBK_OPTIONS],
&mOptions[RSK_GBK_MEDALLION_COUNT],
&mOptions[RSK_GBK_STONE_COUNT],
&mOptions[RSK_GBK_DUNGEON_COUNT],
&mOptions[RSK_GBK_REWARD_COUNT],
&mOptions[RSK_GBK_TOKEN_COUNT],
&mOptions[RSK_GBK_TRIFORCE_COUNT],
&mOptions[RSK_GANONS_SOUL],
&mOptions[RSK_GANONS_SOUL_OPTIONS],
&mOptions[RSK_GANONS_SOUL_MEDALLION_COUNT],
&mOptions[RSK_GANONS_SOUL_STONE_COUNT],
&mOptions[RSK_GANONS_SOUL_DUNGEON_COUNT],
&mOptions[RSK_GANONS_SOUL_REWARD_COUNT],
&mOptions[RSK_GANONS_SOUL_TOKEN_COUNT],
&mOptions[RSK_GANONS_SOUL_TRIFORCE_COUNT],
&mOptions[RSK_WINCON],
&mOptions[RSK_WINCON_OPTIONS],
&mOptions[RSK_WINCON_MEDALLION_COUNT],
&mOptions[RSK_WINCON_STONE_COUNT],
&mOptions[RSK_WINCON_DUNGEON_COUNT],
&mOptions[RSK_WINCON_REWARD_COUNT],
&mOptions[RSK_WINCON_TOKEN_COUNT],
&mOptions[RSK_WINCON_TRIFORCE_COUNT] },
WidgetContainerType::SECTION);
mOptionGroups[RSG_MENU_COLUMN_LOGIC_WINCON] = OptionGroup::SubGroup("",
std::initializer_list<OptionGroup*>{
&mOptionGroups[RSG_ITEM_POOL],
@@ -1855,6 +1949,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT],
&mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT],
&mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT],
&mOptions[RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT],
&mOptions[RSK_GANONS_TRIALS],
&mOptions[RSK_TRIAL_COUNT],
&mOptions[RSK_MEDALLION_LOCKED_TRIALS],
@@ -2104,6 +2199,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT],
&mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT],
&mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT],
&mOptions[RSK_RAINBOW_BRIDGE_TRIFORCE_COUNT],
&mOptions[RSK_BRIDGE_OPTIONS],
&mOptions[RSK_GANONS_TRIALS],
&mOptions[RSK_TRIAL_COUNT],
@@ -2132,9 +2228,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_DECOUPLED_ENTRANCES],
&mOptions[RSK_BOMBCHU_BAG],
&mOptions[RSK_ENABLE_BOMBCHU_DROPS],
&mOptions[RSK_TRIFORCE_HUNT],
&mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL],
&mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED],
&mOptions[RSK_TRIFORCE_HUNT_PIECES_LOCATION],
&mOptions[RSK_MQ_DUNGEON_RANDOM],
&mOptions[RSK_MQ_DUNGEON_COUNT],
@@ -2239,12 +2333,29 @@ void Settings::CreateOptions() {
&mOptions[RSK_GERUDO_KEYS],
&mOptions[RSK_BOSS_KEYSANITY],
&mOptions[RSK_GANONS_BOSS_KEY],
&mOptions[RSK_LACS_STONE_COUNT],
&mOptions[RSK_LACS_MEDALLION_COUNT],
&mOptions[RSK_LACS_DUNGEON_COUNT],
&mOptions[RSK_LACS_REWARD_COUNT],
&mOptions[RSK_LACS_TOKEN_COUNT],
&mOptions[RSK_LACS_OPTIONS],
&mOptions[RSK_GBK_STONE_COUNT],
&mOptions[RSK_GBK_MEDALLION_COUNT],
&mOptions[RSK_GBK_DUNGEON_COUNT],
&mOptions[RSK_GBK_REWARD_COUNT],
&mOptions[RSK_GBK_TOKEN_COUNT],
&mOptions[RSK_GBK_TRIFORCE_COUNT],
&mOptions[RSK_GBK_OPTIONS],
&mOptions[RSK_GANONS_SOUL],
&mOptions[RSK_GANONS_SOUL_STONE_COUNT],
&mOptions[RSK_GANONS_SOUL_MEDALLION_COUNT],
&mOptions[RSK_GANONS_SOUL_DUNGEON_COUNT],
&mOptions[RSK_GANONS_SOUL_REWARD_COUNT],
&mOptions[RSK_GANONS_SOUL_TOKEN_COUNT],
&mOptions[RSK_GANONS_SOUL_TRIFORCE_COUNT],
&mOptions[RSK_GANONS_SOUL_OPTIONS],
&mOptions[RSK_WINCON],
&mOptions[RSK_WINCON_STONE_COUNT],
&mOptions[RSK_WINCON_MEDALLION_COUNT],
&mOptions[RSK_WINCON_DUNGEON_COUNT],
&mOptions[RSK_WINCON_REWARD_COUNT],
&mOptions[RSK_WINCON_TOKEN_COUNT],
&mOptions[RSK_WINCON_TRIFORCE_COUNT],
&mOptions[RSK_WINCON_OPTIONS],
&mOptions[RSK_KEYRINGS],
&mOptions[RSK_KEYRINGS_RANDOM_COUNT],
&mOptions[RSK_KEYRINGS_GERUDO_FORTRESS],
@@ -2535,11 +2646,6 @@ void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocation
mOptions[RSK_STARTING_AGE].Set(RO_AGE_CHILD);
}
// Force 100 GS Shuffle if that's where Ganon's Boss Key is
if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) {
mOptions[RSK_SHUFFLE_100_GS_REWARD].Set(1);
}
// If we only have MQ, set all dungeons to MQ
if (OTRGlobals::Instance->HasMasterQuest() && !OTRGlobals::Instance->HasOriginal()) {
mOptions[RSK_MQ_DUNGEON_RANDOM].Set(RO_MQ_DUNGEONS_SET_NUMBER);
@@ -2890,18 +2996,54 @@ void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocation
// TODO: Random Starting Time
if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
mLACSCondition = RO_LACS_STONES;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)) {
mLACSCondition = RO_LACS_MEDALLIONS;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
mLACSCondition = RO_LACS_REWARDS;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS)) {
mLACSCondition = RO_LACS_DUNGEONS;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
mLACSCondition = RO_LACS_TOKENS;
if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_STONES)) {
mGBKCondition = RO_CHECK_TRIGGER_STONES;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_MEDALLIONS)) {
mGBKCondition = RO_CHECK_TRIGGER_MEDALLIONS;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_REWARDS)) {
mGBKCondition = RO_CHECK_TRIGGER_REWARDS;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_DUNGEONS)) {
mGBKCondition = RO_CHECK_TRIGGER_DUNGEONS;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_TOKENS)) {
mGBKCondition = RO_CHECK_TRIGGER_TOKENS;
} else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_TRIFORCE_PIECES)) {
mGBKCondition = RO_CHECK_TRIGGER_TRIFORCE_PIECES;
} else {
mLACSCondition = RO_LACS_VANILLA;
mGBKCondition = RO_CHECK_TRIGGER_NONE;
}
if (mOptions[RSK_GANONS_SOUL].Is(RO_GANONS_SOUL_STONES)) {
mGanonsSoulCondition = RO_CHECK_TRIGGER_STONES;
} else if (mOptions[RSK_GANONS_SOUL].Is(RO_GANONS_SOUL_MEDALLIONS)) {
mGanonsSoulCondition = RO_CHECK_TRIGGER_MEDALLIONS;
} else if (mOptions[RSK_GANONS_SOUL].Is(RO_GANONS_SOUL_REWARDS)) {
mGanonsSoulCondition = RO_CHECK_TRIGGER_REWARDS;
} else if (mOptions[RSK_GANONS_SOUL].Is(RO_GANONS_SOUL_DUNGEONS)) {
mGanonsSoulCondition = RO_CHECK_TRIGGER_DUNGEONS;
} else if (mOptions[RSK_GANONS_SOUL].Is(RO_GANONS_SOUL_TOKENS)) {
mGanonsSoulCondition = RO_CHECK_TRIGGER_TOKENS;
} else if (mOptions[RSK_GANONS_SOUL].Is(RO_GANONS_SOUL_TRIFORCE_PIECES)) {
mGanonsSoulCondition = RO_CHECK_TRIGGER_TRIFORCE_PIECES;
} else {
mGanonsSoulCondition = RO_CHECK_TRIGGER_NONE;
}
if (mOptions[RSK_WINCON].Is(RO_WINCON_STONES)) {
mWinCondition = RO_WINCON_STONES;
} else if (mOptions[RSK_WINCON].Is(RO_WINCON_MEDALLIONS)) {
mWinCondition = RO_WINCON_MEDALLIONS;
} else if (mOptions[RSK_WINCON].Is(RO_WINCON_REWARDS)) {
mWinCondition = RO_WINCON_REWARDS;
} else if (mOptions[RSK_WINCON].Is(RO_WINCON_DUNGEONS)) {
mWinCondition = RO_WINCON_DUNGEONS;
} else if (mOptions[RSK_WINCON].Is(RO_WINCON_TOKENS)) {
mWinCondition = RO_WINCON_TOKENS;
} else if (mOptions[RSK_WINCON].Is(RO_WINCON_TRIFORCE_PIECES)) {
mWinCondition = RO_WINCON_TRIFORCE_PIECES;
} else if (mOptions[RSK_WINCON].Is(RO_WINCON_ANYWHERE)) {
mWinCondition = RO_WINCON_ANYWHERE;
} else {
mWinCondition = RO_WINCON_DEFEAT_GANON;
}
if (!mOptions[RSK_SHUFFLE_WARP_SONGS]) {
+1
View File
@@ -1533,6 +1533,7 @@ extern "C" void InitOTR(int argc, char* argv[]) {
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion4Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion5Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion6Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion7Updater>());
conf->RunVersionUpdates();
SohGui::SetupGuiElements();
+3 -7
View File
@@ -572,10 +572,7 @@ void SaveManager::StartupCheckAndInitMeta(int fileNum) {
(int16_t)baseBlock["randomizerInf"][RAND_INF_HAS_WALLET >> 4] & (1 << (RAND_INF_HAS_WALLET & 0xF));
fileMetaInfo[fileNum].triforcePieces = randoBlock.value("triforcePiecesCollected", 0);
nlohmann::json& randoSettings = randoBlock["randoSettings"];
if (randoSettings[RSK_TRIFORCE_HUNT].get<uint8_t>() != 0) {
fileMetaInfo[fileNum].maxTriforcePieces =
randoSettings[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].get<uint8_t>() + 1;
}
fileMetaInfo[fileNum].maxTriforcePieces = randoSettings[RSK_TRIFORCE_HUNT_PIECES_TOTAL].get<uint8_t>();
fileMetaInfo[fileNum].hasFishingRod = (int16_t)baseBlock["randomizerInf"][RAND_INF_FISHING_POLE_FOUND >> 4] &
(1 << (RAND_INF_FISHING_POLE_FOUND & 0xF));
fileMetaInfo[fileNum].fishingPoleShuffled = randoSettings[RSK_SHUFFLE_FISHING_POLE].get<uint8_t>() != 0;
@@ -621,9 +618,8 @@ void SaveManager::InitMeta(int fileNum) {
fileMetaInfo[fileNum].health = gSaveContext.health;
auto randoContext = Rando::Context::GetInstance();
fileMetaInfo[fileNum].maxTriforcePieces = IS_RANDO && (bool)randoContext->GetOption(RSK_TRIFORCE_HUNT)
? randoContext->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1
: 0;
fileMetaInfo[fileNum].maxTriforcePieces =
IS_RANDO ? randoContext->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() : 0;
fileMetaInfo[fileNum].fishingPoleShuffled =
IS_RANDO ? (bool)randoContext->GetOption(RSK_SHUFFLE_FISHING_POLE) : false;
+79
View File
@@ -1,6 +1,7 @@
#include "ConfigUpdaters.h"
#include <libultraship/bridge/consolevariablebridge.h>
#include "soh/Enhancements/randomizer/randomizerEnums.h"
namespace SOH {
struct Migration {
@@ -1447,6 +1448,16 @@ static const Migration version6Migrations[] = {
{ nullptr, nullptr },
};
static const Migration version7Migrations[] = {
{ "gRandoSettings.LacsStoneCount", "gRandoSettings.GbkStoneCount" },
{ "gRandoSettings.LacsMedallionCount", "gRandoSettings.GbkMedallionCount" },
{ "gRandoSettings.LacsRewardCount", "gRandoSettings.GbkRewardCount" },
{ "gRandoSettings.LacsDungeonCount", "gRandoSettings.GbkDungeonCount" },
{ "gRandoSettings.LacsTokenCount", "gRandoSettings.GbkTokenCount" },
{ "gRandoSettings.LacsRewardOptions", "gRandoSettings.GbkRewardOptions" },
{ nullptr, nullptr },
};
static void ApplyMigrationActions(const Migration* migrations) {
while (migrations->from != nullptr) {
if (migrations->to != nullptr) {
@@ -1469,6 +1480,8 @@ ConfigVersion5Updater::ConfigVersion5Updater() : ConfigVersionUpdater(5) {
}
ConfigVersion6Updater::ConfigVersion6Updater() : ConfigVersionUpdater(6) {
}
ConfigVersion7Updater::ConfigVersion7Updater() : ConfigVersionUpdater(7) {
}
void ConfigVersion1Updater::Update(Ship::Config* conf) {
if (conf->GetInt("Window.Width", 640) == 640) {
@@ -1581,4 +1594,70 @@ void ConfigVersion5Updater::Update(Ship::Config* conf) {
void ConfigVersion6Updater::Update(Ship::Config* conf) {
ApplyMigrationActions(version6Migrations);
}
void ConfigVersion7Updater::Update(Ship::Config* conf) {
ApplyMigrationActions(version7Migrations);
// Ganon's Boss Key: the LACS-* values were replaced by generic Trigger-* values
switch (CVarGetInteger("gRandoSettings.ShuffleGanonBossKey", 0)) {
case 6: // LACS-Vanilla (Shadow and Spirit Medallions)
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_MEDALLIONS);
CVarSetInteger("gRandoSettings.GbkMedallionCount", 2);
break;
case 7: // LACS-Stones
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_STONES);
break;
case 8: // LACS-Medallions
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_MEDALLIONS);
break;
case 9: // LACS-Rewards
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_REWARDS);
break;
case 10: // LACS-Dungeons
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_DUNGEONS);
break;
case 11: // LACS-Tokens
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_TOKENS);
break;
case 12: // 100 GS Reward
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_TOKENS);
CVarSetInteger("gRandoSettings.GbkTokenCount", 100);
break;
}
// Triforce Hunt: the Off/Win/GBK combobox was folded into TriforceHuntTotalPieces (0 = off)
// and the required piece count moved to the Wincon/GBK trigger counts
switch (CVarGetInteger("gRandoSettings.TriforceHunt", 0)) {
case 1: // Win
CVarSetInteger("gRandoSettings.ShuffleWincon", RO_WINCON_TRIFORCE_PIECES);
CVarSetInteger("gRandoSettings.WinconTriforceCount",
CVarGetInteger("gRandoSettings.TriforceHuntRequiredPieces", 19));
CVarSetInteger("gRandoSettings.TriforceHuntTotalPieces",
CVarGetInteger("gRandoSettings.TriforceHuntTotalPieces", 29));
break;
case 2: // Ganon's Boss Key
CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_TRIFORCE_PIECES);
CVarSetInteger("gRandoSettings.GbkTriforceCount",
CVarGetInteger("gRandoSettings.TriforceHuntRequiredPieces", 19));
CVarSetInteger("gRandoSettings.TriforceHuntTotalPieces",
CVarGetInteger("gRandoSettings.TriforceHuntTotalPieces", 29));
break;
default: // Off; a leftover total would now silently enable the hunt
CVarClear("gRandoSettings.TriforceHuntTotalPieces");
break;
}
CVarClear("gRandoSettings.TriforceHunt");
CVarClear("gRandoSettings.TriforceHuntRequiredPieces");
// Rainbow Bridge: Triforce Pieces was inserted at 7, shifting Greg from 7 to 8
if (CVarGetInteger("gRandoSettings.RainbowBridge", 0) == 7) {
CVarSetInteger("gRandoSettings.RainbowBridge", RO_BRIDGE_GREG);
}
// Boss Souls: On + Ganon was split into On plus the standalone Ganon's Soul setting
if (CVarGetInteger("gRandoSettings.ShuffleBossSouls", 0) == 2) {
CVarSetInteger("gRandoSettings.ShuffleBossSouls", RO_BOSS_SOULS_ON);
CVarSetInteger("gRandoSettings.ShuffleGanonsSoul", RO_GANONS_SOUL_ANYWHERE);
}
}
} // namespace SOH
+6
View File
@@ -36,4 +36,10 @@ class ConfigVersion6Updater final : public Ship::ConfigVersionUpdater {
ConfigVersion6Updater();
void Update(Ship::Config* conf);
};
class ConfigVersion7Updater final : public Ship::ConfigVersionUpdater {
public:
ConfigVersion7Updater();
void Update(Ship::Config* conf);
};
} // namespace SOH
+9 -9
View File
@@ -42,8 +42,8 @@ bool Scene_CommandSpawnList(PlayState* play, SOH::ISceneCommand* cmd) {
ActorEntry* entries = (ActorEntry*)cmdStartPos->GetRawPointer();
play->linkActorEntry = &entries[play->setupEntranceList[play->curSpawn].spawn];
play->linkAgeOnLoad = ((void)0, gSaveContext.linkAge);
s16 linkObjectId = gLinkObjectIds[((void)0, gSaveContext.linkAge)];
play->linkAgeOnLoad = gSaveContext.linkAge;
s16 linkObjectId = gLinkObjectIds[gSaveContext.linkAge];
Object_Spawn(&play->objectCtx, linkObjectId);
@@ -262,13 +262,13 @@ bool Scene_CommandTimeSettings(PlayState* play, SOH::ISceneCommand* cmd) {
gTimeIncrement = play->envCtx.timeIncrement;
}
play->envCtx.sunPos.x = -(Math_SinS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f;
play->envCtx.sunPos.y = (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f;
play->envCtx.sunPos.z = (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 20.0f) * 25.0f;
play->envCtx.sunPos.x = -(Math_SinS(gSaveContext.dayTime - 0x8000) * 120.0f) * 25.0f;
play->envCtx.sunPos.y = (Math_CosS(gSaveContext.dayTime - 0x8000) * 120.0f) * 25.0f;
play->envCtx.sunPos.z = (Math_CosS(gSaveContext.dayTime - 0x8000) * 20.0f) * 25.0f;
if (((play->envCtx.timeIncrement == 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) ||
(gSaveContext.entranceIndex == ENTR_LAKE_HYLIA_WARP_PAD)) {
gSaveContext.skyboxTime = ((void)0, gSaveContext.dayTime);
gSaveContext.skyboxTime = gSaveContext.dayTime;
if ((gSaveContext.skyboxTime >= 0x2AAC) && (gSaveContext.skyboxTime < 0x4555)) {
gSaveContext.skyboxTime = 0x3556;
} else if ((gSaveContext.skyboxTime >= 0x4555) && (gSaveContext.skyboxTime < 0x5556)) {
@@ -336,9 +336,9 @@ bool Scene_CommandAlternateHeaderList(PlayState* play, SOH::ISceneCommand* cmd)
// s32 pad;
// SceneCmd* altHeader;
// osSyncPrintf("\n[ZU]sceneset age =[%X]", ((void)0, gSaveContext.linkAge));
// osSyncPrintf("\n[ZU]sceneset time =[%X]", ((void)0, gSaveContext.cutsceneIndex));
// osSyncPrintf("\n[ZU]sceneset counter=[%X]", ((void)0, gSaveContext.sceneSetupIndex));
// osSyncPrintf("\n[ZU]sceneset age =[%X]", gSaveContext.linkAge);
// osSyncPrintf("\n[ZU]sceneset time =[%X]", gSaveContext.cutsceneIndex);
// osSyncPrintf("\n[ZU]sceneset counter=[%X]", gSaveContext.sceneSetupIndex);
if (gSaveContext.sceneSetupIndex != 0) {
SOH::Scene* desiredHeader =
+4 -18
View File
@@ -327,6 +327,10 @@ u8 CheckDungeonCount() {
dungeonCount++;
}
if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_GANONS_TOWER)) {
dungeonCount++;
}
return dungeonCount;
}
@@ -348,24 +352,6 @@ u8 CheckBridgeRewardCount() {
return bridgeRewardCount;
}
u8 CheckLACSRewardCount() {
u8 lacsRewardCount = 0;
switch (Randomizer_GetSettingValue(RSK_LACS_OPTIONS)) {
case RO_LACS_WILDCARD_REWARD:
if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) {
lacsRewardCount += 1;
}
break;
case RO_LACS_GREG_REWARD:
if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) {
lacsRewardCount += 1;
}
break;
}
return lacsRewardCount;
}
void Play_Init(GameState* thisx) {
PlayState* play = (PlayState*)thisx;
GraphicsContext* gfxCtx = play->state.gfxCtx;
+2 -1
View File
@@ -1656,7 +1656,8 @@ void Player_DrawGetItemImpl(PlayState* play, Player* this, Vec3f* refPos, s32 dr
if (this->getItemEntry.modIndex == MOD_RANDOMIZER && this->getItemEntry.getItemId == RG_ICE_TRAP) {
Player_DrawGetItemIceTrap(play, this, refPos, drawIdPlusOne, height);
} else if (this->getItemEntry.modIndex == MOD_RANDOMIZER && this->getItemEntry.getItemId == RG_TRIFORCE_PIECE) {
} else if (this->getItemEntry.modIndex == MOD_RANDOMIZER &&
(this->getItemEntry.getItemId == RG_TRIFORCE_PIECE || this->getItemEntry.getItemId == RG_TRIFORCE)) {
Randomizer_DrawTriforcePieceGI(play, this->getItemEntry);
} else if (this->getItemEntry.drawFunc != NULL) {
this->getItemEntry.drawFunc(play, &this->getItemEntry);
@@ -1692,14 +1692,18 @@ void func_8090120C(BossGanon2* this, PlayState* play) {
(player->meleeWeaponState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) {
func_80064520(play, &play->csCtx);
GameInteractor_ExecuteOnBossDefeat(&this->actor);
this->subCamId = Play_CreateSubCamera(play);
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
Play_ChangeCameraStatus(play, this->subCamId, CAM_STAT_ACTIVE);
this->csState = 7;
this->csTimer = 0;
Animation_MorphToPlayOnce(&this->skelAnime, &gGanonFinalBlowAnim, 0.0f);
this->unk_194 = Animation_GetLastFrame(&gGanonFinalBlowAnim);
play->startPlayerCutscene(play, &this->actor, 0x61);
if (GameInteractor_Should(VB_SLAY_GANON, true)) {
this->subCamId = Play_CreateSubCamera(play);
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
Play_ChangeCameraStatus(play, this->subCamId, CAM_STAT_ACTIVE);
this->csState = 7;
this->csTimer = 0;
Animation_MorphToPlayOnce(&this->skelAnime, &gGanonFinalBlowAnim, 0.0f);
this->unk_194 = Animation_GetLastFrame(&gGanonFinalBlowAnim);
play->startPlayerCutscene(play, &this->actor, 0x61);
} else {
break;
}
} else {
break;
}