Merge remote-tracking branch 'origin/develop' into actor-accessibility-experiments

This commit is contained in:
Demur Rumed 2025-08-17 16:14:20 +00:00
commit 37c03f4453
26 changed files with 599 additions and 443 deletions

View file

@ -1522,7 +1522,7 @@ typedef struct {
/* 0x34 */ s32 isEnabled; /* 0x34 */ s32 isEnabled;
} StickDirectionPrompt; } StickDirectionPrompt;
typedef struct { typedef struct FileChooseContext {
/* 0x00000 */ GameState state; /* 0x00000 */ GameState state;
/* 0x000A4 */ Vtx* windowVtx; /* 0x000A4 */ Vtx* windowVtx;
/* 0x000A8 */ u8* staticSegment; /* 0x000A8 */ u8* staticSegment;

View file

@ -7,7 +7,7 @@
#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/gameplaystats.h" #include "soh/Enhancements/gameplaystats.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/boss-rush/BossRushTypes.h" #include "soh/Enhancements/boss-rush/BossRush.h"
typedef enum { typedef enum {
/* 0x0 */ MAGIC_STATE_IDLE, // Regular gameplay /* 0x0 */ MAGIC_STATE_IDLE, // Regular gameplay

View file

@ -19,7 +19,7 @@ static std::array<const char*, ACTORCAT_MAX> sCatToStrArray{
"SWITCH", "BG", "PLAYER", "EXPLOSIVE", "NPC", "ENEMY", "PROP", "ITEMACTION", "MISC", "BOSS", "DOOR", "CHEST", "SWITCH", "BG", "PLAYER", "EXPLOSIVE", "NPC", "ENEMY", "PROP", "ITEMACTION", "MISC", "BOSS", "DOOR", "CHEST",
}; };
#define DEFINE_SCENE(_1, _2, enumName, _4, _5, _6) #enumName #define DEFINE_SCENE(_1, _2, enumName, _4, _5, _6) #enumName,
static std::array<const char*, SCENE_ID_MAX> sSceneIdToStrArray{ static std::array<const char*, SCENE_ID_MAX> sSceneIdToStrArray{
#include "tables/scene_table.h" #include "tables/scene_table.h"

View file

@ -0,0 +1,51 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
#include "./enhancementTypes.h"
extern "C" {
#include "functions.h"
#include "macros.h"
extern PlayState* gPlayState;
extern SaveContext gSaveContext;
}
void RegisterBonkDamage() {
COND_HOOK(OnPlayerBonk, CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE) != BONK_DAMAGE_NONE,
[] {
uint16_t bonkDamage = 0;
switch (CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE)) {
case BONK_DAMAGE_NONE:
return;
case BONK_DAMAGE_OHKO:
gSaveContext.health = 0;
return;
case BONK_DAMAGE_QUARTER_HEART:
bonkDamage = 4;
break;
case BONK_DAMAGE_HALF_HEART:
bonkDamage = 8;
break;
case BONK_DAMAGE_1_HEART:
bonkDamage = 16;
break;
case BONK_DAMAGE_2_HEARTS:
bonkDamage = 32;
break;
case BONK_DAMAGE_4_HEARTS:
bonkDamage = 64;
break;
case BONK_DAMAGE_8_HEARTS:
bonkDamage = 128;
break;
default:
break;
}
Health_ChangeBy(gPlayState, -bonkDamage);
// Set invincibility to make Link flash red as a visual damage indicator.
Player* player = GET_PLAYER(gPlayState);
player->invincibilityTimer = 28;
});
}
static RegisterShipInitFunc registerBonkDamage(RegisterBonkDamage, { CVAR_ENHANCEMENT("BonkDamageMult") });

View file

@ -2,6 +2,7 @@
#include "sequence.h" #include "sequence.h"
#include "sfx.h" #include "sfx.h"
#include "soh/cvar_prefixes.h" #include "soh/cvar_prefixes.h"
#include "soh/Notification/Notification.h"
#include <vector> #include <vector>
#include <utils/StringHelper.h> #include <utils/StringHelper.h>
#include <libultraship/bridge.h> #include <libultraship/bridge.h>
@ -458,3 +459,11 @@ extern "C" bool AudioCollection_HasSequenceNum(uint16_t seqId) {
extern "C" size_t AudioCollection_SequenceMapSize() { extern "C" size_t AudioCollection_SequenceMapSize() {
return AudioCollection::Instance->SequenceMapSize(); return AudioCollection::Instance->SequenceMapSize();
} }
extern "C" void AudioCollection_EmitSongNameNotification(s32 seqId) {
const char* sequenceName = AudioCollection_GetSequenceName(seqId);
if (sequenceName != NULL) {
Notification::Emit({ .message = "Currently playing: " + std::string(sequenceName),
.remainingTime = (float)CVarGetInteger(CVAR_AUDIO("SeqNameOverlayDuration"), 5) });
}
}

View file

@ -73,4 +73,5 @@ void AudioCollection_AddToCollection(char* otrPath, uint16_t seqNum);
const char* AudioCollection_GetSequenceName(uint16_t seqId); const char* AudioCollection_GetSequenceName(uint16_t seqId);
bool AudioCollection_HasSequenceNum(uint16_t seqId); bool AudioCollection_HasSequenceNum(uint16_t seqId);
size_t AudioCollection_SequenceMapSize(); size_t AudioCollection_SequenceMapSize();
void AudioCollection_EmitSongNameNotification(s32 seqId);
#endif #endif

View file

@ -2,6 +2,8 @@
#include "soh/OTRGlobals.h" #include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh_assets.h"
#include "soh/frame_interpolation.h"
#include <array> #include <array>
#include <string> #include <string>
@ -14,14 +16,87 @@ extern "C" {
#include "src/overlays/actors/ovl_Boss_Goma/z_boss_goma.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_Boss_Mo/z_boss_mo.h"
#include "src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
extern PlayState* gPlayState; #include "src/overlays/gamestates/ovl_file_choose/file_choose.h"
#include "objects/gameplay_keep/gameplay_keep.h"
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_nes_static/icon_item_nes_static.h"
#include "textures/icon_item_ger_static/icon_item_ger_static.h" #include "textures/icon_item_ger_static/icon_item_ger_static.h"
#include "textures/icon_item_fra_static/icon_item_fra_static.h" #include "textures/icon_item_fra_static/icon_item_fra_static.h"
extern PlayState* gPlayState;
Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point);
void FileChoose_UpdateStickDirectionPromptAnim(GameState* thisx);
void FileChoose_DrawTextRec(GraphicsContext* gfxCtx, s32 r, s32 g, s32 b, s32 a, f32 x, f32 y, f32 z, s32 s, s32 t,
f32 dx, f32 dy);
} }
typedef enum {
BR_CHOICE_BOSSES_ALL,
BR_CHOICE_BOSSES_CHILD,
BR_CHOICE_BOSSES_ADULT,
BR_CHOICE_BOSSES_GANONDORF_GANON
} BossRushBossesChoices;
typedef enum {
BR_CHOICE_HEARTS_10,
BR_CHOICE_HEARTS_15,
BR_CHOICE_HEARTS_20,
BR_CHOICE_HEARTS_3,
BR_CHOICE_HEARTS_5,
BR_CHOICE_HEARTS_7
} BossRushHeartsChoices;
typedef enum {
BR_CHOICE_AMMO_LIMITED,
BR_CHOICE_AMMO_FULL,
BR_CHOICE_AMMO_MAXED,
} BossRushAmmoChoices;
typedef enum {
BR_CHOICE_HEAL_GANONDORF,
BR_CHOICE_HEAL_EVERYBOSS,
BR_CHOICE_HEAL_NEVER,
} BossRushHealChoices;
typedef enum {
BR_CHOICE_MAGIC_SINGLE,
BR_CHOICE_MAGIC_DOUBLE,
} BossRushMagicChoices;
typedef enum {
BR_CHOICE_BGS_NO,
BR_CHOICE_BGS_YES,
} BossRushBgsChoices;
typedef enum {
BR_CHOICE_BOTTLE_NO,
BR_CHOICE_BOTTLE_EMPTY,
BR_CHOICE_BOTTLE_FAIRY,
BR_CHOICE_BOTTLE_REDPOTION,
BR_CHOICE_BOTTLE_GREENPOTION,
BR_CHOICE_BOTTLE_BLUEPOTION
} BossRushBottleChoices;
typedef enum {
BR_CHOICE_LONGSHOT_NO,
BR_CHOICE_LONGSHOT_YES,
} BossRushLongshotChoices;
typedef enum {
BR_CHOICE_HOVERBOOTS_NO,
BR_CHOICE_HOVERBOOTS_YES,
} BossRushHoverBootsChoices;
typedef enum {
BR_CHOICE_BUNNYHOOD_NO,
BR_CHOICE_BUNNYHOOD_YES,
} BossRushBunnyHoodChoices;
typedef enum {
BR_CHOICE_TIMER_YES,
BR_CHOICE_TIMER_NO,
} BossRushTimerChoices;
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;
@ -114,6 +189,180 @@ u8 BossRush_GetSettingOptionsAmount(u8 optionIndex) {
return static_cast<u8>(BossRushOptions[optionIndex].choices.size()); return static_cast<u8>(BossRushOptions[optionIndex].choices.size());
} }
void FileChoose_UpdateBossRushMenu(GameState* gameState) {
static s8 sLastBossRushOptionIndex = -1;
static s8 sLastBossRushOptionValue = -1;
FileChoose_UpdateStickDirectionPromptAnim(gameState);
FileChooseContext* fileChooseContext = (FileChooseContext*)gameState;
Input* input = &fileChooseContext->state.input[0];
bool dpad = CVarGetInteger(CVAR_SETTING("DpadInText"), 0);
// Fade in elements after opening Boss Rush options menu
fileChooseContext->bossRushUIAlpha += 25;
if (fileChooseContext->bossRushUIAlpha > 255) {
fileChooseContext->bossRushUIAlpha = 255;
}
// Animate up/down arrows.
fileChooseContext->bossRushArrowOffset += 1;
if (fileChooseContext->bossRushArrowOffset >= 30) {
fileChooseContext->bossRushArrowOffset = 0;
}
// Move menu selection up or down.
if (ABS(fileChooseContext->stickRelY) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN | BTN_DUP))) {
// Move down
if (fileChooseContext->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) {
// When selecting past the last option, cycle back to the first option.
if ((fileChooseContext->bossRushIndex + 1) > BR_OPTIONS_MAX - 1) {
fileChooseContext->bossRushIndex = 0;
fileChooseContext->bossRushOffset = 0;
} else {
fileChooseContext->bossRushIndex++;
// When last visible option is selected when moving down, offset the list down by one.
if (fileChooseContext->bossRushIndex - fileChooseContext->bossRushOffset >
BOSSRUSH_MAX_OPTIONS_ON_SCREEN - 1) {
fileChooseContext->bossRushOffset++;
}
}
} else if (fileChooseContext->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) {
// When selecting past the first option, cycle back to the last option and offset the list to view it
// properly.
if ((fileChooseContext->bossRushIndex - 1) < 0) {
fileChooseContext->bossRushIndex = BR_OPTIONS_MAX - 1;
fileChooseContext->bossRushOffset =
fileChooseContext->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1;
} else {
// When first visible option is selected when moving up, offset the list up by one.
if (fileChooseContext->bossRushIndex - fileChooseContext->bossRushOffset == 0) {
fileChooseContext->bossRushOffset--;
}
fileChooseContext->bossRushIndex--;
}
}
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
}
// Cycle through choices for currently selected option.
if (ABS(fileChooseContext->stickRelX) > 30 ||
(dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
if (fileChooseContext->stickRelX > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DRIGHT))) {
// If exceeding the amount of choices for the selected option, cycle back to the first.
if ((gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] + 1) ==
BossRush_GetSettingOptionsAmount(fileChooseContext->bossRushIndex)) {
gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] = 0;
} else {
gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]++;
}
} else if (fileChooseContext->stickRelX < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT))) {
// If cycling back when already at the first choice for the selected option, cycle back to the last choice.
if ((gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] - 1) < 0) {
gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] =
BossRush_GetSettingOptionsAmount(fileChooseContext->bossRushIndex) - 1;
} else {
gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]--;
}
}
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
}
if (sLastBossRushOptionIndex != fileChooseContext->bossRushIndex ||
sLastBossRushOptionValue != gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]) {
GameInteractor_ExecuteOnUpdateFileBossRushOptionSelection(
fileChooseContext->bossRushIndex,
gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]);
sLastBossRushOptionIndex = fileChooseContext->bossRushIndex;
sLastBossRushOptionValue = gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex];
}
if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
fileChooseContext->configMode = CM_BOSS_RUSH_TO_QUEST;
return;
}
// Load into the game.
if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
fileChooseContext->buttonIndex = 0xFE;
fileChooseContext->menuMode = FS_MENU_MODE_SELECT;
fileChooseContext->selectMode = SM_FADE_OUT;
fileChooseContext->prevConfigMode = fileChooseContext->configMode;
return;
}
}
void FileChoose_DrawBossRushMenuWindowContents(FileChooseContext* fileChooseContext) {
OPEN_DISPS(fileChooseContext->state.gfxCtx);
uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language;
uint8_t listOffset = fileChooseContext->bossRushOffset;
uint8_t textAlpha = fileChooseContext->bossRushUIAlpha;
// Draw arrows to indicate that the list can scroll up or down.
// Arrow up
if (listOffset > 0) {
uint16_t arrowUpX = 140;
uint16_t arrowUpY = 76 - (fileChooseContext->bossRushArrowOffset / 10);
gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowUpTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOLOD);
gSPWideTextureRectangle(POLY_OPA_DISP++, arrowUpX << 2, arrowUpY << 2, (arrowUpX + 8) << 2, (arrowUpY + 8) << 2,
G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
}
// Arrow down
if (BR_OPTIONS_MAX - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) {
uint16_t arrowDownX = 140;
uint16_t arrowDownY = 181 + (fileChooseContext->bossRushArrowOffset / 10);
gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOLOD);
gSPWideTextureRectangle(POLY_OPA_DISP++, arrowDownX << 2, arrowDownY << 2, (arrowDownX + 8) << 2,
(arrowDownY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
}
// Draw options. There's more options than what fits on the screen, so the visible options
// depend on the current offset of the list. Currently selected option pulses in
// color and has arrows surrounding the option.
for (uint8_t i = listOffset; i - listOffset < BOSSRUSH_MAX_OPTIONS_ON_SCREEN; i++) {
uint16_t textYOffset = (i - listOffset) * 16;
// Option name.
Interface_DrawTextLine(fileChooseContext->state.gfxCtx, (char*)BossRush_GetSettingName(i, language), 65,
(87 + textYOffset), 255, 255, 80, textAlpha, 0.8f, true);
// Selected choice for option.
uint16_t finalKerning = Interface_DrawTextLine(
fileChooseContext->state.gfxCtx,
(char*)BossRush_GetSettingChoiceName(i, gSaveContext.ship.quest.data.bossRush.options[i], language), 165,
(87 + textYOffset), 255, 255, 255, textAlpha, 0.8f, true);
// Draw arrows around selected option.
if (fileChooseContext->bossRushIndex == i) {
Gfx_SetupDL_39Opa(fileChooseContext->state.gfxCtx);
gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOLOD);
FileChoose_DrawTextRec(fileChooseContext->state.gfxCtx, fileChooseContext->stickLeftPrompt.arrowColorR,
fileChooseContext->stickLeftPrompt.arrowColorG,
fileChooseContext->stickLeftPrompt.arrowColorB, textAlpha, 160, (92 + textYOffset),
0.42f, 0, 0, -1.0f, 1.0f);
FileChoose_DrawTextRec(fileChooseContext->state.gfxCtx, fileChooseContext->stickRightPrompt.arrowColorR,
fileChooseContext->stickRightPrompt.arrowColorG,
fileChooseContext->stickRightPrompt.arrowColorB, textAlpha, (171 + finalKerning),
(92 + textYOffset), 0.42f, 0, 0, 1.0f, 1.0f);
}
}
CLOSE_DISPS(fileChooseContext->state.gfxCtx);
}
void BossRush_SpawnBlueWarps(PlayState* play) { void BossRush_SpawnBlueWarps(PlayState* play) {
// Spawn blue warps in Chamber of Sages based on what bosses have been defeated. // Spawn blue warps in Chamber of Sages based on what bosses have been defeated.
@ -316,7 +565,7 @@ void BossRush_HandleCompleteBoss(PlayState* play) {
} }
} }
void BossRush_InitSave() { extern "C" void BossRush_InitSave() {
// Set player name to Lonk for the few textboxes that show up during Boss Rush. Player can't input their own name. // Set player name to Lonk for the few textboxes that show up during Boss Rush. Player can't input their own name.
std::array<char, 8> brPlayerName = { 21, 50, 49, 46, 62, 62, 62, 62 }; std::array<char, 8> brPlayerName = { 21, 50, 49, 46, 62, 62, 62, 62 };
@ -609,6 +858,17 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
*should = false; *should = false;
break; break;
} }
// Handle the heal on blue warp
case VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE: {
if (*should) {
BossRush_HandleBlueWarpHeal(gPlayState);
}
break;
}
case VB_SHOW_GAMEPLAY_TIMER: {
*should |= gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES;
break;
}
// Prevent saving // Prevent saving
case VB_BE_ABLE_TO_SAVE: case VB_BE_ABLE_TO_SAVE:
// Disable doors so the player can't leave the boss rooms backwards. // Disable doors so the player can't leave the boss rooms backwards.
@ -629,25 +889,6 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
va_end(args); va_end(args);
} }
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) { void BossRush_OnSceneInitHandler(s16 sceneNum) {
// Unpause the timer when the scene loaded isn't the Chamber of Sages. // Unpause the timer when the scene loaded isn't the Chamber of Sages.
if (sceneNum != SCENE_CHAMBER_OF_THE_SAGES) { if (sceneNum != SCENE_CHAMBER_OF_THE_SAGES) {
@ -667,38 +908,32 @@ void BossRush_OnBlueWarpUpdate(void* actor) {
} }
} }
void BossRush_RegisterHooks() { void RegisterBossRush() {
static u32 onVanillaBehaviorHook = 0; COND_HOOK(OnLoadGame, true, [](int32_t fileNum) {
static u32 onSceneInitHook = 0; COND_ID_HOOK(OnActorInit, ACTOR_DEMO_SA, IS_BOSS_RUSH, [](void* actorPtr) {
static u32 onActorInitHook = 0; BossRush_SpawnBlueWarps(gPlayState);
static u32 onBossDefeatHook = 0; Actor_Kill((Actor*)actorPtr);
static u32 onActorUpdate = 0; GET_PLAYER(gPlayState)->actor.world.rot.y = 27306;
GET_PLAYER(gPlayState)->actor.shape.rot.y = 27306;
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) { // Remove bushes, used in Gohma's arena
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook); COND_ID_HOOK(OnActorInit, ACTOR_EN_KUSA, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
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; // Remove pots, used in Barinade's and Ganondorf's arenas
onSceneInitHook = 0; COND_ID_HOOK(OnActorInit, ACTOR_OBJ_TSUBO, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
onActorInitHook = 0;
onBossDefeatHook = 0;
onActorUpdate = 0;
if (!IS_BOSS_RUSH) // Remove chests, mainly for the chest in King Dodongo's boss room
return; COND_ID_HOOK(OnActorInit, ACTOR_EN_BOX, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>( COND_HOOK(OnVanillaBehavior, IS_BOSS_RUSH, BossRush_OnVanillaBehaviorHandler);
BossRush_OnVanillaBehaviorHandler);
onSceneInitHook = COND_HOOK(OnSceneInit, IS_BOSS_RUSH, BossRush_OnSceneInitHandler);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(BossRush_OnSceneInitHandler);
onActorInitHook = COND_HOOK(OnBossDefeat, IS_BOSS_RUSH, BossRush_OnBossDefeatHandler);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(BossRush_OnActorInitHandler);
onBossDefeatHook = COND_ID_HOOK(OnActorUpdate, ACTOR_DOOR_WARP1, IS_BOSS_RUSH, BossRush_OnBlueWarpUpdate);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnBossDefeat>(BossRush_OnBossDefeatHandler);
onActorUpdate = GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorUpdate>(
ACTOR_DOOR_WARP1, BossRush_OnBlueWarpUpdate);
}); });
} }
static RegisterShipInitFunc initFunc(RegisterBossRush);

View file

@ -1,16 +1,41 @@
#pragma once #pragma once
#include "z64.h" #include <libultraship/libultra/types.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void BossRush_HandleBlueWarpHeal(PlayState* play); struct GameState;
void BossRush_InitSave(); struct FileChooseContext;
void FileChoose_UpdateBossRushMenu(struct GameState* gameState);
void FileChoose_DrawBossRushMenuWindowContents(struct FileChooseContext* fileChooseContext);
const char* BossRush_GetSettingName(u8 optionIndex, u8 language); const char* BossRush_GetSettingName(u8 optionIndex, u8 language);
const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language); const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language);
u8 BossRush_GetSettingOptionsAmount(u8 optionIndex); u8 BossRush_GetSettingOptionsAmount(u8 optionIndex);
void BossRush_RegisterHooks();
#ifdef __cplusplus #ifdef __cplusplus
}; };
#endif #endif
#define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6
typedef enum {
BR_OPTIONS_BOSSES,
BR_OPTIONS_HEARTS,
BR_OPTIONS_AMMO,
BR_OPTIONS_HEAL,
BR_OPTIONS_HYPERBOSSES,
BR_OPTIONS_MAGIC,
BR_OPTIONS_BGS,
BR_OPTIONS_BOTTLE,
BR_OPTIONS_LONGSHOT,
BR_OPTIONS_HOVERBOOTS,
BR_OPTIONS_BUNNYHOOD,
BR_OPTIONS_TIMER,
BR_OPTIONS_MAX,
} BossRushOptionEnums;
typedef enum {
BR_CHOICE_HYPERBOSSES_NO,
BR_CHOICE_HYPERBOSSES_YES,
} BossRushHyperBossesChoices;

