mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-07-30 11:39:07 -07:00
1169 lines
58 KiB
C++
1169 lines
58 KiB
C++
#include <libultraship/bridge.h>
|
|
#include "soh/OTRGlobals.h"
|
|
#include "soh/Enhancements/randomizer/randomizerTypes.h"
|
|
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
|
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
|
#include "soh/Enhancements/enhancementTypes.h"
|
|
|
|
extern "C" {
|
|
#include "src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.h"
|
|
#include "src/overlays/actors/ovl_Elf_Msg/z_elf_msg.h"
|
|
#include "src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h"
|
|
#include "src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.h"
|
|
#include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h"
|
|
#include "src/overlays/actors/ovl_En_Owl/z_en_owl.h"
|
|
#include "src/overlays/actors/ovl_En_Ko/z_en_ko.h"
|
|
#include "src/overlays/actors/ovl_En_Ma1/z_en_ma1.h"
|
|
#include "src/overlays/actors/ovl_En_Ru2/z_en_ru2.h"
|
|
#include "src/overlays/actors/ovl_En_Zl4/z_en_zl4.h"
|
|
#include "src/overlays/actors/ovl_En_Box/z_en_box.h"
|
|
#include "src/overlays/actors/ovl_Demo_Im/z_demo_im.h"
|
|
#include "src/overlays/actors/ovl_En_Sa/z_en_sa.h"
|
|
#include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h"
|
|
#include "src/overlays/actors/ovl_En_Tk/z_en_tk.h"
|
|
#include "src/overlays/actors/ovl_En_Fu/z_en_fu.h"
|
|
#include "src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h"
|
|
#include "src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h"
|
|
#include "src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.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_Po_Sisters/z_en_po_sisters.h"
|
|
#include <overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h>
|
|
#include <overlays/actors/ovl_En_Ik/z_en_ik.h>
|
|
#include <objects/object_gnd/object_gnd.h>
|
|
extern SaveContext gSaveContext;
|
|
extern PlayState* gPlayState;
|
|
extern int32_t D_8011D3AC;
|
|
|
|
extern void func_808ADEF0(BgSpot03Taki* bgSpot03Taki, PlayState* play);
|
|
extern void BgSpot03Taki_ApplyOpeningAlpha(BgSpot03Taki* bgSpot03Taki, s32 bufferIndex);
|
|
|
|
extern void func_80AF36EC(EnRu2* enRu2, PlayState* play);
|
|
}
|
|
|
|
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetContextOptionIndex()
|
|
|
|
void EnMa1_EndTeachSong(EnMa1* enMa1, PlayState* play) {
|
|
if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) {
|
|
Flags_SetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
|
|
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
|
|
enMa1->actor.flags &= ~ACTOR_FLAG_WILL_TALK;
|
|
play->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
enMa1->actionFunc = func_80AA0D88;
|
|
enMa1->unk_1E0 = 1;
|
|
enMa1->interactInfo.talkState = NPC_TALK_STATE_IDLE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void EnFu_EndTeachSong(EnFu* enFu, PlayState* play) {
|
|
if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) {
|
|
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
|
|
enFu->actionFunc = EnFu_WaitAdult;
|
|
enFu->actor.flags &= ~ACTOR_FLAG_WILL_TALK;
|
|
|
|
play->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
Flags_SetEventChkInf(EVENTCHKINF_PLAYED_SONG_OF_STORMS_IN_WINDMILL);
|
|
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_SONG_OF_STORMS);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void EnDntDemo_JudgeSkipToReward(EnDntDemo* enDntDemo, PlayState* play) {
|
|
// todo: figure out a better way to handle toggling so we don't
|
|
// need to double check cvars like this
|
|
if(!(IS_RANDO || CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO))) {
|
|
EnDntDemo_Judge(enDntDemo, play);
|
|
return;
|
|
}
|
|
|
|
if (enDntDemo->actor.xzDistToPlayer > 30.0f) {
|
|
EnDntDemo_Judge(enDntDemo, play);
|
|
return;
|
|
}
|
|
|
|
Player* player = GET_PLAYER(play);
|
|
switch (Player_GetMask(play)) {
|
|
case PLAYER_MASK_SKULL: {
|
|
Flags_SetItemGetInf(ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE);
|
|
return;
|
|
}
|
|
case PLAYER_MASK_TRUTH: {
|
|
Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE);
|
|
return;
|
|
}
|
|
default: {
|
|
EnDntDemo_Judge(enDntDemo, play);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BgSpot03Taki_KeepOpen(BgSpot03Taki* bgSpot03Taki, PlayState* play) {
|
|
}
|
|
|
|
static int successChimeCooldown = 0;
|
|
void RateLimitedSuccessChime() {
|
|
if (successChimeCooldown == 0) {
|
|
// Currently disabled, need to find a better way to do this, while being consistent with vanilla
|
|
// func_80078884(NA_SE_SY_CORRECT_CHIME);
|
|
successChimeCooldown = 120;
|
|
}
|
|
}
|
|
|
|
bool ForcedDialogIsDisabled(ForcedDialogMode type) {
|
|
return (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipForcedDialog"),
|
|
IS_RANDO ? FORCED_DIALOG_SKIP_ALL : FORCED_DIALOG_SKIP_NONE) &
|
|
type) != 0;
|
|
}
|
|
|
|
void TimeSaverOnGameFrameUpdateHandler() {
|
|
if (successChimeCooldown > 0) {
|
|
successChimeCooldown--;
|
|
}
|
|
}
|
|
|
|
void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_list originalArgs) {
|
|
va_list args;
|
|
va_copy(args, originalArgs);
|
|
|
|
switch (id) {
|
|
case VB_PLAY_TRANSITION_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO) {
|
|
// Song of Time
|
|
if (gSaveContext.entranceIndex == ENTR_TEMPLE_OF_TIME_ENTRANCE && gSaveContext.cutsceneIndex == 0xFFF7) {
|
|
gSaveContext.entranceIndex = ENTR_HYRULE_FIELD_16;
|
|
gSaveContext.cutsceneIndex = 0;
|
|
gSaveContext.nextTransitionType = 3;
|
|
*should = false;
|
|
}
|
|
|
|
// Requiem of Spirit
|
|
if ((gSaveContext.entranceIndex == ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT);
|
|
// Normally happens in the cutscene
|
|
gSaveContext.dayTime = gSaveContext.skyboxTime = 0xAC60;
|
|
if (GameInteractor_Should(VB_GIVE_ITEM_REQUIEM_OF_SPIRIT, true)) {
|
|
Item_Give(gPlayState, ITEM_SONG_REQUIEM);
|
|
}
|
|
*should = false;
|
|
}
|
|
|
|
u8 meetsBurningKakRequirements =
|
|
LINK_IS_ADULT &&
|
|
gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_FRONT_GATE &&
|
|
Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) &&
|
|
Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) &&
|
|
Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) &&
|
|
!Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL);
|
|
if (GameInteractor_Should(VB_BE_ELIGIBLE_FOR_NOCTURNE_OF_SHADOW, meetsBurningKakRequirements)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL);
|
|
// Normally happens in the cutscene
|
|
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_NOCTURNE_OF_SHADOW);
|
|
if (GameInteractor_Should(VB_GIVE_ITEM_NOCTURNE_OF_SHADOW, true)) {
|
|
Item_Give(gPlayState, ITEM_SONG_NOCTURNE);
|
|
}
|
|
*should = false;
|
|
}
|
|
}
|
|
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
// LACS
|
|
u8 meetsLACSRequirements =
|
|
LINK_IS_ADULT &&
|
|
(gEntranceTable[((void)0, 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_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);
|
|
}
|
|
*should = false;
|
|
}
|
|
}
|
|
|
|
if (gSaveContext.entranceIndex == ENTR_GANONS_TOWER_COLLAPSE_EXTERIOR_0) {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipTowerEscape"), false) || IS_BOSS_RUSH) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO);
|
|
gSaveContext.entranceIndex = ENTR_GANON_BOSS_0;
|
|
*should = false;
|
|
}
|
|
}
|
|
|
|
if (gSaveContext.entranceIndex == ENTR_CASTLE_COURTYARD_GUARDS_DAY_0) {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipChildStealth"), false)) {
|
|
gSaveContext.entranceIndex = ENTR_CASTLE_COURTYARD_ZELDA_0;
|
|
*should = false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_ENTRANCE_CS: {
|
|
s32* entranceFlag = va_arg(args, s32*);
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && (*entranceFlag != EVENTCHKINF_EPONA_OBTAINED)) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_ONEPOINT_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) {
|
|
s16* csId = va_arg(args, s16*);
|
|
BgSpot03Taki* taki = NULL;
|
|
switch (*csId) {
|
|
case 4180:
|
|
case 4100:
|
|
*should = false;
|
|
RateLimitedSuccessChime();
|
|
taki = (BgSpot03Taki*)Actor_FindNearby(gPlayState, &GET_PLAYER(gPlayState)->actor,
|
|
ACTOR_BG_SPOT03_TAKI, ACTORCAT_BG, 999.0f);
|
|
if (taki != NULL) {
|
|
func_8003EBF8(gPlayState, &gPlayState->colCtx.dyna, taki->dyna.bgId);
|
|
}
|
|
break;
|
|
default:
|
|
SPDLOG_INFO("VB_PLAY_ONEPOINT_CS {}", *csId);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_ONEPOINT_ACTOR_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) {
|
|
Actor* actor = va_arg(args, Actor*);
|
|
|
|
// there are a few checks throughout the game (such as chest spawns) that rely on this
|
|
// the checks are for func_8005B198() == this->dyna.actor.category
|
|
// func_8005B198 just returns D_8011D3AC
|
|
// D_8011D3AC is set to camera->target->category in Camera_Demo5
|
|
D_8011D3AC = actor->category;
|
|
|
|
switch (actor->category) {
|
|
case ACTORCAT_BG:
|
|
if (actor->id == ACTOR_BG_DDAN_KD) {
|
|
BgDdanKd* ddanKd = (BgDdanKd*)actor;
|
|
Flags_SetSwitch(gPlayState, ddanKd->dyna.actor.params);
|
|
}
|
|
if (actor->id == ACTOR_BG_MORI_HINERI) {
|
|
break;
|
|
}
|
|
// This is handled in the FasterHeavyBlockLift enhancement
|
|
if (actor->id == ACTOR_BG_HEAVY_BLOCK) {
|
|
break;
|
|
}
|
|
|
|
RateLimitedSuccessChime();
|
|
*should = false;
|
|
break;
|
|
}
|
|
switch (actor->id) {
|
|
case ACTOR_OBJ_SWITCH: {
|
|
ObjSwitch *switchActor = (ObjSwitch*)actor;
|
|
switchActor->cooldownTimer = 0;
|
|
*should = false;
|
|
RateLimitedSuccessChime();
|
|
break;
|
|
}
|
|
case ACTOR_BG_BDAN_SWITCH: {
|
|
// The switch in jabu that you are intended to press with a box to reach barrinade
|
|
// can be skipped by either a frame perfect roll open or with OI
|
|
// The One Point for that switch is used in common setups for the former and is required for the latter to work
|
|
if (actor->params == 14848 && gPlayState->sceneNum == SCENE_JABU_JABU && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)){
|
|
break;
|
|
}
|
|
BgBdanSwitch* switchActor = (BgBdanSwitch*)actor;
|
|
switchActor->unk_1D8 = 0;
|
|
switchActor->unk_1DA = 0;
|
|
*should = false;
|
|
RateLimitedSuccessChime();
|
|
break;
|
|
}
|
|
case ACTOR_BG_HIDAN_KOUSI: {
|
|
BgHidanKousi* switchActor = (BgHidanKousi*)actor;
|
|
BgHidanKousi_SetupAction(switchActor, func_80889C18);
|
|
*should = false;
|
|
RateLimitedSuccessChime();
|
|
break;
|
|
}
|
|
case ACTOR_BG_HIDAN_FWBIG:
|
|
case ACTOR_EN_EX_ITEM:
|
|
case ACTOR_EN_DNT_NOMAL:
|
|
case ACTOR_EN_DNT_DEMO:
|
|
case ACTOR_BG_HAKA_ZOU: {
|
|
*should = false;
|
|
break;
|
|
}
|
|
case ACTOR_EN_TA:
|
|
case ACTOR_DOOR_SHUTTER:
|
|
case ACTOR_BG_ICE_SHUTTER:
|
|
case ACTOR_OBJ_LIGHTSWITCH:
|
|
case ACTOR_EN_BOX:
|
|
case ACTOR_OBJ_SYOKUDAI:
|
|
case ACTOR_OBJ_TIMEBLOCK:
|
|
case ACTOR_EN_PO_SISTERS:
|
|
case ACTOR_OBJ_ICE_POLY:
|
|
case ACTOR_BG_YDAN_MARUTA:
|
|
case ACTOR_BG_SPOT18_SHUTTER:
|
|
case ACTOR_BG_SPOT05_SOKO:
|
|
case ACTOR_BG_SPOT18_BASKET:
|
|
case ACTOR_BG_HIDAN_CURTAIN:
|
|
case ACTOR_BG_MORI_HINERI:
|
|
case ACTOR_BG_MIZU_SHUTTER:
|
|
case ACTOR_SHOT_SUN:
|
|
*should = false;
|
|
RateLimitedSuccessChime();
|
|
break;
|
|
}
|
|
if (*should) {
|
|
SPDLOG_INFO("VB_PLAY_ONEPOINT_ACTOR_CS ID:{} Cat:{}", actor->id, actor->category);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case VB_SHOW_TITLE_CARD:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO)) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
case VB_WONDER_TALK: {
|
|
//We want to show the frog hint if it is on, regardless of cutscene settings
|
|
if (ForcedDialogIsDisabled(FORCED_DIALOG_SKIP_NPC) &&
|
|
!(gPlayState->sceneNum == SCENE_ZORAS_RIVER && IS_RANDO && RAND_GET_OPTION(RSK_FROGS_HINT))) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_NAVI_TALK: {
|
|
if (ForcedDialogIsDisabled(FORCED_DIALOG_SKIP_NAVI)) {
|
|
ElfMsg* naviTalk = va_arg(args, ElfMsg*);
|
|
int32_t paramsHighByte = naviTalk->actor.params >> 8;
|
|
if ((paramsHighByte & 0x80) == 0 && (paramsHighByte & 0x3F) != 0x3F) {
|
|
Flags_SetSwitch(gPlayState, paramsHighByte & 0x3F);
|
|
Actor_Kill(&naviTalk->actor);
|
|
*should = false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case VB_NOT_BE_GREETED_BY_SARIA:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && !Flags_GetInfTable(INFTABLE_GREETED_BY_SARIA)) {
|
|
Flags_SetInfTable(INFTABLE_GREETED_BY_SARIA);
|
|
*should = true;
|
|
}
|
|
break;
|
|
case VB_PLAY_SHIEK_BLOCK_MASTER_SWORD_CS:
|
|
case VB_GIVE_ITEM_LIGHT_ARROW:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
case VB_PLAY_NABOORU_CAPTURED_CS:
|
|
if (*should == true && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_NABOORU_CAPTURED_BY_TWINROVA);
|
|
*should = false;
|
|
}
|
|
break;
|
|
case VB_PLAY_PULL_MASTER_SWORD_CS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL)) {
|
|
// Normally, these would be done in the cutscene, but we're skipping it
|
|
Flags_SetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL);
|
|
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER);
|
|
Flags_SetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL);
|
|
Flags_SetEventChkInf(EVENTCHKINF_TIME_TRAVELED_TO_ADULT);
|
|
if (GameInteractor_Should(VB_GIVE_ITEM_LIGHT_MEDALLION, true)) {
|
|
Item_Give(gPlayState, ITEM_MEDALLION_LIGHT);
|
|
}
|
|
}
|
|
*should = false;
|
|
}
|
|
break;
|
|
case VB_OWL_INTERACTION: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && *should) {
|
|
EnOwl* enOwl = va_arg(args, EnOwl*);
|
|
s32 owlType = (enOwl->actor.params & 0xFC0) >> 6;
|
|
|
|
if (((enOwl->actor.params & 0xFC0) >> 6) == 1) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_KAEPORA_BY_LOST_WOODS);
|
|
}
|
|
|
|
func_80ACA62C(enOwl, gPlayState);
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_OVERRIDE_LINK_THE_GORON_DIALOGUE: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
|
u16* textId = va_arg(args, u16*);
|
|
// If the doors are not open yet, prioritize opening them
|
|
if (!Flags_GetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED)) {
|
|
*textId = 0x3036;
|
|
*should = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_EYEDROP_CREATION_ANIM:
|
|
case VB_PLAY_EYEDROPS_CS:
|
|
case VB_PLAY_DROP_FISH_FOR_JABU_CS:
|
|
case VB_PLAY_DARUNIAS_JOY_CS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
case VB_PLAY_ZELDAS_LULLABY_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO) {
|
|
DemoIm* demoIm = va_arg(args, DemoIm*);
|
|
Player* player = GET_PLAYER(gPlayState);
|
|
player->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
|
|
player->stateFlags1 |= PLAYER_STATE1_GETTING_ITEM;
|
|
func_80986794(demoIm);
|
|
|
|
static uint32_t demoImUpdateHook = 0;
|
|
static uint32_t demoImKillHook = 0;
|
|
demoImUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* actorRef) mutable {
|
|
Actor* actor = static_cast<Actor*>(actorRef);
|
|
if (actor->id == ACTOR_DEMO_IM && (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO)) {
|
|
DemoIm* demoIm = static_cast<DemoIm*>(actorRef);
|
|
Player* player = GET_PLAYER(gPlayState);
|
|
player->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
|
|
player->stateFlags1 |= PLAYER_STATE1_GETTING_ITEM;
|
|
|
|
if (Animation_OnFrame(&demoIm->skelAnime, 25.0f)) {
|
|
Audio_PlaySoundGeneral(NA_SE_IT_DEKU, &demoIm->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(demoImUpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(demoImKillHook);
|
|
demoImUpdateHook = 0;
|
|
demoImKillHook = 0;
|
|
} else if (Animation_OnFrame(&demoIm->skelAnime, 15.0f)) {
|
|
Player* player = GET_PLAYER(gPlayState);
|
|
// SOH [Randomizer] In entrance rando have impa bring link back to the front of castle grounds
|
|
if (IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) {
|
|
gPlayState->nextEntranceIndex = ENTR_CASTLE_GROUNDS_SOUTH_EXIT;
|
|
} else {
|
|
gPlayState->nextEntranceIndex = ENTR_HYRULE_FIELD_17;
|
|
}
|
|
gSaveContext.dayTime = gSaveContext.skyboxTime = 0x8000;
|
|
gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
|
|
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
|
|
gSaveContext.nextTransitionType = 2;
|
|
Player_SetCsActionWithHaltedActors(gPlayState, &player->actor, 8);
|
|
}
|
|
}
|
|
});
|
|
demoImKillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(demoImUpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(demoImKillHook);
|
|
demoImUpdateHook = 0;
|
|
demoImKillHook = 0;
|
|
});
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_SARIAS_SONG_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO) {
|
|
EnSa* enSa = va_arg(args, EnSa*);
|
|
enSa->actionFunc = func_80AF6B20;
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_DESPAWN_HORSE_RACE_COW: {
|
|
if (Flags_GetEventChkInf(EVENTCHKINF_WON_COW_IN_MALONS_RACE) && CVarGetInteger(CVAR_ENHANCEMENT("CowOfTime"), 0)) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_DRAIN_WELL_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
*should = false;
|
|
Flags_SetSwitch(gPlayState, 0x2);
|
|
Flags_SetEventChkInf(EVENTCHKINF_PLAYED_SONG_OF_STORMS_IN_WINDMILL);
|
|
Flags_SetEventChkInf(EVENTCHKINF_DRAINED_WELL_IN_KAKARIKO);
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_SUNS_SONG_CS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO) {
|
|
*should = false;
|
|
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_SUNS_SONG);
|
|
// SoH [Randomizer] TODO: Increment time X amount (find out X)
|
|
// When time is 0, it's changed to 0x46A7
|
|
// When it's 0x8000, it's changed to 0xC090
|
|
}
|
|
break;
|
|
case VB_PLAY_ROYAL_FAMILY_TOMB_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_ROYAL_FAMILY_TOMB_EXPLODE: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
|
*should = Flags_GetEventChkInf(EVENTCHKINF_DESTROYED_ROYAL_FAMILY_TOMB);
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_DOOR_OF_TIME_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
|
*should = false;
|
|
Flags_SetEnv(gPlayState, 2);
|
|
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
|
|
}
|
|
break;
|
|
}
|
|
case VB_GIVE_ITEM_MINUET_OF_FOREST:
|
|
case VB_GIVE_ITEM_BOLERO_OF_FIRE:
|
|
case VB_GIVE_ITEM_SERENADE_OF_WATER:
|
|
case VB_GIVE_ITEM_REQUIEM_OF_SPIRIT:
|
|
case VB_GIVE_ITEM_NOCTURNE_OF_SHADOW:
|
|
case VB_GIVE_ITEM_PRELUDE_OF_LIGHT:
|
|
case VB_GIVE_ITEM_ZELDAS_LULLABY:
|
|
case VB_GIVE_ITEM_EPONAS_SONG:
|
|
case VB_GIVE_ITEM_SARIAS_SONG:
|
|
case VB_GIVE_ITEM_SUNS_SONG:
|
|
case VB_GIVE_ITEM_SONG_OF_TIME:
|
|
case VB_GIVE_ITEM_SONG_OF_STORMS:
|
|
case VB_PLAY_MINUET_OF_FOREST_CS:
|
|
case VB_PLAY_BOLERO_OF_FIRE_CS:
|
|
case VB_PLAY_SERENADE_OF_WATER_CS:
|
|
case VB_PLAY_PRELUDE_OF_LIGHT_CS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
case VB_FREEZE_ON_SKULL_TOKEN:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0)) {
|
|
*should = false;
|
|
}
|
|
break;
|
|
case VB_DAMPE_IN_GRAVEYARD_DESPAWN:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("DampeAllNight"), 0)) {
|
|
*should = LINK_IS_ADULT || gPlayState->sceneNum != SCENE_GRAVEYARD;
|
|
}
|
|
break;
|
|
case VB_BE_VALID_GRAVEDIGGING_SPOT:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("DampeWin"), 0)) {
|
|
EnTk *enTk = va_arg(args, EnTk*);
|
|
enTk->validDigHere = true;
|
|
*should = true;
|
|
}
|
|
break;
|
|
case VB_BE_DAMPE_GRAVEDIGGING_GRAND_PRIZE:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("DampeWin"), 0)) {
|
|
EnTk *enTk = va_arg(args, EnTk*);
|
|
enTk->currentReward = 3;
|
|
*should = true;
|
|
}
|
|
break;
|
|
case VB_DAMPE_GRAVEDIGGING_GRAND_PRIZE_BE_HEART_PIECE:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("GravediggingTourFix"), 0) || IS_RANDO) {
|
|
*should = !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE);
|
|
}
|
|
break;
|
|
case VB_FIX_SAW_SOFTLOCK:
|
|
// Animation Count should be no more than 1 to guarantee putaway is complete after giving the saw
|
|
// As this is vanilla behavior, it only applies with the Fix toggle or Skip Text enabled.
|
|
*should = (CVarGetInteger(CVAR_ENHANCEMENT("FixSawSoftlock"), 0) != 0 || CVarGetInteger(CVAR_ENHANCEMENT("SkipText"), 0) != 0) ? gPlayState->animationCtx.animationCount > 1 : *should;
|
|
break;
|
|
case VB_BIGGORON_CONSIDER_SWORD_FORGED:
|
|
*should = Environment_GetBgsDayCount() >= CVarGetInteger(CVAR_ENHANCEMENT("ForgeTime"), 3);
|
|
break;
|
|
case VB_BE_ELIGIBLE_FOR_GREAT_FAIRY_REWARD: {
|
|
BgDyYoseizo* bgDyYoseizo = va_arg(args, BgDyYoseizo*);
|
|
RandomizerInf flag = RAND_INF_MAX;
|
|
|
|
if (gPlayState->sceneNum == SCENE_GREAT_FAIRYS_FOUNTAIN_SPELLS) {
|
|
switch (bgDyYoseizo->fountainType) {
|
|
case 0:
|
|
flag = RAND_INF_ZF_GREAT_FAIRY_REWARD;
|
|
break;
|
|
case 1:
|
|
flag = RAND_INF_HC_GREAT_FAIRY_REWARD;
|
|
break;
|
|
case 2:
|
|
flag = RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (bgDyYoseizo->fountainType) {
|
|
case 0:
|
|
flag = RAND_INF_DMT_GREAT_FAIRY_REWARD;
|
|
break;
|
|
case 1:
|
|
flag = RAND_INF_DMC_GREAT_FAIRY_REWARD;
|
|
break;
|
|
case 2:
|
|
flag = RAND_INF_OGC_GREAT_FAIRY_REWARD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (flag != RAND_INF_MAX && (IS_RANDO || CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO))) {
|
|
if (IS_RANDO || *should) {
|
|
Flags_SetRandomizerInf(flag);
|
|
gSaveContext.healthAccumulator = 0x140;
|
|
Magic_Fill(gPlayState);
|
|
}
|
|
*should = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VB_PLAY_RAINBOW_BRIDGE_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
*should = false;
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_RAINBOW_BRIDGE_BUILT)) {
|
|
func_800F595C(NA_BGM_BRIDGE_TO_GANONS);
|
|
// This would have been set 2 frames later, but we're skipping now so the sound doesn't play twice
|
|
Flags_SetEventChkInf(EVENTCHKINF_RAINBOW_BRIDGE_BUILT);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case VB_PHANTOM_GANON_DEATH_SCENE: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO || IS_BOSS_RUSH)) {
|
|
*should = false;
|
|
BossGanondrof* pg = va_arg(args, BossGanondrof*);
|
|
Player* player = GET_PLAYER(gPlayState);
|
|
if (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;
|
|
}
|
|
case VB_NABOORU_KNUCKLE_DEATH_SCENE: {
|
|
EnIk* ik = va_arg(args, EnIk*);
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO)) {
|
|
// Because no CS in rando, we hide the death of the knuckle by spawning flames and kill the actor
|
|
if ((ik->actor.colChkInfo.health <= 10)) {
|
|
s32 i;
|
|
Vec3f pos;
|
|
Vec3f sp7C = { 0.0f, 0.5f, 0.0f };
|
|
int flameAmount = 100;
|
|
|
|
for (i = flameAmount; i >= 0; i--) {
|
|
pos.x = ik->actor.world.pos.x + Rand_CenteredFloat(120.0f);
|
|
pos.z = ik->actor.world.pos.z + Rand_CenteredFloat(120.0f);
|
|
pos.y = ik->actor.world.pos.y + 20.0f + Rand_CenteredFloat(120.0f);
|
|
EffectSsDeadDb_Spawn(gPlayState, &pos, &sp7C, &sp7C, 100, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9,
|
|
true);
|
|
}
|
|
Actor_Kill(&ik->actor);
|
|
}
|
|
*should = false;
|
|
}
|
|
break;
|
|
}
|
|
case VB_PLAY_SLOW_CHEST_CS: {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("FastChests"), 0)) {
|
|
*should = false;
|
|
} else if (CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), CSMC_DISABLED) && *should) {
|
|
EnBox* enBox = va_arg(args, EnBox*);
|
|
*should = enBox->dyna.actor.scale.x != 0.005f;
|
|
}
|
|
break;
|
|
}
|
|
case VB_SKIP_SCARECROWS_SONG: {
|
|
if (gPlayState->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING && CVarGetInteger(CVAR_ENHANCEMENT("InstantScarecrow"), 0) && gSaveContext.scarecrowSpawnSongSet) {
|
|
*should = true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
static uint32_t enMa1UpdateHook = 0;
|
|
static uint32_t enMa1KillHook = 0;
|
|
static uint32_t enFuUpdateHook = 0;
|
|
static uint32_t enFuKillHook = 0;
|
|
static uint32_t bgSpot02UpdateHook = 0;
|
|
static uint32_t bgSpot02KillHook = 0;
|
|
static uint32_t bgSpot03UpdateHook = 0;
|
|
static uint32_t bgSpot03KillHook = 0;
|
|
static uint32_t enPoSistersUpdateHook = 0;
|
|
static uint32_t enPoSistersKillHook = 0;
|
|
void TimeSaverOnActorInitHandler(void* actorRef) {
|
|
Actor* actor = static_cast<Actor*>(actorRef);
|
|
|
|
if (actor->id == ACTOR_EN_MA1 && gPlayState->sceneNum == SCENE_LON_LON_RANCH) {
|
|
enMa1UpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
|
|
Actor* innerActor = static_cast<Actor*>(innerActorRef);
|
|
if (innerActor->id == ACTOR_EN_MA1 && (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO)) {
|
|
EnMa1* enMa1 = static_cast<EnMa1*>(innerActorRef);
|
|
if (enMa1->actionFunc == func_80AA106C) {
|
|
enMa1->actionFunc = EnMa1_EndTeachSong;
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enMa1UpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enMa1KillHook);
|
|
enMa1UpdateHook = 0;
|
|
enMa1KillHook = 0;
|
|
// They've already learned the song
|
|
} else if (enMa1->actionFunc == func_80AA0D88) {
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enMa1UpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enMa1KillHook);
|
|
enMa1UpdateHook = 0;
|
|
enMa1KillHook = 0;
|
|
}
|
|
}
|
|
});
|
|
enMa1KillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enMa1UpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enMa1KillHook);
|
|
enMa1UpdateHook = 0;
|
|
enMa1KillHook = 0;
|
|
});
|
|
}
|
|
|
|
if (actor->id == ACTOR_EN_FU) {
|
|
enFuUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
|
|
Actor* innerActor = static_cast<Actor*>(innerActorRef);
|
|
if (innerActor->id == ACTOR_EN_FU && (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO)) {
|
|
EnFu* enFu = static_cast<EnFu*>(innerActorRef);
|
|
if (enFu->actionFunc == EnFu_TeachSong) {
|
|
enFu->actionFunc = EnFu_EndTeachSong;
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enFuUpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enFuKillHook);
|
|
enFuUpdateHook = 0;
|
|
enFuKillHook = 0;
|
|
}
|
|
}
|
|
});
|
|
enFuKillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enFuUpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enFuKillHook);
|
|
enFuUpdateHook = 0;
|
|
enFuKillHook = 0;
|
|
});
|
|
}
|
|
|
|
if (actor->id == ACTOR_EN_OWL && gPlayState->sceneNum == SCENE_ZORAS_RIVER && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 0) == 2) {
|
|
Actor_Kill(actor);
|
|
}
|
|
|
|
if (actor->id == ACTOR_BG_SPOT02_OBJECTS && actor->params == 2) {
|
|
bgSpot02UpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
|
|
Actor* innerActor = static_cast<Actor*>(innerActorRef);
|
|
if (innerActor->id == ACTOR_BG_SPOT02_OBJECTS && innerActor->params == 2 && (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO))) {
|
|
BgSpot02Objects* bgSpot02 = static_cast<BgSpot02Objects*>(innerActorRef);
|
|
if (bgSpot02->actionFunc == func_808ACC34) {
|
|
bgSpot02->actionFunc = func_808AC908;
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(bgSpot02UpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(bgSpot02KillHook);
|
|
bgSpot02UpdateHook = 0;
|
|
bgSpot02KillHook = 0;
|
|
}
|
|
}
|
|
});
|
|
bgSpot02KillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(bgSpot02UpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(bgSpot02KillHook);
|
|
bgSpot02UpdateHook = 0;
|
|
bgSpot02KillHook = 0;
|
|
});
|
|
}
|
|
|
|
if (actor->id == ACTOR_BG_SPOT03_TAKI) {
|
|
bgSpot03UpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
|
|
Actor* innerActor = static_cast<Actor*>(innerActorRef);
|
|
|
|
if (innerActor->id != ACTOR_BG_SPOT03_TAKI) {
|
|
return;
|
|
}
|
|
|
|
bool shouldKeepOpen;
|
|
switch (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 0)) {
|
|
case 1:
|
|
shouldKeepOpen = Flags_GetEventChkInf(EVENTCHKINF_OPENED_ZORAS_DOMAIN);
|
|
break;
|
|
case 2:
|
|
if (IS_RANDO && RAND_GET_OPTION(RSK_SLEEPING_WATERFALL) == RO_WATERFALL_OPEN) {
|
|
shouldKeepOpen = true;
|
|
} else {
|
|
shouldKeepOpen = CHECK_QUEST_ITEM(QUEST_SONG_LULLABY) &&
|
|
(INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME ||
|
|
INV_CONTENT(ITEM_OCARINA_FAIRY) == ITEM_OCARINA_FAIRY);
|
|
}
|
|
break;
|
|
default:
|
|
shouldKeepOpen = false;
|
|
break;
|
|
}
|
|
|
|
if (!shouldKeepOpen) {
|
|
return;
|
|
}
|
|
|
|
BgSpot03Taki* bgSpot03 = static_cast<BgSpot03Taki*>(innerActorRef);
|
|
if (bgSpot03->actionFunc == func_808ADEF0) {
|
|
bgSpot03->actionFunc = BgSpot03Taki_KeepOpen;
|
|
bgSpot03->state = WATERFALL_OPENED;
|
|
bgSpot03->openingAlpha = 0.0f;
|
|
Flags_SetSwitch(gPlayState, bgSpot03->switchFlag);
|
|
func_8003EBF8(gPlayState, &gPlayState->colCtx.dyna, bgSpot03->dyna.bgId);
|
|
BgSpot03Taki_ApplyOpeningAlpha(bgSpot03, 0);
|
|
BgSpot03Taki_ApplyOpeningAlpha(bgSpot03, 1);
|
|
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(bgSpot03UpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(bgSpot03KillHook);
|
|
bgSpot03UpdateHook = 0;
|
|
bgSpot03KillHook = 0;
|
|
}
|
|
});
|
|
bgSpot03KillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(bgSpot03UpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(bgSpot03KillHook);
|
|
bgSpot03UpdateHook = 0;
|
|
bgSpot03KillHook = 0;
|
|
});
|
|
}
|
|
|
|
if (actor->id == ACTOR_EN_DNT_DEMO && (IS_RANDO || CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO))) {
|
|
EnDntDemo* enDntDemo = static_cast<EnDntDemo*>(actorRef);
|
|
enDntDemo->actionFunc = EnDntDemo_JudgeSkipToReward;
|
|
}
|
|
|
|
// Forest Temple entrance cutscene
|
|
// This is a bit of a hack, we can't effectively override the behavior of the torches
|
|
// or poes from which the cutscene is triggered until we can have a "BeforeActorInit" hook.
|
|
// So for now we're just going to set the flag before they get to the room the cutscene is in
|
|
if (gPlayState->sceneNum == SCENE_FOREST_TEMPLE && actor->id == ACTOR_EN_ST && !Flags_GetSwitch(gPlayState, 0x1B)) {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), 0) && !CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
|
|
Flags_SetSwitch(gPlayState, 0x1B);
|
|
}
|
|
}
|
|
|
|
// Forest Temple purple poe fight speedup
|
|
if (actor->id == ACTOR_EN_PO_SISTERS && actor->params == 28) {
|
|
enPoSistersUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
|
|
Actor* innerActor = static_cast<Actor*>(innerActorRef);
|
|
if (innerActor->id == ACTOR_EN_PO_SISTERS && innerActor->params == 28 && (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO))) {
|
|
EnPoSisters* enPoSisters = static_cast<EnPoSisters*>(innerActorRef);
|
|
if (enPoSisters->actionFunc == func_80ADB338) {
|
|
enPoSisters->unk_19C = 0;
|
|
}
|
|
}
|
|
});
|
|
enPoSistersKillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enPoSistersUpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enPoSistersKillHook);
|
|
enPoSistersUpdateHook = 0;
|
|
enPoSistersKillHook = 0;
|
|
});
|
|
}
|
|
|
|
// Fire Temple Darunia cutscene
|
|
if (actor->id == ACTOR_EN_DU && gPlayState->sceneNum == SCENE_FIRE_TEMPLE) {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), 0) && !CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
|
|
Flags_SetInfTable(INFTABLE_SPOKE_TO_DARUNIA_IN_FIRE_TEMPLE);
|
|
Actor_Kill(actor);
|
|
}
|
|
}
|
|
|
|
// Water Temple Ruto cutscene
|
|
if (actor->id == ACTOR_EN_RU2 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
EnRu2* enRu2 = (EnRu2*)actor;
|
|
func_80AF36EC(enRu2, gPlayState);
|
|
Actor_Kill(actor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TimeSaverOnSceneInitHandler(int16_t sceneNum) {
|
|
switch (sceneNum) {
|
|
case SCENE_HYRULE_CASTLE:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO) && !Flags_GetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE)) {
|
|
Flags_SetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE);
|
|
Flags_SetInfTable(INFTABLE_MET_CHILD_MALON_AT_CASTLE_OR_MARKET);
|
|
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_CASTLE_OR_MARKET);
|
|
}
|
|
break;
|
|
case SCENE_LON_LON_RANCH:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO) && GameInteractor_Should(VB_MALON_RETURN_FROM_CASTLE, Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE))) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_RANCH);
|
|
Flags_SetInfTable(INFTABLE_CHILD_MALON_SAID_EPONA_WAS_AFRAID_OF_YOU);
|
|
Flags_SetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON);
|
|
}
|
|
break;
|
|
case SCENE_DEKU_TREE_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_GOHMA_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_GOHMA_BATTLE);
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_DODONGOS_CAVERN_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_KING_DODONGO_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_KING_DODONGO_BATTLE);
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_JABU_JABU_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE);
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_FOREST_TEMPLE_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_PHANTOM_GANON_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_PHANTOM_GANON_BATTLE);
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_FIRE_TEMPLE_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_VOLVAGIA_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_VOLVAGIA_BATTLE);
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_WATER_TEMPLE_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_MORPHA_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_MORPHA_BATTLE);
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_SPIRIT_TEMPLE_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE);
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_NABOORU_BATTLE);
|
|
Flags_SetEventChkInf(EVENTCHKINF_NABOORU_ORDERED_TO_FIGHT_BY_TWINROVA);
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_SHADOW_TEMPLE_BOSS:
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
|
|
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_BONGO_BONGO_BATTLE)) {
|
|
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_BONGO_BONGO_BATTLE);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GetItemEntry vanillaQueuedItemEntry = GET_ITEM_NONE;
|
|
|
|
void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) {
|
|
switch (flagType) {
|
|
case FLAG_EVENT_CHECK_INF:
|
|
switch (flag) {
|
|
case EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FAIRY_OCARINA).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_KOKIRI_EMERALD).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_GORON_RUBY).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZORA_SAPPHIRE).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FOREST_MEDALLION).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FIRE_MEDALLION).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_WATER_MEDALLION).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_ARROWS).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_TIME_TRAVELED_TO_ADULT:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_MEDALLION).GetGIEntry_Copy();
|
|
break;
|
|
}
|
|
break;
|
|
case FLAG_RANDOMIZER_INF:
|
|
switch (flag) {
|
|
case RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SHADOW_MEDALLION).GetGIEntry_Copy();
|
|
break;
|
|
case RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SPIRIT_MEDALLION).GetGIEntry_Copy();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
|
switch (flagType) {
|
|
case FLAG_RANDOMIZER_INF:
|
|
switch (flag) {
|
|
case RAND_INF_ZF_GREAT_FAIRY_REWARD:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FARORES_WIND).GetGIEntry_Copy();
|
|
break;
|
|
case RAND_INF_HC_GREAT_FAIRY_REWARD:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DINS_FIRE).GetGIEntry_Copy();
|
|
break;
|
|
case RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_NAYRUS_LOVE).GetGIEntry_Copy();
|
|
break;
|
|
case RAND_INF_DMT_GREAT_FAIRY_REWARD:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MAGIC_SINGLE).GetGIEntry_Copy();
|
|
break;
|
|
case RAND_INF_DMC_GREAT_FAIRY_REWARD:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MAGIC_DOUBLE).GetGIEntry_Copy();
|
|
break;
|
|
case RAND_INF_OGC_GREAT_FAIRY_REWARD:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DOUBLE_DEFENSE).GetGIEntry_Copy();
|
|
break;
|
|
}
|
|
break;
|
|
case FLAG_ITEM_GET_INF:
|
|
switch (flag) {
|
|
case ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DEKU_STICK_CAPACITY_30).GetGIEntry_Copy();
|
|
break;
|
|
case ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DEKU_NUT_CAPACITY_40).GetGIEntry_Copy();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO)) {
|
|
switch (flagType) {
|
|
case FLAG_EVENT_CHECK_INF:
|
|
switch (flag) {
|
|
case EVENTCHKINF_LEARNED_ZELDAS_LULLABY:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZELDAS_LULLABY).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_MINUET_OF_FOREST:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MINUET_OF_FOREST).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_BOLERO_OF_FIRE:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_BOLERO_OF_FIRE).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_SERENADE_OF_WATER:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SERENADE_OF_WATER).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_REQUIEM_OF_SPIRIT).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_NOCTURNE_OF_SHADOW).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_PRELUDE_OF_LIGHT).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_SARIAS_SONG:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SARIAS_SONG).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_SONG_OF_TIME:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SONG_OF_TIME).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_SONG_OF_STORMS:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SONG_OF_STORMS).GetGIEntry_Copy();
|
|
break;
|
|
case EVENTCHKINF_LEARNED_SUNS_SONG:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SUNS_SONG).GetGIEntry_Copy();
|
|
break;
|
|
}
|
|
break;
|
|
case FLAG_RANDOMIZER_INF:
|
|
switch (flag) {
|
|
case RAND_INF_LEARNED_EPONA_SONG:
|
|
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_EPONAS_SONG).GetGIEntry_Copy();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TimeSaverOnPlayerUpdateHandler() {
|
|
if (vanillaQueuedItemEntry.itemId == ITEM_NONE) return;
|
|
|
|
Player* player = GET_PLAYER(gPlayState);
|
|
if (player == NULL || Player_InBlockingCsMode(gPlayState, player) || player->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM || player->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) {
|
|
return;
|
|
}
|
|
|
|
SPDLOG_INFO("Attempting to give Item: mod {} item {}", vanillaQueuedItemEntry.modIndex, vanillaQueuedItemEntry.itemId);
|
|
GiveItemEntryWithoutActor(gPlayState, vanillaQueuedItemEntry);
|
|
if (player->stateFlags1 & PLAYER_STATE1_IN_WATER) {
|
|
// Allow the player to receive the item while swimming
|
|
player->stateFlags2 |= PLAYER_STATE2_UNDERWATER;
|
|
Player_ActionHandler_2(player, gPlayState);
|
|
}
|
|
}
|
|
|
|
void TimeSaverOnItemReceiveHandler(GetItemEntry receivedItemEntry) {
|
|
if (vanillaQueuedItemEntry.itemId == ITEM_NONE) return;
|
|
|
|
if (vanillaQueuedItemEntry.modIndex == receivedItemEntry.modIndex && vanillaQueuedItemEntry.itemId == receivedItemEntry.itemId) {
|
|
SPDLOG_INFO("Item received: mod {} item {}", receivedItemEntry.modIndex, receivedItemEntry.itemId);
|
|
vanillaQueuedItemEntry = GET_ITEM_NONE;
|
|
}
|
|
}
|
|
|
|
static uint32_t onSceneInitHook = 0;
|
|
static uint32_t onVanillaBehaviorHook = 0;
|
|
static uint32_t onActorInitHook = 0;
|
|
static uint32_t onGameFrameUpdate = 0;
|
|
static uint32_t onFlagSetHook = 0;
|
|
static uint32_t onPlayerUpdateHook = 0;
|
|
static uint32_t onItemReceiveHook = 0;
|
|
void TimeSaverRegisterHooks() {
|
|
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) mutable {
|
|
vanillaQueuedItemEntry = GET_ITEM_NONE;
|
|
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnGameFrameUpdate>(onGameFrameUpdate);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnFlagSet>(onFlagSetHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(onPlayerUpdateHook);
|
|
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnItemReceive>(onItemReceiveHook);
|
|
|
|
onSceneInitHook = 0;
|
|
onVanillaBehaviorHook = 0;
|
|
onActorInitHook = 0;
|
|
onGameFrameUpdate = 0;
|
|
onFlagSetHook = 0;
|
|
onPlayerUpdateHook = 0;
|
|
onItemReceiveHook = 0;
|
|
|
|
onSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(TimeSaverOnSceneInitHandler);
|
|
onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(TimeSaverOnVanillaBehaviorHandler);
|
|
onActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(TimeSaverOnActorInitHandler);
|
|
onGameFrameUpdate = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(TimeSaverOnGameFrameUpdateHandler);
|
|
|
|
if (IS_RANDO) return;
|
|
|
|
onFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(TimeSaverOnFlagSetHandler);
|
|
onPlayerUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(TimeSaverOnPlayerUpdateHandler);
|
|
onItemReceiveHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(TimeSaverOnItemReceiveHandler);
|
|
});
|
|
}
|