diff --git a/soh/include/z64save.h b/soh/include/z64save.h index c6db012e1df..1c2172563da 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -276,7 +276,7 @@ typedef struct { /* */ u16 pendingSaleMod; /* */ uint8_t questId; /* */ uint32_t isBossRushPaused; - /* */ uint8_t bossRushOptions[BOSSRUSH_OPTIONS_AMOUNT]; + /* */ uint8_t bossRushOptions[BR_OPTIONS_MAX]; /* */ u8 pendingIceTrapCount; /* */ SohStats sohStats; /* */ FaroresWindData backupFW; diff --git a/soh/soh/Enhancements/boss-rush/BossRush.cpp b/soh/soh/Enhancements/boss-rush/BossRush.cpp index 9dbbad52748..51fd4db27d6 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.cpp +++ b/soh/soh/Enhancements/boss-rush/BossRush.cpp @@ -1,19 +1,33 @@ #include "BossRush.h" #include "soh/OTRGlobals.h" -#include "functions.h" -#include "macros.h" -#include "variables.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include #include #include +extern "C" { + #include "functions.h" + #include "macros.h" + #include "variables.h" + #include "src/overlays/actors/ovl_Boss_Goma/z_boss_goma.h" + #include "src/overlays/actors/ovl_Boss_Mo/z_boss_mo.h" + #include "src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + extern PlayState* gPlayState; + + Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point); + #include "textures/icon_item_nes_static/icon_item_nes_static.h" + #include "textures/icon_item_ger_static/icon_item_ger_static.h" + #include "textures/icon_item_fra_static/icon_item_fra_static.h" +} + typedef struct BossRushSetting { std::array name; std::vector> choices; } BossRushSetting; -BossRushSetting BossRushOptions[BOSSRUSH_OPTIONS_AMOUNT] = { +BossRushSetting BossRushOptions[BR_OPTIONS_MAX] = { { { "BOSSES:", "BOSSE:", "BOSS:" }, { @@ -112,15 +126,15 @@ BossRushSetting BossRushOptions[BOSSRUSH_OPTIONS_AMOUNT] = { } }; -const char* BossRush_GetSettingName(uint8_t optionIndex, uint8_t language) { +const char* BossRush_GetSettingName(u8 optionIndex, u8 language) { return BossRushOptions[optionIndex].name[language].c_str(); } -const char* BossRush_GetSettingChoiceName(uint8_t optionIndex, uint8_t choiceIndex, uint8_t language) { +const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language) { return BossRushOptions[optionIndex].choices[choiceIndex][language].c_str(); } -uint8_t BossRush_GetSettingOptionsAmount(uint8_t optionIndex) { +u8 BossRush_GetSettingOptionsAmount(u8 optionIndex) { return BossRushOptions[optionIndex].choices.size(); } @@ -168,8 +182,44 @@ void BossRush_SpawnBlueWarps(PlayState* play) { } } -void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) { +void BossRush_SetEquipment(u8 linkAge) { + std::array brButtonItems; + std::array brCButtonSlots; + + // Set Child Equipment. + if (linkAge == LINK_AGE_CHILD) { + brButtonItems = { + ITEM_SWORD_KOKIRI, ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE + }; + + brCButtonSlots = { SLOT_STICK, SLOT_NUT, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }; + Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI); + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_DEKU); + // Set Adult equipment. + } else { + brButtonItems = { ITEM_SWORD_MASTER, ITEM_BOW, ITEM_HAMMER, ITEM_BOMB, + ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }; + + brCButtonSlots = { SLOT_BOW, SLOT_HAMMER, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }; + + Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_MASTER); + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); + Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_GORON); + } + + // Button Items + for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.buttonItems); button++) { + gSaveContext.equips.buttonItems[button] = brButtonItems[button]; + } + + // C buttons + for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); button++) { + gSaveContext.equips.cButtonSlots[button] = brCButtonSlots[button]; + } +} + +void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) { // If warping from Chamber of Sages, choose the correct boss room to teleport to. if (play->sceneNum == SCENE_CHAMBER_OF_THE_SAGES) { // Gohma & Phantom Ganon @@ -202,10 +252,13 @@ void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) { // Ganondork } else if (warpPosX == -199 && warpPosZ == 0) { play->nextEntranceIndex = ENTR_GANONDORF_BOSS_0; + } else { + SPDLOG_ERROR("[BossRush]: Unknown blue warp in chamber of sages at position ({}, {}). Warping back to chamber of sages.", warpPosX, warpPosZ); + play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; } // If coming from a boss room, teleport back to Chamber of Sages and set flag. } else { - play->nextEntranceIndex = SCENE_HAIRAL_NIWA2; + play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; if (CheckDungeonCount() == 3) { play->linkAgeOnLoad = LINK_AGE_ADULT; @@ -223,6 +276,10 @@ void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) { } } } + + play->transitionTrigger = TRANS_TRIGGER_START; + play->transitionType = TRANS_TYPE_FADE_WHITE; + gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE_SLOW; } void BossRush_HandleBlueWarpHeal(PlayState* play) { @@ -235,10 +292,6 @@ void BossRush_HandleBlueWarpHeal(PlayState* play) { } void BossRush_HandleCompleteBoss(PlayState* play) { - if (!IS_BOSS_RUSH) { - return; - } - gSaveContext.isBossRushPaused = 1; switch (play->sceneNum) { case SCENE_DEKU_TREE_BOSS: @@ -308,7 +361,7 @@ void BossRush_InitSave() { } // Set health - uint16_t health = 16; + u16 health = 16; switch (gSaveContext.bossRushOptions[BR_OPTIONS_HEARTS]) { case BR_CHOICE_HEARTS_7: health *= 7; @@ -418,7 +471,7 @@ void BossRush_InitSave() { } // Upgrades - uint8_t upgradeLevel = 1; + u8 upgradeLevel = 1; if (gSaveContext.bossRushOptions[BR_OPTIONS_AMMO] == BR_CHOICE_AMMO_MAXED) { upgradeLevel = 3; } @@ -450,40 +503,209 @@ void BossRush_InitSave() { } } -void BossRush_SetEquipment(uint8_t linkAge) { - - std::array brButtonItems; - std::array brCButtonSlots; - - // Set Child Equipment. - if (linkAge == LINK_AGE_CHILD) { - brButtonItems = { - ITEM_SWORD_KOKIRI, ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE - }; +static void* sSavePromptNoChoiceTexs[] = { + (void*)gPauseNoENGTex, + (void*)gPauseNoGERTex, + (void*)gPauseNoFRATex +}; - brCButtonSlots = { SLOT_STICK, SLOT_NUT, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }; +void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void* optionalArg) { + switch (id) { + // Allow not healing before ganon + case VB_GANON_HEAL_BEFORE_FIGHT: { + if (gSaveContext.bossRushOptions[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_NEVER) { + *should = false; + } + break; + } + // Replace the blue warp transitions with ones that lead back to the chamber of sages + case VB_BLUE_WARP_APPLY_ENTRANCE_AND_CUTSCENE: { + DoorWarp1* blueWarp = static_cast(optionalArg); + BossRush_HandleBlueWarp(gPlayState, blueWarp->actor.world.pos.x, blueWarp->actor.world.pos.z); + *should = false; + break; + } + // Spawn clean blue warps (no ruto, adult animation, etc) + case VB_SPAWN_BLUE_WARP: { + switch (gPlayState->sceneNum) { + case SCENE_DEKU_TREE_BOSS: { + BossGoma* bossGoma = static_cast(optionalArg); + static Vec3f roomCenter = { -150.0f, 0.0f, -350.0f }; + Vec3f childPos = roomCenter; + + for (s32 i = 0; i < 10000; i++) { + if ((fabsf(childPos.x - GET_PLAYER(gPlayState)->actor.world.pos.x) < 100.0f && + fabsf(childPos.z - GET_PLAYER(gPlayState)->actor.world.pos.z) < 100.0f) || + (fabsf(childPos.x - bossGoma->actor.world.pos.x) < 150.0f && + fabsf(childPos.z - bossGoma->actor.world.pos.z) < 150.0f)) { + childPos.x = Rand_CenteredFloat(400.0f) + -150.0f; + childPos.z = Rand_CenteredFloat(400.0f) + -350.0f; + } else { + break; + } + } + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, childPos.x, bossGoma->actor.world.pos.y, childPos.z, 0, 0, 0, WARP_DUNGEON_ADULT, false); + break; + } + case SCENE_DODONGOS_CAVERN_BOSS: { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, WARP_DUNGEON_ADULT, false); + break; + } + case SCENE_JABU_JABU_BOSS: { + static Vec3f sWarpPos[] = { + { 10.0f, 0.0f, 30.0f }, + { 260.0f, 0.0f, -470.0f }, + { -240.0f, 0.0f, -470.0f }, + }; + + s32 sp7C = 2; + for (s32 i = 2; i > 0; i -= 1) { + if (Math_Vec3f_DistXYZ(&sWarpPos[i], &GET_PLAYER(gPlayState)->actor.world.pos) < + Math_Vec3f_DistXYZ(&sWarpPos[i - 1], &GET_PLAYER(gPlayState)->actor.world.pos)) { + sp7C = i - 1; + } + } + + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, sWarpPos[sp7C].x, sWarpPos[sp7C].y, sWarpPos[sp7C].z, 0, 0, 0, WARP_DUNGEON_ADULT, false); + break; + } + case SCENE_FOREST_TEMPLE_BOSS: { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, 14.0f, -33.0f, -3315.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true); + break; + } + case SCENE_FIRE_TEMPLE_BOSS: { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true); + break; + } + case SCENE_WATER_TEMPLE_BOSS: { + BossMo* bossMo = static_cast(optionalArg); + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, bossMo->actor.world.pos.x, -280.0f, bossMo->actor.world.pos.z, 0, 0, 0, WARP_DUNGEON_ADULT, true); + break; + } + case SCENE_SPIRIT_TEMPLE_BOSS: { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true); + break; + } + case SCENE_SHADOW_TEMPLE_BOSS: { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, -50.0f, 0.0f, 400.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true); + break; + } + default: { + SPDLOG_WARN("[BossRush]: Blue warp spawned in unhandled scene, ignoring"); + return; + } + } + *should = false; + break; + } + // Skip past the "Save?" window when pressing B while paused and instead close the menu. + case VB_CLOSE_PAUSE_MENU: { + if (CHECK_BTN_ALL(gPlayState->state.input[0].press.button, BTN_B)) { + *should = true; + } + break; + } + // Show "No" twice because the player can't continue. + case VB_RENDER_YES_ON_CONTINUE_PROMPT: { + Gfx** disp = static_cast(optionalArg); + *disp = KaleidoScope_QuadTextureIA8(*disp, sSavePromptNoChoiceTexs[gSaveContext.language], 48, 16, 12); + *should = false; + break; + } + // Break the dodongo breakable floor immediately so the player can jump in the hole immediately. + case VB_BG_BREAKWALL_BREAK: { + *should = true; + break; + } + // Skip past the "Save?" window when dying and go to the "Continue?" screen immediately. + case VB_TRANSITION_TO_SAVE_SCREEN_ON_DEATH: { + PauseContext* pauseCtx = static_cast(optionalArg); + pauseCtx->state = 0xF; + *should = false; + break; + } + // Prevent saving + case VB_BE_ABLE_TO_SAVE: + // Disable doors so the player can't leave the boss rooms backwards. + case VB_BE_ABLE_TO_OPEN_DOORS: + // There's no heart containers in boss rush + case VB_SPAWN_HEART_CONTAINER: + // Rupees are useless in boss rush + case VB_RENDER_RUPEE_COUNTER: { + *should = false; + break; + } + // Prevent warning spam + default: { + break; + } + } +} - Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI); - Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_DEKU); - // Set Adult equipment. - } else { - brButtonItems = { ITEM_SWORD_MASTER, ITEM_BOW, ITEM_HAMMER, ITEM_BOMB, - ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }; +void BossRush_OnActorInitHandler(void* actorRef) { + Actor* actor = static_cast(actorRef); - brCButtonSlots = { SLOT_BOW, SLOT_HAMMER, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }; + if (actor->id == ACTOR_DEMO_SA && gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES) { + BossRush_SpawnBlueWarps(gPlayState); + Actor_Kill(actor); + GET_PLAYER(gPlayState)->actor.world.rot.y = GET_PLAYER(gPlayState)->actor.shape.rot.y = 27306; + return; + } - Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_MASTER); - Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); - Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_GORON); + // Remove chests, mainly for the chest in King Dodongo's boss room. + // Remove bushes, used in Gohma's arena. + // Remove pots, used in Barinade's and Ganondorf's arenas. + if (actor->id == ACTOR_EN_KUSA || actor->id == ACTOR_OBJ_TSUBO || actor->id == ACTOR_EN_BOX) { + Actor_Kill(actor); + return; } +} - // Button Items - for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.buttonItems); button++) { - gSaveContext.equips.buttonItems[button] = brButtonItems[button]; +void BossRush_OnSceneInitHandler(s16 sceneNum) { + // Unpause the timer when the scene loaded isn't the Chamber of Sages. + if (sceneNum != SCENE_CHAMBER_OF_THE_SAGES) { + gSaveContext.isBossRushPaused = 0; } +} - // C buttons - for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); button++) { - gSaveContext.equips.cButtonSlots[button] = brCButtonSlots[button]; +void BossRush_OnBossDefeatHandler(void* refActor) { + BossRush_HandleCompleteBoss(gPlayState); +} + +void BossRush_OnBlueWarpUpdate(void* actor) { + DoorWarp1* blueWarp = static_cast(actor); + + if (blueWarp->warpTimer > 160) { + BossRush_HandleBlueWarp(gPlayState, blueWarp->actor.world.pos.x, blueWarp->actor.world.pos.z); } } + +void BossRush_RegisterHooks() { + static u32 onVanillaBehaviorHook = 0; + static u32 onSceneInitHook = 0; + static u32 onActorInitHook = 0; + static u32 onBossDefeatHook = 0; + static u32 onActorUpdate = 0; + + GameInteractor::Instance->RegisterGameHook([](int32_t fileNum) { + GameInteractor::Instance->UnregisterGameHook(onVanillaBehaviorHook); + GameInteractor::Instance->UnregisterGameHook(onSceneInitHook); + GameInteractor::Instance->UnregisterGameHook(onActorInitHook); + GameInteractor::Instance->UnregisterGameHook(onBossDefeatHook); + GameInteractor::Instance->UnregisterGameHookForID(onActorUpdate); + + onVanillaBehaviorHook = 0; + onSceneInitHook = 0; + onActorInitHook = 0; + onBossDefeatHook = 0; + onActorUpdate = 0; + + if (!IS_BOSS_RUSH) return; + + onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook(BossRush_OnVanillaBehaviorHandler); + onSceneInitHook = GameInteractor::Instance->RegisterGameHook(BossRush_OnSceneInitHandler); + onActorInitHook = GameInteractor::Instance->RegisterGameHook(BossRush_OnActorInitHandler); + onBossDefeatHook = GameInteractor::Instance->RegisterGameHook(BossRush_OnBossDefeatHandler); + onActorUpdate = GameInteractor::Instance->RegisterGameHookForID(ACTOR_DOOR_WARP1, BossRush_OnBlueWarpUpdate); + }); +} \ No newline at end of file diff --git a/soh/soh/Enhancements/boss-rush/BossRush.h b/soh/soh/Enhancements/boss-rush/BossRush.h index caa6ce86e5a..292cbaf26ad 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.h +++ b/soh/soh/Enhancements/boss-rush/BossRush.h @@ -1,20 +1,16 @@ #pragma once -#include "BossRushTypes.h" -#include "variables.h" +#include "z64.h" #ifdef __cplusplus extern "C" { #endif -void BossRush_SpawnBlueWarps(PlayState* play); -void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ); -void BossRush_HandleBlueWarpHeal(PlayState* play); -void BossRush_InitSave(); -void BossRush_SetEquipment(uint8_t linkAge); -void BossRush_HandleCompleteBoss(PlayState* play); -const char* BossRush_GetSettingName(uint8_t optionIndex, uint8_t language); -const char* BossRush_GetSettingChoiceName(uint8_t optionIndex, uint8_t choiceIndex, uint8_t language); -uint8_t BossRush_GetSettingOptionsAmount(uint8_t optionIndex); + void BossRush_HandleBlueWarpHeal(PlayState* play); + void BossRush_InitSave(); + const char* BossRush_GetSettingName(u8 optionIndex, u8 language); + const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language); + u8 BossRush_GetSettingOptionsAmount(u8 optionIndex); + void BossRush_RegisterHooks(); #ifdef __cplusplus }; #endif diff --git a/soh/soh/Enhancements/boss-rush/BossRushTypes.h b/soh/soh/Enhancements/boss-rush/BossRushTypes.h index e39cba997a9..cf4d3aaba19 100644 --- a/soh/soh/Enhancements/boss-rush/BossRushTypes.h +++ b/soh/soh/Enhancements/boss-rush/BossRushTypes.h @@ -1,6 +1,5 @@ #pragma once -#define BOSSRUSH_OPTIONS_AMOUNT 12 #define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6 typedef enum { @@ -15,7 +14,8 @@ typedef enum { BR_OPTIONS_LONGSHOT, BR_OPTIONS_HOVERBOOTS, BR_OPTIONS_BUNNYHOOD, - BR_OPTIONS_TIMER + BR_OPTIONS_TIMER, + BR_OPTIONS_MAX, } BossRushOptionEnums; typedef enum { diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index d96a3ab55a5..1a86987f8ac 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -247,6 +247,46 @@ typedef enum { ``` */ VB_DRAW_AMMO_COUNT, + // Vanilla condition: true + VB_HAVE_OCARINA_NOTE_D4, + // Vanilla condition: true + VB_HAVE_OCARINA_NOTE_D5, + // Vanilla condition: true + VB_HAVE_OCARINA_NOTE_F4, + // Vanilla condition: true + VB_HAVE_OCARINA_NOTE_B4, + // Vanilla condition: true + VB_HAVE_OCARINA_NOTE_A4, + // Vanilla condition: false + VB_SKIP_SCARECROWS_SONG, + // Vanilla condition: true + VB_RENDER_RUPEE_COUNTER, + // Vanilla condition: true + VB_RENDER_KEY_COUNTER, + // Vanilla condition: true + VB_SPAWN_HEART_CONTAINER, + // Vanilla condition: true + VB_BE_ABLE_TO_OPEN_DOORS, + // Vanilla condition: true + VB_REVERT_SPOILING_ITEMS, + // Vanilla condition: Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP) || BREG(2) + VB_BE_ABLE_TO_PLAY_BOMBCHU_BOWLING, + // Vanilla condition: true + VB_BE_ABLE_TO_SAVE, + // Vanilla condition: true + VB_TRANSITION_TO_SAVE_SCREEN_ON_DEATH, + // Vanilla condition: true + VB_RENDER_YES_ON_CONTINUE_PROMPT, + // Vanilla condition: CHECK_BTN_ALL(input->press.button, BTN_START) + VB_CLOSE_PAUSE_MENU, + // Vanilla condition: true + VB_SPAWN_BLUE_WARP, + // Vanilla condition: this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF + VB_BLUE_WARP_APPLY_ENTRANCE_AND_CUTSCENE, + // Vanilla condition: this->collider.base.acFlags & 2 + VB_BG_BREAKWALL_BREAK, + // Vanilla condition: true + VB_GANON_HEAL_BEFORE_FIGHT, VB_FREEZE_LINK_FOR_BLOCK_THROW, VB_MOVE_THROWN_ACTOR, diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index a3fc284eef9..5669da66195 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -24,6 +24,7 @@ DEFINE_HOOK(OnActorInit, (void* actor)); DEFINE_HOOK(OnActorUpdate, (void* actor)); DEFINE_HOOK(OnActorKill, (void* actor)); DEFINE_HOOK(OnEnemyDefeat, (void* actor)); +DEFINE_HOOK(OnBossDefeat, (void* actor)); DEFINE_HOOK(OnPlayerBonk, ()); DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(OnPlayDrawEnd, ()); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 93c8fc75eba..49724b66cf2 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -100,6 +100,13 @@ void GameInteractor_ExecuteOnEnemyDefeat(void* actor) { GameInteractor::Instance->ExecuteHooksForFilter(actor); } +void GameInteractor_ExecuteOnBossDefeat(void* actor) { + GameInteractor::Instance->ExecuteHooks(actor); + GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor); + GameInteractor::Instance->ExecuteHooksForPtr((uintptr_t)actor, actor); + GameInteractor::Instance->ExecuteHooksForFilter(actor); +} + void GameInteractor_ExecuteOnPlayerBonk() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 82deeaf8020..e1cba237db1 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -22,6 +22,7 @@ void GameInteractor_ExecuteOnActorInit(void* actor); void GameInteractor_ExecuteOnActorUpdate(void* actor); void GameInteractor_ExecuteOnActorKill(void* actor); void GameInteractor_ExecuteOnEnemyDefeat(void* actor); +void GameInteractor_ExecuteOnBossDefeat(void* actor); void GameInteractor_ExecuteOnPlayerBonk(); void GameInteractor_ExecuteOnOcarinaSongAction(); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 2c0368fea0c..adac0d96812 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -3,7 +3,7 @@ #include "game-interactor/GameInteractor.h" #include "tts/tts.h" #include "soh/OTRGlobals.h" -#include "soh/Enhancements/boss-rush/BossRushTypes.h" +#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/randomizer/3drando/random.hpp" #include "soh/Enhancements/cosmetics/authenticGfxPatches.h" @@ -784,48 +784,6 @@ void RegisterResetNaviTimer() { }); } -f32 triforcePieceScale; - -void RegisterTriforceHunt() { - GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsGameplayPaused() && - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT)) { - - // Warp to credits - if (GameInteractor::State::TriforceHuntCreditsWarpActive) { - gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; - gSaveContext.nextCutsceneIndex = 0xFFF2; - gPlayState->transitionTrigger = TRANS_TRIGGER_START; - gPlayState->transitionType = TRANS_TYPE_FADE_WHITE; - GameInteractor::State::TriforceHuntCreditsWarpActive = 0; - } - - // Reset Triforce Piece scale for GI animation. Triforce Hunt allows for multiple triforce models, - // and cycles through them based on the amount of triforce pieces collected. It takes a little while - // for the count to increase during the GI animation, so the model is entirely hidden until that piece - // has been added. That scale has to be reset after the textbox is closed, and this is the best way - // to ensure it's done at that point in time specifically. - if (GameInteractor::State::TriforceHuntPieceGiven) { - triforcePieceScale = 0.0f; - GameInteractor::State::TriforceHuntPieceGiven = 0; - } - } - }); -} - -void RegisterGrantGanonsBossKey() { - GameInteractor::Instance->RegisterGameHook([]() { - // Triforce Hunt needs the check if the player isn't being teleported to the credits scene. - if (!GameInteractor::IsGameplayPaused() && IS_RANDO && - Flags_GetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY) && gPlayState->transitionTrigger != TRANS_TRIGGER_START && - (1 << 0 & gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER]) == 0) { - GetItemEntry getItemEntry = - ItemTableManager::Instance->RetrieveItemEntry(MOD_RANDOMIZER, RG_GANONS_CASTLE_BOSS_KEY); - GiveItemEntryWithoutActor(gPlayState, getItemEntry); - } - }); -} - //this map is used for enemies that can be uniquely identified by their id //and that are always counted //enemies that can't be uniquely identified by their id @@ -873,7 +831,7 @@ static std::unordered_map uniqueEnemyIdToStatCount = { void RegisterEnemyDefeatCounts() { GameInteractor::Instance->RegisterGameHook([](void* refActor) { - Actor* actor = (Actor*)refActor; + Actor* actor = static_cast(refActor); if (uniqueEnemyIdToStatCount.contains(actor->id)) { gSaveContext.sohStats.count[uniqueEnemyIdToStatCount[actor->id]]++; } else { @@ -1017,6 +975,45 @@ void RegisterEnemyDefeatCounts() { }); } +void RegisterBossDefeatTimestamps() { + GameInteractor::Instance->RegisterGameHook([](void* refActor) { + Actor* actor = static_cast(refActor); + switch (actor->id) { + case ACTOR_BOSS_DODONGO: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_KING_DODONGO] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_FD2: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_VOLVAGIA] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_GANON: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANONDORF] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_GANON2: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME; + gSaveContext.sohStats.gameComplete = true; + break; + case ACTOR_BOSS_GANONDROF: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_GOMA: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GOHMA] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_MO: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_MORPHA] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_SST: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BONGO_BONGO] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_TW: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_TWINROVA] = GAMEPLAYSTAT_TOTAL_TIME; + break; + case ACTOR_BOSS_VA: + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BARINADE] = GAMEPLAYSTAT_TOTAL_TIME; + break; + } + }); +} + typedef enum { ADD_ICE_TRAP, ADD_BURN_TRAP, @@ -1165,83 +1162,6 @@ void RegisterAltTrapTypes() { }); } -void RegisterRandomizerSheikSpawn() { - GameInteractor::Instance->RegisterGameHook([]() { - if (!gPlayState) return; - if (!IS_RANDO || !LINK_IS_ADULT || !OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHEIK_LA_HINT)) return; - switch (gPlayState->sceneNum) { - case SCENE_TEMPLE_OF_TIME: - if (gPlayState->roomCtx.curRoom.num == 1) { - Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_XC, -104, -40, 2382, 0, 0x8000, 0, SHEIK_TYPE_RANDO, false); - } - break; - case SCENE_INSIDE_GANONS_CASTLE: - if (gPlayState->roomCtx.curRoom.num == 1){ - Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_XC, 101, 150, 137, 0, 0, 0, SHEIK_TYPE_RANDO, false); - } - break; - default: break; - } - }); -} - -//Boss souls require an additional item (represented by a RAND_INF) to spawn a boss in a particular lair -void RegisterBossSouls() { - GameInteractor::Instance->RegisterGameHook([](void* actor) { - if (!gPlayState) return; - if (!IS_RANDO || !(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS))) return; - RandomizerInf rand_inf = RAND_INF_MAX; - Actor* actual = (Actor*)actor; - switch (gPlayState->sceneNum){ - case SCENE_DEKU_TREE_BOSS: - rand_inf = RAND_INF_GOHMA_SOUL; - break; - case SCENE_DODONGOS_CAVERN_BOSS: - rand_inf = RAND_INF_KING_DODONGO_SOUL; - break; - case SCENE_JABU_JABU_BOSS: - rand_inf = RAND_INF_BARINADE_SOUL; - break; - case SCENE_FOREST_TEMPLE_BOSS: - rand_inf = RAND_INF_PHANTOM_GANON_SOUL; - break; - case SCENE_FIRE_TEMPLE_BOSS: - rand_inf = RAND_INF_VOLVAGIA_SOUL; - break; - case SCENE_WATER_TEMPLE_BOSS: - rand_inf = RAND_INF_MORPHA_SOUL; - break; - case SCENE_SHADOW_TEMPLE_BOSS: - rand_inf = RAND_INF_BONGO_BONGO_SOUL; - break; - case SCENE_SPIRIT_TEMPLE_BOSS: - rand_inf = RAND_INF_TWINROVA_SOUL; - break; - case SCENE_GANONDORF_BOSS: - case SCENE_GANON_BOSS: - if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS) == RO_BOSS_SOULS_ON_PLUS_GANON) { - rand_inf = RAND_INF_GANON_SOUL; - } - break; - default: break; - } - - //Deletes all actors in the boss category if the soul isn't found. - //Some actors, like Dark Link, Arwings, and Zora's Sapphire...?, are in this category despite not being actual bosses, - //so ignore any "boss" if `rand_inf` doesn't change from RAND_INF_MAX. - if (rand_inf != RAND_INF_MAX) { - if (!Flags_GetRandomizerInf(rand_inf) && actual->category == ACTORCAT_BOSS) { - Actor_Delete(&gPlayState->actorCtx, actual, gPlayState); - } - //Special case for Phantom Ganon's horse (and fake), as they're considered "background actors", - //but still control the boss fight flow. - if (!Flags_GetRandomizerInf(RAND_INF_PHANTOM_GANON_SOUL) && actual->id == ACTOR_EN_FHG) { - Actor_Delete(&gPlayState->actorCtx, actual, gPlayState); - } - } - }); -} - void UpdateHurtContainerModeState(bool newState) { static bool hurtEnabled = false; if (hurtEnabled == newState) { @@ -1460,131 +1380,6 @@ void RegisterPauseMenuHooks() { }); } -//from z_player.c -typedef struct { - /* 0x00 */ Vec3f pos; - /* 0x0C */ s16 yaw; -} SpecialRespawnInfo; // size = 0x10 - -//special respawns used when voided out without swim to prevent infinite loops -std::map swimSpecialRespawnInfo = { - { - ENTR_ZORAS_RIVER_3,//hf to zr in water - { { -1455.443, -20, 1384.826 }, 28761 } - }, - { - ENTR_HYRULE_FIELD_14,//zr to hf in water - { { 5830.209, -92.16, 3925.911 }, -20025 } - }, - { - ENTR_LOST_WOODS_7,//zr to lw - { { 1978.718, -36.908, -855 }, -16384 } - }, - { - ENTR_ZORAS_RIVER_4,//lw to zr - { { 4082.366, 860.442, -1018.949 }, -32768 } - }, - { - ENTR_LAKE_HYLIA_1,//gv to lh - { { -3276.416, -1033, 2908.421 }, 11228 } - }, - { - ENTR_WATER_TEMPLE_0,//lh to water temple - { { -182, 780, 759.5 }, -32768 } - }, - { - ENTR_LAKE_HYLIA_2,//water temple to lh - { { -955.028, -1306.9, 6768.954 }, -32768 } - }, - { - ENTR_ZORAS_DOMAIN_4,//lh to zd - { { -109.86, 11.396, -9.933 }, -29131 } - }, - { - ENTR_LAKE_HYLIA_7,//zd to lh - { { -912, -1326.967, 3391 }, 0 } - }, - { - ENTR_GERUDO_VALLEY_1,//caught by gerudos as child - { { -424, -2051, -74 }, 16384 } - } -}; - -void RegisterNoSwim() { - GameInteractor::Instance->RegisterGameHook([]() { - if ( - IS_RANDO && - (GET_PLAYER(gPlayState)->stateFlags1 & PLAYER_STATE1_IN_WATER) && - !Flags_GetRandomizerInf(RAND_INF_CAN_SWIM) && - CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) != EQUIP_VALUE_BOOTS_IRON - ) { - //if you void out in water temple without swim you get instantly kicked out to prevent softlocks - if (gPlayState->sceneNum == SCENE_WATER_TEMPLE) { - GameInteractor::RawAction::TeleportPlayer(Entrance_OverrideNextIndex(ENTR_LAKE_HYLIA_2));//lake hylia from water temple - return; - } - - if (swimSpecialRespawnInfo.find(gSaveContext.entranceIndex) != swimSpecialRespawnInfo.end()) { - SpecialRespawnInfo* respawnInfo = &swimSpecialRespawnInfo.at(gSaveContext.entranceIndex); - - Play_SetupRespawnPoint(gPlayState, RESPAWN_MODE_DOWN, 0xDFF); - gSaveContext.respawn[RESPAWN_MODE_DOWN].pos = respawnInfo->pos; - gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw; - } - - Play_TriggerVoidOut(gPlayState); - } - }); -} - -void RegisterNoWallet() { - GameInteractor::Instance->RegisterGameHook([]() { - if (IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_WALLET)) { - gSaveContext.rupees = 0; - } - }); -} - -void RegisterInfiniteUpgrades() { - GameInteractor::Instance->RegisterGameHook([]() { - if (!IS_RANDO) { - return; - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_QUIVER)) { - AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_BOMB_BAG)) { - AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_BULLET_BAG)) { - AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_STICK_UPGRADE)) { - AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_NUT_UPGRADE)) { - AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { - gSaveContext.magic = gSaveContext.magicCapacity; - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_BOMBCHUS)) { - AMMO(ITEM_BOMBCHU) = 50; - } - - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MONEY)) { - gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); - } - }); -} - extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); void PatchCompasses() { @@ -1605,30 +1400,8 @@ void RegisterRandomizerCompasses() { }); } -extern "C" void func_8099485C(DoorGerudo* gerudoDoor, PlayState* play); - -void RegisterSkeletonKey() { - GameInteractor::Instance->RegisterGameHook([](void* refActor) { - if (Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) { - Actor* actor = static_cast(refActor); - if (actor->id == ACTOR_EN_DOOR) { - EnDoor* door = (EnDoor*)actor; - door->lockTimer = 0; - } else if (actor->id == ACTOR_DOOR_SHUTTER) { - DoorShutter* shutterDoor = (DoorShutter*)actor; - if (shutterDoor->doorType == SHUTTER_KEY_LOCKED) { - shutterDoor->unk_16E = 0; - } - } else if (actor->id == ACTOR_DOOR_GERUDO) { - DoorGerudo* gerudoDoor = (DoorGerudo*)actor; - gerudoDoor->actionFunc = func_8099485C; - gerudoDoor->dyna.actor.world.pos.y = gerudoDoor->dyna.actor.home.pos.y + 200.0f; - } - } - }); -} - void InitMods() { + BossRush_RegisterHooks(); RandomizerRegisterHooks(); TimeSaverRegisterHooks(); CheatsRegisterHooks(); @@ -1658,24 +1431,17 @@ void InitMods() { RegisterMenuPathFix(); RegisterMirrorModeHandler(); RegisterResetNaviTimer(); - RegisterTriforceHunt(); - RegisterGrantGanonsBossKey(); RegisterEnemyDefeatCounts(); + RegisterBossDefeatTimestamps(); RegisterAltTrapTypes(); - RegisterRandomizerSheikSpawn(); - RegisterBossSouls(); RegisterRandomizedEnemySizes(); RegisterOpenAllHours(); RegisterToTMedallions(); - RegisterNoSwim(); - RegisterNoWallet(); - RegisterInfiniteUpgrades(); RegisterRandomizerCompasses(); NameTag_RegisterHooks(); RegisterFloorSwitchesHook(); RegisterPatchHandHandler(); RegisterHurtContainerModeHandler(); RegisterPauseMenuHooks(); - RegisterSkeletonKey(); RandoKaleido_RegisterHooks(); } diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 8c86af5ef65..af56293cb4c 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -2,6 +2,7 @@ #include "soh/OTRGlobals.h" #include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/custom-message/CustomMessageTypes.h" +#include "soh/Enhancements/item-tables/ItemTableManager.h" #include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/dungeon.h" #include "soh/Enhancements/randomizer/fishsanity.h" @@ -35,6 +36,10 @@ extern "C" { #include "src/overlays/actors/ovl_Obj_Comb/z_obj_comb.h" #include "src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h" #include "src/overlays/actors/ovl_En_Ge1/z_en_ge1.h" +#include "src/overlays/actors/ovl_En_Door/z_en_door.h" +#include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h" +#include "src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h" +#include "src/overlays/actors/ovl_En_Xc/z_en_xc.h" #include "src/overlays/actors/ovl_Fishing/z_fishing.h" #include "adult_trade_shuffle.h" #include "draw.h" @@ -1179,6 +1184,79 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void } break; } + case VB_HAVE_OCARINA_NOTE_D4: { + if (!Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_A)) { + *should = false; + } + break; + } + case VB_HAVE_OCARINA_NOTE_D5: { + if (!Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_UP)) { + *should = false; + } + break; + } + case VB_HAVE_OCARINA_NOTE_F4: { + if (!Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_DOWN)) { + *should = false; + } + break; + } + case VB_HAVE_OCARINA_NOTE_B4: { + if (!Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_LEFT)) { + *should = false; + } + break; + } + case VB_HAVE_OCARINA_NOTE_A4: { + if (!Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_RIGHT)) { + *should = false; + } + break; + } + case VB_SKIP_SCARECROWS_SONG: { + int ocarinaButtonCount = 0; + for (int i = VB_HAVE_OCARINA_NOTE_D4; i <= VB_HAVE_OCARINA_NOTE_A4; i++) { + if (GameInteractor_Should((GIVanillaBehavior)i, true, NULL)) { + ocarinaButtonCount++; + } + } + + if (ocarinaButtonCount < 2) { + *should = false; + break; + } + + if (gPlayState->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING && RAND_GET_OPTION(RSK_SKIP_SCARECROWS_SONG)) { + *should = true; + break; + } + break; + } + case VB_RENDER_KEY_COUNTER: { + if (Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) { + *should = false; + } + break; + } + case VB_RENDER_RUPEE_COUNTER: { + if (!Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) || Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MONEY)) { + *should = false; + } + break; + } + case VB_REVERT_SPOILING_ITEMS: { + if (RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE)) { + *should = false; + } + break; + } + case VB_BE_ABLE_TO_PLAY_BOMBCHU_BOWLING: { + // Only check for bomb bag when bombchus aren't in logic + // and only check for bombchus when bombchus are in logic + *should = INV_CONTENT((RAND_GET_OPTION(RSK_BOMBCHUS_IN_LOGIC) ? ITEM_BOMBCHU : ITEM_BOMB)) != ITEM_NONE; + break; + } case VB_SHOULD_CHECK_FOR_FISHING_RECORD: { f32 sFishOnHandLength = *static_cast(optionalArg); *should = *should || ShouldGiveFishingPrize(sFishOnHandLength); @@ -1222,7 +1300,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void fishData->actor->stateAndTimer = 0; } } - } + } break; } case VB_TRADE_TIMER_ODD_MUSHROOM: @@ -1286,6 +1364,10 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) { CheckTracker::RecalculateAllAreaTotals(); } + if (RAND_GET_OPTION(RSK_SHUFFLE_ENTRANCES)) { + Entrance_OverrideWeatherState(); + } + // LACs & Prelude checks static uint32_t updateHook = 0; @@ -1577,6 +1659,291 @@ void RandomizerOnActorInitHandler(void* actorRef) { ) { Actor_Kill(actor); } + + if (RAND_GET_OPTION(RSK_SHUFFLE_BOSS_SOULS)) { + //Boss souls require an additional item (represented by a RAND_INF) to spawn a boss in a particular lair + RandomizerInf currentBossSoulRandInf = RAND_INF_MAX; + switch (gPlayState->sceneNum){ + case SCENE_DEKU_TREE_BOSS: + currentBossSoulRandInf = RAND_INF_GOHMA_SOUL; + break; + case SCENE_DODONGOS_CAVERN_BOSS: + currentBossSoulRandInf = RAND_INF_KING_DODONGO_SOUL; + break; + case SCENE_JABU_JABU_BOSS: + currentBossSoulRandInf = RAND_INF_BARINADE_SOUL; + break; + case SCENE_FOREST_TEMPLE_BOSS: + currentBossSoulRandInf = RAND_INF_PHANTOM_GANON_SOUL; + break; + case SCENE_FIRE_TEMPLE_BOSS: + currentBossSoulRandInf = RAND_INF_VOLVAGIA_SOUL; + break; + case SCENE_WATER_TEMPLE_BOSS: + currentBossSoulRandInf = RAND_INF_MORPHA_SOUL; + break; + case SCENE_SHADOW_TEMPLE_BOSS: + currentBossSoulRandInf = RAND_INF_BONGO_BONGO_SOUL; + break; + case SCENE_SPIRIT_TEMPLE_BOSS: + currentBossSoulRandInf = RAND_INF_TWINROVA_SOUL; + break; + case SCENE_GANONDORF_BOSS: + case SCENE_GANON_BOSS: + if (RAND_GET_OPTION(RSK_SHUFFLE_BOSS_SOULS) == RO_BOSS_SOULS_ON_PLUS_GANON) { + currentBossSoulRandInf = RAND_INF_GANON_SOUL; + } + break; + default: + break; + } + + //Deletes all actors in the boss category if the soul isn't found. + //Some actors, like Dark Link, Arwings, and Zora's Sapphire...?, are in this category despite not being actual bosses, + //so ignore any "boss" if `currentBossSoulRandInf` doesn't change from RAND_INF_MAX. + if (currentBossSoulRandInf != RAND_INF_MAX) { + if (!Flags_GetRandomizerInf(currentBossSoulRandInf) && actor->category == ACTORCAT_BOSS) { + Actor_Delete(&gPlayState->actorCtx, actor, gPlayState); + } + //Special case for Phantom Ganon's horse (and fake), as they're considered "background actors", + //but still control the boss fight flow. + if (!Flags_GetRandomizerInf(RAND_INF_PHANTOM_GANON_SOUL) && actor->id == ACTOR_EN_FHG) { + Actor_Delete(&gPlayState->actorCtx, actor, gPlayState); + } + } + } + + // In MQ Spirit, remove the large silver block in the hole as child so the chest in the silver block hallway + // can be guaranteed accessible + if ( + actor->id == ACTOR_OBJ_OSHIHIKI && + LINK_IS_CHILD && + IsGameMasterQuest() && + gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE && actor->room == 6 && // Spirit Temple silver block hallway + actor->params == 0x9C7 // Silver block that is marked as in the hole + ) { + Actor_Kill(actor); + return; + } + + if ( + // If child is in the adult shooting gallery or adult in the child shooting gallery, then despawn the shooting gallery man + actor->id == ACTOR_EN_SYATEKI_MAN && + RAND_GET_OPTION(RSK_SHUFFLE_INTERIOR_ENTRANCES) && + ( + (LINK_IS_CHILD && Entrance_SceneAndSpawnAre(SCENE_SHOOTING_GALLERY, 0x00)) || //Kakariko Village -> Adult Shooting Gallery, index 003B in the entrance table + (LINK_IS_ADULT && Entrance_SceneAndSpawnAre(SCENE_SHOOTING_GALLERY, 0x01)) //Market -> Child Shooting Gallery, index 016D in the entrance table + ) + ) { + Actor_Kill(actor); + return; + } +} + +void RandomizerOnGameFrameUpdateHandler() { + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_QUIVER)) { + AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); + } + + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_BOMB_BAG)) { + AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); + } + + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_BULLET_BAG)) { + AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); + } + + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_STICK_UPGRADE)) { + AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); + } + + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_NUT_UPGRADE)) { + AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); + } + + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { + gSaveContext.magic = gSaveContext.magicCapacity; + } + + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_BOMBCHUS)) { + AMMO(ITEM_BOMBCHU) = 50; + } + + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MONEY)) { + gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); + } + + if (!Flags_GetRandomizerInf(RAND_INF_HAS_WALLET)) { + gSaveContext.rupees = 0; + } +} + +extern "C" void func_8099485C(DoorGerudo* gerudoDoor, PlayState* play); + +void RandomizerOnActorUpdateHandler(void* refActor) { + Actor* actor = static_cast(refActor); + + if (Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) { + if (actor->id == ACTOR_EN_DOOR) { + EnDoor* door = reinterpret_cast(actor); + door->lockTimer = 0; + } else if (actor->id == ACTOR_DOOR_SHUTTER) { + DoorShutter* shutterDoor = reinterpret_cast(actor); + if (shutterDoor->doorType == SHUTTER_KEY_LOCKED) { + shutterDoor->unk_16E = 0; + } + } else if (actor->id == ACTOR_DOOR_GERUDO) { + DoorGerudo* gerudoDoor = (DoorGerudo*)actor; + gerudoDoor->actionFunc = func_8099485C; + gerudoDoor->dyna.actor.world.pos.y = gerudoDoor->dyna.actor.home.pos.y + 200.0f; + } + } + + // In ER, override the warp song locations. Also removes the warp song cutscene + if (RAND_GET_OPTION(RSK_SHUFFLE_ENTRANCES) && actor->id == ACTOR_DEMO_KANKYO && actor->params == 0x000F) { // Warp Song particles + Entrance_SetWarpSongEntrance(); + } + + if (actor->id == ACTOR_OBJ_COMB) { + ObjComb* combActor = reinterpret_cast(actor); + combActor->actor.shape.rot.x = Math_SinS(combActor->unk_1B2) * CLAMP_MIN(combActor->unk_1B0, 0) + combActor->actor.home.rot.x; + } +} + +//from z_player.c +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 yaw; +} SpecialRespawnInfo; // size = 0x10 + +//special respawns used when voided out without swim to prevent infinite loops +std::map swimSpecialRespawnInfo = { + { + ENTR_ZORAS_RIVER_3,//hf to zr in water + { { -1455.443, -20, 1384.826 }, 28761 } + }, + { + ENTR_HYRULE_FIELD_14,//zr to hf in water + { { 5730.209, -20, 3725.911 }, -20025 } + }, + { + ENTR_LOST_WOODS_7,//zr to lw + { { 1978.718, -36.908, -855 }, -16384 } + }, + { + ENTR_ZORAS_RIVER_4,//lw to zr + { { 4082.366, 860.442, -1018.949 }, -32768 } + }, + { + ENTR_LAKE_HYLIA_1,//gv to lh + { { -3276.416, -1033, 2908.421 }, 11228 } + }, + { + ENTR_WATER_TEMPLE_0,//lh to water temple + { { -182, 780, 759.5 }, -32768 } + }, + { + ENTR_LAKE_HYLIA_2,//water temple to lh + { { -955.028, -1306.9, 6768.954 }, -32768 } + }, + { + ENTR_ZORAS_DOMAIN_4,//lh to zd + { { -109.86, 11.396, -9.933 }, -29131 } + }, + { + ENTR_LAKE_HYLIA_7,//zd to lh + { { -912, -1326.967, 3391 }, 0 } + }, + { + ENTR_GERUDO_VALLEY_1,//caught by gerudos as child + { { -424, -2051, -74 }, 16384 } + }, + { + ENTR_HYRULE_FIELD_7,//mk to hf (can be a problem when it then turns night) + { { 0, 0, 1100 }, 0 } + }, + { + ENTR_ZORAS_FOUNTAIN_0,//jabu blue warp to zf + { { -1580, 150, 1670 }, 8000 } + }, +}; + +f32 triforcePieceScale; + +void RandomizerOnPlayerUpdateHandler() { + if ( + (GET_PLAYER(gPlayState)->stateFlags1 & PLAYER_STATE1_IN_WATER) && + !Flags_GetRandomizerInf(RAND_INF_CAN_SWIM) && + CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) != EQUIP_VALUE_BOOTS_IRON + ) { + //if you void out in water temple without swim you get instantly kicked out to prevent softlocks + if (gPlayState->sceneNum == SCENE_WATER_TEMPLE) { + GameInteractor::RawAction::TeleportPlayer(Entrance_OverrideNextIndex(ENTR_LAKE_HYLIA_2));//lake hylia from water temple + } else { + if (swimSpecialRespawnInfo.find(gSaveContext.entranceIndex) != swimSpecialRespawnInfo.end()) { + SpecialRespawnInfo* respawnInfo = &swimSpecialRespawnInfo.at(gSaveContext.entranceIndex); + + Play_SetupRespawnPoint(gPlayState, RESPAWN_MODE_DOWN, 0xDFF); + gSaveContext.respawn[RESPAWN_MODE_DOWN].pos = respawnInfo->pos; + gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw; + } + + Play_TriggerVoidOut(gPlayState); + } + } + + // Triforce Hunt needs the check if the player isn't being teleported to the credits scene. + if ( + !GameInteractor::IsGameplayPaused() && + Flags_GetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY) && + gPlayState->transitionTrigger != TRANS_TRIGGER_START && + (1 << 0 & gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER]) == 0 + ) { + GiveItemEntryWithoutActor(gPlayState, ItemTableManager::Instance->RetrieveItemEntry(MOD_RANDOMIZER, RG_GANONS_CASTLE_BOSS_KEY)); + } + + if ( + !GameInteractor::IsGameplayPaused() && + RAND_GET_OPTION(RSK_TRIFORCE_HUNT) + ) { + // Warp to credits + if (GameInteractor::State::TriforceHuntCreditsWarpActive) { + gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; + gSaveContext.nextCutsceneIndex = 0xFFF2; + gPlayState->transitionTrigger = TRANS_TRIGGER_START; + gPlayState->transitionType = TRANS_TYPE_FADE_WHITE; + GameInteractor::State::TriforceHuntCreditsWarpActive = 0; + } + + // Reset Triforce Piece scale for GI animation. Triforce Hunt allows for multiple triforce models, + // and cycles through them based on the amount of triforce pieces collected. It takes a little while + // for the count to increase during the GI animation, so the model is entirely hidden until that piece + // has been added. That scale has to be reset after the textbox is closed, and this is the best way + // to ensure it's done at that point in time specifically. + if (GameInteractor::State::TriforceHuntPieceGiven) { + triforcePieceScale = 0.0f; + GameInteractor::State::TriforceHuntPieceGiven = 0; + } + } +} + +void RandomizerOnSceneSpawnActorsHandler() { + if (LINK_IS_ADULT && RAND_GET_OPTION(RSK_SHEIK_LA_HINT)) { + switch (gPlayState->sceneNum) { + case SCENE_TEMPLE_OF_TIME: + if (gPlayState->roomCtx.curRoom.num == 1) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_XC, -104, -40, 2382, 0, 0x8000, 0, SHEIK_TYPE_RANDO, false); + } + break; + case SCENE_INSIDE_GANONS_CASTLE: + if (gPlayState->roomCtx.curRoom.num == 1) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_XC, 101, 150, 137, 0, 0, 0, SHEIK_TYPE_RANDO, false); + } + break; + default: + break; + } + } } void RandomizerRegisterHooks() { @@ -1588,6 +1955,10 @@ void RandomizerRegisterHooks() { static uint32_t onVanillaBehaviorHook = 0; static uint32_t onSceneInitHook = 0; static uint32_t onActorInitHook = 0; + static uint32_t onActorUpdateHook = 0; + static uint32_t onPlayerUpdateHook = 0; + static uint32_t onGameFrameUpdateHook = 0; + static uint32_t onSceneSpawnActorsHook = 0; static uint32_t fishsanityOnActorInitHook = 0; static uint32_t fishsanityOnFlagSetHook = 0; @@ -1608,6 +1979,10 @@ void RandomizerRegisterHooks() { GameInteractor::Instance->UnregisterGameHook(onVanillaBehaviorHook); GameInteractor::Instance->UnregisterGameHook(onSceneInitHook); GameInteractor::Instance->UnregisterGameHook(onActorInitHook); + GameInteractor::Instance->UnregisterGameHook(onActorUpdateHook); + GameInteractor::Instance->UnregisterGameHook(onPlayerUpdateHook); + GameInteractor::Instance->UnregisterGameHook(onGameFrameUpdateHook); + GameInteractor::Instance->UnregisterGameHook(onSceneSpawnActorsHook); GameInteractor::Instance->UnregisterGameHook(fishsanityOnActorInitHook); GameInteractor::Instance->UnregisterGameHook(fishsanityOnFlagSetHook); @@ -1623,6 +1998,10 @@ void RandomizerRegisterHooks() { onVanillaBehaviorHook = 0; onSceneInitHook = 0; onActorInitHook = 0; + onActorUpdateHook = 0; + onPlayerUpdateHook = 0; + onGameFrameUpdateHook = 0; + onSceneSpawnActorsHook = 0; fishsanityOnActorInitHook = 0; fishsanityOnFlagSetHook = 0; @@ -1632,6 +2011,14 @@ void RandomizerRegisterHooks() { if (!IS_RANDO) return; + // Setup the modified entrance table and entrance shuffle table for rando + Entrance_Init(); + + // Handle randomized spawn positions after the save context has been setup from load + if (RAND_GET_OPTION(RSK_SHUFFLE_ENTRANCES)) { + Entrance_SetSavewarpEntrance(); + } + onFlagSetHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnFlagSetHandler); onSceneFlagSetHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnSceneFlagSetHandler); onPlayerUpdateForRCQueueHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnPlayerUpdateForRCQueueHandler); @@ -1640,15 +2027,19 @@ void RandomizerRegisterHooks() { onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnVanillaBehaviorHandler); onSceneInitHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnSceneInitHandler); onActorInitHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnActorInitHandler); + onActorUpdateHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnActorUpdateHandler); + onPlayerUpdateHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnPlayerUpdateHandler); + onGameFrameUpdateHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnGameFrameUpdateHandler); + onSceneSpawnActorsHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnSceneSpawnActorsHandler); if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) { OTRGlobals::Instance->gRandoContext->GetFishsanity()->InitializeFromSave(); fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnActorInitHandler); - fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnFlagSetHandler); - fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnActorUpdateHandler); - fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnSceneInitHandler); - fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnVanillaBehaviorHandler); + fishsanityOnFlagSetHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnFlagSetHandler); + fishsanityOnActorUpdateHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnActorUpdateHandler); + fishsanityOnSceneInitHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnSceneInitHandler); + fishsanityOnVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook(Rando::Fishsanity::OnVanillaBehaviorHandler); } }); } diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 77910d68714..ec56b95cc90 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -650,6 +650,12 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void* } break; } + case VB_SKIP_SCARECROWS_SONG: { + if (gPlayState->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING && CVarGetInteger(CVAR_ENHANCEMENT("InstantScarecrow"), 0) && gSaveContext.scarecrowSpawnSongSet) { + *should = true; + } + break; + } } } diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index faded27e306..83d22c94054 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -13,7 +13,6 @@ #include "functions.h" #include "macros.h" #include -#include "soh/Enhancements/boss-rush/BossRush.h" #include #include "SohGui.hpp" diff --git a/soh/soh/z_play_otr.cpp b/soh/soh/z_play_otr.cpp index 237b48ef37b..47e4279b274 100644 --- a/soh/soh/z_play_otr.cpp +++ b/soh/soh/z_play_otr.cpp @@ -77,10 +77,6 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) { gSaveContext.worldMapArea = 0; OTRScene_ExecuteCommands(play, (SOH::Scene*)play->sceneSegment); Play_InitEnvironment(play, play->skyboxId); - // Unpause the timer for Boss Rush when the scene loaded isn't the Chamber of Sages. - if (IS_BOSS_RUSH && play->sceneNum != SCENE_CHAMBER_OF_THE_SAGES) { - gSaveContext.isBossRushPaused = 0; - } /* auto data = static_cast(Ship::Context::GetInstance() ->GetResourceManager() ->ResourceLoad("object_link_child\\object_link_childVtx_01FE08") diff --git a/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c index a1364b8a975..145ab7c1ca6 100644 --- a/soh/src/code/code_800EC960.c +++ b/soh/src/code/code_800EC960.c @@ -1,6 +1,8 @@ #include #include "global.h" #include "soh/Enhancements/audio/AudioEditor.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" // TODO: can these macros be shared between files? code_800F9280 seems to use // versions without any casts... @@ -1626,23 +1628,23 @@ void func_800ED458(s32 arg0) { } Audio_OcaUpdateBtnMap(customControls, dpad, rStick); - if (D_8016BA18 & sOcarinaD4BtnMap && (!IS_RANDO || Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_A))) { + if (D_8016BA18 & sOcarinaD4BtnMap && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D4, true, NULL)) { osSyncPrintf("Presss NA_KEY_D4 %08x\n", sOcarinaD4BtnMap); sCurOcarinaBtnVal = 2; sCurOcarinaBtnIdx = 0; - } else if (D_8016BA18 & sOcarinaF4BtnMap && (!IS_RANDO || Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_DOWN))) { + } else if (D_8016BA18 & sOcarinaF4BtnMap && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_F4, true, NULL)) { osSyncPrintf("Presss NA_KEY_F4 %08x\n", sOcarinaF4BtnMap); sCurOcarinaBtnVal = 5; sCurOcarinaBtnIdx = 1; - } else if (D_8016BA18 & sOcarinaA4BtnMap && (!IS_RANDO || Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_RIGHT))) { + } else if (D_8016BA18 & sOcarinaA4BtnMap && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true, NULL)) { osSyncPrintf("Presss NA_KEY_A4 %08x\n", sOcarinaA4BtnMap); sCurOcarinaBtnVal = 9; sCurOcarinaBtnIdx = 2; - } else if (D_8016BA18 & sOcarinaB4BtnMap && (!IS_RANDO || Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_LEFT))) { + } else if (D_8016BA18 & sOcarinaB4BtnMap && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true, NULL)) { osSyncPrintf("Presss NA_KEY_B4 %08x\n", sOcarinaA4BtnMap); sCurOcarinaBtnVal = 0xB; sCurOcarinaBtnIdx = 3; - } else if (D_8016BA18 & sOcarinaD5BtnMap && (!IS_RANDO || Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_UP))) { + } else if (D_8016BA18 & sOcarinaD5BtnMap && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true, NULL)) { osSyncPrintf("Presss NA_KEY_D5 %08x\n", sOcarinaD5BtnMap); sCurOcarinaBtnVal = 0xE; sCurOcarinaBtnIdx = 4; diff --git a/soh/src/code/z_horse.c b/soh/src/code/z_horse.c index 48c76396ffe..33ba7a84d7f 100644 --- a/soh/src/code/z_horse.c +++ b/soh/src/code/z_horse.c @@ -1,6 +1,8 @@ #include "global.h" #include "vt.h" #include +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" s32 func_8006CFC0(s32 scene) { s32 validScenes[] = { SCENE_HYRULE_FIELD, SCENE_LAKE_HYLIA, SCENE_GERUDO_VALLEY, SCENE_GERUDOS_FORTRESS, SCENE_LON_LON_RANCH }; @@ -75,9 +77,9 @@ void func_8006D0EC(PlayState* play, Player* player) { } else if ((play->sceneNum == gSaveContext.horseData.scene) && (((Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED) != 0) && (!IS_RANDO || (IS_RANDO && CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && - Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_UP) && - Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_LEFT) && - Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_RIGHT) && + GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true, NULL) && + GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true, NULL) && + GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true, NULL) && (INV_CONTENT(ITEM_OCARINA_FAIRY) != ITEM_NONE)))) || DREG(1) != 0)) { // "Set by existence of horse %d %d %d" osSyncPrintf("馬存在によるセット %d %d %d\n", gSaveContext.horseData.scene, Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED), diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 1ca58cf0017..0d14468f227 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -5372,8 +5372,7 @@ void Interface_Draw(PlayState* play) { if (fullUi) { s16 PosX_RC; s16 PosY_RC; - //when not having a wallet (or infinite money) in rando, don't calculate the ruppe icon - if (!IS_RANDO || (Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) && !Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MONEY))) { + if (GameInteractor_Should(VB_RENDER_RUPEE_COUNTER, true, NULL)) { // Rupee Icon if (CVarGetInteger(CVAR_ENHANCEMENT("DynamicWalletIcon"), 0)) { switch (CUR_UPG_VALUE(UPG_WALLET)) { @@ -5444,14 +5443,10 @@ void Interface_Draw(PlayState* play) { PosX_RC = PosX_RC_ori; } gDPSetPrimColor(OVERLAY_DISP++, 0, 0, rColor.r, rColor.g, rColor.b, interfaceCtx->magicAlpha); - // Draw Rupee icon. Hide in Boss Rush. - if (!IS_BOSS_RUSH) { - OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gRupeeCounterIconTex, 16, 16, PosX_RC, PosY_RC, 16, 16, 1 << 10, 1 << 10); - } + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gRupeeCounterIconTex, 16, 16, PosX_RC, PosY_RC, 16, 16, 1 << 10, 1 << 10); } - //when having the skeleton key in rando, don't render the small key counter - if (!Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) { + if (GameInteractor_Should(VB_RENDER_KEY_COUNTER, true, NULL)) { switch (play->sceneNum) { case SCENE_FOREST_TEMPLE: case SCENE_FIRE_TEMPLE: @@ -5467,7 +5462,6 @@ void Interface_Draw(PlayState* play) { case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: case SCENE_TREASURE_BOX_SHOP: - if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] >= 0) { s16 X_Margins_SKC; s16 Y_Margins_SKC; @@ -5533,49 +5527,47 @@ void Interface_Draw(PlayState* play) { } } - // Rupee Counter - gDPPipeSync(OVERLAY_DISP++); + if (GameInteractor_Should(VB_RENDER_RUPEE_COUNTER, true, NULL)) { + // Rupee Counter + gDPPipeSync(OVERLAY_DISP++); - if (gSaveContext.rupees == CUR_CAPACITY(UPG_WALLET)) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, interfaceCtx->magicAlpha); - } else if (gSaveContext.rupees != 0) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); - } else { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, interfaceCtx->magicAlpha); - } + if (gSaveContext.rupees == CUR_CAPACITY(UPG_WALLET)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, interfaceCtx->magicAlpha); + } else if (gSaveContext.rupees != 0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, interfaceCtx->magicAlpha); + } - gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, - PRIMITIVE, 0); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); - interfaceCtx->counterDigits[0] = interfaceCtx->counterDigits[1] = 0; - interfaceCtx->counterDigits[2] = gSaveContext.rupees; + interfaceCtx->counterDigits[0] = interfaceCtx->counterDigits[1] = 0; + interfaceCtx->counterDigits[2] = gSaveContext.rupees; - if ((interfaceCtx->counterDigits[2] > 9999) || (interfaceCtx->counterDigits[2] < 0)) { - interfaceCtx->counterDigits[2] &= 0xDDD; - } + if ((interfaceCtx->counterDigits[2] > 9999) || (interfaceCtx->counterDigits[2] < 0)) { + interfaceCtx->counterDigits[2] &= 0xDDD; + } - while (interfaceCtx->counterDigits[2] >= 100) { - interfaceCtx->counterDigits[0]++; - interfaceCtx->counterDigits[2] -= 100; - } + while (interfaceCtx->counterDigits[2] >= 100) { + interfaceCtx->counterDigits[0]++; + interfaceCtx->counterDigits[2] -= 100; + } - while (interfaceCtx->counterDigits[2] >= 10) { - interfaceCtx->counterDigits[1]++; - interfaceCtx->counterDigits[2] -= 10; - } + while (interfaceCtx->counterDigits[2] >= 10) { + interfaceCtx->counterDigits[1]++; + interfaceCtx->counterDigits[2] -= 10; + } - svar2 = rupeeDigitsFirst[CUR_UPG_VALUE(UPG_WALLET)]; - svar5 = rupeeDigitsCount[CUR_UPG_VALUE(UPG_WALLET)]; + svar2 = rupeeDigitsFirst[CUR_UPG_VALUE(UPG_WALLET)]; + svar5 = rupeeDigitsCount[CUR_UPG_VALUE(UPG_WALLET)]; - // Draw Rupee Counter. Hide in Boss Rush and when not having a wallet in rando. - if (!IS_BOSS_RUSH && (!IS_RANDO || Flags_GetRandomizerInf(RAND_INF_HAS_WALLET))) { for (svar1 = 0, svar3 = 16; svar1 < svar5; svar1++, svar2++, svar3 += 8) { OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[svar2]]), 8, 16, PosX_RC + svar3, PosY_RC, 8, 16, 1 << 10, 1 << 10); } } - } - else { + } else { // Make sure item counts have black backgrounds gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha); gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index a54314d0027..56c7868891e 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -1804,10 +1804,6 @@ void* Play_LoadFile(PlayState* play, RomFile* file) { } void Play_InitEnvironment(PlayState* play, s16 skyboxId) { - // For entrance rando, ensure the correct weather state and sky mode is applied - if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { - Entrance_OverrideWeatherState(); - } Skybox_Init(&play->state, &play->skyboxCtx, skyboxId); Environment_Init(play, &play->envCtx, 0); } diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 9daa5a3cf7a..8a848f30138 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -2,6 +2,8 @@ #include "vt.h" #include +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h" #include "soh/Enhancements/randomizer/savefile.h" @@ -198,7 +200,7 @@ void Sram_OpenSave() { } } - if (!(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE))) { + if (GameInteractor_Should(VB_REVERT_SPOILING_ITEMS, true, NULL)) { for (i = 0; i < ARRAY_COUNT(gSpoilingItems); i++) { if (INV_CONTENT(ITEM_TRADE_ADULT) == gSpoilingItems[i]) { INV_CONTENT(gSpoilingItemReverts[i]) = gSpoilingItemReverts[i]; diff --git a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c index 03c9e96ad57..c2db563b9cb 100644 --- a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c +++ b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c @@ -8,6 +8,7 @@ #include "scenes/dungeons/ddan/ddan_scene.h" #include "objects/object_bwall/object_bwall.h" #include "objects/object_kingdodongo/object_kingdodongo.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED @@ -274,9 +275,8 @@ void BgBreakwall_Wait(BgBreakwall* this, PlayState* play) { } } } - - // Break the floor immediately in Boss Rush so the player can jump in the hole immediately. - if (this->collider.base.acFlags & 2 || blueFireArrowHit || IS_BOSS_RUSH) { + + if (GameInteractor_Should(VB_BG_BREAKWALL_BREAK, this->collider.base.acFlags & 2 || blueFireArrowHit, NULL)) { Vec3f effectPos; s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index d3f2795759d..f93e5910c72 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -4,7 +4,6 @@ #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include // malloc @@ -341,7 +340,9 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD); Actor_Spawn(&play->actorCtx, play, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, 0x6000, true); - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0, true); + } } this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; @@ -1559,8 +1560,7 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) { this->cameraAt.x = camera->at.x; this->cameraAt.y = camera->at.y; this->cameraAt.z = camera->at.z; - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_KING_DODONGO] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); break; case 5: tempSin = Math_SinS(this->actor.shape.rot.y - 0x1388) * 150.0f; @@ -1845,7 +1845,7 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) { if (this->unk_1DA == 820) { Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { Actor_Spawn( &play->actorCtx, play, ACTOR_ITEM_B_HEART, Math_SinS(this->actor.shape.rot.y) * -50.0f + this->actor.world.pos.x, this->actor.world.pos.y, @@ -1864,10 +1864,8 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) { Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); func_80064534(play, &play->csCtx); Player_SetCsActionWithHaltedActors(play, &this->actor, 7); - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD); - } else { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, WARP_DUNGEON_ADULT, false); } this->skelAnime.playSpeed = 0.0f; Flags_SetClear(play, play->roomCtx.curRoom.num); diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c index 72908fd2f8e..032a487a667 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -14,6 +14,8 @@ #include "objects/gameplay_keep/gameplay_keep.h" #include "soh/frame_interpolation.h" +#include "soh/Enhancements/game-interactor/GameInteractor.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) @@ -227,7 +229,9 @@ void BossFd_Init(Actor* thisx, PlayState* play) { Actor_Kill(&this->actor); Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0, WARP_DUNGEON_ADULT); - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 0.0f, 100.0f, 200.0f, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 0.0f, 100.0f, 200.0f, 0, 0, 0, 0, true); + } } else { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_BOSS_FD2, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, this->introState); @@ -913,7 +917,7 @@ void BossFd_Fly(BossFd* this, PlayState* play) { this->actionFunc = BossFd_Wait; this->actor.world.pos.y -= 1000.0f; } - if (this->timers[0] == 7 && !IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, this->timers[0] == 7, NULL)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true); } diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c index 2a03f1839e4..a30091ad3db 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -10,7 +10,7 @@ #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "vt.h" #include "soh/frame_interpolation.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) @@ -789,12 +789,9 @@ void BossFd2_Death(BossFd2* this, PlayState* play) { this->deathCamera = 0; func_80064534(play, &play->csCtx); Player_SetCsActionWithHaltedActors(play, &this->actor, 7); - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0, WARP_DUNGEON_ADULT); - } else { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0, - WARP_DUNGEON_ADULT, true); } Flags_SetClear(play, play->roomCtx.curRoom.num); } @@ -899,8 +896,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD); Enemy_StartFinishingBlow(play, &this->actor); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_VOLVAGIA] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); } else if (damage) { BossFd2_SetupDamaged(this, play); this->work[FD2_DAMAGE_FLASH_TIMER] = 10; diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index 145fdcfd9e9..f4c227a290f 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -11,7 +11,7 @@ #include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include @@ -581,7 +581,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { BossGanon_SetIntroCsCamera(this, 11); this->unk_198 = 2; this->timers[2] = 110; - if (!(IS_BOSS_RUSH && gSaveContext.bossRushOptions[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_NEVER)) { + if (GameInteractor_Should(VB_GANON_HEAL_BEFORE_FIGHT, true, NULL)) { gSaveContext.healthAccumulator = 0x140; } Audio_QueueSeqCmd(NA_BGM_STOP); @@ -2806,8 +2806,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE); Audio_QueueSeqCmd(0x100100FF); this->screenFlashTimer = 4; - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANONDORF] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index d9dd602c5d5..97cc537b4c7 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -7,7 +7,7 @@ #include "objects/object_ganon_anime3/object_ganon_anime3.h" #include "objects/object_geff/object_geff.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include @@ -1688,9 +1688,7 @@ void func_8090120C(BossGanon2* this, PlayState* play) { if ((ABS(temp_a0_2) < 0x2000) && (sqrtf(SQ(temp_f14) + SQ(temp_f12)) < 70.0f) && (player->meleeWeaponState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) { func_80064520(play, &play->csCtx); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); - gSaveContext.sohStats.gameComplete = true; + GameInteractor_ExecuteOnBossDefeat(&this->actor); this->unk_39E = Play_CreateSubCamera(play); Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); Play_ChangeCameraStatus(play, this->unk_39E, CAM_STAT_ACTIVE); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c index 05740bff9f4..1a1b0c24bf5 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c @@ -11,7 +11,6 @@ #include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" #include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.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) @@ -234,10 +233,14 @@ void BossGanondrof_Init(Actor* thisx, PlayState* play) { this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { Actor_Kill(&this->actor); - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, - GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 200.0f + GND_BOSSROOM_CENTER_X, - GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, + GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); + } + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 200.0f + GND_BOSSROOM_CENTER_X, + GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, 0, true); + } } else { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, this->actor.params); @@ -1059,8 +1062,10 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) { bodyDecayLevel = 10; if (this->timers[0] == 150) { Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, - GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, + GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); + } } Math_ApproachZeroF(&this->cameraEye.y, 0.05f, 1.0f); // GND_BOSSROOM_CENTER_Y + 33.0f @@ -1076,7 +1081,7 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) { this->deathCamera = 0; func_80064534(play, &play->csCtx); Player_SetCsActionWithHaltedActors(play, &this->actor, 7); - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z + 200.0f, 0, 0, 0, 0, true); } @@ -1219,8 +1224,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { if ((s8)this->actor.colChkInfo.health <= 0) { BossGanondrof_SetupDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); return; } } diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c index 842ec0fab09..3e0096ece90 100644 --- a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c @@ -4,7 +4,8 @@ #include "overlays/actors/ovl_En_Goma/z_en_goma.h" #include "overlays/actors/ovl_Door_Shutter/z_door_shutter.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" -#include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor.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) @@ -339,9 +340,13 @@ void BossGoma_Init(Actor* thisx, PlayState* play) { if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { Actor_Kill(&this->actor); - Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -640.0f, 0.0f, 0, 0, - 0, WARP_DUNGEON_CHILD); - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 141.0f, -640.0f, -84.0f, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -640.0f, 0.0f, 0, 0, + 0, WARP_DUNGEON_CHILD); + } + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 141.0f, -640.0f, -84.0f, 0, 0, 0, 0, true); + } } for (int i = 0; i < ARRAY_COUNT(sClearPixelTex16); i++) { @@ -1118,7 +1123,7 @@ void BossGoma_Defeated(BossGoma* this, PlayState* play) { this->timer = 70; this->decayingProgress = 0; this->subCameraFollowSpeed = 0.0f; - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true); } @@ -1152,12 +1157,9 @@ void BossGoma_Defeated(BossGoma* this, PlayState* play) { } } - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, childPos.x, this->actor.world.pos.y, childPos.z, 0, 0, 0, WARP_DUNGEON_CHILD); - } else { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, childPos.x, this->actor.world.pos.y, - childPos.z, 0, 0, 0, WARP_DUNGEON_ADULT, false); } Flags_SetClear(play, play->roomCtx.curRoom.num); } @@ -1841,8 +1843,7 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) { } else { BossGoma_SetupDefeated(this, play); Enemy_StartFinishingBlow(play, &this->actor); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GOHMA] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); } this->invincibilityFrames = 10; diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index 7d82b93a48a..6551f25b0d1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -12,7 +12,8 @@ #include "vt.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include @@ -370,9 +371,13 @@ void BossMo_Init(Actor* thisx, PlayState* play2) { Collider_SetCylinder(play, &this->coreCollider, &this->actor, &sCylinderInit); if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { Actor_Kill(&this->actor); - Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -280.0f, 0.0f, 0, - 0, 0, WARP_DUNGEON_ADULT); - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -200.0f, -280.0f, 0.0f, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -280.0f, 0.0f, 0, + 0, 0, WARP_DUNGEON_ADULT); + } + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -200.0f, -280.0f, 0.0f, 0, 0, 0, 0, true); + } play->roomCtx.unk_74[0] = 0xFF; MO_WATER_LEVEL(play) = -500; return; @@ -1116,16 +1121,17 @@ void BossMo_Tentacle(BossMo* this, PlayState* play) { BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)play->specialEffects, &spD4, &spE0, ((300 - indS1) * .0015f) + 0.13f); } - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, this->actor.world.pos.x, -280.0f, this->actor.world.pos.z, 0, 0, 0, WARP_DUNGEON_ADULT); + } + + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x + 200.0f, -280.0f, this->actor.world.pos.z, 0, 0, 0, 0, true); - } else { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, this->actor.world.pos.x, -280.0f, - this->actor.world.pos.z, 0, 0, 0, WARP_DUNGEON_ADULT, true); } + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); Flags_SetClear(play, play->roomCtx.curRoom.num); } @@ -1793,8 +1799,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) || ((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) { Enemy_StartFinishingBlow(play, &this->actor); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_MORPHA] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); this->csState = MO_DEATH_START; sMorphaTent1->drawActor = false; diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index de754328d7d..6a4b0f30c37 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -11,7 +11,8 @@ #include "overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor.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 | ACTOR_FLAG_DRAGGED_BY_HOOKSHOT) @@ -293,10 +294,14 @@ void BossSst_Init(Actor* thisx, PlayState* play2) { this->actor.home.pos = this->actor.world.pos; this->actor.shape.rot.y = 0; if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, - ROOM_CENTER_Z + 400.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true); - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, ROOM_CENTER_X, ROOM_CENTER_Y, - ROOM_CENTER_Z - 200.0f, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, + ROOM_CENTER_Z + 400.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true); + } + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, ROOM_CENTER_X, ROOM_CENTER_Y, + ROOM_CENTER_Z - 200.0f, 0, 0, 0, 0, true); + } Actor_Kill(&this->actor); } else { sHands[LEFT] = @@ -1202,9 +1207,11 @@ void BossSst_HeadFinish(BossSst* this, PlayState* play) { Flags_SetClear(play, play->roomCtx.curRoom.num); } } else if (this->effects[0].alpha == 0) { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z, 0, 0, 0, - WARP_DUNGEON_ADULT, true); - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z, 0, 0, 0, + WARP_DUNGEON_ADULT, true); + } + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, (Math_SinS(this->actor.shape.rot.y) * 200.0f) + ROOM_CENTER_X, ROOM_CENTER_Y, Math_CosS(this->actor.shape.rot.y) * 200.0f + ROOM_CENTER_Z, 0, 0, 0, 0, true); @@ -2563,8 +2570,7 @@ void BossSst_HeadCollisionCheck(BossSst* this, PlayState* play) { if (Actor_ApplyDamage(&this->actor) == 0) { Enemy_StartFinishingBlow(play, &this->actor); BossSst_HeadSetupDeath(this, play); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BONGO_BONGO] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); } else { BossSst_HeadSetupDamage(this); } diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index 40f8127cd88..4c198d07fd8 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -4,7 +4,8 @@ #include "objects/object_tw/object_tw.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include @@ -535,9 +536,14 @@ void BossTw_Init(Actor* thisx, PlayState* play2) { if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // twinrova has been defeated. Actor_Kill(&this->actor); - Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, - 0, 0, WARP_DUNGEON_ADULT); - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -600.0f, 230.0f, 0.0f, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, + 0, 0, WARP_DUNGEON_ADULT); + } + + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -600.0f, 230.0f, 0.0f, 0, 0, 0, 0, true); + } } else { sKotakePtr = (BossTw*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_BOSS_TW, this->actor.world.pos.x, this->actor.world.pos.y, @@ -2795,14 +2801,15 @@ void BossTw_TwinrovaDeathCS(BossTw* this, PlayState* play) { func_80064534(play, &play->csCtx); Player_SetCsActionWithHaltedActors(play, &this->actor, 7); Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, 0, 0, WARP_DUNGEON_ADULT); + } + + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -600.0f, 230.f, 0.0f, 0, 0, 0, 0, true); - } else { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, 0, 0, - WARP_DUNGEON_ADULT, true); } + this->actor.world.pos.y = -2000.0f; this->workf[UNK_F18] = 0.0f; sKoumePtr->visible = sKotakePtr->visible = false; @@ -5289,8 +5296,7 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { BossTw_TwinrovaSetupDeathCS(this, play); Enemy_StartFinishingBlow(play, &this->actor); Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_TWINROVA] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); return; } diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index 77a497e7a16..16cb91d99d9 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -15,7 +15,8 @@ #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "soh/frame_interpolation.h" -#include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor.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) @@ -641,11 +642,16 @@ void BossVa_Init(Actor* thisx, PlayState* play2) { if (Flags_GetEventChkInf(EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP)) { warpId = ACTOR_DOOR_WARP1; } - Actor_Spawn(&play->actorCtx, play, warpId, this->actor.world.pos.x, this->actor.world.pos.y, - this->actor.world.pos.z, 0, 0, 0, - 0, true); //! params could be WARP_DUNGEON_CHILD however this can also spawn Ru1 - Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x + 160.0f, - this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true); + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { + Actor_Spawn(&play->actorCtx, play, warpId, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, + 0, true); //! params could be WARP_DUNGEON_CHILD however this can also spawn Ru1 + } + + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x + 160.0f, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true); + } sDoorState = 100; Actor_Kill(&this->actor); } else { @@ -1401,8 +1407,7 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) { if (sFightPhase >= PHASE_DEATH) { BossVa_SetupBodyDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); - gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BARINADE] = GAMEPLAYSTAT_TOTAL_TIME; - BossRush_HandleCompleteBoss(play); + GameInteractor_ExecuteOnBossDefeat(&this->actor); return; } this->actor.speedXZ = -10.0f; @@ -1657,7 +1662,7 @@ void BossVa_BodyDeath(BossVa* this, PlayState* play) { Player_SetCsActionWithHaltedActors(play, &this->actor, 7); sCsState++; - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true); } @@ -1669,12 +1674,9 @@ void BossVa_BodyDeath(BossVa* this, PlayState* play) { } } - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_RU1, sWarpPos[sp7C].x, sWarpPos[sp7C].y, sWarpPos[sp7C].z, 0, 0, 0, 0, true); - } else { - Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, sWarpPos[sp7C].x, sWarpPos[sp7C].y, - sWarpPos[sp7C].z, 0, 0, 0, WARP_DUNGEON_ADULT, false); } } case DEATH_FINISH: diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c index 89628cc3479..ed707ffad89 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c @@ -426,12 +426,6 @@ void DemoKankyo_KillDoorOfTimeCollision(DemoKankyo* this, PlayState* play) { void DemoKankyo_Update(Actor* thisx, PlayState* play) { DemoKankyo* this = (DemoKankyo*)thisx; this->actionFunc(this, play); - - // In ER, override the warp song locations. Also removes the warp song cutscene - if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES) && - thisx->params == 0x000F) { // Warp Song particles - Entrance_SetWarpSongEntrance(); - } } void DemoKankyo_Draw(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.c b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.c index b0d210bbc6e..26bf4c761ba 100644 --- a/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.c +++ b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.c @@ -8,7 +8,6 @@ #include "overlays/actors/ovl_En_Elf/z_en_elf.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "objects/object_sa/object_sa.h" -#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "vt.h" @@ -257,21 +256,13 @@ void func_8098E960(DemoSa* this, PlayState* play) { if ((gSaveContext.chamberCutsceneNum == 0) && (gSaveContext.sceneSetupIndex < 4)) { player = GET_PLAYER(play); - if (!IS_BOSS_RUSH) { - this->action = 1; - play->csCtx.segment = D_8099010C; - gSaveContext.cutsceneTrigger = 2; - if (GameInteractor_Should(VB_GIVE_ITEM_FOREST_MEDALLION, true, NULL)) { - Item_Give(play, ITEM_MEDALLION_FOREST); - } - player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; - } else { - this->action = 1; - if (gSaveContext.linkAge == LINK_AGE_CHILD) { - player->actor.world.rot.y = player->actor.shape.rot.y = -5461 + 0x8000; - } - BossRush_SpawnBlueWarps(play); + this->action = 1; + play->csCtx.segment = D_8099010C; + gSaveContext.cutsceneTrigger = 2; + if (GameInteractor_Should(VB_GIVE_ITEM_FOREST_MEDALLION, true, NULL)) { + Item_Give(play, ITEM_MEDALLION_FOREST); } + player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; } } diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c index 242ac1ddbc8..be1756613ed 100644 --- a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c @@ -749,9 +749,7 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) { this->warpTimer++; if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) { - if (IS_BOSS_RUSH) { - BossRush_HandleBlueWarp(play, this->actor.world.pos.x, this->actor.world.pos.z); - } else if (play->sceneNum == SCENE_FOREST_TEMPLE_BOSS) { + if (play->sceneNum == SCENE_FOREST_TEMPLE_BOSS) { if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, !Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP), EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP)) { Flags_SetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP); if (GameInteractor_Should(VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_FOREST)) { diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c index 6df2802f831..26e7f7e7a17 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c @@ -3,6 +3,8 @@ #include "overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h" #include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" #include "objects/object_bg/object_bg.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_NO_LOCKON) @@ -141,25 +143,10 @@ void EnBomBowMan_BlinkAwake(EnBomBowlMan* this, PlayState* play) { if (frameCount == 30.0f) { this->dialogState = TEXT_STATE_EVENT; - // Check for beaten Dodongo's Cavern if Rando is disabled - if (!IS_RANDO) { - if ((Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) || BREG(2)) { - this->actor.textId = 0xBF; - } else { - this->actor.textId = 0x7058; - } - } - - // In randomizer, only check for bomb bag when bombchus aren't in logic - // and only check for bombchus when bombchus are in logic - if (IS_RANDO) { - u8 bombchusInLogic = Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC); - if ((!bombchusInLogic && INV_CONTENT(ITEM_BOMB) == ITEM_NONE) || - (bombchusInLogic && INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE)) { - this->actor.textId = 0x7058; - } else { - this->actor.textId = 0xBF; - } + if (GameInteractor_Should(VB_BE_ABLE_TO_PLAY_BOMBCHU_BOWLING, (Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) || BREG(2), NULL)) { + this->actor.textId = 0xBF; + } else { + this->actor.textId = 0x7058; } } Message_ContinueTextbox(play, this->actor.textId); diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c index 41234d54f7f..ef7a6330b4d 100644 --- a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c @@ -207,11 +207,6 @@ void EnBox_Init(Actor* thisx, PlayState* play2) { if (play->sceneNum == SCENE_FOREST_TEMPLE && this->dyna.actor.params == 10222) { this->movementFlags = ENBOX_MOVE_IMMOBILE; } - - // Delete chests in Boss Rush. Mainly for the chest in King Dodongo's boss room. - if (IS_BOSS_RUSH) { - EnBox_SetupAction(this, EnBox_Destroy); - } } void EnBox_Destroy(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c index c8bea148fa8..08d1e1c9818 100644 --- a/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c +++ b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c @@ -7,6 +7,8 @@ #include "z_en_kakasi2.h" #include "vt.h" #include "objects/object_ka/object_ka.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA | ACTOR_FLAG_NO_LOCKON) @@ -116,21 +118,8 @@ void func_80A90264(EnKakasi2* this, PlayState* play) { Player* player = GET_PLAYER(play); this->unk_194++; - - int ocarinaButtonCount = 0; - for (int i = RAND_INF_HAS_OCARINA_A; i <= RAND_INF_HAS_OCARINA_C_DOWN; i++) { - if (Flags_GetRandomizerInf(i)) { - ocarinaButtonCount++; - } - } - - bool hasTwoOcarinaButtons = !IS_RANDO || ocarinaButtonCount >= 2; - - bool skipScarecrow = hasTwoOcarinaButtons && play->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING && - ((CVarGetInteger(CVAR_ENHANCEMENT("InstantScarecrow"), 0) && gSaveContext.scarecrowSpawnSongSet) || - (IS_RANDO && Randomizer_GetSettingValue(RSK_SKIP_SCARECROWS_SONG))); - if ((BREG(1) != 0) || skipScarecrow && (this->actor.xzDistToPlayer < this->maxSpawnDistance.x) && + if ((BREG(1) != 0) || GameInteractor_Should(VB_SKIP_SCARECROWS_SONG, false, NULL) && (this->actor.xzDistToPlayer < this->maxSpawnDistance.x) && (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < this->maxSpawnDistance.y)) { this->actor.draw = func_80A90948; Collider_InitCylinder(play, &this->collider); diff --git a/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c index 04b0ca1e31c..a0e529991fa 100644 --- a/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c +++ b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c @@ -277,11 +277,6 @@ void EnKusa_Destroy(Actor* thisx, PlayState* play2) { } void EnKusa_SetupWaitObject(EnKusa* this) { - // Kill bushes in Boss Rush. Used in Gohma's arena. - if (IS_BOSS_RUSH) { - Actor_Kill(this); - } - EnKusa_SetupAction(this, EnKusa_WaitObject); } diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c index eca7cc18ce7..dcaaa897b1c 100644 --- a/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c +++ b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c @@ -157,15 +157,6 @@ void EnSyatekiMan_Init(Actor* thisx, PlayState* play) { s32 pad; EnSyatekiMan* this = (EnSyatekiMan*)thisx; - if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_INTERIOR_ENTRANCES)) { - // If child is in the adult shooting gallery or adult in the child shooting gallery, then despawn the shooting gallery man - if ((LINK_IS_CHILD && Entrance_SceneAndSpawnAre(SCENE_SHOOTING_GALLERY, 0x00)) || //Kakariko Village -> Adult Shooting Gallery, index 003B in the entrance table - (LINK_IS_ADULT && Entrance_SceneAndSpawnAre(SCENE_SHOOTING_GALLERY, 0x01))) { //Market -> Child Shooting Gallery, index 016D in the entrance table - Actor_Kill(thisx); - return; - } - } - osSyncPrintf("\n\n"); // "Old man appeared!! Muhohohohohohohon" osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 親父登場!!むほほほほほほほーん ☆☆☆☆☆ \n" VT_RST); diff --git a/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.c b/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.c index 19bf701fa73..a804da12ebd 100644 --- a/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.c +++ b/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.c @@ -200,12 +200,7 @@ void ObjComb_Update(Actor* thisx, PlayState* play) { this->unk_1B2 += 0x2EE0; this->actionFunc(this, play); - s16 wiggleOffset = this->unk_1B0; - - if (IS_RANDO && this->unk_1B0 < 0) { - wiggleOffset = 0; - } - this->actor.shape.rot.x = Math_SinS(this->unk_1B2) * wiggleOffset + this->actor.home.rot.x; + this->actor.shape.rot.x = Math_SinS(this->unk_1B2) * this->unk_1B0 + this->actor.home.rot.x; } void ObjComb_Draw(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.c b/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.c index ae26db3ac99..026598ac923 100644 --- a/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.c +++ b/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.c @@ -273,15 +273,6 @@ void ObjOshihiki_Init(Actor* thisx, PlayState* play2) { PlayState* play = play2; ObjOshihiki* this = (ObjOshihiki*)thisx; - // In MQ Spirit, remove the large silver block in the hole as child so the chest in the silver block hallway - // can be guaranteed accessible - if (IS_RANDO && LINK_IS_CHILD && ResourceMgr_IsGameMasterQuest() && - play->sceneNum == SCENE_SPIRIT_TEMPLE && thisx->room == 6 && // Spirit Temple silver block hallway - thisx->params == 0x9C7) { // Silver block that is marked as in the hole - Actor_Kill(thisx); - return; - } - ObjOshihiki_CheckType(this, play); if ((((this->dyna.actor.params >> 8) & 0xFF) >= 0) && (((this->dyna.actor.params >> 8) & 0xFF) <= 0x3F)) { diff --git a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c index 0dd03a5bef9..9be71e91b0c 100644 --- a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c +++ b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c @@ -221,11 +221,6 @@ void ObjTsubo_WaterBreak(ObjTsubo* this, PlayState* play) { } void ObjTsubo_SetupWaitForObject(ObjTsubo* this) { - // Remove pots in Boss Rush. Present in Barinade's and Ganondorf's arenas. - if (IS_BOSS_RUSH) { - Actor_Kill(this); - } - this->actionFunc = ObjTsubo_WaitForObject; } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 11e89672450..0acf4f1f0d8 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4843,8 +4843,7 @@ s32 Player_ActionChange_1(Player* this, PlayState* play) { if ((this->doorType != PLAYER_DOORTYPE_NONE) && (!(this->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) || ((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)))) { - // Disable doors in Boss Rush so the player can't leave the boss rooms backwards. - if ((CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (Player_Action_8084F9A0 == this->actionFunc)) && !IS_BOSS_RUSH) { + if ((CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (Player_Action_8084F9A0 == this->actionFunc)) && GameInteractor_Should(VB_BE_ABLE_TO_OPEN_DOORS, true, NULL)) { doorActor = this->doorActor; if (this->doorType <= PLAYER_DOORTYPE_AJAR) { diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 55a4b9f7c12..3814eff2410 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -1388,7 +1388,7 @@ void FileChoose_UpdateBossRushMenu(GameState* thisx) { // Move down if (this->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) { // When selecting past the last option, cycle back to the first option. - if ((this->bossRushIndex + 1) > BOSSRUSH_OPTIONS_AMOUNT - 1) { + if ((this->bossRushIndex + 1) > BR_OPTIONS_MAX - 1) { this->bossRushIndex = 0; this->bossRushOffset = 0; } else { @@ -1401,7 +1401,7 @@ void FileChoose_UpdateBossRushMenu(GameState* thisx) { } else if (this->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) { // When selecting past the first option, cycle back to the last option and offset the list to view it properly. if ((this->bossRushIndex - 1) < 0) { - this->bossRushIndex = BOSSRUSH_OPTIONS_AMOUNT - 1; + this->bossRushIndex = BR_OPTIONS_MAX - 1; this->bossRushOffset = this->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1; } else { // When first visible option is selected when moving up, offset the list up by one. @@ -2306,7 +2306,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) { (arrowUpY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11)); } // Arrow down - if (BOSSRUSH_OPTIONS_AMOUNT - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) { + if (BR_OPTIONS_MAX - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) { uint16_t arrowDownX = 140; uint16_t arrowDownY = 181 + (this->bossRushArrowOffset / 10); gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, @@ -3045,16 +3045,6 @@ void FileChoose_LoadGame(GameState* thisx) { gSaveContext.naviTimer = 0; - if (IS_RANDO) { - // Setup the modified entrance table and entrance shuffle table for rando - Entrance_Init(); - - // Handle randomized spawn positions after the save context has been setup from load - if (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { - Entrance_SetSavewarpEntrance(); - } - } - GameInteractor_ExecuteOnLoadGame(gSaveContext.fileNum); } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c index c81a89c4e3b..9c4224181de 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c @@ -2,6 +2,8 @@ #include "textures/parameter_static/parameter_static.h" #include "textures/icon_item_static/icon_item_static.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" extern const char* digitTextures[]; @@ -12,7 +14,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) { aButtonColor = (Color_RGB8){ 80, 255, 150 }; } - if (IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_A)) { + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D4, true, NULL)) { aButtonColor = (Color_RGB8){ 191, 191, 191 }; } @@ -24,7 +26,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CUpButton.Changed"), 0)) { cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CUpButton.Value"), cUpButtonColor); } - if (IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_UP)) { + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true, NULL)) { cUpButtonColor = (Color_RGB8){ 191, 191, 191 }; } @@ -32,7 +34,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.Changed"), 0)) { cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CDownButton.Value"), cDownButtonColor); } - if (IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_DOWN)) { + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_F4, true, NULL)) { cDownButtonColor = (Color_RGB8){ 191, 191, 191 }; } @@ -40,7 +42,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CLeftButton.Changed"), 0)) { cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CLeftButton.Value"), cLeftButtonColor); } - if (IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_LEFT)) { + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true, NULL)) { cLeftButtonColor = (Color_RGB8){ 191, 191, 191 }; } @@ -48,7 +50,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.Changed"), 0)) { cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CRightButton.Value"), cRightButtonColor); } - if (IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_RIGHT)) { + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true, NULL)) { cRightButtonColor = (Color_RGB8){ 191, 191, 191 }; } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 68ace2c396d..1902781e5b9 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -1650,13 +1650,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_RENDER_YES_ON_CONTINUE_PROMPT, true, &POLY_KAL_DISP)) { POLY_KAL_DISP = KaleidoScope_QuadTextureIA8( POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][0], 48, 16, 12); - } else { - // Show "No" twice in Boss Rush because the player can't save within it. - POLY_KAL_DISP = KaleidoScope_QuadTextureIA8( - POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][1], 48, 16, 12); } POLY_KAL_DISP = @@ -4002,9 +3998,7 @@ void KaleidoScope_Update(PlayState* play) case 6: switch (pauseCtx->unk_1E4) { case 0: - // Boss Rush skips past the "Save?" window when pressing B while paused. - if (CHECK_BTN_ALL(input->press.button, BTN_START) || - (CHECK_BTN_ALL(input->press.button, BTN_B) && IS_BOSS_RUSH)) { + if (GameInteractor_Should(VB_CLOSE_PAUSE_MENU, CHECK_BTN_ALL(input->press.button, BTN_START), NULL)) { if (CVarGetInteger(CVAR_CHEAT("EasyPauseBuffer"), 0) || CVarGetInteger(CVAR_CHEAT("EasyInputBuffer"), 0)) { // Easy pause buffer is 13 frames, 12 for kaledio to end, and one more to advance a single frame CVarSetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 13); @@ -4391,10 +4385,8 @@ void KaleidoScope_Update(PlayState* play) VREG(88) = 66; WREG(2) = 0; pauseCtx->alpha = 255; - if (!IS_BOSS_RUSH) { + if (GameInteractor_Should(VB_TRANSITION_TO_SAVE_SCREEN_ON_DEATH, true, pauseCtx)) { pauseCtx->state = 0xE; - } else { - pauseCtx->state = 0xF; } gSaveContext.deaths++; if (gSaveContext.deaths > 999) { @@ -4439,7 +4431,7 @@ void KaleidoScope_Update(PlayState* play) case 0x10: if (CHECK_BTN_ALL(input->press.button, BTN_A) || CHECK_BTN_ALL(input->press.button, BTN_START)) { - if (pauseCtx->promptChoice == 0 && !IS_BOSS_RUSH) { + if (pauseCtx->promptChoice == 0 && GameInteractor_Should(VB_BE_ABLE_TO_SAVE, true, NULL)) { Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); Play_SaveSceneFlags(play); @@ -4512,7 +4504,7 @@ void KaleidoScope_Update(PlayState* play) R_PAUSE_MENU_MODE = 0; func_800981B8(&play->objectCtx); func_800418D0(&play->colCtx, play); - if (pauseCtx->promptChoice == 0 && !IS_BOSS_RUSH) { + if (pauseCtx->promptChoice == 0 && GameInteractor_Should(VB_BE_ABLE_TO_SAVE, true, NULL)) { Play_TriggerRespawn(play); gSaveContext.respawnFlag = -2; // In ER, handle death warp to last entrance from grottos