VBify Ruto (#4602)

This commit is contained in:
Garrett Cox 2024-12-02 18:56:32 -06:00 committed by GitHub
parent 4edb83e2f3
commit bbe3bb72b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 175 additions and 29 deletions

View File

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

View File

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

View File

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

View File

@ -10,7 +10,9 @@ void TimeSavers_Register() {
SkipZeldaFleeingCastle_Register();
SkipIntro_Register();
// SkipMiscInteractions
MoveJabuJabuElevator_Register();
MoveMidoInKokiriForest_Register();
SkipChildRutoInteractions_Register();
FasterHeavyBlockLift_Register();
FasterRupeeAccumulator_Register();
}

View File

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

View File

@ -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 ***/

View File

@ -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:

View File

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

View File

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