summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Cox <garrettjcox@gmail.com>2024-12-02 18:56:32 -0600
committerGitHub <noreply@github.com>2024-12-02 17:56:32 -0700
commitbbe3bb72b6ab53e05a30143638535110f69423b7 (patch)
treeebe399b323c32b3bdece65958c48035e761ad72a
parent4edb83e2f3c3be1d288f5898118f7857ea8d2817 (diff)
VBify Ruto (#4602)
-rw-r--r--soh/include/z64save.h16
-rw-r--r--soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/MoveJabuJabuElevator.cpp27
-rw-r--r--soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp93
-rw-r--r--soh/soh/Enhancements/TimeSavers/TimeSavers.cpp2
-rw-r--r--soh/soh/Enhancements/TimeSavers/TimeSavers.h2
-rw-r--r--soh/soh/Enhancements/game-interactor/GameInteractor.h13
-rw-r--r--soh/soh/Enhancements/randomizer/hook_handlers.cpp6
-rw-r--r--soh/soh/Enhancements/randomizer/savefile.cpp7
-rw-r--r--soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c38
9 files changed, 175 insertions, 29 deletions
diff --git a/soh/include/z64save.h b/soh/include/z64save.h
index bf0d20697..b64efb459 100644
--- a/soh/include/z64save.h
+++ b/soh/include/z64save.h
@@ -735,14 +735,14 @@ typedef enum {
#define INFTABLE_12A 0x12A
#define INFTABLE_138 0x138
#define INFTABLE_139 0x139
-#define INFTABLE_140 0x140
-#define INFTABLE_RUTO_IN_JJ_MEET_RUTO 0x141
-#define INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME 0x142
-#define INFTABLE_143 0x143
-#define INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE 0x144
-#define INFTABLE_145 0x145
-#define INFTABLE_146 0x146
-#define INFTABLE_147 0x147
+#define INFTABLE_140 0x140 // Left her on blue switch in fork room (causes her to spawn in fork room)
+#define INFTABLE_RUTO_IN_JJ_MEET_RUTO 0x141 // Jumped down hole from hole room
+#define INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME 0x142 // in the basement
+#define INFTABLE_143 0x143 // Sat down in basement (causes her to get upset if this is set when actor is spawned)
+#define INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE 0x144 // Entered the room with the sapphire
+#define INFTABLE_145 0x145 // Thrown to sapphire (not kidnapped yet)
+#define INFTABLE_146 0x146 // Kidnapped
+#define INFTABLE_147 0x147 // Brought ruto back up to holes room, causes her to spawn in holes room instead of basement
#define INFTABLE_160 0x160
#define INFTABLE_161 0x161
#define INFTABLE_162 0x162
diff --git a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/MoveJabuJabuElevator.cpp b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/MoveJabuJabuElevator.cpp
new file mode 100644
index 000000000..1e1c6ceb5
--- /dev/null
+++ b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/MoveJabuJabuElevator.cpp
@@ -0,0 +1,27 @@
+#include "soh/Enhancements/game-interactor/GameInteractor.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh/OTRGlobals.h"
+
+extern "C" {
+#include "src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.h"
+}
+
+/**
+ * Adjusts the behavior of the elevator to start near the bottom if you are entering the room from the bottom
+ */
+void MoveJabuJabuElevator_Register() {
+ GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorInit>(ACTOR_BG_BDAN_OBJECTS, [](void* actorRef) {
+ Player* player = GET_PLAYER(gPlayState);
+ BgBdanObjects* bgBdanObjects = static_cast<BgBdanObjects*>(actorRef);
+
+ if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
+ return;
+ }
+
+ if (bgBdanObjects->dyna.actor.params == 1) {
+ if (player->actor.world.pos.y < -500.0f) {
+ bgBdanObjects->timer = 220;
+ }
+ }
+ });
+}
diff --git a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp
new file mode 100644
index 000000000..aaaf1b39f
--- /dev/null
+++ b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp
@@ -0,0 +1,93 @@
+#include "soh/Enhancements/game-interactor/GameInteractor.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh/OTRGlobals.h"
+
+extern "C" {
+#include "overlays/actors/ovl_En_Ru1/z_en_ru1.h"
+#include "assets/objects/object_ru1/object_ru1.h"
+
+Actor* func_80AEB124(PlayState* play);
+}
+
+void SkipChildRutoInteractions_Register() {
+ // Skips the Child Ruto introduction cutscene, where she drops down into the hole in Jabu-Jabu's Belly
+ REGISTER_VB_SHOULD(VB_PLAY_CHILD_RUTO_INTRO, {
+ EnRu1* enRu1 = va_arg(args, EnRu1*);
+
+ if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
+ return;
+ }
+
+ Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO);
+ Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME);
+ Flags_SetInfTable(INFTABLE_143);
+ enRu1->drawConfig = 1;
+ enRu1->actor.world.pos.x = 127.0f;
+ enRu1->actor.world.pos.y = -340.0f;
+ enRu1->actor.world.pos.z = -3041.0f;
+ enRu1->actor.shape.rot.y = enRu1->actor.world.rot.y = -5098;
+
+ if (*should) {
+ Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildTurnAroundAnim, 1.0f, 0,
+ Animation_GetLastFrame((void*)&gRutoChildTurnAroundAnim), ANIMMODE_ONCE, -8.0f);
+ enRu1->action = 10;
+ }
+
+ *should = false;
+ });
+
+ // Skips a short dialogue sequence where Ruto tells you to throw her to the Sapphire
+ REGISTER_VB_SHOULD(VB_RUTO_WANT_TO_BE_TOSSED_TO_SAPPHIRE, {
+ if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
+ return;
+ }
+
+ if (*should) {
+ Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE);
+ *should = false;
+ }
+ });
+
+ // Prevents Ruto from running to the Sapphire when she wants to be tossed to it, instead she just stands up and waits for link to get closer
+ REGISTER_VB_SHOULD(VB_RUTO_RUN_TO_SAPPHIRE, {
+ EnRu1* enRu1 = va_arg(args, EnRu1*);
+ DynaPolyActor* dynaPolyActor = va_arg(args, DynaPolyActor*);
+
+ if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
+ return;
+ }
+
+ if (*should) {
+ enRu1->unk_28C = (BgBdanObjects*)dynaPolyActor;
+ Flags_SetInfTable(INFTABLE_145);
+ Flags_SetSwitch(gPlayState, 0x02);
+ Flags_SetSwitch(gPlayState, 0x1F);
+ enRu1->action = 42;
+ Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildWait2Anim, 1.0f, 0,
+ Animation_GetLastFrame((void*)&gRutoChildWait2Anim), ANIMMODE_LOOP, -8.0f);
+ enRu1->unk_28C->cameraSetting = 1;
+ Actor* sapphire = func_80AEB124(gPlayState);
+ if (sapphire != NULL) {
+ Actor_Kill(sapphire);
+ }
+ enRu1->actor.room = gPlayState->roomCtx.curRoom.num;
+ *should = false;
+ }
+ });
+
+ // This overrides the behavior that causes Ruto to get upset at you before sitting back down again when INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME is set
+ GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorInit>(ACTOR_EN_RU1, [](void* actorRef) {
+ EnRu1* enRu1 = static_cast<EnRu1*>(actorRef);
+ if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
+ return;
+ }
+
+ if (enRu1->action == 22) {
+ enRu1->action = 27;
+ enRu1->drawConfig = 1;
+ enRu1->actor.flags |= ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY;
+ Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildSittingAnim, 1.0f, 0.0f,
+ Animation_GetLastFrame((void*)&gRutoChildSittingAnim), ANIMMODE_LOOP, 0.0f);
+ }
+ });
+}
diff --git a/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp b/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp
index 6814b9332..a198f8289 100644
--- a/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp
+++ b/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp
@@ -10,7 +10,9 @@ void TimeSavers_Register() {
SkipZeldaFleeingCastle_Register();
SkipIntro_Register();
// SkipMiscInteractions
+ MoveJabuJabuElevator_Register();
MoveMidoInKokiriForest_Register();
+ SkipChildRutoInteractions_Register();
FasterHeavyBlockLift_Register();
FasterRupeeAccumulator_Register();
}
diff --git a/soh/soh/Enhancements/TimeSavers/TimeSavers.h b/soh/soh/Enhancements/TimeSavers/TimeSavers.h
index 0cec3edfa..ad521c6c2 100644
--- a/soh/soh/Enhancements/TimeSavers/TimeSavers.h
+++ b/soh/soh/Enhancements/TimeSavers/TimeSavers.h
@@ -12,7 +12,9 @@ void TimeSavers_Register();
void SkipZeldaFleeingCastle_Register();
void SkipIntro_Register();
// SkipMiscInteractions
+ void MoveJabuJabuElevator_Register();
void MoveMidoInKokiriForest_Register();
+ void SkipChildRutoInteractions_Register();
void FasterHeavyBlockLift_Register();
void FasterRupeeAccumulator_Register();
diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h
index 61cfbe72e..8b52b9b37 100644
--- a/soh/soh/Enhancements/game-interactor/GameInteractor.h
+++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h
@@ -343,6 +343,19 @@ typedef enum {
VB_GIVE_RANDO_FISHING_PRIZE,
VB_PLAY_THROW_ANIMATION,
VB_INFLICT_VOID_DAMAGE,
+ // Vanilla condition: Close enough & various cutscene checks
+ // Opt: *EnRu1
+ VB_PLAY_CHILD_RUTO_INTRO,
+ // Vanilla condition: !INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE && in the big okto room
+ // Opt: *EnRu1
+ VB_RUTO_WANT_TO_BE_TOSSED_TO_SAPPHIRE,
+ // Vanilla condition: Landed on the platform in the big okto room
+ // Opt: *EnRu1
+ VB_RUTO_RUN_TO_SAPPHIRE,
+ // Vanilla condition: !Flags_GetInfTable(INFTABLE_145)
+ // Opt: *EnRu1
+ VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED,
+
/*** Give Items ***/
diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp
index 6ff188b97..000081cec 100644
--- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp
+++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp
@@ -1552,6 +1552,12 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
*should = false;
break;
}
+ // We need to override the vanilla behavior here because the player might sequence break and get Ruto kidnapped before accessing other
+ // checks that require Ruto. So if she's kidnapped we allow her to spawn again
+ case VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED: {
+ *should = !Flags_GetInfTable(INFTABLE_145) || Flags_GetInfTable(INFTABLE_146);
+ break;
+ }
case VB_FREEZE_ON_SKULL_TOKEN:
case VB_TRADE_TIMER_ODD_MUSHROOM:
case VB_TRADE_TIMER_FROG:
diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp
index 02a31e7b5..0012c8311 100644
--- a/soh/soh/Enhancements/randomizer/savefile.cpp
+++ b/soh/soh/Enhancements/randomizer/savefile.cpp
@@ -253,10 +253,11 @@ extern "C" void Randomizer_InitSaveFile() {
// Flags_SetInfTable(INFTABLE_CHILD_MALON_SAID_EPONA_WAS_AFRAID_OF_YOU);
// Flags_SetInfTable(INFTABLE_SPOKE_TO_INGO_ONCE_AS_ADULT);
+ // Now handled by cutscene skips
// Ruto already met in jabu and spawns down the hole immediately
- Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO);
- Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME);
- Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE);
+ // Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO);
+ // Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME);
+ // Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE);
// Now handled by cutscene skips
// Skip cutscenes before Nabooru fight
diff --git a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c
index 9b6e4601e..df5b7e106 100644
--- a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c
+++ b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c
@@ -8,6 +8,7 @@
#include "objects/object_ru1/object_ru1.h"
#include "vt.h"
#include "soh/ResourceManagerHelpers.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_CAN_PRESS_SWITCH)
@@ -763,14 +764,6 @@ void func_80AEC2C0(EnRu1* this, PlayState* play) {
func_80AEC070(this, play, something);
}
-// Convenience function used so that Ruto always spawns in Jabu in rando, even after she's been kidnapped
-// Equivalent to !Flags_GetInfTable(INFTABLE_145) in vanilla
-bool shouldSpawnRuto() {
- // Flags_GetInfTable(INFTABLE_146) check is to prevent Ruto from spawning during the short period of time when
- // she's on the Zora's Sapphire pedestal but hasn't been kidnapped yet (would result in multiple Rutos otherwise)
- return !Flags_GetInfTable(INFTABLE_145) || (IS_RANDO && (Flags_GetInfTable(INFTABLE_146)));
-}
-
void func_80AEC320(EnRu1* this, PlayState* play) {
s8 actorRoom;
@@ -778,7 +771,10 @@ void func_80AEC320(EnRu1* this, PlayState* play) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
this->action = 7;
EnRu1_SetMouthIndex(this, 1);
- } else if ((Flags_GetInfTable(INFTABLE_147)) && !Flags_GetInfTable(INFTABLE_140) && shouldSpawnRuto()) {
+ } else if (
+ Flags_GetInfTable(INFTABLE_147) && !Flags_GetInfTable(INFTABLE_140) &&
+ GameInteractor_Should(VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED, !Flags_GetInfTable(INFTABLE_145), this)
+ ) {
if (!func_80AEB020(this, play)) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
actorRoom = this->actor.room;
@@ -867,9 +863,9 @@ void func_80AEC780(EnRu1* this, PlayState* play) {
s32 pad;
Player* player = GET_PLAYER(play);
- if ((func_80AEC5FC(this, play)) && (!Play_InCsMode(play)) &&
+ if (GameInteractor_Should(VB_PLAY_CHILD_RUTO_INTRO, (func_80AEC5FC(this, play)) && (!Play_InCsMode(play)) &&
(!(player->stateFlags1 & (PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_CLIMBING_LADDER))) &&
- (player->actor.bgCheckFlags & 1)) {
+ (player->actor.bgCheckFlags & 1), this)) {
play->csCtx.segment = &D_80AF0880;
gSaveContext.cutsceneTrigger = 1;
@@ -1183,8 +1179,11 @@ void func_80AED414(EnRu1* this, PlayState* play) {
void func_80AED44C(EnRu1* this, PlayState* play) {
s8 actorRoom;
- if ((Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO)) && shouldSpawnRuto() && !Flags_GetInfTable(INFTABLE_140) &&
- !Flags_GetInfTable(INFTABLE_147)) {
+ if (
+ Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO) &&
+ GameInteractor_Should(VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED, !Flags_GetInfTable(INFTABLE_145), this) &&
+ !Flags_GetInfTable(INFTABLE_140) && !Flags_GetInfTable(INFTABLE_147)
+ ) {
if (!func_80AEB020(this, play)) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
actorRoom = this->actor.room;
@@ -1550,8 +1549,8 @@ s32 func_80AEE394(EnRu1* this, PlayState* play) {
colCtx = &play->colCtx;
floorBgId = this->actor.floorBgId; // necessary match, can't move this out of this block unfortunately
dynaPolyActor = DynaPoly_GetActor(colCtx, floorBgId);
- if (dynaPolyActor != NULL && dynaPolyActor->actor.id == ACTOR_BG_BDAN_OBJECTS &&
- dynaPolyActor->actor.params == 0 && !Player_InCsMode(play) && play->msgCtx.msgLength == 0) {
+ if (GameInteractor_Should(VB_RUTO_RUN_TO_SAPPHIRE, dynaPolyActor != NULL && dynaPolyActor->actor.id == ACTOR_BG_BDAN_OBJECTS &&
+ dynaPolyActor->actor.params == 0 && !Player_InCsMode(play) && play->msgCtx.msgLength == 0, this, dynaPolyActor)) {
func_80AEE02C(this);
play->csCtx.segment = &D_80AF10A4;
gSaveContext.cutsceneTrigger = 1;
@@ -1611,7 +1610,7 @@ s32 func_80AEE6D0(EnRu1* this, PlayState* play) {
s32 pad;
s8 curRoomNum = play->roomCtx.curRoom.num;
- if (!Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE) && (func_80AEB124(play) != 0)) {
+ if (GameInteractor_Should(VB_RUTO_WANT_TO_BE_TOSSED_TO_SAPPHIRE, !Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE) && (func_80AEB124(play) != 0), this)) {
if (!Player_InCsMode(play)) {
Animation_Change(&this->skelAnime, &gRutoChildSeesSapphireAnim, 1.0f, 0,
Animation_GetLastFrame(&gRutoChildSquirmAnim), ANIMMODE_LOOP, -8.0f);
@@ -2190,8 +2189,11 @@ void func_80AEFF40(EnRu1* this, PlayState* play) {
void func_80AEFF94(EnRu1* this, PlayState* play) {
s8 actorRoom;
- if ((Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO)) && (Flags_GetInfTable(INFTABLE_140)) && shouldSpawnRuto() &&
- (!(func_80AEB020(this, play)))) {
+ if (
+ Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO) && Flags_GetInfTable(INFTABLE_140) &&
+ GameInteractor_Should(VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED, !Flags_GetInfTable(INFTABLE_145), this) &&
+ (!(func_80AEB020(this, play)))
+ ) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
actorRoom = this->actor.room;
this->action = 22;