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;
} StickDirectionPrompt;
typedef struct {
typedef struct FileChooseContext {
/* 0x00000 */ GameState state;
/* 0x000A4 */ Vtx* windowVtx;
/* 0x000A8 */ u8* staticSegment;

View file

@ -7,7 +7,7 @@
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/gameplaystats.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/boss-rush/BossRushTypes.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
typedef enum {
/* 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",
};
#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{
#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 "sfx.h"
#include "soh/cvar_prefixes.h"
#include "soh/Notification/Notification.h"
#include <vector>
#include <utils/StringHelper.h>
#include <libultraship/bridge.h>
@ -458,3 +459,11 @@ extern "C" bool AudioCollection_HasSequenceNum(uint16_t seqId) {
extern "C" size_t AudioCollection_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);
bool AudioCollection_HasSequenceNum(uint16_t seqId);
size_t AudioCollection_SequenceMapSize();
void AudioCollection_EmitSongNameNotification(s32 seqId);
#endif

View file

@ -2,6 +2,8 @@
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh_assets.h"
#include "soh/frame_interpolation.h"
#include <array>
#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_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 "src/overlays/gamestates/ovl_file_choose/file_choose.h"
#include "objects/gameplay_keep/gameplay_keep.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_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 {
std::array<std::string, LANGUAGE_MAX> name;
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());
}
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) {
// 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.
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;
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
case VB_BE_ABLE_TO_SAVE:
// 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);
}
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) {
@ -667,38 +908,32 @@ void BossRush_OnBlueWarpUpdate(void* actor) {
}
}
void BossRush_RegisterHooks() {
static u32 onVanillaBehaviorHook = 0;
static u32 onSceneInitHook = 0;
static u32 onActorInitHook = 0;
static u32 onBossDefeatHook = 0;
static u32 onActorUpdate = 0;
void RegisterBossRush() {
COND_HOOK(OnLoadGame, true, [](int32_t fileNum) {
COND_ID_HOOK(OnActorInit, ACTOR_DEMO_SA, IS_BOSS_RUSH, [](void* actorPtr) {
BossRush_SpawnBlueWarps(gPlayState);
Actor_Kill((Actor*)actorPtr);
GET_PLAYER(gPlayState)->actor.world.rot.y = 27306;
GET_PLAYER(gPlayState)->actor.shape.rot.y = 27306;
});
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);
// Remove bushes, used in Gohma's arena
COND_ID_HOOK(OnActorInit, ACTOR_EN_KUSA, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
onVanillaBehaviorHook = 0;
onSceneInitHook = 0;
onActorInitHook = 0;
onBossDefeatHook = 0;
onActorUpdate = 0;
// Remove pots, used in Barinade's and Ganondorf's arenas
COND_ID_HOOK(OnActorInit, ACTOR_OBJ_TSUBO, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
if (!IS_BOSS_RUSH)
return;
// Remove chests, mainly for the chest in King Dodongo's boss room
COND_ID_HOOK(OnActorInit, ACTOR_EN_BOX, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
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);
COND_HOOK(OnVanillaBehavior, IS_BOSS_RUSH, BossRush_OnVanillaBehaviorHandler);
COND_HOOK(OnSceneInit, IS_BOSS_RUSH, BossRush_OnSceneInitHandler);
COND_HOOK(OnBossDefeat, IS_BOSS_RUSH, BossRush_OnBossDefeatHandler);
COND_ID_HOOK(OnActorUpdate, ACTOR_DOOR_WARP1, IS_BOSS_RUSH, BossRush_OnBlueWarpUpdate);
});
}
static RegisterShipInitFunc initFunc(RegisterBossRush);

View file

@ -1,16 +1,41 @@
#pragma once
#include "z64.h"
#include <libultraship/libultra/types.h>
#ifdef __cplusplus
extern "C" {
#endif
void BossRush_HandleBlueWarpHeal(PlayState* play);
void BossRush_InitSave();
struct GameState;
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_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language);
u8 BossRush_GetSettingOptionsAmount(u8 optionIndex);
void BossRush_RegisterHooks();
#ifdef __cplusplus
};
#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 {
TIME_TRAVEL_DISABLED,
TIME_TRAVEL_OOT,
TIME_TRAVEL_OOT_MS,
TIME_TRAVEL_ANY,
TIME_TRAVEL_ANY_MS
} TimeTravelType;
typedef enum {

View file

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

View file

@ -2225,6 +2225,20 @@ typedef enum {
// #### `result`
// ```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->skelanime.curFrame > 45 && this->iceSmokeTimer < 100
// ```

View file

@ -51,9 +51,48 @@ typedef enum {
/* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c
/* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c
/* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c
/* 0xAA */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c
/* 0xAB */ TIMESTAMP_MAX
/* 0xAB */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c
/* 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;
typedef enum {

View file

@ -6,7 +6,6 @@
#include "soh/SaveManager.h"
#include "soh/ResourceManagerHelpers.h"
#include "soh/resource/type/Skeleton.h"
#include "soh/Enhancements/boss-rush/BossRushTypes.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/randomizer/3drando/random.hpp"
@ -49,7 +48,6 @@ extern "C" {
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
extern void Overlay_DisplayText(float duration, const char* text);
}
// GreyScaleEndDlist
@ -131,10 +129,27 @@ void RegisterOcarinaTimeTravel() {
bool notNearAnySource = !nearbyTimeBlockEmpty && !nearbyTimeBlock && !nearbyOcarinaSpot && !nearbyDoorOfTime &&
!nearbyFrogs && !nearbyGossipStone;
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);
// TODO: Once Swordless Adult is fixed: Remove the Master Sword check
if (justPlayedSoT && notNearAnySource && (hasOcarinaOfTime || doesntNeedOcarinaOfTime) && hasMasterSword) {
int timeTravelSetting = CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0);
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();
}
});
@ -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) {
switch (sceneNum) {
case SCENE_HYRULE_FIELD:
@ -957,7 +929,6 @@ void RegisterCustomSkeletons() {
}
void InitMods() {
BossRush_RegisterHooks();
RandomizerRegisterHooks();
TimeSaverRegisterHooks();
RegisterTTS();
@ -966,7 +937,6 @@ void InitMods() {
RegisterDeleteFileOnDeath();
RegisterHyperBosses();
UpdateHyperEnemiesState();
RegisterBonkDamage();
RegisterMenuPathFix();
RegisterMirrorModeHandler();
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_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[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_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));

View file

@ -30,7 +30,7 @@ void RegionTable_Init_GerudoFortress() {
LOCATION(RC_GF_SOUTHMOST_CENTER_CRATE, logic->CanBreakCrates()),
LOCATION(RC_GF_MID_SOUTH_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
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,
},
{
RR_GF_NORTHMOST_CENTER_CRATE,
RC_GF_NORTHMOST_CENTER_CRATE,
RAND_INF_GF_NORTHMOST_CENTER_CRATE,
},
{
@ -5783,6 +5783,53 @@ void RandomizerSettingsWindow::InitElement() {
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
// (special cases for rando items)
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
if (item == RG_GANONS_CASTLE_BOSS_KEY) {
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
@ -5806,6 +5859,7 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
}
return;
}
// Count any bombchu pack as bombchus
if ((item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHUS) {
if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = 0) {
@ -5813,11 +5867,15 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
}
return;
}
if (item == RG_MAGIC_SINGLE) {
gSaveContext.ship.stats.itemTimestamp[ITEM_SINGLE_MAGIC] = time;
return;
}
if (item == RG_DOUBLE_DEFENSE) {
gSaveContext.ship.stats.itemTimestamp[ITEM_DOUBLE_DEFENSE] = time;
return;
}
}

View file

@ -2396,7 +2396,7 @@ typedef enum {
RC_GF_SOUTHMOST_CENTER_CRATE,
RC_GF_MID_SOUTH_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_NW_CRATE,
RC_GF_HBA_RANGE_CRATE_1,

View file

@ -449,7 +449,8 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
result.currentCapacity =
IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) ? 0 : CUR_CAPACITY(UPG_WALLET);
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;
break;
case ITEM_BOMBCHU:
@ -502,8 +503,29 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
result.maxCapacity = GERUDO_TRAINING_GROUND_SMALL_KEY_MAX;
break;
case SCENE_THIEVES_HIDEOUT:
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;
case SCENE_INSIDE_GANONS_CASTLE:
result.maxCapacity = GANONS_CASTLE_SMALL_KEY_MAX;
break;

View file

@ -2600,15 +2600,6 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
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) {
SetCurrentGrottoIDForTracker(entranceIndex);
}

View file

@ -156,8 +156,6 @@ void Randomizer_SetSpoilerLoaded(bool spoilerLoaded);
uint8_t Randomizer_GenerateRandomizer();
void Randomizer_ShowRandomizerMenu();
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_RetrieveEntry(s16 modIndex, s16 getItemID);
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 = {
{ TIME_TRAVEL_DISABLED, "Disabled" },
{ TIME_TRAVEL_OOT, "Ocarina of Time" },
{ TIME_TRAVEL_OOT_MS, "Ocarina of Time + Master Sword" },
{ TIME_TRAVEL_ANY, "Any Ocarina" },
{ TIME_TRAVEL_ANY_MS, "Any Ocarina + Master Sword" },
};
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"
"Time Blocks can still be used properly.\n\n"
"Requirements:\n"
" - Obtained the Ocarina of Time (depends on selection)\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 Ocarina Playing spots"));

View file

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

View file

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

View file

@ -1,7 +1,6 @@
#include "z_door_warp1.h"
#include "objects/object_warp1/object_warp1.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/OTRGlobals.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);
if (DoorWarp1_PlayerInRange(this, play)) {
// Heal player in Boss Rush
if (IS_BOSS_RUSH) {
BossRush_HandleBlueWarpHeal(play);
}
if (GameInteractor_Should(VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE, DoorWarp1_PlayerInRange(this, play), this)) {
player = GET_PLAYER(play);
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) {
FileChoose_UpdateStickDirectionPromptAnim(thisx);
FileChooseContext* this = (FileChooseContext*)thisx;
@ -2590,63 +2486,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
break;
}
} else if (this->configMode == CM_BOSS_RUSH_MENU) {
uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language;
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);
}
}
FileChoose_DrawBossRushMenuWindowContents(this);
} else if (this->configMode == CM_RANDOMIZER_SETTINGS_MENU) {
uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language;
uint8_t textAlpha = this->randomizerUIAlpha;