Adds Quick Boss Death checkbox and implements Phantom Ganon's quick death (#119)

* Adds Quick Boss Death checkbox and implements Phantom Ganon's quick death.

* Clarifies relocation comment.
This commit is contained in:
Christopher Leggett 2024-04-30 23:31:08 -04:00 committed by GitHub
parent 7e7445ebcc
commit baaa00569d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 80 additions and 56 deletions

View File

@ -376,6 +376,10 @@ typedef enum {
/*** Fixes ***/ /*** Fixes ***/
// Vanilla condition: false // Vanilla condition: false
GI_VB_FIX_SAW_SOFTLOCK, GI_VB_FIX_SAW_SOFTLOCK,
/*** Quick Boss Deaths ***/
// Vanilla condition: true
GI_VB_PHANTOM_GANON_DEATH_SCENE,
} GIVanillaBehavior; } GIVanillaBehavior;
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -24,6 +24,8 @@ extern "C" {
#include "src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h" #include "src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h"
#include "src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h" #include "src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h"
#include "src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h" #include "src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h"
#include <overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h>
#include <objects/object_gnd/object_gnd.h>
extern SaveContext gSaveContext; extern SaveContext gSaveContext;
extern PlayState* gPlayState; extern PlayState* gPlayState;
extern int32_t D_8011D3AC; extern int32_t D_8011D3AC;
@ -719,6 +721,31 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void*
} }
break; break;
} }
case GI_VB_PHANTOM_GANON_DEATH_SCENE: {
if (CVarGetInteger("gTimeSavers.SkipCutscene.QuickBossDeaths", IS_RANDO || IS_BOSS_RUSH)) {
*should = false;
BossGanondrof* pg = static_cast<BossGanondrof*>(opt);
Player* player = GET_PLAYER(gPlayState);
if (pg != nullptr && pg->work[GND_ACTION_STATE] == DEATH_SPASM) {
// Skip to death scream animation and move ganondrof to middle
pg->deathState = DEATH_SCREAM;
pg->timers[0] = 50;
AnimationHeader* screamAnim = (AnimationHeader*)gPhantomGanonScreamAnim;
Animation_MorphToLoop(&pg->skelAnime, screamAnim, -10.0f);
pg->actor.world.pos.x = GND_BOSSROOM_CENTER_X;
pg->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 83.0f;
pg->actor.world.pos.z = GND_BOSSROOM_CENTER_Z;
pg->actor.shape.rot.y = 0;
pg->work[GND_BODY_DECAY_INDEX] = 0;
Audio_PlayActorSound2(&pg->actor, NA_SE_EN_FANTOM_LAST);
// Move Player out of the center of the room
player->actor.world.pos.x = GND_BOSSROOM_CENTER_X - 200.0f;
player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z;
}
}
break;
}
} }
} }

View File