View file

@ -1,91 +0,0 @@
#pragma once
#define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6
typedef enum {
BR_OPTIONS_BOSSES,
BR_OPTIONS_HEARTS,
BR_OPTIONS_AMMO,
BR_OPTIONS_HEAL,
BR_OPTIONS_HYPERBOSSES,
BR_OPTIONS_MAGIC,
BR_OPTIONS_BGS,
BR_OPTIONS_BOTTLE,
BR_OPTIONS_LONGSHOT,
BR_OPTIONS_HOVERBOOTS,
BR_OPTIONS_BUNNYHOOD,
BR_OPTIONS_TIMER,
BR_OPTIONS_MAX,
} BossRushOptionEnums;
typedef enum {
BR_CHOICE_BOSSES_ALL,
BR_CHOICE_BOSSES_CHILD,
BR_CHOICE_BOSSES_ADULT,
BR_CHOICE_BOSSES_GANONDORF_GANON
} BossRushBossesChoices;
typedef enum {
BR_CHOICE_HEARTS_10,
BR_CHOICE_HEARTS_15,
BR_CHOICE_HEARTS_20,
BR_CHOICE_HEARTS_3,
BR_CHOICE_HEARTS_5,
BR_CHOICE_HEARTS_7
} BossRushHeartsChoices;
typedef enum {
BR_CHOICE_AMMO_LIMITED,
BR_CHOICE_AMMO_FULL,
BR_CHOICE_AMMO_MAXED,
} BossRushAmmoChoices;
typedef enum {
BR_CHOICE_HEAL_GANONDORF,
BR_CHOICE_HEAL_EVERYBOSS,
BR_CHOICE_HEAL_NEVER,
} BossRushHealChoices;
typedef enum {
BR_CHOICE_HYPERBOSSES_NO,
BR_CHOICE_HYPERBOSSES_YES,
} BossRushHyperBossesChoices;
typedef enum {
BR_CHOICE_MAGIC_SINGLE,
BR_CHOICE_MAGIC_DOUBLE,
} BossRushMagicChoices;
typedef enum {
BR_CHOICE_BGS_NO,
BR_CHOICE_BGS_YES,
} BossRushBgsChoices;
typedef enum {
BR_CHOICE_BOTTLE_NO,
BR_CHOICE_BOTTLE_EMPTY,
BR_CHOICE_BOTTLE_FAIRY,
BR_CHOICE_BOTTLE_REDPOTION,
BR_CHOICE_BOTTLE_GREENPOTION,
BR_CHOICE_BOTTLE_BLUEPOTION
} BossRushBottleChoices;
typedef enum {
BR_CHOICE_LONGSHOT_NO,
BR_CHOICE_LONGSHOT_YES,
} BossRushLongshotChoices;
typedef enum {
BR_CHOICE_HOVERBOOTS_NO,
BR_CHOICE_HOVERBOOTS_YES,
} BossRushHoverBootsChoices;
typedef enum {
BR_CHOICE_BUNNYHOOD_NO,
BR_CHOICE_BUNNYHOOD_YES,
} BossRushBunnyHoodChoices;
typedef enum {
BR_CHOICE_TIMER_YES,
BR_CHOICE_TIMER_NO,
} BossRushTimerChoices;

