* Convert ocarina buttons & skip scarecrow song to VB

* Move most of boss rush & rupee/key counters to VB

* Move BossRush_HandleCompleteBoss to VB

* Convert boss timestamps to VB

* Move being able to open doors to VB

* Convert Entrance_OverrideWeatherState to VB

* Move boss souls to hook_handlers.cpp

* Update hook_handlers.cpp

* Move infinite upgrades to hook_handlers.cpp

* Move skeleton key to hook_handlers.cpp

* Move swim and child wallet to hook_handlers.cpp

* Move ganons boss key to hook_handlers.cpp

* Move triforce hunt to hook_handlers.cpp

* Move randomizer sheik spawn to hook_handlers.cpp

* Update BossRush.h

* Convert spoiling items to VB

* Move load game stuff to hook_handlers.cpp

* Move warp song handling to hook_handlers.cpp

* Convert being able to play bowling to VB

* Move shooting gallery man handling to hook_handlers.cpp

* Move spirit temple silver block removal to hook_handlers.cpp

* Fix build

* Move last beehive stuff to hook_handlers.cpp

* Fix build

* Add VB_CLOSE_PAUSE_MENU

* Add VB_BE_ABLE_TO_SAVE

* Add VB_RENDER_YES_ON_CONTINUE_PROMPT

* Add VB_SPAWN_BLUE_WARP

* Add VB_BLUE_WARP_ADULT_WARP_OUT

* Add VB_BG_BREAKWALL_BREAK

* Convert Saria stuff to VB

* Remove now unused check

* Add VB_GANON_HEAL_BEFORE_FIGHT

* Update hook_handlers.cpp

* Fix blue warp offsets

* Fixes from review

* Improve documentation

* Update BossRush.cpp

* Fix my stupidity

* Fix #4327

* Update hook_handlers.cpp

* Fix blue warps

* Use ultralib types & clean header

* Replace options amount macro with BR_OPTIONS_MAX

* Remove unused includes

* Remove accidental line doubling

* Tweaks to boss rush (#6)

* Update GameInteractor_HookTable.h

---------

Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
This commit is contained in:
Pepe20129 2024-10-07 23:36:21 +02:00 committed by GitHub
parent 7450cee0b2
commit 2822dfc3f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 963 additions and 621 deletions

View File

@ -276,7 +276,7 @@ typedef struct {
/* */ u16 pendingSaleMod; /* */ u16 pendingSaleMod;
/* */ uint8_t questId; /* */ uint8_t questId;
/* */ uint32_t isBossRushPaused; /* */ uint32_t isBossRushPaused;
/* */ uint8_t bossRushOptions[BOSSRUSH_OPTIONS_AMOUNT]; /* */ uint8_t bossRushOptions[BR_OPTIONS_MAX];
/* */ u8 pendingIceTrapCount; /* */ u8 pendingIceTrapCount;
/* */ SohStats sohStats; /* */ SohStats sohStats;
/* */ FaroresWindData backupFW; /* */ FaroresWindData backupFW;

View File

@ -1,19 +1,33 @@
#include "BossRush.h" #include "BossRush.h"
#include "soh/OTRGlobals.h" #include "soh/OTRGlobals.h"
#include "functions.h" #include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "macros.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "variables.h"
#include <array> #include <array>
#include <string> #include <string>
#include <vector> #include <vector>
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 { typedef struct BossRushSetting {
std::array<std::string, LANGUAGE_MAX> name; std::array<std::string, LANGUAGE_MAX> name;
std::vector<std::array<std::string, LANGUAGE_MAX>> choices; std::vector<std::array<std::string, LANGUAGE_MAX>> choices;
} BossRushSetting; } BossRushSetting;
BossRushSetting BossRushOptions[BOSSRUSH_OPTIONS_AMOUNT] = { BossRushSetting BossRushOptions[BR_OPTIONS_MAX] = {
{ {
{ "BOSSES:", "BOSSE:", "BOSS:" }, { "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(); 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(); 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(); 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<u8, 8> brButtonItems;
std::array<u8, 7> 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 warping from Chamber of Sages, choose the correct boss room to teleport to.
if (play->sceneNum == SCENE_CHAMBER_OF_THE_SAGES) { if (play->sceneNum == SCENE_CHAMBER_OF_THE_SAGES) {
// Gohma & Phantom Ganon // Gohma & Phantom Ganon
@ -202,10 +252,13 @@ void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) {
// Ganondork // Ganondork
} else if (warpPosX == -199 && warpPosZ == 0) { } else if (warpPosX == -199 && warpPosZ == 0) {
play->nextEntranceIndex = ENTR_GANONDORF_BOSS_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. // If coming from a boss room, teleport back to Chamber of Sages and set flag.
} else { } else {
play->nextEntranceIndex = SCENE_HAIRAL_NIWA2; play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
if (CheckDungeonCount() == 3) { if (CheckDungeonCount() == 3) {
play->linkAgeOnLoad = LINK_AGE_ADULT; 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) { void BossRush_HandleBlueWarpHeal(PlayState* play) {
@ -235,10 +292,6 @@ void BossRush_HandleBlueWarpHeal(PlayState* play) {
} }
void BossRush_HandleCompleteBoss(PlayState* play) { void BossRush_HandleCompleteBoss(PlayState* play) {
if (!IS_BOSS_RUSH) {
return;
}
gSaveContext.isBossRushPaused = 1; gSaveContext.isBossRushPaused = 1;
switch (play->sceneNum) { switch (play->sceneNum) {
case SCENE_DEKU_TREE_BOSS: case SCENE_DEKU_TREE_BOSS:
@ -308,7 +361,7 @@ void BossRush_InitSave() {
} }
// Set health // Set health
uint16_t health = 16; u16 health = 16;
switch (gSaveContext.bossRushOptions[BR_OPTIONS_HEARTS]) { switch (gSaveContext.bossRushOptions[BR_OPTIONS_HEARTS]) {
case BR_CHOICE_HEARTS_7: case BR_CHOICE_HEARTS_7:
health *= 7; health *= 7;
@ -418,7 +471,7 @@ void BossRush_InitSave() {
} }
// Upgrades // Upgrades
uint8_t upgradeLevel = 1; u8 upgradeLevel = 1;
if (gSaveContext.bossRushOptions[BR_OPTIONS_AMMO] == BR_CHOICE_AMMO_MAXED) { if (gSaveContext.bossRushOptions[BR_OPTIONS_AMMO] == BR_CHOICE_AMMO_MAXED) {
upgradeLevel = 3; upgradeLevel = 3;
} }
@ -450,40 +503,209 @@ void BossRush_InitSave() {
} }
} }
void BossRush_SetEquipment(uint8_t linkAge) { static void* sSavePromptNoChoiceTexs[] = {
(void*)gPauseNoENGTex,
(void*)gPauseNoGERTex,
(void*)gPauseNoFRATex
};
std::array<u8, 8> brButtonItems; void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void* optionalArg) {
std::array<u8, 7> brCButtonSlots; 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<DoorWarp1*>(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<BossGoma*>(optionalArg);
static Vec3f roomCenter = { -150.0f, 0.0f, -350.0f };
Vec3f childPos = roomCenter;
// Set Child Equipment. for (s32 i = 0; i < 10000; i++) {
if (linkAge == LINK_AGE_CHILD) { if ((fabsf(childPos.x - GET_PLAYER(gPlayState)->actor.world.pos.x) < 100.0f &&
brButtonItems = { fabsf(childPos.z - GET_PLAYER(gPlayState)->actor.world.pos.z) < 100.0f) ||
ITEM_SWORD_KOKIRI, ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE (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 },
};
brCButtonSlots = { SLOT_STICK, SLOT_NUT, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }; 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;
}
}
Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI); Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, sWarpPos[sp7C].x, sWarpPos[sp7C].y, sWarpPos[sp7C].z, 0, 0, 0, WARP_DUNGEON_ADULT, false);
Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_DEKU); break;
// Set Adult equipment. }
} else { case SCENE_FOREST_TEMPLE_BOSS: {
brButtonItems = { ITEM_SWORD_MASTER, ITEM_BOW, ITEM_HAMMER, ITEM_BOMB, Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_WARP1, 14.0f, -33.0f, -3315.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true);
ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }; break;
}
brCButtonSlots = { SLOT_BOW, SLOT_HAMMER, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }; 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);
Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_MASTER); break;
Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); }
Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_GORON); case SCENE_WATER_TEMPLE_BOSS: {
} BossMo* bossMo = static_cast<BossMo*>(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);
// Button Items break;
for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.buttonItems); button++) { }
gSaveContext.equips.buttonItems[button] = brButtonItems[button]; 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;
// C buttons }
for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); button++) { case SCENE_SHADOW_TEMPLE_BOSS: {
gSaveContext.equips.cButtonSlots[button] = brCButtonSlots[button]; 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<Gfx**>(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<PauseContext*>(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;
}
} }
} }
void BossRush_OnActorInitHandler(void* actorRef) {
Actor* actor = static_cast<Actor*>(actorRef);
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;
}
// 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;
}
}
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;
}
}
void BossRush_OnBossDefeatHandler(void* refActor) {
BossRush_HandleCompleteBoss(gPlayState);
}
void BossRush_OnBlueWarpUpdate(void* actor) {
DoorWarp1* blueWarp = static_cast<DoorWarp1*>(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<GameInteractor::OnLoadGame>([](int32_t fileNum) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnBossDefeat>(onBossDefeatHook);
GameInteractor::Instance->UnregisterGameHookForID<GameInteractor::OnActorUpdate>(onActorUpdate);
onVanillaBehaviorHook = 0;
onSceneInitHook = 0;
onActorInitHook = 0;
onBossDefeatHook = 0;
onActorUpdate = 0;
if (!IS_BOSS_RUSH) return;
onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(BossRush_OnVanillaBehaviorHandler);
onSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(BossRush_OnSceneInitHandler);
onActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(BossRush_OnActorInitHandler);
onBossDefeatHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnBossDefeat>(BossRush_OnBossDefeatHandler);
onActorUpdate = GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorUpdate>(ACTOR_DOOR_WARP1, BossRush_OnBlueWarpUpdate);
});
}