@ -581,6 +581,7 @@ void DrawEnhancementsMenu() {
CVarGetInteger("gTimeSavers.SkipCutscene.Story", IS_RANDO) && CVarGetInteger("gTimeSavers.SkipCutscene.Story", IS_RANDO) &&
CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", IS_RANDO) && CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", IS_RANDO) &&
CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", IS_RANDO) && CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", IS_RANDO) &&
CVarGetInteger("gTimeSavers.SkipCutscene.QuickBossDeaths", IS_RANDO) &&
CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", IS_RANDO) && CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", IS_RANDO) &&
CVarGetInteger("gTimeSavers.NoForcedDialog", IS_RANDO) && CVarGetInteger("gTimeSavers.NoForcedDialog", IS_RANDO) &&
CVarGetInteger("gTimeSavers.SkipOwlInteractions", IS_RANDO) && CVarGetInteger("gTimeSavers.SkipOwlInteractions", IS_RANDO) &&
@ -592,6 +593,7 @@ void DrawEnhancementsMenu() {
CVarGetInteger("gTimeSavers.SkipCutscene.Story", IS_RANDO) || CVarGetInteger("gTimeSavers.SkipCutscene.Story", IS_RANDO) ||
CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", IS_RANDO) || CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", IS_RANDO) ||
CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", IS_RANDO) || CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", IS_RANDO) ||
CVarGetInteger("gTimeSavers.SkipCutscene.QuickBossDeaths", IS_RANDO) ||
CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", IS_RANDO) || CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", IS_RANDO) ||
CVarGetInteger("gTimeSavers.NoForcedDialog", IS_RANDO) || CVarGetInteger("gTimeSavers.NoForcedDialog", IS_RANDO) ||
CVarGetInteger("gTimeSavers.SkipOwlInteractions", IS_RANDO) || CVarGetInteger("gTimeSavers.SkipOwlInteractions", IS_RANDO) ||
@ -608,6 +610,7 @@ void DrawEnhancementsMenu() {
CVarSetInteger("gTimeSavers.SkipCutscene.Story", 1); CVarSetInteger("gTimeSavers.SkipCutscene.Story", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.LearnSong", 1); CVarSetInteger("gTimeSavers.SkipCutscene.LearnSong", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.BossIntro", 1); CVarSetInteger("gTimeSavers.SkipCutscene.BossIntro", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.QuickBossDeaths", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.OnePoint", 1); CVarSetInteger("gTimeSavers.SkipCutscene.OnePoint", 1);
CVarSetInteger("gTimeSavers.NoForcedDialog", 1); CVarSetInteger("gTimeSavers.NoForcedDialog", 1);
CVarSetInteger("gTimeSavers.SkipOwlInteractions", 1); CVarSetInteger("gTimeSavers.SkipOwlInteractions", 1);
@ -619,6 +622,7 @@ void DrawEnhancementsMenu() {
CVarSetInteger("gTimeSavers.SkipCutscene.Story", 0); CVarSetInteger("gTimeSavers.SkipCutscene.Story", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.LearnSong", 0); CVarSetInteger("gTimeSavers.SkipCutscene.LearnSong", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.BossIntro", 0); CVarSetInteger("gTimeSavers.SkipCutscene.BossIntro", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.QuickBossDeaths", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.OnePoint", 0); CVarSetInteger("gTimeSavers.SkipCutscene.OnePoint", 0);
CVarSetInteger("gTimeSavers.NoForcedDialog", 0); CVarSetInteger("gTimeSavers.NoForcedDialog", 0);
CVarSetInteger("gTimeSavers.SkipOwlInteractions", 0); CVarSetInteger("gTimeSavers.SkipOwlInteractions", 0);
@ -633,6 +637,7 @@ void DrawEnhancementsMenu() {
UIWidgets::PaddedEnhancementCheckbox("Skip Story Cutscenes", "gTimeSavers.SkipCutscene.Story", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); UIWidgets::PaddedEnhancementCheckbox("Skip Story Cutscenes", "gTimeSavers.SkipCutscene.Story", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
UIWidgets::PaddedEnhancementCheckbox("Skip Song Cutscenes", "gTimeSavers.SkipCutscene.LearnSong", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); UIWidgets::PaddedEnhancementCheckbox("Skip Song Cutscenes", "gTimeSavers.SkipCutscene.LearnSong", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
UIWidgets::PaddedEnhancementCheckbox("Skip Boss Introductions", "gTimeSavers.SkipCutscene.BossIntro", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); UIWidgets::PaddedEnhancementCheckbox("Skip Boss Introductions", "gTimeSavers.SkipCutscene.BossIntro", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
UIWidgets::PaddedEnhancementCheckbox("Quick Boss Deaths", "gTimeSavers.SkipCutscene.QuickBossDeaths", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
UIWidgets::PaddedEnhancementCheckbox("Skip One Point Cutscenes (Chests, Door Unlocks, etc)", "gTimeSavers.SkipCutscene.OnePoint", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); UIWidgets::PaddedEnhancementCheckbox("Skip One Point Cutscenes (Chests, Door Unlocks, etc)", "gTimeSavers.SkipCutscene.OnePoint", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
UIWidgets::PaddedEnhancementCheckbox("No Forced Dialog", "gTimeSavers.NoForcedDialog", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); UIWidgets::PaddedEnhancementCheckbox("No Forced Dialog", "gTimeSavers.NoForcedDialog", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
UIWidgets::Tooltip("Prevent forced conversations with Navi or other NPCs"); UIWidgets::Tooltip("Prevent forced conversations with Navi or other NPCs");

View File

@ -12,19 +12,10 @@
#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" #include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
typedef enum {
/* 0 */ NOT_DEAD,
/* 1 */ DEATH_START,
/* 2 */ DEATH_THROES,
/* 3 */ DEATH_WARP,
/* 4 */ DEATH_SCREAM,
/* 5 */ DEATH_DISINTEGRATE,
/* 6 */ DEATH_FINISH
} BossGanondrofDeathState;
typedef enum { typedef enum {
/* 0 */ THROW_NORMAL, /* 0 */ THROW_NORMAL,
/* 1 */ THROW_SLOW /* 1 */ THROW_SLOW
@ -42,12 +33,6 @@ typedef enum {
/* 3 */ CHARGE_FINISH /* 3 */ CHARGE_FINISH
} BossGanondrofChargeAction; } BossGanondrofChargeAction;
typedef enum {
/* 0 */ DEATH_SPASM,
/* 1 */ DEATH_LIMP,
/* 2 */ DEATH_HUNCHED
} BossGanondrofDeathAction;
void BossGanondrof_Init(Actor* thisx, PlayState* play); void BossGanondrof_Init(Actor* thisx, PlayState* play);
void BossGanondrof_Destroy(Actor* thisx, PlayState* play); void BossGanondrof_Destroy(Actor* thisx, PlayState* play);
void BossGanondrof_Update(Actor* thisx, PlayState* play); void BossGanondrof_Update(Actor* thisx, PlayState* play);
@ -959,26 +944,13 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) {
case DEATH_THROES: case DEATH_THROES:
switch (this->work[GND_ACTION_STATE]) { switch (this->work[GND_ACTION_STATE]) {
case DEATH_SPASM: case DEATH_SPASM:
if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME]) && !IS_RANDO && !IS_BOSS_RUSH) { if (GameInteractor_Should(GI_VB_PHANTOM_GANON_DEATH_SCENE, true, this)) {
this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonAirDamageAnim); if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) {
Animation_Change(&this->skelAnime, &gPhantomGanonAirDamageAnim, 0.5f, 0.0f, this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonAirDamageAnim);
this->fwork[GND_END_FRAME], ANIMMODE_ONCE_INTERP, 0.0f); Animation_Change(&this->skelAnime, &gPhantomGanonAirDamageAnim, 0.5f, 0.0f,
this->work[GND_ACTION_STATE] = DEATH_LIMP; this->fwork[GND_END_FRAME], ANIMMODE_ONCE_INTERP, 0.0f);
} else if (IS_RANDO || IS_BOSS_RUSH) { this->work[GND_ACTION_STATE] = DEATH_LIMP;
// Skip to death scream animation and move ganondrof to middle }
this->deathState = DEATH_SCREAM;
this->timers[0] = 50;
Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonScreamAnim, -10.0f);
this->actor.world.pos.x = GND_BOSSROOM_CENTER_X;
this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 83.0f;
this->actor.world.pos.z = GND_BOSSROOM_CENTER_Z;
this->actor.shape.rot.y = 0;
this->work[GND_BODY_DECAY_INDEX] = 0;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAST);
// Move Player out of the center of the room
player->actor.world.pos.x = GND_BOSSROOM_CENTER_X - 200.0f;
player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z;
} }
break; break;
case DEATH_LIMP: case DEATH_LIMP:
@ -991,26 +963,25 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) {
bodyDecayLevel = 1; bodyDecayLevel = 1;
break; break;
} }
if (IS_RANDO || IS_BOSS_RUSH) { if (GameInteractor_Should(GI_VB_PHANTOM_GANON_DEATH_SCENE, true, NULL)) {
break; Math_ApproachS(&this->actor.shape.rot.y, this->work[GND_VARIANCE_TIMER] * -100, 5, 0xBB8);
} Math_ApproachF(&this->cameraNextEye.z, this->targetPos.z + 60.0f, 0.02f, 0.5f);
Math_ApproachS(&this->actor.shape.rot.y, this->work[GND_VARIANCE_TIMER] * -100, 5, 0xBB8); Math_ApproachF(&this->actor.world.pos.y, GND_BOSSROOM_CENTER_Y + 133.0f, 0.05f, 100.0f);
Math_ApproachF(&this->cameraNextEye.z, this->targetPos.z + 60.0f, 0.02f, 0.5f); this->actor.world.pos.y += Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500);
Math_ApproachF(&this->actor.world.pos.y, GND_BOSSROOM_CENTER_Y + 133.0f, 0.05f, 100.0f); this->cameraNextAt.x = this->targetPos.x;
this->actor.world.pos.y += Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); this->cameraNextAt.y = this->targetPos.y - 10.0f;
this->cameraNextAt.x = this->targetPos.x; this->cameraNextAt.z = this->targetPos.z;
this->cameraNextAt.y = this->targetPos.y - 10.0f; if (this->timers[0] == 0) {
this->cameraNextAt.z = this->targetPos.z; this->deathState = DEATH_WARP;
if (this->timers[0] == 0) { this->timers[0] = 350;
this->deathState = DEATH_WARP; this->timers[1] = 50;
this->timers[0] = 350; this->fwork[GND_CAMERA_ZOOM] = 300.0f;
this->timers[1] = 50; this->cameraNextEye.y = GND_BOSSROOM_CENTER_Y + 233.0f;
this->fwork[GND_CAMERA_ZOOM] = 300.0f; player->actor.world.pos.x = GND_BOSSROOM_CENTER_X - 200.0f;
this->cameraNextEye.y = GND_BOSSROOM_CENTER_Y + 233.0f; player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z;
player->actor.world.pos.x = GND_BOSSROOM_CENTER_X - 200.0f; holdCamera = true;
player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; bodyDecayLevel = 1;
holdCamera = true; }
bodyDecayLevel = 1;
} }
break; break;
case DEATH_WARP: case DEATH_WARP:

View File

@ -61,6 +61,23 @@ typedef enum {
/* 13 */ GND_FLOAT_COUNT = 13 /* 13 */ GND_FLOAT_COUNT = 13
} BossGanondrofF32Var; } BossGanondrofF32Var;
// SOH [Enhancements] Relocated from z_boss_ganondrof.c to use in time saver.
typedef enum {
/* 0 */ NOT_DEAD,
/* 1 */ DEATH_START,
/* 2 */ DEATH_THROES,
/* 3 */ DEATH_WARP,
/* 4 */ DEATH_SCREAM,
/* 5 */ DEATH_DISINTEGRATE,
/* 6 */ DEATH_FINISH
} BossGanondrofDeathState;
typedef enum {
/* 0 */ DEATH_SPASM,
/* 1 */ DEATH_LIMP,
/* 2 */ DEATH_HUNCHED
} BossGanondrofDeathAction;
typedef struct BossGanondrof { typedef struct BossGanondrof {
/* 0x0000 */ Actor actor; /* 0x0000 */ Actor actor;
/* 0x014C */ SkelAnime skelAnime; /* 0x014C */ SkelAnime skelAnime;