View file

@ -112,7 +112,9 @@ typedef enum {
typedef enum { typedef enum {
TIME_TRAVEL_DISABLED, TIME_TRAVEL_DISABLED,
TIME_TRAVEL_OOT, TIME_TRAVEL_OOT,
TIME_TRAVEL_OOT_MS,
TIME_TRAVEL_ANY, TIME_TRAVEL_ANY,
TIME_TRAVEL_ANY_MS
} TimeTravelType; } TimeTravelType;
typedef enum { typedef enum {

View file

@ -50,7 +50,7 @@ namespace GameInteractionEffect {
// MARK: - Flags // MARK: - Flags
GameInteractionEffectQueryResult SetSceneFlag::CanBeApplied() { GameInteractionEffectQueryResult SetSceneFlag::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }
@ -62,7 +62,7 @@ void SetSceneFlag::_Apply() {
} }
GameInteractionEffectQueryResult UnsetSceneFlag::CanBeApplied() { GameInteractionEffectQueryResult UnsetSceneFlag::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }
@ -74,7 +74,7 @@ void UnsetSceneFlag::_Apply() {
} }
GameInteractionEffectQueryResult SetFlag::CanBeApplied() { GameInteractionEffectQueryResult SetFlag::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }
@ -86,7 +86,7 @@ void SetFlag::_Apply() {
} }
GameInteractionEffectQueryResult UnsetFlag::CanBeApplied() { GameInteractionEffectQueryResult UnsetFlag::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }
@ -99,7 +99,7 @@ void UnsetFlag::_Apply() {
// MARK: - ModifyHeartContainers // MARK: - ModifyHeartContainers
GameInteractionEffectQueryResult ModifyHeartContainers::CanBeApplied() { GameInteractionEffectQueryResult ModifyHeartContainers::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if ((parameters[0] > 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) > 0x140)) || } else if ((parameters[0] > 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) > 0x140)) ||
(parameters[0] < 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) < 0x10))) { (parameters[0] < 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) < 0x10))) {
@ -115,7 +115,7 @@ void ModifyHeartContainers::_Apply() {
// MARK: - FillMagic // MARK: - FillMagic
GameInteractionEffectQueryResult FillMagic::CanBeApplied() { GameInteractionEffectQueryResult FillMagic::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if (!gSaveContext.isMagicAcquired || gSaveContext.magic >= ((gSaveContext.isDoubleMagicAcquired + 1) * 48)) { } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic >= ((gSaveContext.isDoubleMagicAcquired + 1) * 48)) {
return GameInteractionEffectQueryResult::NotPossible; return GameInteractionEffectQueryResult::NotPossible;
@ -129,7 +129,7 @@ void FillMagic::_Apply() {
// MARK: - EmptyMagic // MARK: - EmptyMagic
GameInteractionEffectQueryResult EmptyMagic::CanBeApplied() { GameInteractionEffectQueryResult EmptyMagic::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if (!gSaveContext.isMagicAcquired || gSaveContext.magic <= 0) { } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic <= 0) {
return GameInteractionEffectQueryResult::NotPossible; return GameInteractionEffectQueryResult::NotPossible;
@ -143,7 +143,7 @@ void EmptyMagic::_Apply() {
// MARK: - ModifyRupees // MARK: - ModifyRupees
GameInteractionEffectQueryResult ModifyRupees::CanBeApplied() { GameInteractionEffectQueryResult ModifyRupees::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if ((parameters[0] < 0 && gSaveContext.rupees <= 0) || } else if ((parameters[0] < 0 && gSaveContext.rupees <= 0) ||
(parameters[0] > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET))) { (parameters[0] > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET))) {
@ -158,7 +158,7 @@ void ModifyRupees::_Apply() {
// MARK: - NoUI // MARK: - NoUI
GameInteractionEffectQueryResult NoUI::CanBeApplied() { GameInteractionEffectQueryResult NoUI::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -173,7 +173,7 @@ void NoUI::_Remove() {
// MARK: - ModifyGravity // MARK: - ModifyGravity
GameInteractionEffectQueryResult ModifyGravity::CanBeApplied() { GameInteractionEffectQueryResult ModifyGravity::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -188,7 +188,7 @@ void ModifyGravity::_Remove() {
// MARK: - ModifyHealth // MARK: - ModifyHealth
GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() { GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) || } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) ||
(parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))) { (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))) {
@ -204,7 +204,7 @@ void ModifyHealth::_Apply() {
// MARK: - SetPlayerHealth // MARK: - SetPlayerHealth
GameInteractionEffectQueryResult SetPlayerHealth::CanBeApplied() { GameInteractionEffectQueryResult SetPlayerHealth::CanBeApplied() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -217,7 +217,7 @@ void SetPlayerHealth::_Apply() {
// MARK: - FreezePlayer // MARK: - FreezePlayer
GameInteractionEffectQueryResult FreezePlayer::CanBeApplied() { GameInteractionEffectQueryResult FreezePlayer::CanBeApplied() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -230,7 +230,7 @@ void FreezePlayer::_Apply() {
// MARK: - BurnPlayer // MARK: - BurnPlayer
GameInteractionEffectQueryResult BurnPlayer::CanBeApplied() { GameInteractionEffectQueryResult BurnPlayer::CanBeApplied() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -243,7 +243,7 @@ void BurnPlayer::_Apply() {
// MARK: - ElectrocutePlayer // MARK: - ElectrocutePlayer
GameInteractionEffectQueryResult ElectrocutePlayer::CanBeApplied() { GameInteractionEffectQueryResult ElectrocutePlayer::CanBeApplied() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -256,7 +256,7 @@ void ElectrocutePlayer::_Apply() {
// MARK: - KnockbackPlayer // MARK: - KnockbackPlayer
GameInteractionEffectQueryResult KnockbackPlayer::CanBeApplied() { GameInteractionEffectQueryResult KnockbackPlayer::CanBeApplied() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() ||
player->stateFlags2 & PLAYER_STATE2_CRAWLING) { player->stateFlags2 & PLAYER_STATE2_CRAWLING) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
@ -269,7 +269,7 @@ void KnockbackPlayer::_Apply() {
// MARK: - ModifyLinkSize // MARK: - ModifyLinkSize
GameInteractionEffectQueryResult ModifyLinkSize::CanBeApplied() { GameInteractionEffectQueryResult ModifyLinkSize::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -284,7 +284,7 @@ void ModifyLinkSize::_Remove() {
// MARK: - InvisibleLink // MARK: - InvisibleLink
GameInteractionEffectQueryResult InvisibleLink::CanBeApplied() { GameInteractionEffectQueryResult InvisibleLink::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -299,7 +299,7 @@ void InvisibleLink::_Remove() {
// MARK: - PacifistMode // MARK: - PacifistMode
GameInteractionEffectQueryResult PacifistMode::CanBeApplied() { GameInteractionEffectQueryResult PacifistMode::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -314,7 +314,7 @@ void PacifistMode::_Remove() {
// MARK: - DisableZTargeting // MARK: - DisableZTargeting
GameInteractionEffectQueryResult DisableZTargeting::CanBeApplied() { GameInteractionEffectQueryResult DisableZTargeting::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -329,7 +329,7 @@ void DisableZTargeting::_Remove() {
// MARK: - WeatherRainstorm // MARK: - WeatherRainstorm
GameInteractionEffectQueryResult WeatherRainstorm::CanBeApplied() { GameInteractionEffectQueryResult WeatherRainstorm::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -344,7 +344,7 @@ void WeatherRainstorm::_Remove() {
// MARK: - ReverseControls // MARK: - ReverseControls
GameInteractionEffectQueryResult ReverseControls::CanBeApplied() { GameInteractionEffectQueryResult ReverseControls::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -359,7 +359,7 @@ void ReverseControls::_Remove() {
// MARK: - ForceEquipBoots // MARK: - ForceEquipBoots
GameInteractionEffectQueryResult ForceEquipBoots::CanBeApplied() { GameInteractionEffectQueryResult ForceEquipBoots::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -374,7 +374,7 @@ void ForceEquipBoots::_Remove() {
// MARK: - ModifyMovementSpeedMultiplier // MARK: - ModifyMovementSpeedMultiplier
GameInteractionEffectQueryResult ModifyMovementSpeedMultiplier::CanBeApplied() { GameInteractionEffectQueryResult ModifyMovementSpeedMultiplier::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -393,7 +393,7 @@ void ModifyMovementSpeedMultiplier::_Remove() {
// MARK: - OneHitKO // MARK: - OneHitKO
GameInteractionEffectQueryResult OneHitKO::CanBeApplied() { GameInteractionEffectQueryResult OneHitKO::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -408,7 +408,7 @@ void OneHitKO::_Remove() {
// MARK: - ModifyDefenseModifier // MARK: - ModifyDefenseModifier
GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() { GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -423,7 +423,7 @@ void ModifyDefenseModifier::_Remove() {
// MARK: - GiveOrTakeShield // MARK: - GiveOrTakeShield
GameInteractionEffectQueryResult GiveOrTakeShield::CanBeApplied() { GameInteractionEffectQueryResult GiveOrTakeShield::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if ((parameters[0] > 0 && ((gBitFlags[parameters[0] - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_TYPE_SHIELD]) & } else if ((parameters[0] > 0 && ((gBitFlags[parameters[0] - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_TYPE_SHIELD]) &
gSaveContext.inventory.equipment)) || gSaveContext.inventory.equipment)) ||
@ -441,7 +441,7 @@ void GiveOrTakeShield::_Apply() {
// MARK: - TeleportPlayer // MARK: - TeleportPlayer
GameInteractionEffectQueryResult TeleportPlayer::CanBeApplied() { GameInteractionEffectQueryResult TeleportPlayer::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -453,7 +453,7 @@ void TeleportPlayer::_Apply() {
// MARK: - ClearAssignedButtons // MARK: - ClearAssignedButtons
GameInteractionEffectQueryResult ClearAssignedButtons::CanBeApplied() { GameInteractionEffectQueryResult ClearAssignedButtons::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -465,7 +465,7 @@ void ClearAssignedButtons::_Apply() {
// MARK: - SetTimeOfDay // MARK: - SetTimeOfDay
GameInteractionEffectQueryResult SetTimeOfDay::CanBeApplied() { GameInteractionEffectQueryResult SetTimeOfDay::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -477,7 +477,7 @@ void SetTimeOfDay::_Apply() {
// MARK: - SetCollisionViewer // MARK: - SetCollisionViewer
GameInteractionEffectQueryResult SetCollisionViewer::CanBeApplied() { GameInteractionEffectQueryResult SetCollisionViewer::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -492,7 +492,7 @@ void SetCollisionViewer::_Remove() {
// MARK: - RandomizeCosmetics // MARK: - RandomizeCosmetics
GameInteractionEffectQueryResult RandomizeCosmetics::CanBeApplied() { GameInteractionEffectQueryResult RandomizeCosmetics::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -504,7 +504,7 @@ void RandomizeCosmetics::_Apply() {
// MARK: - PressButton // MARK: - PressButton
GameInteractionEffectQueryResult PressButton::CanBeApplied() { GameInteractionEffectQueryResult PressButton::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -516,7 +516,7 @@ void PressButton::_Apply() {
// MARK: - PressRandomButton // MARK: - PressRandomButton
GameInteractionEffectQueryResult PressRandomButton::CanBeApplied() { GameInteractionEffectQueryResult PressRandomButton::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -528,7 +528,7 @@ void PressRandomButton::_Apply() {
// MARK: - AddOrTakeAmmo // MARK: - AddOrTakeAmmo
GameInteractionEffectQueryResult AddOrTakeAmmo::CanBeApplied() { GameInteractionEffectQueryResult AddOrTakeAmmo::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if (!GameInteractor::CanAddOrTakeAmmo(parameters[0], parameters[1])) { } else if (!GameInteractor::CanAddOrTakeAmmo(parameters[0], parameters[1])) {
return GameInteractionEffectQueryResult::NotPossible; return GameInteractionEffectQueryResult::NotPossible;
@ -542,7 +542,7 @@ void AddOrTakeAmmo::_Apply() {
// MARK: - RandomBombFuseTimer // MARK: - RandomBombFuseTimer
GameInteractionEffectQueryResult RandomBombFuseTimer::CanBeApplied() { GameInteractionEffectQueryResult RandomBombFuseTimer::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -557,7 +557,7 @@ void RandomBombFuseTimer::_Remove() {
// MARK: - DisableLedgeGrabs // MARK: - DisableLedgeGrabs
GameInteractionEffectQueryResult DisableLedgeGrabs::CanBeApplied() { GameInteractionEffectQueryResult DisableLedgeGrabs::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -572,7 +572,7 @@ void DisableLedgeGrabs::_Remove() {
// MARK: - RandomWind // MARK: - RandomWind
GameInteractionEffectQueryResult RandomWind::CanBeApplied() { GameInteractionEffectQueryResult RandomWind::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -587,7 +587,7 @@ void RandomWind::_Remove() {
// MARK: - RandomBonks // MARK: - RandomBonks
GameInteractionEffectQueryResult RandomBonks::CanBeApplied() { GameInteractionEffectQueryResult RandomBonks::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -602,7 +602,7 @@ void RandomBonks::_Remove() {
// MARK: - PlayerInvincibility // MARK: - PlayerInvincibility
GameInteractionEffectQueryResult PlayerInvincibility::CanBeApplied() { GameInteractionEffectQueryResult PlayerInvincibility::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -617,7 +617,7 @@ void PlayerInvincibility::_Remove() {
// MARK: - SlipperyFloor // MARK: - SlipperyFloor
GameInteractionEffectQueryResult SlipperyFloor::CanBeApplied() { GameInteractionEffectQueryResult SlipperyFloor::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
@ -632,7 +632,7 @@ void SlipperyFloor::_Remove() {
// MARK: - SpawnEnemyWithOffset // MARK: - SpawnEnemyWithOffset
GameInteractionEffectQueryResult SpawnEnemyWithOffset::CanBeApplied() { GameInteractionEffectQueryResult SpawnEnemyWithOffset::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }
return GameInteractor::RawAction::SpawnEnemyWithOffset(parameters[0], parameters[1]); return GameInteractor::RawAction::SpawnEnemyWithOffset(parameters[0], parameters[1]);
@ -644,7 +644,7 @@ void SpawnEnemyWithOffset::_Apply() {
// MARK: - SpawnActor // MARK: - SpawnActor
GameInteractionEffectQueryResult SpawnActor::CanBeApplied() { GameInteractionEffectQueryResult SpawnActor::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded(true)) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }
return GameInteractor::RawAction::SpawnActor(parameters[0], parameters[1]); return GameInteractor::RawAction::SpawnActor(parameters[0], parameters[1]);

View file

@ -2225,6 +2225,20 @@ typedef enum {
// #### `result` // #### `result`
// ```c // ```c
// DoorWarp1_PlayerInRange(this, play)
// ```
// #### `args`
// - `*DoorWarp1`
VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE,
// #### `result`
// ```c
// (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum
// <= 2)
// ```
// #### `args`
// - `*PlayState`
VB_SHOW_GAMEPLAY_TIMER,
// (this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open && // (this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open &&
// this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100 // this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100
// ``` // ```

View file

@ -51,9 +51,48 @@ typedef enum {
/* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c /* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c
/* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c /* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c
/* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c /* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c
/* 0xAA */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c /* 0xAB */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c
/* 0xAB */ TIMESTAMP_MAX /* 0xAC */ TIMESTAMP_FOUND_GOHMA_SOUL,
/* 0xAD */ TIMESTAMP_FOUND_KING_DODONGO_SOUL,
/* 0xAE */ TIMESTAMP_FOUND_BARINADE_SOUL,
/* 0xAF */ TIMESTAMP_FOUND_PHANTOM_GANON_SOUL,
/* 0xB0 */ TIMESTAMP_FOUND_VOLVAGIA_SOUL,
/* 0xB1 */ TIMESTAMP_FOUND_MORPHA_SOUL,
/* 0xB2 */ TIMESTAMP_FOUND_BONGO_BONGO_SOUL,
/* 0xB3 */ TIMESTAMP_FOUND_TWINROVA_SOUL,
/* 0xB5 */ TIMESTAMP_FOUND_GANON_SOUL,
/* 0xB6 */ TIMESTAMP_FOUND_BRONZE_SCALE,
/* 0xB7 */ TIMESTAMP_FOUND_OCARINA_A_BUTTON,
/* 0xB8 */ TIMESTAMP_FOUND_OCARINA_C_UP_BUTTON,
/* 0xB9 */ TIMESTAMP_FOUND_OCARINA_C_DOWN_BUTTON,
/* 0xBA */ TIMESTAMP_FOUND_OCARINA_C_LEFT_BUTTON,
/* 0xBB */ TIMESTAMP_FOUND_OCARINA_C_RIGHT_BUTTON,
/* 0xBC */ TIMESTAMP_FOUND_FISHING_POLE,
/* 0xBD */ TIMESTAMP_FOUND_GUARD_HOUSE_KEY,
/* 0xBE */ TIMESTAMP_FOUND_MARKET_BAZAAR_KEY,
/* 0xBF */ TIMESTAMP_FOUND_MARKET_POTION_SHOP_KEY,
/* 0xC0 */ TIMESTAMP_FOUND_MASK_SHOP_KEY,
/* 0xC1 */ TIMESTAMP_FOUND_MARKET_SHOOTING_GALLERY_KEY,
/* 0xC2 */ TIMESTAMP_FOUND_BOMBCHU_BOWLING_KEY,
/* 0xC3 */ TIMESTAMP_FOUND_TREASURE_CHEST_GAME_BUILDING_KEY,
/* 0xC4 */ TIMESTAMP_FOUND_BOMBCHU_SHOP_KEY,
/* 0xC5 */ TIMESTAMP_FOUND_RICHARDS_HOUSE_KEY,
/* 0xC6 */ TIMESTAMP_FOUND_ALLEY_HOUSE_KEY,
/* 0xC7 */ TIMESTAMP_FOUND_KAK_BAZAAR_KEY,
/* 0xC8 */ TIMESTAMP_FOUND_KAK_POTION_SHOP_KEY,
/* 0xC9 */ TIMESTAMP_FOUND_BOSS_HOUSE_KEY,
/* 0xCA */ TIMESTAMP_FOUND_GRANNYS_POTION_SHOP_KEY,
/* 0xCB */ TIMESTAMP_FOUND_SKULLTULA_HOUSE_KEY,
/* 0xCC */ TIMESTAMP_FOUND_IMPAS_HOUSE_KEY,
/* 0xCD */ TIMESTAMP_FOUND_WINDMILL_KEY,
/* 0xCE */ TIMESTAMP_FOUND_KAK_SHOOTING_GALLERY_KEY,
/* 0xCF */ TIMESTAMP_FOUND_DAMPES_HUT_KEY,
/* 0xD0 */ TIMESTAMP_FOUND_TALONS_HOUSE_KEY,
/* 0xD1 */ TIMESTAMP_FOUND_STABLES_KEY,
/* 0xD2 */ TIMESTAMP_FOUND_BACK_TOWER_KEY,
/* 0xD3 */ TIMESTAMP_FOUND_HYLIA_LAB_KEY,
/* 0xD4 */ TIMESTAMP_FOUND_FISHING_HOLE_KEY,
/* 0xD5 */ TIMESTAMP_MAX
} GameplayStatTimestamp; } GameplayStatTimestamp;
typedef enum { typedef enum {

View file

@ -6,7 +6,6 @@
#include "soh/SaveManager.h" #include "soh/SaveManager.h"
#include "soh/ResourceManagerHelpers.h" #include "soh/ResourceManagerHelpers.h"
#include "soh/resource/type/Skeleton.h" #include "soh/resource/type/Skeleton.h"
#include "soh/Enhancements/boss-rush/BossRushTypes.h"
#include "soh/Enhancements/boss-rush/BossRush.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"
@ -49,7 +48,6 @@ extern "C" {
extern SaveContext gSaveContext; extern SaveContext gSaveContext;
extern PlayState* gPlayState; extern PlayState* gPlayState;
extern void Overlay_DisplayText(float duration, const char* text);
} }
// GreyScaleEndDlist // GreyScaleEndDlist
@ -131,10 +129,27 @@ void RegisterOcarinaTimeTravel() {
bool notNearAnySource = !nearbyTimeBlockEmpty && !nearbyTimeBlock && !nearbyOcarinaSpot && !nearbyDoorOfTime && bool notNearAnySource = !nearbyTimeBlockEmpty && !nearbyTimeBlock && !nearbyOcarinaSpot && !nearbyDoorOfTime &&
!nearbyFrogs && !nearbyGossipStone; !nearbyFrogs && !nearbyGossipStone;
bool hasOcarinaOfTime = (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME); bool hasOcarinaOfTime = (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME);
bool doesntNeedOcarinaOfTime = CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0) == 2;
bool hasMasterSword = CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER); bool hasMasterSword = CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER);
// TODO: Once Swordless Adult is fixed: Remove the Master Sword check int timeTravelSetting = CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0);
if (justPlayedSoT && notNearAnySource && (hasOcarinaOfTime || doesntNeedOcarinaOfTime) && hasMasterSword) { bool meetsTimeTravelRequirements = false;
switch (timeTravelSetting) {
case TIME_TRAVEL_ANY:
meetsTimeTravelRequirements = true;
break;
case TIME_TRAVEL_ANY_MS:
meetsTimeTravelRequirements = hasMasterSword;
break;
case TIME_TRAVEL_OOT_MS:
meetsTimeTravelRequirements = hasMasterSword && hasOcarinaOfTime;
break;
case TIME_TRAVEL_OOT:
default:
meetsTimeTravelRequirements = hasOcarinaOfTime;
break;
}
if (justPlayedSoT && notNearAnySource && meetsTimeTravelRequirements) {
SwitchAge(); SwitchAge();
} }
}); });
@ -283,49 +298,6 @@ void UpdateHyperEnemiesState() {
} }
} }
void RegisterBonkDamage() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerBonk>([]() {
uint8_t bonkOption = CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE);
if (bonkOption == BONK_DAMAGE_NONE) {
return;
}
if (bonkOption == BONK_DAMAGE_OHKO) {
gSaveContext.health = 0;
return;
}
uint16_t bonkDamage = 0;
switch (bonkOption) {
case BONK_DAMAGE_QUARTER_HEART:
bonkDamage = 4;
break;
case BONK_DAMAGE_HALF_HEART:
bonkDamage = 8;
break;
case BONK_DAMAGE_1_HEART:
bonkDamage = 16;
break;
case BONK_DAMAGE_2_HEARTS:
bonkDamage = 32;
break;
case BONK_DAMAGE_4_HEARTS:
bonkDamage = 64;
break;
case BONK_DAMAGE_8_HEARTS:
bonkDamage = 128;
break;
default:
break;
}
Health_ChangeBy(gPlayState, -bonkDamage);
// Set invincibility to make Link flash red as a visual damage indicator.
Player* player = GET_PLAYER(gPlayState);
player->invincibilityTimer = 28;
});
}
void UpdateDirtPathFixState(int32_t sceneNum) { void UpdateDirtPathFixState(int32_t sceneNum) {
switch (sceneNum) { switch (sceneNum) {
case SCENE_HYRULE_FIELD: case SCENE_HYRULE_FIELD:
@ -957,7 +929,6 @@ void RegisterCustomSkeletons() {
} }
void InitMods() { void InitMods() {
BossRush_RegisterHooks();
RandomizerRegisterHooks(); RandomizerRegisterHooks();
TimeSaverRegisterHooks(); TimeSaverRegisterHooks();
RegisterTTS(); RegisterTTS();
@ -966,7 +937,6 @@ void InitMods() {
RegisterDeleteFileOnDeath(); RegisterDeleteFileOnDeath();
RegisterHyperBosses(); RegisterHyperBosses();
UpdateHyperEnemiesState(); UpdateHyperEnemiesState();
RegisterBonkDamage();
RegisterMenuPathFix(); RegisterMenuPathFix();
RegisterMirrorModeHandler(); RegisterMirrorModeHandler();
RegisterResetNaviTimer(); RegisterResetNaviTimer();

View file

@ -336,7 +336,7 @@ void Rando::StaticData::RegisterCrateLocations() {
locationTable[RC_GF_SOUTHMOST_CENTER_CRATE] = Location::Crate(RC_GF_SOUTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1534), "Southmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_SOUTHMOST_CENTER_CRATE)); locationTable[RC_GF_SOUTHMOST_CENTER_CRATE] = Location::Crate(RC_GF_SOUTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1534), "Southmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_SOUTHMOST_CENTER_CRATE));
locationTable[RC_GF_MID_SOUTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_SOUTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1594), "Middle South Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_SOUTH_CENTER_CRATE)); locationTable[RC_GF_MID_SOUTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_SOUTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1594), "Middle South Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_SOUTH_CENTER_CRATE));
locationTable[RC_GF_MID_NORTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_NORTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1782), "Middle North Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_NORTH_CENTER_CRATE)); locationTable[RC_GF_MID_NORTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_NORTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1782), "Middle North Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_NORTH_CENTER_CRATE));
locationTable[RR_GF_NORTHMOST_CENTER_CRATE] = Location::Crate(RR_GF_NORTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1842), "Northmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_NORTHMOST_CENTER_CRATE)); locationTable[RC_GF_NORTHMOST_CENTER_CRATE] = Location::Crate(RC_GF_NORTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1842), "Northmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_NORTHMOST_CENTER_CRATE));
locationTable[RC_GF_OUTSKIRTS_NE_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NE_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-60, -2210), "Outskirts Northeast Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NE_CRATE)); locationTable[RC_GF_OUTSKIRTS_NE_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NE_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-60, -2210), "Outskirts Northeast Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NE_CRATE));
locationTable[RC_GF_OUTSKIRTS_NW_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NW_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-120, -2210), "Outskirts Northwest Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NW_CRATE)); locationTable[RC_GF_OUTSKIRTS_NW_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NW_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-120, -2210), "Outskirts Northwest Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NW_CRATE));
locationTable[RC_GF_HBA_RANGE_CRATE_2] = Location::Crate(RC_GF_HBA_RANGE_CRATE_2, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(4090, -1780), "Horseback Archery Range Crate 2", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_HBA_RANGE_CRATE_2)); locationTable[RC_GF_HBA_RANGE_CRATE_2] = Location::Crate(RC_GF_HBA_RANGE_CRATE_2, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(4090, -1780), "Horseback Archery Range Crate 2", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_HBA_RANGE_CRATE_2));

View file

@ -30,7 +30,7 @@ void RegionTable_Init_GerudoFortress() {
LOCATION(RC_GF_SOUTHMOST_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_SOUTHMOST_CENTER_CRATE, logic->CanBreakCrates()),
LOCATION(RC_GF_MID_SOUTH_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_MID_SOUTH_CENTER_CRATE, logic->CanBreakCrates()),
LOCATION(RC_GF_MID_NORTH_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_MID_NORTH_CENTER_CRATE, logic->CanBreakCrates()),
LOCATION(RR_GF_NORTHMOST_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_NORTHMOST_CENTER_CRATE, logic->CanBreakCrates()),
}, { }, {
//Exits //Exits
Entrance(RR_TH_1_TORCH_CELL, []{return true;}), Entrance(RR_TH_1_TORCH_CELL, []{return true;}),

View file

@ -2332,7 +2332,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
RAND_INF_GF_MID_NORTH_CENTER_CRATE, RAND_INF_GF_MID_NORTH_CENTER_CRATE,
}, },
{ {
RR_GF_NORTHMOST_CENTER_CRATE, RC_GF_NORTHMOST_CENTER_CRATE,
RAND_INF_GF_NORTHMOST_CENTER_CRATE, RAND_INF_GF_NORTHMOST_CENTER_CRATE,
}, },
{ {
@ -5783,6 +5783,53 @@ void RandomizerSettingsWindow::InitElement() {
mSettings->UpdateOptionProperties(); mSettings->UpdateOptionProperties();
} }
static std::unordered_map<RandomizerGet, GameplayStatTimestamp> randomizerGetToStatsTimeStamp = {
{ RG_GOHMA_SOUL, TIMESTAMP_FOUND_GOHMA_SOUL },
{ RG_KING_DODONGO_SOUL, TIMESTAMP_FOUND_KING_DODONGO_SOUL },
{ RG_BARINADE_SOUL, TIMESTAMP_FOUND_BARINADE_SOUL },
{ RG_PHANTOM_GANON_SOUL, TIMESTAMP_FOUND_PHANTOM_GANON_SOUL },
{ RG_VOLVAGIA_SOUL, TIMESTAMP_FOUND_VOLVAGIA_SOUL },
{ RG_MORPHA_SOUL, TIMESTAMP_FOUND_MORPHA_SOUL },
{ RG_BONGO_BONGO_SOUL, TIMESTAMP_FOUND_BONGO_BONGO_SOUL },
{ RG_TWINROVA_SOUL, TIMESTAMP_FOUND_TWINROVA_SOUL },
{ RG_GANON_SOUL, TIMESTAMP_FOUND_GANON_SOUL },
{ RG_BRONZE_SCALE, TIMESTAMP_FOUND_BRONZE_SCALE },
{ RG_OCARINA_A_BUTTON, TIMESTAMP_FOUND_OCARINA_A_BUTTON },
{ RG_OCARINA_C_UP_BUTTON, TIMESTAMP_FOUND_OCARINA_C_UP_BUTTON },
{ RG_OCARINA_C_DOWN_BUTTON, TIMESTAMP_FOUND_OCARINA_C_DOWN_BUTTON },
{ RG_OCARINA_C_LEFT_BUTTON, TIMESTAMP_FOUND_OCARINA_C_LEFT_BUTTON },
{ RG_OCARINA_C_RIGHT_BUTTON, TIMESTAMP_FOUND_OCARINA_C_RIGHT_BUTTON },
{ RG_FISHING_POLE, TIMESTAMP_FOUND_FISHING_POLE },
{ RG_GUARD_HOUSE_KEY, TIMESTAMP_FOUND_GUARD_HOUSE_KEY },
{ RG_MARKET_BAZAAR_KEY, TIMESTAMP_FOUND_MARKET_BAZAAR_KEY },
{ RG_MARKET_POTION_SHOP_KEY, TIMESTAMP_FOUND_MARKET_POTION_SHOP_KEY },
{ RG_MASK_SHOP_KEY, TIMESTAMP_FOUND_MASK_SHOP_KEY },
{ RG_MARKET_SHOOTING_GALLERY_KEY, TIMESTAMP_FOUND_MARKET_SHOOTING_GALLERY_KEY },
{ RG_BOMBCHU_BOWLING_KEY, TIMESTAMP_FOUND_BOMBCHU_BOWLING_KEY },
{ RG_TREASURE_CHEST_GAME_BUILDING_KEY, TIMESTAMP_FOUND_TREASURE_CHEST_GAME_BUILDING_KEY },
{ RG_BOMBCHU_SHOP_KEY, TIMESTAMP_FOUND_BOMBCHU_SHOP_KEY },
{ RG_RICHARDS_HOUSE_KEY, TIMESTAMP_FOUND_RICHARDS_HOUSE_KEY },
{ RG_ALLEY_HOUSE_KEY, TIMESTAMP_FOUND_ALLEY_HOUSE_KEY },
{ RG_KAK_BAZAAR_KEY, TIMESTAMP_FOUND_KAK_BAZAAR_KEY },
{ RG_KAK_POTION_SHOP_KEY, TIMESTAMP_FOUND_KAK_POTION_SHOP_KEY },
{ RG_BOSS_HOUSE_KEY, TIMESTAMP_FOUND_BOSS_HOUSE_KEY },
{ RG_GRANNYS_POTION_SHOP_KEY, TIMESTAMP_FOUND_GRANNYS_POTION_SHOP_KEY },
{ RG_SKULLTULA_HOUSE_KEY, TIMESTAMP_FOUND_SKULLTULA_HOUSE_KEY },
{ RG_IMPAS_HOUSE_KEY, TIMESTAMP_FOUND_IMPAS_HOUSE_KEY },
{ RG_WINDMILL_KEY, TIMESTAMP_FOUND_WINDMILL_KEY },
{ RG_KAK_SHOOTING_GALLERY_KEY, TIMESTAMP_FOUND_KAK_SHOOTING_GALLERY_KEY },
{ RG_DAMPES_HUT_KEY, TIMESTAMP_FOUND_DAMPES_HUT_KEY },
{ RG_TALONS_HOUSE_KEY, TIMESTAMP_FOUND_TALONS_HOUSE_KEY },
{ RG_STABLES_KEY, TIMESTAMP_FOUND_STABLES_KEY },
{ RG_BACK_TOWER_KEY, TIMESTAMP_FOUND_BACK_TOWER_KEY },
{ RG_HYLIA_LAB_KEY, TIMESTAMP_FOUND_HYLIA_LAB_KEY },
{ RG_FISHING_HOLE_KEY, TIMESTAMP_FOUND_FISHING_HOLE_KEY },
};
// Gameplay stat tracking: Update time the item was acquired // Gameplay stat tracking: Update time the item was acquired
// (special cases for rando items) // (special cases for rando items)
void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
@ -5797,6 +5844,12 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
// Use ITEM_KEY_BOSS to timestamp Ganon's boss key // Use ITEM_KEY_BOSS to timestamp Ganon's boss key
if (item == RG_GANONS_CASTLE_BOSS_KEY) { if (item == RG_GANONS_CASTLE_BOSS_KEY) {
gSaveContext.ship.stats.itemTimestamp[ITEM_KEY_BOSS] = time; gSaveContext.ship.stats.itemTimestamp[ITEM_KEY_BOSS] = time;
return;
}
if (randomizerGetToStatsTimeStamp.contains((RandomizerGet)item)) {
gSaveContext.ship.stats.itemTimestamp[randomizerGetToStatsTimeStamp[(RandomizerGet)item]] = time;
return;
} }
// Count any bottled item as a bottle // Count any bottled item as a bottle
@ -5806,6 +5859,7 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
} }
return; return;
} }
// Count any bombchu pack as bombchus // Count any bombchu pack as bombchus
if ((item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHUS) { if ((item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHUS) {
if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = 0) { if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = 0) {
@ -5813,11 +5867,15 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
} }
return; return;
} }
if (item == RG_MAGIC_SINGLE) { if (item == RG_MAGIC_SINGLE) {
gSaveContext.ship.stats.itemTimestamp[ITEM_SINGLE_MAGIC] = time; gSaveContext.ship.stats.itemTimestamp[ITEM_SINGLE_MAGIC] = time;
return;
} }
if (item == RG_DOUBLE_DEFENSE) { if (item == RG_DOUBLE_DEFENSE) {
gSaveContext.ship.stats.itemTimestamp[ITEM_DOUBLE_DEFENSE] = time; gSaveContext.ship.stats.itemTimestamp[ITEM_DOUBLE_DEFENSE] = time;
return;
} }
} }

View file

@ -2396,7 +2396,7 @@ typedef enum {
RC_GF_SOUTHMOST_CENTER_CRATE, RC_GF_SOUTHMOST_CENTER_CRATE,
RC_GF_MID_SOUTH_CENTER_CRATE, RC_GF_MID_SOUTH_CENTER_CRATE,
RC_GF_MID_NORTH_CENTER_CRATE, RC_GF_MID_NORTH_CENTER_CRATE,
RR_GF_NORTHMOST_CENTER_CRATE, RC_GF_NORTHMOST_CENTER_CRATE,
RC_GF_OUTSKIRTS_NE_CRATE, RC_GF_OUTSKIRTS_NE_CRATE,
RC_GF_OUTSKIRTS_NW_CRATE, RC_GF_OUTSKIRTS_NW_CRATE,
RC_GF_HBA_RANGE_CRATE_1, RC_GF_HBA_RANGE_CRATE_1,

View file

@ -449,7 +449,8 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
result.currentCapacity = result.currentCapacity =
IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) ? 0 : CUR_CAPACITY(UPG_WALLET); IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) ? 0 : CUR_CAPACITY(UPG_WALLET);
result.maxCapacity = result.maxCapacity =
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INCLUDE_TYCOON_WALLET) ? 999 : 500; IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INCLUDE_TYCOON_WALLET) ? 999
: 500;
result.currentAmmo = gSaveContext.rupees; result.currentAmmo = gSaveContext.rupees;
break; break;
case ITEM_BOMBCHU: case ITEM_BOMBCHU:
@ -502,7 +503,28 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
result.maxCapacity = GERUDO_TRAINING_GROUND_SMALL_KEY_MAX; result.maxCapacity = GERUDO_TRAINING_GROUND_SMALL_KEY_MAX;
break; break;
case SCENE_THIEVES_HIDEOUT: case SCENE_THIEVES_HIDEOUT:
result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX; if (IS_RANDO) {
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS)) {
case RO_GF_CARPENTERS_NORMAL:
result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX;
break;
case RO_GF_CARPENTERS_FAST:
result.maxCapacity = 1;
break;
case RO_GF_CARPENTERS_FREE:
result.maxCapacity = 0;
break;
default:
result.maxCapacity = 0;
SPDLOG_ERROR(
"Invalid value for RSK_GERUDO_FORTRESS: " +
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS));
assert(false);
break;
}
} else {
result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX;
}
break; break;
case SCENE_INSIDE_GANONS_CASTLE: case SCENE_INSIDE_GANONS_CASTLE:
result.maxCapacity = GANONS_CASTLE_SMALL_KEY_MAX; result.maxCapacity = GANONS_CASTLE_SMALL_KEY_MAX;

View file

@ -2600,15 +2600,6 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
return false; return false;
} }
extern "C" void Overlay_DisplayText(float duration, const char* text) {
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(duration, true, text);
}
extern "C" void Overlay_DisplayText_Seconds(int seconds, const char* text) {
float duration = seconds * OTRGlobals::Instance->GetInterpolationFPS() * 0.05;
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(duration, true, text);
}
extern "C" void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex) { extern "C" void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex) {
SetCurrentGrottoIDForTracker(entranceIndex); SetCurrentGrottoIDForTracker(entranceIndex);
} }

View file

@ -156,8 +156,6 @@ void Randomizer_SetSpoilerLoaded(bool spoilerLoaded);
uint8_t Randomizer_GenerateRandomizer(); uint8_t Randomizer_GenerateRandomizer();
void Randomizer_ShowRandomizerMenu(); void Randomizer_ShowRandomizerMenu();
int CustomMessage_RetrieveIfExists(PlayState* play); int CustomMessage_RetrieveIfExists(PlayState* play);
void Overlay_DisplayText(float duration, const char* text);
void Overlay_DisplayText_Seconds(int seconds, const char* text);
GetItemEntry ItemTable_Retrieve(int16_t getItemID); GetItemEntry ItemTable_Retrieve(int16_t getItemID);
GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID); GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID);
void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex); void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex);

View file

@ -56,7 +56,9 @@ static const std::unordered_map<int32_t, const char*> chestStyleMatchesContentsO
static const std::unordered_map<int32_t, const char*> timeTravelOptions = { static const std::unordered_map<int32_t, const char*> timeTravelOptions = {
{ TIME_TRAVEL_DISABLED, "Disabled" }, { TIME_TRAVEL_DISABLED, "Disabled" },
{ TIME_TRAVEL_OOT, "Ocarina of Time" }, { TIME_TRAVEL_OOT, "Ocarina of Time" },
{ TIME_TRAVEL_OOT_MS, "Ocarina of Time + Master Sword" },
{ TIME_TRAVEL_ANY, "Any Ocarina" }, { TIME_TRAVEL_ANY, "Any Ocarina" },
{ TIME_TRAVEL_ANY_MS, "Any Ocarina + Master Sword" },
}; };
static const std::unordered_map<int32_t, const char*> sleepingWaterfallOptions = { static const std::unordered_map<int32_t, const char*> sleepingWaterfallOptions = {
@ -783,9 +785,9 @@ void SohMenu::AddMenuEnhancements() {
.Tooltip("Allows Link to freely change age by playing the Song of Time.\n" .Tooltip("Allows Link to freely change age by playing the Song of Time.\n"
"Time Blocks can still be used properly.\n\n" "Time Blocks can still be used properly.\n\n"
"Requirements:\n" "Requirements:\n"
" - Obtained the Ocarina of Time (depends on selection)\n"
" - Obtained the Song of Time\n" " - Obtained the Song of Time\n"
" - Obtained the Master Sword\n" " - Obtained the Ocarina of Time (depends on selection)\n"
" - Obtained the Master Sword (depends on selection)\n"
" - Not within range of a Time Block\n" " - Not within range of a Time Block\n"
" - Not within range of Ocarina Playing spots")); " - Not within range of Ocarina Playing spots"));

View file

@ -640,10 +640,8 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) {
if (gPlayState != NULL) { if (gPlayState != NULL) {
previousSceneNum = gPlayState->sceneNum; previousSceneNum = gPlayState->sceneNum;
} }
const char* sequenceName = AudioCollection_GetSequenceName(seqId);
if (sequenceName != NULL) { AudioCollection_EmitSongNameNotification(seqId);
Overlay_DisplayText_Seconds(CVarGetInteger(CVAR_AUDIO("SeqNameOverlayDuration"), 5), sequenceName);
}
} }
} }

View file

@ -8,7 +8,6 @@
#include "libultraship/bridge.h" #include "libultraship/bridge.h"
#include "soh/Enhancements/gameplaystats.h" #include "soh/Enhancements/gameplaystats.h"
#include "soh/Enhancements/boss-rush/BossRushTypes.h"
#include "soh/Enhancements/custom-message/CustomMessageInterfaceAddon.h" #include "soh/Enhancements/custom-message/CustomMessageInterfaceAddon.h"
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
#include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/enhancementTypes.h"
@ -6374,11 +6373,10 @@ void Interface_Draw(PlayState* play) {
void Interface_DrawTotalGameplayTimer(PlayState* play) { void Interface_DrawTotalGameplayTimer(PlayState* play) {
// Draw timer based on the Gameplay Stats total time. // Draw timer based on the Gameplay Stats total time.
if (GameInteractor_Should(VB_SHOW_GAMEPLAY_TIMER,
if ((IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES) || CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 &&
(CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2,
gSaveContext.fileNum <= 2)) { play)) {
s32 X_Margins_Timer = 0; s32 X_Margins_Timer = 0;
if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.UseMargins"), 0) != 0) { if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.UseMargins"), 0) != 0) {
if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.PosType"), 0) == ORIGINAL_LOCATION) { if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.PosType"), 0) == ORIGINAL_LOCATION) {

View file

@ -1,7 +1,6 @@
#include "z_door_warp1.h" #include "z_door_warp1.h"
#include "objects/object_warp1/object_warp1.h" #include "objects/object_warp1/object_warp1.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/OTRGlobals.h" #include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
@ -693,12 +692,7 @@ void DoorWarp1_AdultWarpIdle(DoorWarp1* this, PlayState* play) {
Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG);
if (DoorWarp1_PlayerInRange(this, play)) { if (GameInteractor_Should(VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE, DoorWarp1_PlayerInRange(this, play), this)) {
// Heal player in Boss Rush
if (IS_BOSS_RUSH) {
BossRush_HandleBlueWarpHeal(play);
}
player = GET_PLAYER(play); player = GET_PLAYER(play);
OnePointCutscene_Init(play, 0x25E8, 999, &this->actor, MAIN_CAM); OnePointCutscene_Init(play, 0x25E8, 999, &this->actor, MAIN_CAM);

View file

@ -1398,110 +1398,6 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
} }
} }
static s8 sLastBossRushOptionIndex = -1;
static s8 sLastBossRushOptionValue = -1;
void FileChoose_UpdateBossRushMenu(GameState* thisx) {
FileChoose_UpdateStickDirectionPromptAnim(thisx);
FileChooseContext* this = (FileChooseContext*)thisx;
Input* input = &this->state.input[0];
bool dpad = CVarGetInteger(CVAR_SETTING("DpadInText"), 0);
// Fade in elements after opening Boss Rush options menu
this->bossRushUIAlpha += 25;
if (this->bossRushUIAlpha > 255) {
this->bossRushUIAlpha = 255;
}
// Animate up/down arrows.
this->bossRushArrowOffset += 1;
if (this->bossRushArrowOffset >= 30) {
this->bossRushArrowOffset = 0;
}
// Move menu selection up or down.
if (ABS(this->stickRelY) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN | BTN_DUP))) {
// Move down
if (this->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) {
// When selecting past the last option, cycle back to the first option.
if ((this->bossRushIndex + 1) > BR_OPTIONS_MAX - 1) {
this->bossRushIndex = 0;
this->bossRushOffset = 0;
} else {
this->bossRushIndex++;
// When last visible option is selected when moving down, offset the list down by one.
if (this->bossRushIndex - this->bossRushOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN - 1) {
this->bossRushOffset++;
}
}
} else if (this->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) {
// When selecting past the first option, cycle back to the last option and offset the list to view it
// properly.
if ((this->bossRushIndex - 1) < 0) {
this->bossRushIndex = BR_OPTIONS_MAX - 1;
this->bossRushOffset = this->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1;
} else {
// When first visible option is selected when moving up, offset the list up by one.
if (this->bossRushIndex - this->bossRushOffset == 0) {
this->bossRushOffset--;
}
this->bossRushIndex--;
}
}
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
}
// Cycle through choices for currently selected option.
if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
if (this->stickRelX > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DRIGHT))) {
// If exceeding the amount of choices for the selected option, cycle back to the first.
if ((gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] + 1) ==
BossRush_GetSettingOptionsAmount(this->bossRushIndex)) {
gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] = 0;
} else {
gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]++;
}
} else if (this->stickRelX < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT))) {
// If cycling back when already at the first choice for the selected option, cycle back to the last choice.
if ((gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] - 1) < 0) {
gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] =
BossRush_GetSettingOptionsAmount(this->bossRushIndex) - 1;
} else {
gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]--;
}
}
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
}
if (sLastBossRushOptionIndex != this->bossRushIndex ||
sLastBossRushOptionValue != gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]) {
GameInteractor_ExecuteOnUpdateFileBossRushOptionSelection(
this->bossRushIndex, gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]);
sLastBossRushOptionIndex = this->bossRushIndex;
sLastBossRushOptionValue = gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex];
}
if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
this->configMode = CM_BOSS_RUSH_TO_QUEST;
return;
}
// Load into the game.
if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
this->buttonIndex = 0xFE;
this->menuMode = FS_MENU_MODE_SELECT;
this->selectMode = SM_FADE_OUT;
this->prevConfigMode = this->configMode;
return;
}
}
void FileChoose_UpdateRandomizerMenu(GameState* thisx) { void FileChoose_UpdateRandomizerMenu(GameState* thisx) {
FileChoose_UpdateStickDirectionPromptAnim(thisx); FileChoose_UpdateStickDirectionPromptAnim(thisx);
FileChooseContext* this = (FileChooseContext*)thisx; FileChooseContext* this = (FileChooseContext*)thisx;
@ -2590,63 +2486,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
break; break;
} }
} else if (this->configMode == CM_BOSS_RUSH_MENU) { } else if (this->configMode == CM_BOSS_RUSH_MENU) {
uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language; FileChoose_DrawBossRushMenuWindowContents(this);
uint8_t listOffset = this->bossRushOffset;
uint8_t textAlpha = this->bossRushUIAlpha;
// Draw arrows to indicate that the list can scroll up or down.
// Arrow up
if (listOffset > 0) {
uint16_t arrowUpX = 140;
uint16_t arrowUpY = 76 - (this->bossRushArrowOffset / 10);
gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowUpTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
G_TX_NOLOD, G_TX_NOLOD);
gSPWideTextureRectangle(POLY_OPA_DISP++, arrowUpX << 2, arrowUpY << 2, (arrowUpX + 8) << 2,
(arrowUpY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
}
// Arrow down
if (BR_OPTIONS_MAX - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) {
uint16_t arrowDownX = 140;
uint16_t arrowDownY = 181 + (this->bossRushArrowOffset / 10);
gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
G_TX_NOLOD, G_TX_NOLOD);
gSPWideTextureRectangle(POLY_OPA_DISP++, arrowDownX << 2, arrowDownY << 2, (arrowDownX + 8) << 2,
(arrowDownY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
}
// Draw options. There's more options than what fits on the screen, so the visible options
// depend on the current offset of the list. Currently selected option pulses in
// color and has arrows surrounding the option.
for (uint8_t i = listOffset; i - listOffset < BOSSRUSH_MAX_OPTIONS_ON_SCREEN; i++) {
uint16_t textYOffset = (i - listOffset) * 16;
// Option name.
Interface_DrawTextLine(this->state.gfxCtx, BossRush_GetSettingName(i, language), 65, (87 + textYOffset),
255, 255, 80, textAlpha, 0.8f, true);
// Selected choice for option.
uint16_t finalKerning = Interface_DrawTextLine(
this->state.gfxCtx,
BossRush_GetSettingChoiceName(i, gSaveContext.ship.quest.data.bossRush.options[i], language), 165,
(87 + textYOffset), 255, 255, 255, textAlpha, 0.8f, true);
// Draw arrows around selected option.
if (this->bossRushIndex == i) {
Gfx_SetupDL_39Opa(this->state.gfxCtx);
gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOLOD);
FileChoose_DrawTextRec(this->state.gfxCtx, this->stickLeftPrompt.arrowColorR,
this->stickLeftPrompt.arrowColorG, this->stickLeftPrompt.arrowColorB, textAlpha,
160, (92 + textYOffset), 0.42f, 0, 0, -1.0f, 1.0f);
FileChoose_DrawTextRec(this->state.gfxCtx, this->stickRightPrompt.arrowColorR,
this->stickRightPrompt.arrowColorG, this->stickRightPrompt.arrowColorB,
textAlpha, (171 + finalKerning), (92 + textYOffset), 0.42f, 0, 0, 1.0f, 1.0f);
}
}
} else if (this->configMode == CM_RANDOMIZER_SETTINGS_MENU) { } else if (this->configMode == CM_RANDOMIZER_SETTINGS_MENU) {
uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language; uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language;
uint8_t textAlpha = this->randomizerUIAlpha; uint8_t textAlpha = this->randomizerUIAlpha;