From 96be197fd2ac55760902adf072aac12034be0fd1 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Oct 2026 19:17:09 -0400 Subject: [PATCH] Fire Temple Goron Shuffle (#7377) --- soh/assets/custom/lang/en_US.json | 8 + .../vanilla-behavior/GIVanillaBehavior.h | 16 ++ .../hint_list/hint_list_exclude_dungeon.cpp | 52 ++++++ .../randomizer/Messages/Goron.cpp | 16 ++ .../Enhancements/randomizer/RCToRandInf.cpp | 13 ++ .../Enhancements/randomizer/SeedContext.cpp | 1 + .../randomizer/ShuffleFireGorons.cpp | 173 ++++++++++++++++++ soh/soh/Enhancements/randomizer/location.cpp | 8 + soh/soh/Enhancements/randomizer/location.h | 4 + .../location_access/dungeons/fire_temple.cpp | 13 ++ .../randomizerEnums/RandomizerCheck.h | 13 ++ .../randomizerEnums/RandomizerHintTextKey.h | 14 ++ .../randomizerEnums/RandomizerInf.h | 13 ++ .../randomizerEnums/RandomizerMiscEnums.h | 1 + .../randomizerEnums/RandomizerSettingKey.h | 1 + .../randomizer/randomizer_check_objects.cpp | 2 + .../randomizer/randomizer_check_tracker.cpp | 4 + soh/soh/Enhancements/randomizer/settings.cpp | 3 + soh/soh/Enhancements/randomizer/static_data.h | 1 + soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c | 4 +- 20 files changed, 359 insertions(+), 1 deletion(-) create mode 100644 soh/soh/Enhancements/randomizer/ShuffleFireGorons.cpp diff --git a/soh/assets/custom/lang/en_US.json b/soh/assets/custom/lang/en_US.json index c425653192..a9eb2b5cb2 100644 --- a/soh/assets/custom/lang/en_US.json +++ b/soh/assets/custom/lang/en_US.json @@ -548,6 +548,14 @@ "Red Ice will have a particle effect inside it when it holds a randomized item" ] }, + "shuffle_fire_gorons": { + "name": "Shuffle Fire Temple Gorons", + "description": [ + "The Gorons imprisoned in the Fire Temple will give a randomized item ", + "when talked to after being freed.\n", + "Gorons will have a particle effect above them when they hold a randomized item." + ] + }, "shuffle_signs": { "name": "Shuffle Signs", "description": [ diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 41f34ca4e8..1cbb0b7e5b 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -598,6 +598,14 @@ typedef enum { // - None VB_DEKU_STICK_BURN_OUT, + // #### `result` + // ```c + // Flags_GetSwitch(play, (this->actor.params & 0xFC00) >> 0xA) + // ``` + // #### `args` + // - `*EnGo2` + VB_DESPAWN_FREED_FIRE_GORON, + // #### `result` // ```c // Flags_GetItemGetInf(ITEMGETINF_30) @@ -1165,6 +1173,14 @@ typedef enum { // - `*Actor` (EnDivingGame - &this->actor) VB_GIVE_ITEM_FROM_DIVING_MINIGAME, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnGo2` + VB_GIVE_ITEM_FROM_FIRE_GORON, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index 22ec46b0f9..cc783a6b1c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -971,6 +971,58 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne, hinter dem Thron eines Ritters in einem Vulkan#, #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil derrière le trône d’un chevalier dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_FIRE_TEMPLE_NEAR_BOSS_GORON] = HintText(CustomMessage("They say that #freeing a Goron near a dragon# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen nahe eines Drachens# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron près d'un dragon# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_BOSS_KEY_GORON] = HintText(CustomMessage("They say that #freeing a Goron beyond a totem# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen jenseits eines Totems# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron derrière un totem# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON] = HintText(CustomMessage("They say that #freeing a Goron trapped near lava# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines nahe der Lava gefangenen Goronen# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron emprisonné près de la lave# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON] = HintText(CustomMessage("They say that #freeing a Goron walled in over a lava pit# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines eingemauerten Goronen über einem Lavastrom# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron emmuré dans un lac de lave# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON] = HintText(CustomMessage("They say that #freeing a Goron at the end of a maze# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen am Ende eines Labyrinths# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron dans le labyrinthe# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON] = HintText(CustomMessage("They say that #freeing a Goron on a blocked path# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen auf einem blockierten Pfad# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron sous un sol fragile# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON] = HintText(CustomMessage("They say that #freeing a Goron hidden near a maze# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines nahe eines Labyrinths versteckten Goronen# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron caché près du labyrinthe# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON] = HintText(CustomMessage("They say that #freeing a Goron above a maze# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen oberhalb eines Labyrinths# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron au dessus du labyrinthe# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_HIGHEST_GORON] = HintText(CustomMessage("They say that #freeing a Goron atop the Fire Temple# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen an der Spitze des Feuertempels# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron au sommet du Temple du Feu# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_MQ_MAP_GORON] = HintText(CustomMessage("They say that #freeing a Goron beside a map# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen neben einer Karte# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron près d'une carte# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON] = HintText(CustomMessage("They say that #freeing a Goron in a cage above lava# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen in einem Käfig über der Lava# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron en cage au dessus de la lave# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON] = HintText(CustomMessage("They say that #freeing a Goron behind a blocked lava door# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines Goronen hinter einer blockierten Lavatür# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron derrière la porte bloquée du lac de lave# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON] = HintText(CustomMessage("They say that #freeing a Goron hidden beside a maze# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Befreien eines neben einem Labyrinth versteckten Goronen# #[[1]]# gäbe.", + /*french*/ "Selon moi, #libérer le Goron caché à côté du labyrinthe# donne #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_CRATE_FIRE_TEMPLE] = HintText(CustomMessage("They say that a #crate in the Fire Temple# contains #[[1]]#.", /*german*/ "Man erzählt sich, daß eine #Kiste im Feuertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple du Feu# contient #[[1]]#.", {QM_RED, QM_GREEN})); diff --git a/soh/soh/Enhancements/randomizer/Messages/Goron.cpp b/soh/soh/Enhancements/randomizer/Messages/Goron.cpp index d3e8cb3666..0b9d2a5e6e 100644 --- a/soh/soh/Enhancements/randomizer/Messages/Goron.cpp +++ b/soh/soh/Enhancements/randomizer/Messages/Goron.cpp @@ -85,6 +85,22 @@ static CustomMessage FireTempleGoronMessages[] = { "Wat weiß'n ich, wieso ich hier&eingepfercht wurd. Ich mach&doch nur %g\"Pull&Requests\"%w.", "Je ne sais pas comment on m'a jeté&ici. Il faut croire que je dors comme&une pierre.", }, + { + "Hey! Somebody finally stopped!^All the others ran right past me.&Something about %r\"wasting " + "time\"%w...^I'm telling everyone back home!&...They won't believe me.", + "Hey! Endlich bleibt mal&jemand stehen!^Alle anderen sind einfach an mir&vorbeigerannt. Irgendwas " + "von&%r\"Zeitverschwendung\"%w...^Das erzähle ich allen zu Hause!&...Die werden mir nie glauben.", + "Hé! Enfin quelqu'un&qui s'arrête!^Tous les autres m'ont dépassé en&courant. Ils parlaient de&%r\"perte " + "de temps\"%w...^Je vais le raconter à tout le&monde! ...Personne ne me croira.", + }, + { + "You came all this way just to&talk to me?^Nobody does that unless I've got&%rsomething they " + "want%w...^...Hey, why are you looking&at my pockets?", + "Du bist den ganzen Weg gekommen,&nur um mit mir zu reden?^Das macht keiner, außer ich habe&%retwas, das er " + "haben will%w...^...Hey, warum starrst Du so&auf meine Taschen?", + "Tu es venu jusqu'ici juste pour&me parler?^Personne ne fait ça, sauf si j'ai&%rquelque chose qu'il " + "veut%w...^...Hé, pourquoi tu regardes&mes poches comme ça?", + }, }; void BuildGoronMessage(uint16_t* textId, bool* loadFromMessageTable) { diff --git a/soh/soh/Enhancements/randomizer/RCToRandInf.cpp b/soh/soh/Enhancements/randomizer/RCToRandInf.cpp index a579d6eb8a..24a3440ae4 100644 --- a/soh/soh/Enhancements/randomizer/RCToRandInf.cpp +++ b/soh/soh/Enhancements/randomizer/RCToRandInf.cpp @@ -3091,6 +3091,19 @@ std::map rcToRandomizerInf = { RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4 }, { RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5 }, + { RC_FIRE_TEMPLE_NEAR_BOSS_GORON, RAND_INF_FIRE_TEMPLE_NEAR_BOSS_GORON }, + { RC_FIRE_TEMPLE_BOSS_KEY_GORON, RAND_INF_FIRE_TEMPLE_BOSS_KEY_GORON }, + { RC_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON, RAND_INF_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON }, + { RC_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, RAND_INF_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON }, + { RC_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON, RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON }, + { RC_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON, RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON }, + { RC_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON, RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON }, + { RC_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON, RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON }, + { RC_FIRE_TEMPLE_HIGHEST_GORON, RAND_INF_FIRE_TEMPLE_HIGHEST_GORON }, + { RC_FIRE_TEMPLE_MQ_MAP_GORON, RAND_INF_FIRE_TEMPLE_MQ_MAP_GORON }, + { RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON, RAND_INF_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON }, + { RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, RAND_INF_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON }, + { RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON, RAND_INF_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON }, { RC_SHADOW_N_UNDER_BLADE_SILVER, RAND_INF_RC_SHADOW_N_UNDER_BLADE_SILVER }, { RC_SHADOW_TALL_BLOCK_BLADE_SILVER, RAND_INF_RC_SHADOW_TALL_BLOCK_BLADE_SILVER }, { RC_SHADOW_N_NOOK_BLADE_SILVER, RAND_INF_RC_SHADOW_N_NOOK_BLADE_SILVER }, diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp index 9d30135096..44f9adf2a7 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -208,6 +208,7 @@ void Context::GenerateLocationPool() { (location.GetRCType() == RCTYPE_BUSH && !mOptions[RSK_SHUFFLE_BUSHES]) || (location.GetRCType() == RCTYPE_ICICLE && !mOptions[RSK_SHUFFLE_ICICLES]) || (location.GetRCType() == RCTYPE_RED_ICE && !mOptions[RSK_SHUFFLE_RED_ICE]) || + (location.GetRCType() == RCTYPE_FIRE_GORON && !mOptions[RSK_SHUFFLE_FIRE_GORONS]) || (location.GetRCType() == RCTYPE_SIGN && mOptions[RSK_SHUFFLE_SIGNS].Is(RO_SHUFFLE_SIGNS_OFF)) || (location.GetRCType() == RCTYPE_WONDER_ITEM && mOptions[RSK_SHUFFLE_WONDER_ITEMS].Is(RO_SHUFFLE_WONDER_ITEMS_OFF)) || diff --git a/soh/soh/Enhancements/randomizer/ShuffleFireGorons.cpp b/soh/soh/Enhancements/randomizer/ShuffleFireGorons.cpp new file mode 100644 index 0000000000..0141a09df8 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShuffleFireGorons.cpp @@ -0,0 +1,173 @@ +#include + +#include "soh/OTRGlobals.h" +#include "soh/ResourceManagerHelpers.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ObjectExtension/ObjectExtension.h" +#include "soh/Enhancements/randomizer/randomizer.h" +#include "soh/Enhancements/randomizer/RCToRandInf.h" +#include "soh/ShipInit.hpp" +#include "static_data.h" +#include "item_category_adj.h" +#include "particle_cmc.h" +#include + +extern "C" { +#include "macros.h" +#include "functions.h" +#include "overlays/actors/ovl_En_Go2/z_en_go2.h" +extern PlayState* gPlayState; +} + +static s32 FireGoron_GetSwitchFlag(Actor* actor) { + return (actor->params & 0xFC00) >> 0xA; +} + +static bool FireGoron_IsFireGoron(Actor* actor) { + return (actor->params & 0x1F) == GORON_FIRE_GENERIC; +} + +static CheckIdentity IdentifyFireGoron(Actor* actor) { + CheckIdentity goronIdentity; + + goronIdentity.randomizerInf = RAND_INF_MAX; + goronIdentity.randomizerCheck = RC_UNKNOWN_CHECK; + + Rando::Location* location = OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor( + ACTOR_EN_GO2, gPlayState->sceneNum, FireGoron_GetSwitchFlag(actor)); + + IdentifyCheck(&goronIdentity, location); + + return goronIdentity; +} + +static bool FireGoron_IdentityHoldsItem(const CheckIdentity& goronIdentity) { + return goronIdentity.randomizerCheck != RC_UNKNOWN_CHECK && !Flags_GetRandomizerInf(goronIdentity.randomizerInf); +} + +static bool FireGoron_HoldsItem(Actor* actor) { + const auto goronIdentity = ObjectExtension::GetInstance().Get(actor); + return goronIdentity != nullptr && FireGoron_IdentityHoldsItem(*goronIdentity); +} + +static void FireGoron_SpawnSparkles(Actor* actor, const CheckIdentity& goronIdentity) { + if (actor->xzDistToPlayer > 1000.0f) { + return; + } + + bool cmc = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), 0); + int requiresStoneAgony = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); + int isNotCMC = !cmc || (requiresStoneAgony && !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)); + + GetItemEntry goronItem = + Rando::Context::GetInstance()->GetFinalGIEntry(goronIdentity.randomizerCheck, true, GI_NONE); + GetItemCategory getItemCategory = isNotCMC ? ITEM_CATEGORY_MAJOR : Randomizer_AdjustItemCategory(goronItem); + + Color_RGBA8 primColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_PRIMARY); + Color_RGBA8 secColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_SECONDARY); + Color_RGBA8 envColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_FLARE); + + Vec3f pos; + Vec3f velocity = { 0.0f, -0.05f, 0.0f }; + Vec3f accel = { 0.0f, -0.025f, 0.0f }; + + pos.x = Rand_CenteredFloat(10.0f) + actor->world.pos.x; + pos.y = (Rand_ZeroOne() * 10.0f) + actor->world.pos.y + 60.0f; + pos.z = Rand_CenteredFloat(10.0f) + actor->world.pos.z; + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, &secColor, &envColor, 2000, 100); + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, &primColor, &envColor, 2000, 100); +} + +static void FireGoron_RandomizerUpdate(void* actorRef) { + Actor* actor = static_cast(actorRef); + + if (FireGoron_HoldsItem(actor)) { + const auto& goronIdentity = *ObjectExtension::GetInstance().Get(actor); + if (!Rando::Context::GetInstance()->GetItemLocation(goronIdentity.randomizerCheck)->IsExcluded()) { + FireGoron_SpawnSparkles(actor, goronIdentity); + } + } +} + +static void FireGoron_LogUnrecognized(Actor* actor) { + SPDLOG_WARN("Unrecognized Fire Temple goron: scene=0x{:02X} room={} quest={} params=0x{:04X} type={} " + "switchFlag={} pos=({},{},{}) rotY=0x{:04X}", + gPlayState->sceneNum, actor->room, + ResourceMgr_IsSceneMasterQuest(gPlayState->sceneNum) ? "MQ" : "Vanilla", + static_cast(actor->params), actor->params & 0x1F, FireGoron_GetSwitchFlag(actor), + static_cast(actor->world.pos.x), static_cast(actor->world.pos.y), + static_cast(actor->world.pos.z), static_cast(actor->shape.rot.y)); +} + +static void FireGoron_RandomizerInit(void* actorRef) { + Actor* actor = static_cast(actorRef); + + if (!FireGoron_IsFireGoron(actor)) { + return; + } + + auto goronIdentity = IdentifyFireGoron(actor); + if (goronIdentity.randomizerCheck == RC_UNKNOWN_CHECK) { + return; + } + + ObjectExtension::GetInstance().Set(actor, std::move(goronIdentity)); + + if (FireGoron_HoldsItem(actor) && Flags_GetSwitch(gPlayState, FireGoron_GetSwitchFlag(actor))) { + EnGo2_GetItemAnimation(reinterpret_cast(actor), gPlayState); + } +} + +void RegisterShuffleFireGorons() { + bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_FIRE_GORONS); + + COND_ID_HOOK(OnActorInit, ACTOR_EN_GO2, shouldRegister, FireGoron_RandomizerInit); + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_GO2, shouldRegister, FireGoron_RandomizerUpdate); + + COND_VB_SHOULD(VB_DESPAWN_FREED_FIRE_GORON, shouldRegister, { + EnGo2* goron = va_arg(args, EnGo2*); + auto goronIdentity = IdentifyFireGoron(&goron->actor); + + if (*should && FireGoron_IdentityHoldsItem(goronIdentity) && + !Rando::Context::GetInstance()->GetItemLocation(goronIdentity.randomizerCheck)->IsExcluded()) { + *should = false; + } + }); + + COND_VB_SHOULD(VB_GIVE_ITEM_FROM_FIRE_GORON, shouldRegister, { + EnGo2* goron = va_arg(args, EnGo2*); + + if (FireGoron_HoldsItem(&goron->actor)) { + Flags_SetRandomizerInf(ObjectExtension::GetInstance().Get(&goron->actor)->randomizerInf); + } else if (ObjectExtension::GetInstance().Get(&goron->actor) == nullptr) { + FireGoron_LogUnrecognized(&goron->actor); + } + }); +} + +static RegisterShipInitFunc initFunc_ShuffleFireGorons(RegisterShuffleFireGorons, { "IS_RANDO" }); + +void Rando::StaticData::RegisterFireGoronLocations() { + static bool registered = false; + if (registered) + return; + registered = true; + // clang-format off + // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Hint Text Key Spoiler Collection Check + locationTable[RC_FIRE_TEMPLE_NEAR_BOSS_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_NEAR_BOSS_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 8, "Near Boss Goron", RHT_FIRE_TEMPLE_NEAR_BOSS_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_NEAR_BOSS_GORON)); + locationTable[RC_FIRE_TEMPLE_BOSS_KEY_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_BOSS_KEY_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 9, "Boss Key Goron", RHT_FIRE_TEMPLE_BOSS_KEY_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_BOSS_KEY_GORON)); + locationTable[RC_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 1, "Big Lava Room Lower Open Door Goron", RHT_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON)); + locationTable[RC_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 2, "Big Lava Room Blocked Door Goron", RHT_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON)); + locationTable[RC_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 3, "Boulder Maze Lower Goron", RHT_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON)); + locationTable[RC_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 4, "Boulder Maze Shortcut Goron", RHT_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON)); + locationTable[RC_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 5, "Boulder Maze Side Room Goron", RHT_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON)); + locationTable[RC_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 10, "Boulder Maze Upper Goron", RHT_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON)); + locationTable[RC_FIRE_TEMPLE_HIGHEST_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_HIGHEST_GORON, RCQUEST_VANILLA, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 11, "Highest Goron", RHT_FIRE_TEMPLE_HIGHEST_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_HIGHEST_GORON)); + locationTable[RC_FIRE_TEMPLE_MQ_MAP_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_MQ_MAP_GORON, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 9, "MQ Map Goron", RHT_FIRE_TEMPLE_MQ_MAP_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_MAP_GORON)); + locationTable[RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 1, "MQ Big Lava Room Open Door Goron", RHT_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON)); + locationTable[RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 2, "MQ Big Lava Room Blocked Door Goron", RHT_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON)); + locationTable[RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON] = Location::FireGoron(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, 5, "MQ Lizalfos Maze Side Room Goron", RHT_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON)); + // clang-format on +} + +static RegisterShipInitFunc registerFireGoronLocations(Rando::StaticData::RegisterFireGoronLocations); diff --git a/soh/soh/Enhancements/randomizer/location.cpp b/soh/soh/Enhancements/randomizer/location.cpp index a530051a81..0045ee47ca 100644 --- a/soh/soh/Enhancements/randomizer/location.cpp +++ b/soh/soh/Enhancements/randomizer/location.cpp @@ -695,4 +695,12 @@ Rando::Location Rando::Location::RedIce(RandomizerCheck rc, RandomizerCheckQuest return { rc, quest_, RCTYPE_RED_ICE, area_, ACTOR_BG_ICE_SHELTER, scene_, actorParams_, std::move(shortName_), hintKey, RG_NONE, false, collectionCheck }; +} + +Rando::Location Rando::Location::FireGoron(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck) { + return { rc, quest_, RCTYPE_FIRE_GORON, area_, ACTOR_EN_GO2, + scene_, actorParams_, std::move(shortName_), hintKey, RG_NONE, + false, collectionCheck }; } \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/location.h b/soh/soh/Enhancements/randomizer/location.h index 60b850fbdc..fd0117d767 100644 --- a/soh/soh/Enhancements/randomizer/location.h +++ b/soh/soh/Enhancements/randomizer/location.h @@ -264,6 +264,10 @@ class Location { int32_t actorParams_, std::string&& shortName_, RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck); + static Location FireGoron(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck); + static Location OtherHint(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, ActorID actorId_, SceneID scene_, std::string&& shortName_, std::string&& spoilerName_); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 7725ce97e6..f2c639d8f9 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -28,6 +28,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_NEAR_BOSS_ROOM] = Region("Fire Temple Near Boss Room", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_NEAR_BOSS_CHEST, logic->FireTimer() >= 16 && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_NEAR_BOSS_GORON, logic->FireTimer() >= 16 && logic->HasItem(RG_SPEAK_GORON)), //It's plausible to get the pots with rang from the larger square platform, but it's a blind shot that likely needs a setup //and I've only been able to get the nearest 2, regardless it's a trick and probably a specific one like GY crate freestanding with rang }, { @@ -112,6 +113,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_LOOP_GORON_CAGE] = Region("Fire Temple Loop Goron Cage", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_BOSS_KEY_CHEST, logic->CanOpenLargeChest()), + LOCATION(RC_FIRE_TEMPLE_BOSS_KEY_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_LOOP_CAGE_SWITCH, logic->Get(LOGIC_FIRE_LOOP_SWITCH)), @@ -148,6 +150,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_1F_CURVED_CAGE] = Region("Fire Temple 1F Curved Cage", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_BIG_LAVA_ROOM, true), @@ -166,6 +169,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_STRAIGHTFORWARD_CAGE] = Region("Fire Temple Straightforward Cage", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_BIG_LAVA_ROOM, true), @@ -223,6 +227,7 @@ void RegionTable_Init_FireTemple() { }, { //Locations LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_SHORTCUT_ROOM, true), @@ -232,6 +237,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_BOULDER_MAZE_LOWER] = Region("Fire Temple Boulder Maze Lower", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON, logic->HasItem(RG_SPEAK_GORON)), LOCATION(RC_FIRE_TEMPLE_GS_BOULDER_MAZE, logic->HasExplosives() && (logic->IsAdult || logic->HookshotOrBoomerang() || logic->CanGroundJumpslash())), }, { //Exits @@ -244,6 +250,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_3F_CURVED_CAGE] = Region("Fire Temple 3F Curved Cage", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_BOULDER_MAZE_LOWER, true), @@ -305,6 +312,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_BOULDER_MAZE_UPPER] = Region("Fire Temple Boulder Maze Upper", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_UPPER_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_SHORTCUT_CLIMB, logic->HasExplosives()), @@ -410,6 +418,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_SOT_CAGE_SWITCH] = Region("Fire Temple Sot Cage Switch", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_HIGHEST_GORON_CHEST, (logic->CanUse(RG_SONG_OF_TIME) || ctx->GetTrickOption(RT_VISIBLE_COLLISION)) && logic->CanUse(RG_MEGATON_HAMMER) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_HIGHEST_GORON, (logic->CanUse(RG_SONG_OF_TIME) || ctx->GetTrickOption(RT_VISIBLE_COLLISION)) && logic->CanUse(RG_MEGATON_HAMMER) && logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, false), @@ -592,6 +601,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_MQ_LOOP_GORON_CAGE] = Region("Fire Temple MQ Loop Goron Cage", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_MQ_MAP_CHEST, logic->CanOpenLargeChest()), + LOCATION(RC_FIRE_TEMPLE_MQ_MAP_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_LOOP_CAGE_SWITCH, logic->Get(LOGIC_FIRE_OPENED_LOWEST_GORON_CAGE)), @@ -708,6 +718,7 @@ void RegionTable_Init_FireTemple() { //Locations //implies CanGetEnemyDrop(RE_GOLD_SKULLTULA) LOCATION(RC_FIRE_TEMPLE_MQ_GS_BIG_LAVA_ROOM_OPEN_DOOR, logic->CanUse(RG_MEGATON_HAMMER)), + LOCATION(RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON, logic->CanUse(RG_MEGATON_HAMMER) && logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM, true), @@ -716,6 +727,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_MQ_TORCH_LOCKED_CAGE] = Region("Fire Temple MQ Torch Locked Cage", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_CHEST, logic->HasFireSource() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON, logic->HasFireSource() && logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_BIG_LAVA_BLOCKED_DOOR, true), @@ -829,6 +841,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_MQ_3F_CURVED_CAGE] = Region("Fire Temple MQ 3F Curved Cage", SCENE_FIRE_TEMPLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON, logic->HasItem(RG_SPEAK_GORON)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_MAZE_SWITCH_DOOR, true), diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h index 884f8c68a2..e06269f6a1 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h @@ -3533,6 +3533,19 @@ RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2) RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3) RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4) RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_NEAR_BOSS_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_BOSS_KEY_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_HIGHEST_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_MAP_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON) RANDO_ENUM_ITEM(RC_MAX) RANDO_ENUM_END(RandomizerCheck) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h index a17a71818a..4d923b06f8 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h @@ -1701,6 +1701,20 @@ RANDO_ENUM_ITEM(RHT_RED_ICE_ZORAS_DOMAIN) RANDO_ENUM_ITEM(RHT_GERUDO_TRAINING_GROUND_RED_ICE) RANDO_ENUM_ITEM(RHT_ICE_CAVERN_RED_ICE) RANDO_ENUM_ITEM(RHT_GANONS_CASTLE_RED_ICE) +// FIRE TEMPLE GORONS +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_NEAR_BOSS_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_BOSS_KEY_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_HIGHEST_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_MQ_MAP_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON) +RANDO_ENUM_ITEM(RHT_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON) // MAX RANDO_ENUM_ITEM(RHT_MAX) RANDO_ENUM_END(RandomizerHintTextKey) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h index 76dadee6f9..d15be0ffad 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h @@ -3097,6 +3097,19 @@ RANDO_ENUM_ITEM(RAND_INF_HAS_FOUND_DEKU_SHIELD) RANDO_ENUM_ITEM(RAND_INF_HAS_FOUND_HYLIAN_SHIELD) RANDO_ENUM_ITEM(RAND_INF_HAS_FOUND_GORON_TUNIC) RANDO_ENUM_ITEM(RAND_INF_HAS_FOUND_ZORA_TUNIC) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_NEAR_BOSS_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_BOSS_KEY_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_BIG_LAVA_ROOM_LOWER_OPEN_DOOR_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_LOWER_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_SHORTCUT_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_SIDE_ROOM_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_BOULDER_MAZE_UPPER_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_HIGHEST_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_MAP_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_OPEN_DOOR_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM_BLOCKED_DOOR_GORON) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_SIDE_ROOM_GORON) RANDO_ENUM_ITEM(RAND_INF_MAX) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h index 1d9b9c1e0a..9e5309d8ff 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h @@ -139,6 +139,7 @@ RANDO_ENUM_ITEM(RCTYPE_SIGN) // Signs RANDO_ENUM_ITEM(RCTYPE_BEGGAR) // Beggar RANDO_ENUM_ITEM(RCTYPE_ICICLE) // Icicles RANDO_ENUM_ITEM(RCTYPE_RED_ICE) // Red Ice +RANDO_ENUM_ITEM(RCTYPE_FIRE_GORON) // Fire Temple Gorons RANDO_ENUM_END(RandomizerCheckType) RANDO_ENUM_BEGIN(RandomizerCheckQuest) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h index 7f13f4b496..0b2ca16784 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h @@ -311,6 +311,7 @@ RANDO_ENUM_ITEM(RSK_SHUFFLE_SIGNS) RANDO_ENUM_ITEM(RSK_ROCS_FEATHER) RANDO_ENUM_ITEM(RSK_SHUFFLE_ICICLES) RANDO_ENUM_ITEM(RSK_SHUFFLE_RED_ICE) +RANDO_ENUM_ITEM(RSK_SHUFFLE_FIRE_GORONS) RANDO_ENUM_ITEM(RSK_STARTING_KEATON_MASK) RANDO_ENUM_ITEM(RSK_STARTING_SKULL_MASK) RANDO_ENUM_ITEM(RSK_STARTING_SPOOKY_MASK) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index c6b9920d25..0e1ba33220 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -210,6 +210,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleIcicles"), RO_GENERIC_NO)) && (location.GetRCType() != RCTYPE_RED_ICE || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleRedIce"), RO_GENERIC_NO)) && + (location.GetRCType() != RCTYPE_FIRE_GORON || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleFireGorons"), RO_GENERIC_NO)) && (location.GetRCType() != RCTYPE_FISH || Rando::Fishsanity::GetFishLocationIncluded(&location, FSO_SOURCE_CVARS)) && (location.GetRCType() != RCTYPE_ADULT_TRADE || diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 901cbe27d1..dbcfd9b409 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -99,6 +99,7 @@ bool showDungeonWonderItems; bool showBeggar; bool showIcicles; bool showRedIce; +bool showFireGorons; bool showFrogSongRupees; bool showFountainFairies; bool showStoneFairies; @@ -1801,6 +1802,7 @@ void LoadSettings() { showBeggar = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEGGAR); showIcicles = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ICICLES); showRedIce = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_RED_ICE); + showFireGorons = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FIRE_GORONS); } else { // Vanilla showOverworldTokens = true; showDungeonTokens = true; @@ -1822,6 +1824,7 @@ void LoadSettings() { showBeggar = false; showIcicles = false; showRedIce = false; + showFireGorons = false; } fortressFast = false; @@ -1950,6 +1953,7 @@ bool IsCheckShuffled(RandomizerCheck rc) { (showDungeonWonderItems && RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()))) && (loc->GetRCType() != RCTYPE_ICICLE || showIcicles) && (loc->GetRCType() != RCTYPE_RED_ICE || showRedIce) && + (loc->GetRCType() != RCTYPE_FIRE_GORON || showFireGorons) && (loc->GetRCType() != RCTYPE_FISH || Rando::Fishsanity::GetFishLocationIncluded(loc)) && (loc->GetRCType() != RCTYPE_FREESTANDING || (showOverworldFreestanding && RandomizerCheckObjects::AreaIsOverworld(loc->GetArea())) || diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index b373eba2a2..69c7053596 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -869,6 +869,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_SHUFFLE_BUSHES, CVAR_RANDOMIZER_SETTING("ShuffleBushes")); OPT_BOOL(RSK_SHUFFLE_ICICLES, CVAR_RANDOMIZER_SETTING("ShuffleIcicles")); OPT_BOOL(RSK_SHUFFLE_RED_ICE, CVAR_RANDOMIZER_SETTING("ShuffleRedIce")); + OPT_BOOL(RSK_SHUFFLE_FIRE_GORONS, CVAR_RANDOMIZER_SETTING("ShuffleFireGorons")); OPT_U8(RSK_SHUFFLE_SIGNS, {"Off", "Dungeons", "Overworld", "All Signs"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSigns"), WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_SIGNS_OFF); OPT_BOOL(RSK_SHUFFLE_FISHING_POLE, CVAR_RANDOMIZER_SETTING("ShuffleFishingPole")); OPT_CALLBACK(RSK_SHUFFLE_FISHING_POLE, { @@ -2050,6 +2051,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_BUSHES], &mOptions[RSK_SHUFFLE_ICICLES], &mOptions[RSK_SHUFFLE_RED_ICE], + &mOptions[RSK_SHUFFLE_FIRE_GORONS], &mOptions[RSK_SHUFFLE_SIGNS], &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], &mOptions[RSK_SHUFFLE_ADULT_TRADE], @@ -2285,6 +2287,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_BUSHES], &mOptions[RSK_SHUFFLE_ICICLES], &mOptions[RSK_SHUFFLE_RED_ICE], + &mOptions[RSK_SHUFFLE_FIRE_GORONS], &mOptions[RSK_SHUFFLE_SIGNS], &mOptions[RSK_SHUFFLE_KOKIRI_SWORD], &mOptions[RSK_SHUFFLE_OCARINA], diff --git a/soh/soh/Enhancements/randomizer/static_data.h b/soh/soh/Enhancements/randomizer/static_data.h index 837d596b56..5694191712 100644 --- a/soh/soh/Enhancements/randomizer/static_data.h +++ b/soh/soh/Enhancements/randomizer/static_data.h @@ -72,6 +72,7 @@ class StaticData { static void RegisterBeggarLocations(); static void RegisterIcicleLocations(); static void RegisterRedIceLocations(); + static void RegisterFireGoronLocations(); static void InitHashMaps(); static std::array, 17> randomizerFishingPondFish; static std::unordered_map randomizerGrottoFishMap; diff --git a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c index 8d95c53eff..7c56b855fd 100644 --- a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c +++ b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c @@ -1608,7 +1608,8 @@ void EnGo2_Init(Actor* thisx, PlayState* play) { EnGo2_SetupRolling(this, play); break; case GORON_FIRE_GENERIC: - if (Flags_GetSwitch(play, (this->actor.params & 0xFC00) >> 0xA)) { + if (GameInteractor_Should(VB_DESPAWN_FREED_FIRE_GORON, + Flags_GetSwitch(play, (this->actor.params & 0xFC00) >> 0xA), this)) { Actor_Kill(&this->actor); } else { this->isAwake = true; @@ -1930,6 +1931,7 @@ void EnGo2_GoronFireGenericAction(EnGo2* this, PlayState* play) { switch (this->goronState) { case 0: // Wake up if (Message_GetState(&play->msgCtx) == TEXT_STATE_CLOSING) { + GameInteractor_Should(VB_GIVE_ITEM_FROM_FIRE_GORON, true, this); if (GameInteractor_Should(VB_PLAY_GORON_FREE_CS, true)) { EnGo2_GoronFireCamera(this, play); play->msgCtx.msgMode = MSGMODE_PAUSED;