View File

@ -1,20 +1,16 @@
#pragma once #pragma once
#include "BossRushTypes.h" #include "z64.h"
#include "variables.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void BossRush_SpawnBlueWarps(PlayState* play); void BossRush_HandleBlueWarpHeal(PlayState* play);
void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ); void BossRush_InitSave();
void BossRush_HandleBlueWarpHeal(PlayState* play); const char* BossRush_GetSettingName(u8 optionIndex, u8 language);
void BossRush_InitSave(); const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language);
void BossRush_SetEquipment(uint8_t linkAge); u8 BossRush_GetSettingOptionsAmount(u8 optionIndex);
void BossRush_HandleCompleteBoss(PlayState* play); void BossRush_RegisterHooks();
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);
#ifdef __cplusplus #ifdef __cplusplus
}; };
#endif #endif

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#define BOSSRUSH_OPTIONS_AMOUNT 12
#define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6 #define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6
typedef enum { typedef enum {
@ -15,7 +14,8 @@ typedef enum {
BR_OPTIONS_LONGSHOT, BR_OPTIONS_LONGSHOT,
BR_OPTIONS_HOVERBOOTS, BR_OPTIONS_HOVERBOOTS,
BR_OPTIONS_BUNNYHOOD, BR_OPTIONS_BUNNYHOOD,
BR_OPTIONS_TIMER BR_OPTIONS_TIMER,
BR_OPTIONS_MAX,
} BossRushOptionEnums; } BossRushOptionEnums;
typedef enum { typedef enum {

View File

@ -247,6 +247,46 @@ typedef enum {
``` ```
*/ */
VB_DRAW_AMMO_COUNT, 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_FREEZE_LINK_FOR_BLOCK_THROW,
VB_MOVE_THROWN_ACTOR, VB_MOVE_THROWN_ACTOR,

View File

@ -24,6 +24,7 @@ DEFINE_HOOK(OnActorInit, (void* actor));
DEFINE_HOOK(OnActorUpdate, (void* actor)); DEFINE_HOOK(OnActorUpdate, (void* actor));
DEFINE_HOOK(OnActorKill, (void* actor)); DEFINE_HOOK(OnActorKill, (void* actor));
DEFINE_HOOK(OnEnemyDefeat, (void* actor)); DEFINE_HOOK(OnEnemyDefeat, (void* actor));
DEFINE_HOOK(OnBossDefeat, (void* actor));
DEFINE_HOOK(OnPlayerBonk, ()); DEFINE_HOOK(OnPlayerBonk, ());
DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(OnPlayDestroy, ());
DEFINE_HOOK(OnPlayDrawEnd, ()); DEFINE_HOOK(OnPlayDrawEnd, ());

View File

@ -100,6 +100,13 @@ void GameInteractor_ExecuteOnEnemyDefeat(void* actor) {
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnEnemyDefeat>(actor); GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnEnemyDefeat>(actor);
} }
void GameInteractor_ExecuteOnBossDefeat(void* actor) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnBossDefeat>(actor);
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::OnBossDefeat>(((Actor*)actor)->id, actor);
GameInteractor::Instance->ExecuteHooksForPtr<GameInteractor::OnBossDefeat>((uintptr_t)actor, actor);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnBossDefeat>(actor);
}
void GameInteractor_ExecuteOnPlayerBonk() { void GameInteractor_ExecuteOnPlayerBonk() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerBonk>(); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerBonk>();
} }

View File

@ -22,6 +22,7 @@ void GameInteractor_ExecuteOnActorInit(void* actor);
void GameInteractor_ExecuteOnActorUpdate(void* actor); void GameInteractor_ExecuteOnActorUpdate(void* actor);
void GameInteractor_ExecuteOnActorKill(void* actor); void GameInteractor_ExecuteOnActorKill(void* actor);
void GameInteractor_ExecuteOnEnemyDefeat(void* actor); void GameInteractor_ExecuteOnEnemyDefeat(void* actor);
void GameInteractor_ExecuteOnBossDefeat(void* actor);
void GameInteractor_ExecuteOnPlayerBonk(); void GameInteractor_ExecuteOnPlayerBonk();
void GameInteractor_ExecuteOnOcarinaSongAction(); void GameInteractor_ExecuteOnOcarinaSongAction();
void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price);

View File

@ -3,7 +3,7 @@
#include "game-interactor/GameInteractor.h" #include "game-interactor/GameInteractor.h"
#include "tts/tts.h" #include "tts/tts.h"
#include "soh/OTRGlobals.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/enhancementTypes.h"
#include "soh/Enhancements/randomizer/3drando/random.hpp" #include "soh/Enhancements/randomizer/3drando/random.hpp"
#include "soh/Enhancements/cosmetics/authenticGfxPatches.h" #include "soh/Enhancements/cosmetics/authenticGfxPatches.h"
@ -784,48 +784,6 @@ void RegisterResetNaviTimer() {
}); });
} }
f32 triforcePieceScale;
void RegisterTriforceHunt() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>([]() {
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<GameInteractor::OnPlayerUpdate>([]() {
// 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 //this map is used for enemies that can be uniquely identified by their id
//and that are always counted //and that are always counted
//enemies that can't be uniquely identified by their id //enemies that can't be uniquely identified by their id
@ -873,7 +831,7 @@ static std::unordered_map<u16, u16> uniqueEnemyIdToStatCount = {
void RegisterEnemyDefeatCounts() { void RegisterEnemyDefeatCounts() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnEnemyDefeat>([](void* refActor) { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnEnemyDefeat>([](void* refActor) {
Actor* actor = (Actor*)refActor; Actor* actor = static_cast<Actor*>(refActor);
if (uniqueEnemyIdToStatCount.contains(actor->id)) { if (uniqueEnemyIdToStatCount.contains(actor->id)) {
gSaveContext.sohStats.count[uniqueEnemyIdToStatCount[actor->id]]++; gSaveContext.sohStats.count[uniqueEnemyIdToStatCount[actor->id]]++;
} else { } else {
@ -1017,6 +975,45 @@ void RegisterEnemyDefeatCounts() {
}); });
} }
void RegisterBossDefeatTimestamps() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnBossDefeat>([](void* refActor) {
Actor* actor = static_cast<Actor*>(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 { typedef enum {
ADD_ICE_TRAP, ADD_ICE_TRAP,
ADD_BURN_TRAP, ADD_BURN_TRAP,
@ -1165,83 +1162,6 @@ void RegisterAltTrapTypes() {
}); });
} }
void RegisterRandomizerSheikSpawn() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneSpawnActors>([]() {
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<GameInteractor::OnActorInit>([](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) { void UpdateHurtContainerModeState(bool newState) {
static bool hurtEnabled = false; static bool hurtEnabled = false;
if (hurtEnabled == newState) { 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<s32, SpecialRespawnInfo> 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<GameInteractor::OnPlayerUpdate>([]() {
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<GameInteractor::OnGameFrameUpdate>([]() {
if (IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_WALLET)) {
gSaveContext.rupees = 0;
}
});
}
void RegisterInfiniteUpgrades() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
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); extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
void PatchCompasses() { void PatchCompasses() {
@ -1605,30 +1400,8 @@ void RegisterRandomizerCompasses() {
}); });
} }
extern "C" void func_8099485C(DoorGerudo* gerudoDoor, PlayState* play);
void RegisterSkeletonKey() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
if (Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) {
Actor* actor = static_cast<Actor*>(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() { void InitMods() {
BossRush_RegisterHooks();
RandomizerRegisterHooks(); RandomizerRegisterHooks();
TimeSaverRegisterHooks(); TimeSaverRegisterHooks();
CheatsRegisterHooks(); CheatsRegisterHooks();
@ -1658,24 +1431,17 @@ void InitMods() {
RegisterMenuPathFix(); RegisterMenuPathFix();
RegisterMirrorModeHandler(); RegisterMirrorModeHandler();
RegisterResetNaviTimer(); RegisterResetNaviTimer();
RegisterTriforceHunt();
RegisterGrantGanonsBossKey();
RegisterEnemyDefeatCounts(); RegisterEnemyDefeatCounts();
RegisterBossDefeatTimestamps();
RegisterAltTrapTypes(); RegisterAltTrapTypes();
RegisterRandomizerSheikSpawn();
RegisterBossSouls();
RegisterRandomizedEnemySizes(); RegisterRandomizedEnemySizes();
RegisterOpenAllHours(); RegisterOpenAllHours();
RegisterToTMedallions(); RegisterToTMedallions();
RegisterNoSwim();
RegisterNoWallet();
RegisterInfiniteUpgrades();
RegisterRandomizerCompasses(); RegisterRandomizerCompasses();
NameTag_RegisterHooks(); NameTag_RegisterHooks();
RegisterFloorSwitchesHook(); RegisterFloorSwitchesHook();
RegisterPatchHandHandler(); RegisterPatchHandHandler();
RegisterHurtContainerModeHandler(); RegisterHurtContainerModeHandler();
RegisterPauseMenuHooks(); RegisterPauseMenuHooks();
RegisterSkeletonKey();
RandoKaleido_RegisterHooks(); RandoKaleido_RegisterHooks();
} }

View File

@ -2,6 +2,7 @@
#include "soh/OTRGlobals.h" #include "soh/OTRGlobals.h"
#include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/custom-message/CustomMessageTypes.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/randomizerTypes.h"
#include "soh/Enhancements/randomizer/dungeon.h" #include "soh/Enhancements/randomizer/dungeon.h"
#include "soh/Enhancements/randomizer/fishsanity.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_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_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_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 "src/overlays/actors/ovl_Fishing/z_fishing.h"
#include "adult_trade_shuffle.h" #include "adult_trade_shuffle.h"
#include "draw.h" #include "draw.h"
@ -1179,6 +1184,79 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void
} }
break; 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: { case VB_SHOULD_CHECK_FOR_FISHING_RECORD: {
f32 sFishOnHandLength = *static_cast<f32*>(optionalArg); f32 sFishOnHandLength = *static_cast<f32*>(optionalArg);
*should = *should || ShouldGiveFishingPrize(sFishOnHandLength); *should = *should || ShouldGiveFishingPrize(sFishOnHandLength);
@ -1286,6 +1364,10 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) {
CheckTracker::RecalculateAllAreaTotals(); CheckTracker::RecalculateAllAreaTotals();
} }
if (RAND_GET_OPTION(RSK_SHUFFLE_ENTRANCES)) {
Entrance_OverrideWeatherState();
}
// LACs & Prelude checks // LACs & Prelude checks
static uint32_t updateHook = 0; static uint32_t updateHook = 0;
@ -1577,6 +1659,291 @@ void RandomizerOnActorInitHandler(void* actorRef) {
) { ) {
Actor_Kill(actor); 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<Actor*>(refActor);
if (Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) {
if (actor->id == ACTOR_EN_DOOR) {
EnDoor* door = reinterpret_cast<EnDoor*>(actor);
door->lockTimer = 0;
} else if (actor->id == ACTOR_DOOR_SHUTTER) {
DoorShutter* shutterDoor = reinterpret_cast<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;
}
}
// 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<ObjComb*>(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<s32, SpecialRespawnInfo> 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() { void RandomizerRegisterHooks() {
@ -1588,6 +1955,10 @@ void RandomizerRegisterHooks() {
static uint32_t onVanillaBehaviorHook = 0; static uint32_t onVanillaBehaviorHook = 0;
static uint32_t onSceneInitHook = 0; static uint32_t onSceneInitHook = 0;
static uint32_t onActorInitHook = 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 fishsanityOnActorInitHook = 0;
static uint32_t fishsanityOnFlagSetHook = 0; static uint32_t fishsanityOnFlagSetHook = 0;
@ -1608,6 +1979,10 @@ void RandomizerRegisterHooks() {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook); GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook); GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook); GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(onActorUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(onPlayerUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnGameFrameUpdate>(onGameFrameUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneSpawnActors>(onSceneSpawnActorsHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(fishsanityOnActorInitHook); GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(fishsanityOnActorInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnFlagSet>(fishsanityOnFlagSetHook); GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnFlagSet>(fishsanityOnFlagSetHook);
@ -1623,6 +1998,10 @@ void RandomizerRegisterHooks() {
onVanillaBehaviorHook = 0; onVanillaBehaviorHook = 0;
onSceneInitHook = 0; onSceneInitHook = 0;
onActorInitHook = 0; onActorInitHook = 0;
onActorUpdateHook = 0;
onPlayerUpdateHook = 0;
onGameFrameUpdateHook = 0;
onSceneSpawnActorsHook = 0;
fishsanityOnActorInitHook = 0; fishsanityOnActorInitHook = 0;
fishsanityOnFlagSetHook = 0; fishsanityOnFlagSetHook = 0;
@ -1632,6 +2011,14 @@ void RandomizerRegisterHooks() {
if (!IS_RANDO) return; 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<GameInteractor::OnFlagSet>(RandomizerOnFlagSetHandler); onFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(RandomizerOnFlagSetHandler);
onSceneFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(RandomizerOnSceneFlagSetHandler); onSceneFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(RandomizerOnSceneFlagSetHandler);
onPlayerUpdateForRCQueueHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(RandomizerOnPlayerUpdateForRCQueueHandler); onPlayerUpdateForRCQueueHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(RandomizerOnPlayerUpdateForRCQueueHandler);
@ -1640,15 +2027,19 @@ void RandomizerRegisterHooks() {
onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(RandomizerOnVanillaBehaviorHandler); onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(RandomizerOnVanillaBehaviorHandler);
onSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(RandomizerOnSceneInitHandler); onSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(RandomizerOnSceneInitHandler);
onActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(RandomizerOnActorInitHandler); onActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(RandomizerOnActorInitHandler);
onActorUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>(RandomizerOnActorUpdateHandler);
onPlayerUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(RandomizerOnPlayerUpdateHandler);
onGameFrameUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(RandomizerOnGameFrameUpdateHandler);
onSceneSpawnActorsHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneSpawnActors>(RandomizerOnSceneSpawnActorsHandler);
if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) { if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) {
OTRGlobals::Instance->gRandoContext->GetFishsanity()->InitializeFromSave(); OTRGlobals::Instance->gRandoContext->GetFishsanity()->InitializeFromSave();
fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(Rando::Fishsanity::OnActorInitHandler); fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(Rando::Fishsanity::OnActorInitHandler);
fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(Rando::Fishsanity::OnFlagSetHandler); fishsanityOnFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(Rando::Fishsanity::OnFlagSetHandler);
fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>(Rando::Fishsanity::OnActorUpdateHandler); fishsanityOnActorUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>(Rando::Fishsanity::OnActorUpdateHandler);
fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(Rando::Fishsanity::OnSceneInitHandler); fishsanityOnSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(Rando::Fishsanity::OnSceneInitHandler);
fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(Rando::Fishsanity::OnVanillaBehaviorHandler); fishsanityOnVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(Rando::Fishsanity::OnVanillaBehaviorHandler);
} }
}); });
} }

View File

@ -650,6 +650,12 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void*
} }
break; break;
} }
case VB_SKIP_SCARECROWS_SONG: {
if (gPlayState->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING && CVarGetInteger(CVAR_ENHANCEMENT("InstantScarecrow"), 0) && gSaveContext.scarecrowSpawnSongSet) {
*should = true;
}
break;
}
} }
} }

View File

@ -13,7 +13,6 @@
#include "functions.h" #include "functions.h"
#include "macros.h" #include "macros.h"
#include <variables.h> #include <variables.h>
#include "soh/Enhancements/boss-rush/BossRush.h"
#include <libultraship/libultraship.h> #include <libultraship/libultraship.h>
#include "SohGui.hpp" #include "SohGui.hpp"

View File

@ -77,10 +77,6 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) {
gSaveContext.worldMapArea = 0; gSaveContext.worldMapArea = 0;
OTRScene_ExecuteCommands(play, (SOH::Scene*)play->sceneSegment); OTRScene_ExecuteCommands(play, (SOH::Scene*)play->sceneSegment);
Play_InitEnvironment(play, play->skyboxId); 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<LUS::Vertex*>(Ship::Context::GetInstance() /* auto data = static_cast<LUS::Vertex*>(Ship::Context::GetInstance()
->GetResourceManager() ->GetResourceManager()
->ResourceLoad("object_link_child\\object_link_childVtx_01FE08") ->ResourceLoad("object_link_child\\object_link_childVtx_01FE08")

View File

@ -1,6 +1,8 @@
#include <libultraship/libultra.h> #include <libultraship/libultra.h>
#include "global.h" #include "global.h"
#include "soh/Enhancements/audio/AudioEditor.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 // TODO: can these macros be shared between files? code_800F9280 seems to use
// versions without any casts... // versions without any casts...
@ -1626,23 +1628,23 @@ void func_800ED458(s32 arg0) {
} }
Audio_OcaUpdateBtnMap(customControls, dpad, rStick); 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); osSyncPrintf("Presss NA_KEY_D4 %08x\n", sOcarinaD4BtnMap);
sCurOcarinaBtnVal = 2; sCurOcarinaBtnVal = 2;
sCurOcarinaBtnIdx = 0; 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); osSyncPrintf("Presss NA_KEY_F4 %08x\n", sOcarinaF4BtnMap);
sCurOcarinaBtnVal = 5; sCurOcarinaBtnVal = 5;
sCurOcarinaBtnIdx = 1; 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); osSyncPrintf("Presss NA_KEY_A4 %08x\n", sOcarinaA4BtnMap);
sCurOcarinaBtnVal = 9; sCurOcarinaBtnVal = 9;
sCurOcarinaBtnIdx = 2; 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); osSyncPrintf("Presss NA_KEY_B4 %08x\n", sOcarinaA4BtnMap);
sCurOcarinaBtnVal = 0xB; sCurOcarinaBtnVal = 0xB;
sCurOcarinaBtnIdx = 3; 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); osSyncPrintf("Presss NA_KEY_D5 %08x\n", sOcarinaD5BtnMap);
sCurOcarinaBtnVal = 0xE; sCurOcarinaBtnVal = 0xE;
sCurOcarinaBtnIdx = 4; sCurOcarinaBtnIdx = 4;

View File

@ -1,6 +1,8 @@
#include "global.h" #include "global.h"
#include "vt.h" #include "vt.h"
#include <assert.h> #include <assert.h>
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
s32 func_8006CFC0(s32 scene) { s32 func_8006CFC0(s32 scene) {
s32 validScenes[] = { SCENE_HYRULE_FIELD, SCENE_LAKE_HYLIA, SCENE_GERUDO_VALLEY, SCENE_GERUDOS_FORTRESS, SCENE_LON_LON_RANCH }; 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) && } else if ((play->sceneNum == gSaveContext.horseData.scene) &&
(((Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED) != 0) && (!IS_RANDO || (((Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED) != 0) && (!IS_RANDO ||
(IS_RANDO && CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && (IS_RANDO && CHECK_QUEST_ITEM(QUEST_SONG_EPONA) &&
Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_UP) && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true, NULL) &&
Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_LEFT) && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true, NULL) &&
Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_RIGHT) && GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true, NULL) &&
(INV_CONTENT(ITEM_OCARINA_FAIRY) != ITEM_NONE)))) || DREG(1) != 0)) { (INV_CONTENT(ITEM_OCARINA_FAIRY) != ITEM_NONE)))) || DREG(1) != 0)) {
// "Set by existence of horse %d %d %d" // "Set by existence of horse %d %d %d"
osSyncPrintf("馬存在によるセット %d %d %d\n", gSaveContext.horseData.scene, Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED), osSyncPrintf("馬存在によるセット %d %d %d\n", gSaveContext.horseData.scene, Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED),

View File

@ -5372,8 +5372,7 @@ void Interface_Draw(PlayState* play) {
if (fullUi) { if (fullUi) {
s16 PosX_RC; s16 PosX_RC;
s16 PosY_RC; s16 PosY_RC;
//when not having a wallet (or infinite money) in rando, don't calculate the ruppe icon if (GameInteractor_Should(VB_RENDER_RUPEE_COUNTER, true, NULL)) {
if (!IS_RANDO || (Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) && !Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MONEY))) {
// Rupee Icon // Rupee Icon
if (CVarGetInteger(CVAR_ENHANCEMENT("DynamicWalletIcon"), 0)) { if (CVarGetInteger(CVAR_ENHANCEMENT("DynamicWalletIcon"), 0)) {
switch (CUR_UPG_VALUE(UPG_WALLET)) { switch (CUR_UPG_VALUE(UPG_WALLET)) {
@ -5444,14 +5443,10 @@ void Interface_Draw(PlayState* play) {
PosX_RC = PosX_RC_ori; PosX_RC = PosX_RC_ori;
} }
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, rColor.r, rColor.g, rColor.b, interfaceCtx->magicAlpha); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, rColor.r, rColor.g, rColor.b, interfaceCtx->magicAlpha);
// Draw Rupee icon. Hide in Boss Rush. OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gRupeeCounterIconTex, 16, 16, PosX_RC, PosY_RC, 16, 16, 1 << 10, 1 << 10);
if (!IS_BOSS_RUSH) {
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 (GameInteractor_Should(VB_RENDER_KEY_COUNTER, true, NULL)) {
if (!Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) {
switch (play->sceneNum) { switch (play->sceneNum) {
case SCENE_FOREST_TEMPLE: case SCENE_FOREST_TEMPLE:
case SCENE_FIRE_TEMPLE: case SCENE_FIRE_TEMPLE:
@ -5467,7 +5462,6 @@ void Interface_Draw(PlayState* play) {
case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR:
case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE:
case SCENE_TREASURE_BOX_SHOP: case SCENE_TREASURE_BOX_SHOP:
if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] >= 0) { if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] >= 0) {
s16 X_Margins_SKC; s16 X_Margins_SKC;
s16 Y_Margins_SKC; s16 Y_Margins_SKC;
@ -5533,49 +5527,47 @@ void Interface_Draw(PlayState* play) {
} }
} }
// Rupee Counter if (GameInteractor_Should(VB_RENDER_RUPEE_COUNTER, true, NULL)) {
gDPPipeSync(OVERLAY_DISP++); // Rupee Counter
gDPPipeSync(OVERLAY_DISP++);
if (gSaveContext.rupees == CUR_CAPACITY(UPG_WALLET)) { if (gSaveContext.rupees == CUR_CAPACITY(UPG_WALLET)) {
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, interfaceCtx->magicAlpha); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, interfaceCtx->magicAlpha);
} else if (gSaveContext.rupees != 0) { } else if (gSaveContext.rupees != 0) {
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha);
} else { } else {
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, interfaceCtx->magicAlpha); 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, gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0,
PRIMITIVE, 0); PRIMITIVE, 0);
interfaceCtx->counterDigits[0] = interfaceCtx->counterDigits[1] = 0; interfaceCtx->counterDigits[0] = interfaceCtx->counterDigits[1] = 0;
interfaceCtx->counterDigits[2] = gSaveContext.rupees; interfaceCtx->counterDigits[2] = gSaveContext.rupees;
if ((interfaceCtx->counterDigits[2] > 9999) || (interfaceCtx->counterDigits[2] < 0)) { if ((interfaceCtx->counterDigits[2] > 9999) || (interfaceCtx->counterDigits[2] < 0)) {
interfaceCtx->counterDigits[2] &= 0xDDD; interfaceCtx->counterDigits[2] &= 0xDDD;
} }
while (interfaceCtx->counterDigits[2] >= 100) { while (interfaceCtx->counterDigits[2] >= 100) {
interfaceCtx->counterDigits[0]++; interfaceCtx->counterDigits[0]++;
interfaceCtx->counterDigits[2] -= 100; interfaceCtx->counterDigits[2] -= 100;
} }
while (interfaceCtx->counterDigits[2] >= 10) { while (interfaceCtx->counterDigits[2] >= 10) {
interfaceCtx->counterDigits[1]++; interfaceCtx->counterDigits[1]++;
interfaceCtx->counterDigits[2] -= 10; interfaceCtx->counterDigits[2] -= 10;
} }
svar2 = rupeeDigitsFirst[CUR_UPG_VALUE(UPG_WALLET)]; svar2 = rupeeDigitsFirst[CUR_UPG_VALUE(UPG_WALLET)];
svar5 = rupeeDigitsCount[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) { for (svar1 = 0, svar3 = 16; svar1 < svar5; svar1++, svar2++, svar3 += 8) {
OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[svar2]]), OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[svar2]]),
8, 16, PosX_RC + svar3, PosY_RC, 8, 16, 1 << 10, 1 << 10); 8, 16, PosX_RC + svar3, PosY_RC, 8, 16, 1 << 10, 1 << 10);
} }
} }
} } else {
else {
// Make sure item counts have black backgrounds // Make sure item counts have black backgrounds
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha);
gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0);

View File

@ -1804,10 +1804,6 @@ void* Play_LoadFile(PlayState* play, RomFile* file) {
} }
void Play_InitEnvironment(PlayState* play, s16 skyboxId) { 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); Skybox_Init(&play->state, &play->skyboxCtx, skyboxId);
Environment_Init(play, &play->envCtx, 0); Environment_Init(play, &play->envCtx, 0);
} }

View File

@ -2,6 +2,8 @@
#include "vt.h" #include "vt.h"
#include <string.h> #include <string.h>
#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/randomizer_entrance.h"
#include "soh/Enhancements/randomizer/savefile.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++) { for (i = 0; i < ARRAY_COUNT(gSpoilingItems); i++) {
if (INV_CONTENT(ITEM_TRADE_ADULT) == gSpoilingItems[i]) { if (INV_CONTENT(ITEM_TRADE_ADULT) == gSpoilingItems[i]) {
INV_CONTENT(gSpoilingItemReverts[i]) = gSpoilingItemReverts[i]; INV_CONTENT(gSpoilingItemReverts[i]) = gSpoilingItemReverts[i];

View File

@ -8,6 +8,7 @@
#include "scenes/dungeons/ddan/ddan_scene.h" #include "scenes/dungeons/ddan/ddan_scene.h"
#include "objects/object_bwall/object_bwall.h" #include "objects/object_bwall/object_bwall.h"
#include "objects/object_kingdodongo/object_kingdodongo.h" #include "objects/object_kingdodongo/object_kingdodongo.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -275,8 +276,7 @@ void BgBreakwall_Wait(BgBreakwall* this, PlayState* play) {
} }
} }
// Break the floor immediately in Boss Rush so the player can jump in the hole immediately. if (GameInteractor_Should(VB_BG_BREAKWALL_BREAK, this->collider.base.acFlags & 2 || blueFireArrowHit, NULL)) {
if (this->collider.base.acFlags & 2 || blueFireArrowHit || IS_BOSS_RUSH) {
Vec3f effectPos; Vec3f effectPos;
s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF;

View File

@ -4,7 +4,6 @@
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h" #include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include <stdlib.h> // malloc #include <stdlib.h> // 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, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f,
-3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD); -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_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; 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.x = camera->at.x;
this->cameraAt.y = camera->at.y; this->cameraAt.y = camera->at.y;
this->cameraAt.z = camera->at.z; this->cameraAt.z = camera->at.z;
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_KING_DODONGO] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
break; break;
case 5: case 5:
tempSin = Math_SinS(this->actor.shape.rot.y - 0x1388) * 150.0f; 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) { if (this->unk_1DA == 820) {
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); 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( Actor_Spawn(
&play->actorCtx, play, ACTOR_ITEM_B_HEART, &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, 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); Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE);
func_80064534(play, &play->csCtx); func_80064534(play, &play->csCtx);
Player_SetCsActionWithHaltedActors(play, &this->actor, 7); 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); 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; this->skelAnime.playSpeed = 0.0f;
Flags_SetClear(play, play->roomCtx.curRoom.num); Flags_SetClear(play, play->roomCtx.curRoom.num);

View File

@ -14,6 +14,8 @@
#include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_keep/gameplay_keep.h"
#include "soh/frame_interpolation.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) #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_Kill(&this->actor);
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0,
WARP_DUNGEON_ADULT); 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 { } else {
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_BOSS_FD2, this->actor.world.pos.x, 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); 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->actionFunc = BossFd_Wait;
this->actor.world.pos.y -= 1000.0f; 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, 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); this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true);
} }

View File

@ -10,7 +10,7 @@
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "vt.h" #include "vt.h"
#include "soh/frame_interpolation.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) #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; this->deathCamera = 0;
func_80064534(play, &play->csCtx); func_80064534(play, &play->csCtx);
Player_SetCsActionWithHaltedActors(play, &this->actor, 7); 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, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0,
0, WARP_DUNGEON_ADULT); 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); 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_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD); Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD);
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_VOLVAGIA] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
} else if (damage) { } else if (damage) {
BossFd2_SetupDamaged(this, play); BossFd2_SetupDamaged(this, play);
this->work[FD2_DAMAGE_FLASH_TIMER] = 10; this->work[FD2_DAMAGE_FLASH_TIMER] = 10;

View File

@ -11,7 +11,7 @@
#include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h" #include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include <string.h> #include <string.h>
@ -581,7 +581,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
BossGanon_SetIntroCsCamera(this, 11); BossGanon_SetIntroCsCamera(this, 11);
this->unk_198 = 2; this->unk_198 = 2;
this->timers[2] = 110; 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; gSaveContext.healthAccumulator = 0x140;
} }
Audio_QueueSeqCmd(NA_BGM_STOP); Audio_QueueSeqCmd(NA_BGM_STOP);
@ -2806,8 +2806,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) {
func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE); func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE);
Audio_QueueSeqCmd(0x100100FF); Audio_QueueSeqCmd(0x100100FF);
this->screenFlashTimer = 4; this->screenFlashTimer = 4;
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANONDORF] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
} else { } else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY);

View File

@ -7,7 +7,7 @@
#include "objects/object_ganon_anime3/object_ganon_anime3.h" #include "objects/object_ganon_anime3/object_ganon_anime3.h"
#include "objects/object_geff/object_geff.h" #include "objects/object_geff/object_geff.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include <string.h> #include <string.h>
@ -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) && if ((ABS(temp_a0_2) < 0x2000) && (sqrtf(SQ(temp_f14) + SQ(temp_f12)) < 70.0f) &&
(player->meleeWeaponState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) { (player->meleeWeaponState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) {
func_80064520(play, &play->csCtx); func_80064520(play, &play->csCtx);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
gSaveContext.sohStats.gameComplete = true;
this->unk_39E = Play_CreateSubCamera(play); this->unk_39E = Play_CreateSubCamera(play);
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
Play_ChangeCameraStatus(play, this->unk_39E, CAM_STAT_ACTIVE); Play_ChangeCameraStatus(play, this->unk_39E, CAM_STAT_ACTIVE);

View File

@ -11,7 +11,6 @@
#include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" #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/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
@ -234,10 +233,14 @@ void BossGanondrof_Init(Actor* thisx, PlayState* play) {
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { if (Flags_GetClear(play, play->roomCtx.curRoom.num)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y,
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 200.0f + GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true);
GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, 0, 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 { } else {
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG, this->actor.world.pos.x, 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); 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; bodyDecayLevel = 10;
if (this->timers[0] == 150) { if (this->timers[0] == 150) {
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); 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, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); 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 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; this->deathCamera = 0;
func_80064534(play, &play->csCtx); func_80064534(play, &play->csCtx);
Player_SetCsActionWithHaltedActors(play, &this->actor, 7); 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, 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); 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) { if ((s8)this->actor.colChkInfo.health <= 0) {
BossGanondrof_SetupDeath(this, play); BossGanondrof_SetupDeath(this, play);
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
return; return;
} }
} }

View File

@ -4,7 +4,8 @@
#include "overlays/actors/ovl_En_Goma/z_en_goma.h" #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_Shutter/z_door_shutter.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/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) #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)) { if (Flags_GetClear(play, play->roomCtx.curRoom.num)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -640.0f, 0.0f, 0, 0, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
0, WARP_DUNGEON_CHILD); Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -640.0f, 0.0f, 0, 0,
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 141.0f, -640.0f, -84.0f, 0, 0, 0, 0, true); 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++) { for (int i = 0; i < ARRAY_COUNT(sClearPixelTex16); i++) {
@ -1118,7 +1123,7 @@ void BossGoma_Defeated(BossGoma* this, PlayState* play) {
this->timer = 70; this->timer = 70;
this->decayingProgress = 0; this->decayingProgress = 0;
this->subCameraFollowSpeed = 0.0f; 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, 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); 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, 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); 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); Flags_SetClear(play, play->roomCtx.curRoom.num);
} }
@ -1841,8 +1843,7 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) {
} else { } else {
BossGoma_SetupDefeated(this, play); BossGoma_SetupDefeated(this, play);
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GOHMA] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
} }
this->invincibilityFrames = 10; this->invincibilityFrames = 10;

View File

@ -12,7 +12,8 @@
#include "vt.h" #include "vt.h"
#include "soh/frame_interpolation.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 <string.h> #include <string.h>
@ -370,9 +371,13 @@ void BossMo_Init(Actor* thisx, PlayState* play2) {
Collider_SetCylinder(play, &this->coreCollider, &this->actor, &sCylinderInit); Collider_SetCylinder(play, &this->coreCollider, &this->actor, &sCylinderInit);
if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { if (Flags_GetClear(play, play->roomCtx.curRoom.num)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -280.0f, 0.0f, 0, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
0, 0, WARP_DUNGEON_ADULT); Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, -280.0f, 0.0f, 0,
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -200.0f, -280.0f, 0.0f, 0, 0, 0, 0, true); 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; play->roomCtx.unk_74[0] = 0xFF;
MO_WATER_LEVEL(play) = -500; MO_WATER_LEVEL(play) = -500;
return; return;
@ -1116,16 +1121,17 @@ void BossMo_Tentacle(BossMo* this, PlayState* play) {
BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)play->specialEffects, &spD4, &spE0, BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)play->specialEffects, &spD4, &spE0,
((300 - indS1) * .0015f) + 0.13f); ((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, 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, this->actor.world.pos.x, -280.0f, this->actor.world.pos.z, 0, 0, 0,
WARP_DUNGEON_ADULT); 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, 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); -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); Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR);
Flags_SetClear(play, play->roomCtx.curRoom.num); Flags_SetClear(play, play->roomCtx.curRoom.num);
} }
@ -1793,8 +1799,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) {
if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) || if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) ||
((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) { ((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) {
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_MORPHA] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
this->csState = MO_DEATH_START; this->csState = MO_DEATH_START;
sMorphaTent1->drawActor = false; sMorphaTent1->drawActor = false;

View File

@ -11,7 +11,8 @@
#include "overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h" #include "overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/frame_interpolation.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) #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.home.pos = this->actor.world.pos;
this->actor.shape.rot.y = 0; this->actor.shape.rot.y = 0;
if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { if (Flags_GetClear(play, play->roomCtx.curRoom.num)) {
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
ROOM_CENTER_Z + 400.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true); Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y,
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z + 400.0f, 0, 0, 0, WARP_DUNGEON_ADULT, true);
ROOM_CENTER_Z - 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, ROOM_CENTER_X, ROOM_CENTER_Y,
ROOM_CENTER_Z - 200.0f, 0, 0, 0, 0, true);
}
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else { } else {
sHands[LEFT] = sHands[LEFT] =
@ -1202,9 +1207,11 @@ void BossSst_HeadFinish(BossSst* this, PlayState* play) {
Flags_SetClear(play, play->roomCtx.curRoom.num); Flags_SetClear(play, play->roomCtx.curRoom.num);
} }
} else if (this->effects[0].alpha == 0) { } 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, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
WARP_DUNGEON_ADULT, true); Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z, 0, 0, 0,
if (!IS_BOSS_RUSH) { WARP_DUNGEON_ADULT, true);
}
if (GameInteractor_Should(VB_SPAWN_HEART_CONTAINER, true, NULL)) {
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 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_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); 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) { if (Actor_ApplyDamage(&this->actor) == 0) {
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
BossSst_HeadSetupDeath(this, play); BossSst_HeadSetupDeath(this, play);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BONGO_BONGO] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
} else { } else {
BossSst_HeadSetupDamage(this); BossSst_HeadSetupDamage(this);
} }

View File

@ -4,7 +4,8 @@
#include "objects/object_tw/object_tw.h" #include "objects/object_tw/object_tw.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/frame_interpolation.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 <string.h> #include <string.h>
@ -535,9 +536,14 @@ void BossTw_Init(Actor* thisx, PlayState* play2) {
if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { if (Flags_GetClear(play, play->roomCtx.curRoom.num)) {
// twinrova has been defeated. // twinrova has been defeated.
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
0, 0, WARP_DUNGEON_ADULT); Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0,
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -600.0f, 230.0f, 0.0f, 0, 0, 0, 0, true); 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 { } else {
sKotakePtr = (BossTw*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_BOSS_TW, sKotakePtr = (BossTw*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_BOSS_TW,
this->actor.world.pos.x, this->actor.world.pos.y, 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); func_80064534(play, &play->csCtx);
Player_SetCsActionWithHaltedActors(play, &this->actor, 7); Player_SetCsActionWithHaltedActors(play, &this->actor, 7);
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); 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, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0,
0, 0, WARP_DUNGEON_ADULT); 0, 0, WARP_DUNGEON_ADULT);
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);
} }
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);
}
this->actor.world.pos.y = -2000.0f; this->actor.world.pos.y = -2000.0f;
this->workf[UNK_F18] = 0.0f; this->workf[UNK_F18] = 0.0f;
sKoumePtr->visible = sKotakePtr->visible = false; sKoumePtr->visible = sKotakePtr->visible = false;
@ -5289,8 +5296,7 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) {
BossTw_TwinrovaSetupDeathCS(this, play); BossTw_TwinrovaSetupDeathCS(this, play);
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD); Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_TWINROVA] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
return; return;
} }

View File

@ -15,7 +15,8 @@
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/frame_interpolation.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) #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)) { if (Flags_GetEventChkInf(EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP)) {
warpId = ACTOR_DOOR_WARP1; warpId = ACTOR_DOOR_WARP1;
} }
Actor_Spawn(&play->actorCtx, play, warpId, this->actor.world.pos.x, this->actor.world.pos.y, if (GameInteractor_Should(VB_SPAWN_BLUE_WARP, true, this)) {
this->actor.world.pos.z, 0, 0, 0, Actor_Spawn(&play->actorCtx, play, warpId, this->actor.world.pos.x, this->actor.world.pos.y,
0, true); //! params could be WARP_DUNGEON_CHILD however this can also spawn Ru1 this->actor.world.pos.z, 0, 0, 0,
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x + 160.0f, 0, true); //! params could be WARP_DUNGEON_CHILD however this can also spawn Ru1
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true); }
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; sDoorState = 100;
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else { } else {
@ -1401,8 +1407,7 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) {
if (sFightPhase >= PHASE_DEATH) { if (sFightPhase >= PHASE_DEATH) {
BossVa_SetupBodyDeath(this, play); BossVa_SetupBodyDeath(this, play);
Enemy_StartFinishingBlow(play, &this->actor); Enemy_StartFinishingBlow(play, &this->actor);
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BARINADE] = GAMEPLAYSTAT_TOTAL_TIME; GameInteractor_ExecuteOnBossDefeat(&this->actor);
BossRush_HandleCompleteBoss(play);
return; return;
} }
this->actor.speedXZ = -10.0f; this->actor.speedXZ = -10.0f;
@ -1657,7 +1662,7 @@ void BossVa_BodyDeath(BossVa* this, PlayState* play) {
Player_SetCsActionWithHaltedActors(play, &this->actor, 7); Player_SetCsActionWithHaltedActors(play, &this->actor, 7);
sCsState++; 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, 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); 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, Actor_Spawn(&play->actorCtx, play, ACTOR_EN_RU1, sWarpPos[sp7C].x, sWarpPos[sp7C].y,
sWarpPos[sp7C].z, 0, 0, 0, 0, true); 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: case DEATH_FINISH:

View File

@ -426,12 +426,6 @@ void DemoKankyo_KillDoorOfTimeCollision(DemoKankyo* this, PlayState* play) {
void DemoKankyo_Update(Actor* thisx, PlayState* play) { void DemoKankyo_Update(Actor* thisx, PlayState* play) {
DemoKankyo* this = (DemoKankyo*)thisx; DemoKankyo* this = (DemoKankyo*)thisx;
this->actionFunc(this, play); 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) { void DemoKankyo_Draw(Actor* thisx, PlayState* play) {

View File

@ -8,7 +8,6 @@
#include "overlays/actors/ovl_En_Elf/z_en_elf.h" #include "overlays/actors/ovl_En_Elf/z_en_elf.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "objects/object_sa/object_sa.h" #include "objects/object_sa/object_sa.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "vt.h" #include "vt.h"
@ -257,21 +256,13 @@ void func_8098E960(DemoSa* this, PlayState* play) {
if ((gSaveContext.chamberCutsceneNum == 0) && (gSaveContext.sceneSetupIndex < 4)) { if ((gSaveContext.chamberCutsceneNum == 0) && (gSaveContext.sceneSetupIndex < 4)) {
player = GET_PLAYER(play); player = GET_PLAYER(play);
if (!IS_BOSS_RUSH) { this->action = 1;
this->action = 1; play->csCtx.segment = D_8099010C;
play->csCtx.segment = D_8099010C; gSaveContext.cutsceneTrigger = 2;
gSaveContext.cutsceneTrigger = 2; if (GameInteractor_Should(VB_GIVE_ITEM_FOREST_MEDALLION, true, NULL)) {
if (GameInteractor_Should(VB_GIVE_ITEM_FOREST_MEDALLION, true, NULL)) { Item_Give(play, ITEM_MEDALLION_FOREST);
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);
} }
player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000;
} }
} }

View File

@ -749,9 +749,7 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) {
this->warpTimer++; this->warpTimer++;
if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) { if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) {
if (IS_BOSS_RUSH) { if (play->sceneNum == SCENE_FOREST_TEMPLE_BOSS) {
BossRush_HandleBlueWarp(play, this->actor.world.pos.x, this->actor.world.pos.z);
} else 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)) { 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); Flags_SetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP);
if (GameInteractor_Should(VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_FOREST)) { if (GameInteractor_Should(VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_FOREST)) {

View File

@ -3,6 +3,8 @@
#include "overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h" #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 "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h"
#include "objects/object_bg/object_bg.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) #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) { if (frameCount == 30.0f) {
this->dialogState = TEXT_STATE_EVENT; this->dialogState = TEXT_STATE_EVENT;
// Check for beaten Dodongo's Cavern if Rando is disabled if (GameInteractor_Should(VB_BE_ABLE_TO_PLAY_BOMBCHU_BOWLING, (Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) || BREG(2), NULL)) {
if (!IS_RANDO) { this->actor.textId = 0xBF;
if ((Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) || BREG(2)) { } else {
this->actor.textId = 0xBF; this->actor.textId = 0x7058;
} 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;
}
} }
} }
Message_ContinueTextbox(play, this->actor.textId); Message_ContinueTextbox(play, this->actor.textId);

View File

@ -207,11 +207,6 @@ void EnBox_Init(Actor* thisx, PlayState* play2) {
if (play->sceneNum == SCENE_FOREST_TEMPLE && this->dyna.actor.params == 10222) { if (play->sceneNum == SCENE_FOREST_TEMPLE && this->dyna.actor.params == 10222) {
this->movementFlags = ENBOX_MOVE_IMMOBILE; 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) { void EnBox_Destroy(Actor* thisx, PlayState* play) {

View File

@ -7,6 +7,8 @@
#include "z_en_kakasi2.h" #include "z_en_kakasi2.h"
#include "vt.h" #include "vt.h"
#include "objects/object_ka/object_ka.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) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA | ACTOR_FLAG_NO_LOCKON)
@ -117,20 +119,7 @@ void func_80A90264(EnKakasi2* this, PlayState* play) {
this->unk_194++; this->unk_194++;
int ocarinaButtonCount = 0; if ((BREG(1) != 0) || GameInteractor_Should(VB_SKIP_SCARECROWS_SONG, false, NULL) && (this->actor.xzDistToPlayer < this->maxSpawnDistance.x) &&
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) &&
(fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < this->maxSpawnDistance.y)) { (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < this->maxSpawnDistance.y)) {
this->actor.draw = func_80A90948; this->actor.draw = func_80A90948;
Collider_InitCylinder(play, &this->collider); Collider_InitCylinder(play, &this->collider);

View File

@ -277,11 +277,6 @@ void EnKusa_Destroy(Actor* thisx, PlayState* play2) {
} }
void EnKusa_SetupWaitObject(EnKusa* this) { 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); EnKusa_SetupAction(this, EnKusa_WaitObject);
} }

View File

@ -157,15 +157,6 @@ void EnSyatekiMan_Init(Actor* thisx, PlayState* play) {
s32 pad; s32 pad;
EnSyatekiMan* this = (EnSyatekiMan*)thisx; 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"); osSyncPrintf("\n\n");
// "Old man appeared!! Muhohohohohohohon" // "Old man appeared!! Muhohohohohohohon"
osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 親父登場!!むほほほほほほほーん ☆☆☆☆☆ \n" VT_RST); osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 親父登場!!むほほほほほほほーん ☆☆☆☆☆ \n" VT_RST);

View File

@ -200,12 +200,7 @@ void ObjComb_Update(Actor* thisx, PlayState* play) {
this->unk_1B2 += 0x2EE0; this->unk_1B2 += 0x2EE0;
this->actionFunc(this, play); this->actionFunc(this, play);
s16 wiggleOffset = this->unk_1B0; this->actor.shape.rot.x = Math_SinS(this->unk_1B2) * this->unk_1B0 + this->actor.home.rot.x;
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;
} }
void ObjComb_Draw(Actor* thisx, PlayState* play) { void ObjComb_Draw(Actor* thisx, PlayState* play) {

View File

@ -273,15 +273,6 @@ void ObjOshihiki_Init(Actor* thisx, PlayState* play2) {
PlayState* play = play2; PlayState* play = play2;
ObjOshihiki* this = (ObjOshihiki*)thisx; 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); ObjOshihiki_CheckType(this, play);
if ((((this->dyna.actor.params >> 8) & 0xFF) >= 0) && (((this->dyna.actor.params >> 8) & 0xFF) <= 0x3F)) { if ((((this->dyna.actor.params >> 8) & 0xFF) >= 0) && (((this->dyna.actor.params >> 8) & 0xFF) <= 0x3F)) {

View File

@ -221,11 +221,6 @@ void ObjTsubo_WaterBreak(ObjTsubo* this, PlayState* play) {
} }
void ObjTsubo_SetupWaitForObject(ObjTsubo* this) { 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; this->actionFunc = ObjTsubo_WaitForObject;
} }

View File

@ -4843,8 +4843,7 @@ s32 Player_ActionChange_1(Player* this, PlayState* play) {
if ((this->doorType != PLAYER_DOORTYPE_NONE) && if ((this->doorType != PLAYER_DOORTYPE_NONE) &&
(!(this->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) || (!(this->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) ||
((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)))) { ((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)) && GameInteractor_Should(VB_BE_ABLE_TO_OPEN_DOORS, true, NULL)) {
if ((CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (Player_Action_8084F9A0 == this->actionFunc)) && !IS_BOSS_RUSH) {
doorActor = this->doorActor; doorActor = this->doorActor;
if (this->doorType <= PLAYER_DOORTYPE_AJAR) { if (this->doorType <= PLAYER_DOORTYPE_AJAR) {

View File

@ -1388,7 +1388,7 @@ void FileChoose_UpdateBossRushMenu(GameState* thisx) {
// Move down // Move down
if (this->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) { 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. // 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->bossRushIndex = 0;
this->bossRushOffset = 0; this->bossRushOffset = 0;
} else { } else {
@ -1401,7 +1401,7 @@ void FileChoose_UpdateBossRushMenu(GameState* thisx) {
} else if (this->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) { } 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. // 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) { 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; this->bossRushOffset = this->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1;
} else { } else {
// When first visible option is selected when moving up, offset the list up by one. // 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)); (arrowUpY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
} }
// Arrow down // 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 arrowDownX = 140;
uint16_t arrowDownY = 181 + (this->bossRushArrowOffset / 10); uint16_t arrowDownY = 181 + (this->bossRushArrowOffset / 10);
gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA,
@ -3045,16 +3045,6 @@ void FileChoose_LoadGame(GameState* thisx) {
gSaveContext.naviTimer = 0; 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); GameInteractor_ExecuteOnLoadGame(gSaveContext.fileNum);
} }

View File

@ -2,6 +2,8 @@
#include "textures/parameter_static/parameter_static.h" #include "textures/parameter_static/parameter_static.h"
#include "textures/icon_item_static/icon_item_static.h" #include "textures/icon_item_static/icon_item_static.h"
#include "soh/Enhancements/cosmetics/cosmeticsTypes.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[]; 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) { } else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) {
aButtonColor = (Color_RGB8){ 80, 255, 150 }; 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 }; 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)) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CUpButton.Changed"), 0)) {
cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CUpButton.Value"), cUpButtonColor); 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 }; 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)) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.Changed"), 0)) {
cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CDownButton.Value"), cDownButtonColor); 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 }; 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)) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CLeftButton.Changed"), 0)) {
cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CLeftButton.Value"), cLeftButtonColor); 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 }; 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)) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.Changed"), 0)) {
cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CRightButton.Value"), cRightButtonColor); 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 }; cRightButtonColor = (Color_RGB8){ 191, 191, 191 };
} }

View File

@ -1650,13 +1650,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA);
gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); 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 = KaleidoScope_QuadTextureIA8(
POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][0], 48, 16, 12); 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 = POLY_KAL_DISP =
@ -4002,9 +3998,7 @@ void KaleidoScope_Update(PlayState* play)
case 6: case 6:
switch (pauseCtx->unk_1E4) { switch (pauseCtx->unk_1E4) {
case 0: case 0:
// Boss Rush skips past the "Save?" window when pressing B while paused. if (GameInteractor_Should(VB_CLOSE_PAUSE_MENU, CHECK_BTN_ALL(input->press.button, BTN_START), NULL)) {
if (CHECK_BTN_ALL(input->press.button, BTN_START) ||
(CHECK_BTN_ALL(input->press.button, BTN_B) && IS_BOSS_RUSH)) {
if (CVarGetInteger(CVAR_CHEAT("EasyPauseBuffer"), 0) || CVarGetInteger(CVAR_CHEAT("EasyInputBuffer"), 0)) { 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 // Easy pause buffer is 13 frames, 12 for kaledio to end, and one more to advance a single frame
CVarSetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 13); CVarSetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 13);
@ -4391,10 +4385,8 @@ void KaleidoScope_Update(PlayState* play)
VREG(88) = 66; VREG(88) = 66;
WREG(2) = 0; WREG(2) = 0;
pauseCtx->alpha = 255; pauseCtx->alpha = 255;
if (!IS_BOSS_RUSH) { if (GameInteractor_Should(VB_TRANSITION_TO_SAVE_SCREEN_ON_DEATH, true, pauseCtx)) {
pauseCtx->state = 0xE; pauseCtx->state = 0xE;
} else {
pauseCtx->state = 0xF;
} }
gSaveContext.deaths++; gSaveContext.deaths++;
if (gSaveContext.deaths > 999) { if (gSaveContext.deaths > 999) {
@ -4439,7 +4431,7 @@ void KaleidoScope_Update(PlayState* play)
case 0x10: case 0x10:
if (CHECK_BTN_ALL(input->press.button, BTN_A) || CHECK_BTN_ALL(input->press.button, BTN_START)) { 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, Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0,
&D_801333E8); &D_801333E8);
Play_SaveSceneFlags(play); Play_SaveSceneFlags(play);
@ -4512,7 +4504,7 @@ void KaleidoScope_Update(PlayState* play)
R_PAUSE_MENU_MODE = 0; R_PAUSE_MENU_MODE = 0;
func_800981B8(&play->objectCtx); func_800981B8(&play->objectCtx);
func_800418D0(&play->colCtx, play); 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); Play_TriggerRespawn(play);
gSaveContext.respawnFlag = -2; gSaveContext.respawnFlag = -2;
// In ER, handle death warp to last entrance from grottos // In ER, handle death warp to last entrance from grottos