mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-25 19:02:19 -05:00
[Feature] Boss Rush (#2923)
* Ganon(dorf) cutscene skips * Remove leftover code * Load into chamber of sages * Fix loading into chamber without fast file select * Boss warps in chamber done * Change warps back to chamber * Initial proof of concept done * ganon(dorf) cutscene skips * Code cleanup & auto age equipment * Gameplay stats timer + tweaks * Scuffed timer * Better timer * remove arena props + fix arena exits * Fix blue warps * Attempt to fix build * Fix build again * And again.. * Try no. 9001 * Handle dying and saving * Child link face fire medallion * Fix build * Fix warps after reset/death * Disable doors and move player spawns in boss rooms * Fix boss rush logo rendering * Start of ingame options menu * File Select cleanup * Fix build * Render char text PoC * Move functions to be more generic * Fix build * Fix other builds * Initial text scaling/kerning * Special characters prep * All special characters work now * Attempt to fix build * Fix build question mark * Finish all kerning * Start of ingame options menu with vertical scrolling * Barebones functional options menu * More options menu progress * More visual elements for options menu * Options menu visual changes, implement all options, tons of cleanup * Cleanup and comments * Shorter enums * More options * Change default heart count * Finish French translations * Implement timer in cosmetics editor * Uncomment timer requirement * Variable name change * German translation & small UI tweaks * Animated up/down arrows in options UI * Better arrows in options UI * Cleaner timer + make it usable for general gameplay * More cleanup + ganon & ganondorf boss option * Implement never heal option * Slight up arrow in options UI tweak * Add BGS option * Reintroduce ganondorf cutscene skip * Change encoding to UTF on bossrush.cpp * Fix build hopefully * Fixed static variables leading to options not properly resetting * Fix BR completed timestamp * Change timer to render on top of everything * Offset final BR time by 0.1 second from boss timestamps * Add missing check for boss rush * Implement soh_assets.h * Revert merge mistake * Fix special characters with UTF-8 * Fix build * here's the fix you can merge from your phone * Fix quest select crash with oot.otr only * Use OoT's kerning * Fix HD textures on options menu * Fix special character kerning * "Heal every boss" fixes * Seperate headers + bunny hood option * Remove GetUnixTimestamp() externing * Clean up extern "C"'s * Address review comments * Fix build question mark * Remove accidental styling change --------- Co-authored-by: briaguya <briaguya@alice>
This commit is contained in:
parent
fdf9086b2a
commit
2957dc61c3
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
BIN
OTRExporter/assets/textures/parameter_static/gArrowDown.ia16.png
Normal file
BIN
OTRExporter/assets/textures/parameter_static/gArrowDown.ia16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
OTRExporter/assets/textures/parameter_static/gArrowUp.ia16.png
Normal file
BIN
OTRExporter/assets/textures/parameter_static/gArrowUp.ia16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
@ -41,10 +41,19 @@ static const ALIGN_ASSET(2) char gSkullTreasureChestSideAndTopTex[] = dgSkullTre
|
||||
#define dgTitleRandomizerSubtitleTex "__OTR__objects/object_mag/gTitleRandomizerSubtitleTex"
|
||||
static const ALIGN_ASSET(2) char gTitleRandomizerSubtitleTex[] = dgTitleRandomizerSubtitleTex;
|
||||
|
||||
#define dgTitleBossRushSubtitleTex "__OTR__objects/object_mag/gTitleBossRushSubtitleTex"
|
||||
static const ALIGN_ASSET(2) char gTitleBossRushSubtitleTex[] = dgTitleBossRushSubtitleTex;
|
||||
|
||||
// textures
|
||||
#define dgDPad "__OTR__textures/parameter_static/gDPad"
|
||||
static const ALIGN_ASSET(2) char gDPadTex[] = dgDPad;
|
||||
|
||||
#define dgArrowUp "__OTR__textures/parameter_static/gArrowUp"
|
||||
static const ALIGN_ASSET(2) char gArrowUpTex[] = dgArrowUp;
|
||||
|
||||
#define dgArrowDown "__OTR__textures/parameter_static/gArrowDown"
|
||||
static const ALIGN_ASSET(2) char gArrowDownTex[] = dgArrowDown;
|
||||
|
||||
#define dgFileSelMQButtonTex "__OTR__textures/title_static/gFileSelMQButtonTex"
|
||||
static const ALIGN_ASSET(2) char gFileSelMQButtonTex[] = dgFileSelMQButtonTex;
|
||||
|
||||
@ -57,6 +66,15 @@ static const ALIGN_ASSET(2) char gFileSelPleaseChooseAQuestFRATex[] = dgFileSelP
|
||||
#define dgFileSelPleaseChooseAQuestGERTex "__OTR__textures/title_static/gFileSelPleaseChooseAQuestGERTex"
|
||||
static const ALIGN_ASSET(2) char gFileSelPleaseChooseAQuestGERTex[] = dgFileSelPleaseChooseAQuestGERTex;
|
||||
|
||||
#define dgFileSelBossRushSettingsENGTex "__OTR__textures/title_static/gFileSelBossRushSettingsENGTex"
|
||||
static const ALIGN_ASSET(2) char gFileSelBossRushSettingsENGText[] = dgFileSelBossRushSettingsENGTex;
|
||||
|
||||
#define dgFileSelBossRushSettingsFRATex "__OTR__textures/title_static/gFileSelBossRushSettingsFRATex"
|
||||
static const ALIGN_ASSET(2) char gFileSelBossRushSettingsFRAText[] = dgFileSelBossRushSettingsFRATex;
|
||||
|
||||
#define dgFileSelBossRushSettingsGERTex "__OTR__textures/title_static/gFileSelBossRushSettingsGERTex"
|
||||
static const ALIGN_ASSET(2) char gFileSelBossRushSettingsGERText[] = dgFileSelBossRushSettingsGERTex;
|
||||
|
||||
#define dgFileSelRANDButtonTex "__OTR__textures/title_static/gFileSelRANDButtonTex"
|
||||
static const ALIGN_ASSET(2) char gFileSelRANDButtonTex[] = dgFileSelRANDButtonTex;
|
||||
|
||||
|
@ -889,6 +889,7 @@ void KaleidoSetup_Init(PlayState* play);
|
||||
void KaleidoSetup_Destroy(PlayState* play);
|
||||
void func_8006EE50(Font* font, u16 arg1, u16 arg2);
|
||||
void Font_LoadChar(Font* font, u8 character, u16 codePointIndex);
|
||||
void* Font_FetchCharTexture(u8 character);
|
||||
void Font_LoadMessageBoxIcon(Font* font, u16 icon);
|
||||
void Font_LoadOrderedFont(Font* font);
|
||||
s32 func_8006F0A0(s32 arg0);
|
||||
@ -1054,6 +1055,7 @@ void func_800849EC(PlayState* play);
|
||||
void Interface_LoadItemIcon1(PlayState* play, u16 button);
|
||||
void Interface_LoadItemIcon2(PlayState* play, u16 button);
|
||||
void func_80084BF4(PlayState* play, u16 flag);
|
||||
uint16_t Interface_DrawTextLine(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR, uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow);
|
||||
u8 Item_Give(PlayState* play, u8 item);
|
||||
u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry);
|
||||
u8 Item_CheckObtainability(u8 item);
|
||||
@ -2402,6 +2404,7 @@ u8 Message_GetState(MessageContext* msgCtx);
|
||||
void Message_Draw(PlayState* play);
|
||||
void Message_Update(PlayState* play);
|
||||
void Message_SetTables(void);
|
||||
f32 Message_GetCharacterWidth(unsigned char characterIndex);
|
||||
void GameOver_Init(PlayState* play);
|
||||
void GameOver_FadeInLights(PlayState* play);
|
||||
void GameOver_Update(PlayState* play);
|
||||
|
@ -1500,6 +1500,10 @@ typedef struct {
|
||||
f32 stickAnimTween;
|
||||
u8 arrowAnimState;
|
||||
u8 stickAnimState;
|
||||
uint8_t bossRushIndex;
|
||||
uint8_t bossRushOffset;
|
||||
int16_t bossRushUIAlpha;
|
||||
uint16_t bossRushArrowOffset;
|
||||
} FileChooseContext; // size = 0x1CAE0
|
||||
|
||||
typedef enum {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "soh/Enhancements/randomizer/randomizer_inf.h"
|
||||
#include "soh/Enhancements/gameplaystats.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRushTypes.h"
|
||||
|
||||
typedef enum {
|
||||
/* 0x0 */ MAGIC_STATE_IDLE, // Regular gameplay
|
||||
@ -280,6 +281,9 @@ typedef struct {
|
||||
// #region SOH [General]
|
||||
// Upstream TODO: Move these to their own struct or name to more obviously specific to SoH
|
||||
/* */ uint32_t isMasterQuest;
|
||||
/* */ uint32_t isBossRush;
|
||||
/* */ uint32_t isBossRushPaused;
|
||||
/* */ uint8_t bossRushOptions[BOSSRUSH_OPTIONS_AMOUNT];
|
||||
/* */ u8 mqDungeonCount;
|
||||
/* */ u8 pendingIceTrapCount;
|
||||
/* */ SohStats sohStats;
|
||||
|
487
soh/soh/Enhancements/boss-rush/BossRush.cpp
Normal file
487
soh/soh/Enhancements/boss-rush/BossRush.cpp
Normal file
@ -0,0 +1,487 @@
|
||||
#include "BossRush.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "functions.h"
|
||||
#include "macros.h"
|
||||
#include "variables.h"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef struct BossRushSetting {
|
||||
std::array<std::string, LANGUAGE_MAX> name;
|
||||
std::vector<std::array<std::string, LANGUAGE_MAX>> choices;
|
||||
} BossRushSetting;
|
||||
|
||||
BossRushSetting BossRushOptions[BOSSRUSH_OPTIONS_AMOUNT] = {
|
||||
{
|
||||
{ "BOSSES:", "BOSSE:", "BOSS:" },
|
||||
{
|
||||
{ "All", "Alle", "Tous" },
|
||||
{ "Child", "Kind", "Enfant" },
|
||||
{ "Adult", "Erwachsener", "Adulte" },
|
||||
{ "Ganondorf & Ganon", "Ganondorf & Ganon", "Ganondorf & Ganon" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "HEARTS:", "HERZEN:", "COEURS:" },
|
||||
{
|
||||
{ "10", "10", "10" },
|
||||
{ "15", "15", "15" },
|
||||
{ "20", "20", "20" },
|
||||
{ "3", "3", "3" },
|
||||
{ "5", "5", "5" },
|
||||
{ "7", "7", "7" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "AMMO:", "MUNITION:", "MUNITIONS:" },
|
||||
{
|
||||
{ "Limited", "Limitiert", "Limitées" },
|
||||
{ "Full", "Voll", "Pleines" },
|
||||
{ "Maxed", "Maximum", "Maximum" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "HEAL:", "REGENERATION:", "SOIN:" },
|
||||
{
|
||||
{ "Before Ganondorf", "Vor Ganondorf", "Avant Ganondorf" },
|
||||
{ "Every Boss", "Bei jedem Boss", "Tous les Boss" },
|
||||
{ "Never", "Niemals", "Jamais" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "HYPER BOSSES:", "HYPER-BOSSE:", "HYPER BOSS:" },
|
||||
{
|
||||
{ "No", "Nein", "Non" },
|
||||
{ "Yes", "Ja", "Oui" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "MAGIC:", "MAGIE:", "MAGIE:" },
|
||||
{
|
||||
{ "Single", "Einzel", "Simple" },
|
||||
{ "Double", "Doppel", "Double" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "BIG. SWORD:", "BIG.-SCHWERT:", "EPÉE DE BIG.:" },
|
||||
{
|
||||
{ "No", "Nein", "Non" },
|
||||
{ "Yes", "Ja", "Oui" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "BOTTLE:", "FLASCHEN:", "BOUTEILLE:" },
|
||||
{
|
||||
{ "No", "Nein", "Non" },
|
||||
{ "Empty", "Leer", "Vide" },
|
||||
{ "Fairy", "Fee", "Fée" },
|
||||
{ "Red Potion", "Rotes Elixier", "Potion Rouge" },
|
||||
{ "Green Potion", "Grünes Elixier", "Potion Verte" },
|
||||
{ "Blue Potion", "Blaues Elixier", "Potion Bleue" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "LONGSHOT:", "ENTERHAKEN:", "SUPER GRAPPIN:" },
|
||||
{
|
||||
{ "No", "Nein", "Non" },
|
||||
{ "Yes", "Ja", "Oui" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "HOVER BOOTS:", "GLEITSTIEFEL:", "BOTTES DES AIRS:" },
|
||||
{
|
||||
{ "No", "Nein", "Non" },
|
||||
{ "Yes", "Ja", "Oui" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "BUNNY HOOD:", "HASENOHREN:", "MASQUE DU LAPIN:" },
|
||||
{
|
||||
{ "No", "Nein", "Non" },
|
||||
{ "Yes", "Ja", "Oui" }
|
||||
}
|
||||
},
|
||||
{
|
||||
{ "TIMER:", "TIMER:", "TIMER:" },
|
||||
{
|
||||
{ "Yes", "Ja", "Oui" },
|
||||
{ "No", "Nein", "Non" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const char* BossRush_GetSettingName(uint8_t optionIndex, uint8_t language) {
|
||||
return BossRushOptions[optionIndex].name[language].c_str();
|
||||
}
|
||||
|
||||
const char* BossRush_GetSettingChoiceName(uint8_t optionIndex, uint8_t choiceIndex, uint8_t language) {
|
||||
return BossRushOptions[optionIndex].choices[choiceIndex][language].c_str();
|
||||
}
|
||||
|
||||
uint8_t BossRush_GetSettingOptionsAmount(uint8_t optionIndex) {
|
||||
return BossRushOptions[optionIndex].choices.size();
|
||||
}
|
||||
|
||||
void BossRush_SpawnBlueWarps(PlayState* play) {
|
||||
|
||||
// Spawn blue warps in Chamber of Sages based on what bosses have been defeated.
|
||||
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
|
||||
// Forest Medallion (Gohma)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, -100, 6, -170, 0, 0, 0, -1, false);
|
||||
}
|
||||
// Fire Medallion (King Dodongo)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 100, 6, -170, 0, 0, 0, -1, false);
|
||||
}
|
||||
// Water Medallion (Barinade)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 199, 6, 0, 0, 0, 0, -1, false);
|
||||
}
|
||||
} else {
|
||||
// Light Medallion (Ganondorf)
|
||||
if (CheckDungeonCount() == 8) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, -199, 6, 0, 0, 0, 0, -1, false);
|
||||
}
|
||||
// Forest Medallion (Phantom Ganondorf)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FOREST_TEMPLE)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, -100, 6, -170, 0, 0, 0, -1, false);
|
||||
}
|
||||
// Fire Medallion (Volvagia)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 100, 6, -170, 0, 0, 0, -1, false);
|
||||
}
|
||||
// Water Medallion (Morpha)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 199, 6, 0, 0, 0, 0, -1, false);
|
||||
}
|
||||
// Spirit Medallion (Twinrova)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 100, 6, 170, 0, 0, 0, -1, false);
|
||||
}
|
||||
// Shadow Medallion (Bongo Bongo)
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE)) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, -100, 6, 170, 0, 0, 0, -1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) {
|
||||
|
||||
// If warping from Chamber of Sages, choose the correct boss room to teleport to.
|
||||
if (play->sceneNum == SCENE_KENJYANOMA) {
|
||||
// Gohma & Phantom Ganon
|
||||
if (warpPosX == -100 && warpPosZ == -170) {
|
||||
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
|
||||
play->nextEntranceIndex = 0x040F;
|
||||
} else {
|
||||
play->nextEntranceIndex = 0x000C;
|
||||
}
|
||||
// King Dodongo & Volvagia
|
||||
} else if (warpPosX == 100 && warpPosZ == -170) {
|
||||
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
|
||||
play->nextEntranceIndex = 0x040B;
|
||||
} else {
|
||||
play->nextEntranceIndex = 0x0305;
|
||||
}
|
||||
// Barinade & Morb
|
||||
} else if (warpPosX == 199 && warpPosZ == 0) {
|
||||
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
|
||||
play->nextEntranceIndex = 0x0301;
|
||||
} else {
|
||||
play->nextEntranceIndex = 0x0417;
|
||||
}
|
||||
// Twinrova
|
||||
} else if (warpPosX == 100 && warpPosZ == 170) {
|
||||
play->nextEntranceIndex = 0x05EC;
|
||||
// Bongo Bongo
|
||||
} else if (warpPosX == -100 && warpPosZ == 170) {
|
||||
play->nextEntranceIndex = 0x0413;
|
||||
// Ganondork
|
||||
} else if (warpPosX == -199 && warpPosZ == 0) {
|
||||
play->nextEntranceIndex = 0x041F;
|
||||
}
|
||||
// If coming from a boss room, teleport back to Chamber of Sages and set flag.
|
||||
} else {
|
||||
play->nextEntranceIndex = SCENE_HAIRAL_NIWA2;
|
||||
|
||||
if (CheckDungeonCount() == 3) {
|
||||
play->linkAgeOnLoad = LINK_AGE_ADULT;
|
||||
gSaveContext.linkAge = LINK_AGE_ADULT;
|
||||
|
||||
// Change to Adult Link.
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_ALL) {
|
||||
BossRush_SetEquipment(LINK_AGE_ADULT);
|
||||
// Warp to credits.
|
||||
} else if (gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_CHILD) {
|
||||
play->nextEntranceIndex = 0x6B;
|
||||
gSaveContext.nextCutsceneIndex = 0xFFF2;
|
||||
play->sceneLoadFlag = 0x14;
|
||||
play->fadeTransition = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BossRush_HandleBlueWarpHeal(PlayState* play) {
|
||||
|
||||
// This function gets called multiple times per blue warp, so only heal when player isn't at max HP.
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_EVERYBOSS &&
|
||||
gSaveContext.health != gSaveContext.healthCapacity) {
|
||||
Health_ChangeBy(play, 320);
|
||||
}
|
||||
}
|
||||
|
||||
void BossRush_HandleCompleteBoss(PlayState* play) {
|
||||
if (!gSaveContext.isBossRush) {
|
||||
return;
|
||||
}
|
||||
|
||||
gSaveContext.isBossRushPaused = 1;
|
||||
switch (play->sceneNum) {
|
||||
case SCENE_YDAN_BOSS:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE);
|
||||
break;
|
||||
case SCENE_DDAN_BOSS:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN);
|
||||
break;
|
||||
case SCENE_BDAN_BOSS:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY);
|
||||
break;
|
||||
case SCENE_MORIBOSSROOM:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_FOREST_TEMPLE);
|
||||
break;
|
||||
case SCENE_FIRE_BS:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE);
|
||||
break;
|
||||
case SCENE_MIZUSIN_BS:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE);
|
||||
break;
|
||||
case SCENE_JYASINBOSS:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE);
|
||||
break;
|
||||
case SCENE_HAKADAN_BS:
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Fully heal the player after Ganondorf
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_EVERYBOSS &&
|
||||
play->sceneNum == SCENE_GANON_BOSS) {
|
||||
Health_ChangeBy(play, 320);
|
||||
}
|
||||
|
||||
if ((CheckDungeonCount() == 3 && gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_CHILD) ||
|
||||
play->sceneNum == SCENE_GANON_DEMO) {
|
||||
gSaveContext.sohStats.playTimer += 2;
|
||||
gSaveContext.sohStats.gameComplete = 1;
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_BOSSRUSH_FINISH] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
for (int i = 0; i < ARRAY_COUNT(gSaveContext.playerName); i++) {
|
||||
gSaveContext.playerName[i] = brPlayerName[i];
|
||||
}
|
||||
|
||||
gSaveContext.isBossRushPaused = 1;
|
||||
gSaveContext.entranceIndex = 107;
|
||||
gSaveContext.cutsceneIndex = 0x8000;
|
||||
gSaveContext.isMagicAcquired = 1;
|
||||
|
||||
// Set magic
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_MAGIC] == BR_CHOICE_MAGIC_SINGLE) {
|
||||
gSaveContext.magicLevel = 1;
|
||||
gSaveContext.magic = 48;
|
||||
} else {
|
||||
gSaveContext.isDoubleMagicAcquired = 1;
|
||||
gSaveContext.magicLevel = 2;
|
||||
gSaveContext.magic = 96;
|
||||
}
|
||||
|
||||
// Set health
|
||||
uint16_t health = 16;
|
||||
switch (gSaveContext.bossRushOptions[BR_OPTIONS_HEARTS]) {
|
||||
case BR_CHOICE_HEARTS_7:
|
||||
health *= 7;
|
||||
break;
|
||||
case BR_CHOICE_HEARTS_10:
|
||||
health *= 10;
|
||||
break;
|
||||
case BR_CHOICE_HEARTS_15:
|
||||
health *= 15;
|
||||
break;
|
||||
case BR_CHOICE_HEARTS_20:
|
||||
health *= 20;
|
||||
break;
|
||||
case BR_CHOICE_HEARTS_3:
|
||||
health *= 3;
|
||||
break;
|
||||
case BR_CHOICE_HEARTS_5:
|
||||
health *= 5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
gSaveContext.healthCapacity = health;
|
||||
gSaveContext.health = health;
|
||||
|
||||
// Skip boss cutscenes
|
||||
gSaveContext.eventChkInf[7] |= 1; // gohma
|
||||
gSaveContext.eventChkInf[7] |= 2; // dodongo
|
||||
gSaveContext.eventChkInf[7] |= 4; // phantom ganon
|
||||
gSaveContext.eventChkInf[7] |= 8; // volvagia
|
||||
gSaveContext.eventChkInf[7] |= 0x10; // morpha
|
||||
gSaveContext.eventChkInf[7] |= 0x20; // twinrova
|
||||
gSaveContext.eventChkInf[7] |= 0x40; // barinade
|
||||
gSaveContext.eventChkInf[7] |= 0x80; // bongo bongo
|
||||
|
||||
// Sets all rando flags to false
|
||||
for (s32 i = 0; i < ARRAY_COUNT(gSaveContext.randomizerInf); i++) {
|
||||
gSaveContext.randomizerInf[i] = 0;
|
||||
}
|
||||
|
||||
// Set items
|
||||
std::array<u8, 24> brItems = {
|
||||
ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_BOW, ITEM_NONE, ITEM_NONE,
|
||||
ITEM_SLINGSHOT, ITEM_NONE, ITEM_NONE, ITEM_HOOKSHOT, ITEM_NONE, ITEM_NONE,
|
||||
ITEM_BOOMERANG, ITEM_LENS, ITEM_NONE, ITEM_HAMMER, ITEM_ARROW_LIGHT, ITEM_NONE,
|
||||
ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE,
|
||||
};
|
||||
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_LONGSHOT] == BR_CHOICE_LONGSHOT_YES) {
|
||||
brItems[9] = ITEM_LONGSHOT;
|
||||
}
|
||||
|
||||
switch (gSaveContext.bossRushOptions[BR_OPTIONS_BOTTLE]) {
|
||||
case BR_CHOICE_BOTTLE_EMPTY:
|
||||
brItems[18] = ITEM_BOTTLE;
|
||||
break;
|
||||
case BR_CHOICE_BOTTLE_FAIRY:
|
||||
brItems[18] = ITEM_FAIRY;
|
||||
break;
|
||||
case BR_CHOICE_BOTTLE_REDPOTION:
|
||||
brItems[18] = ITEM_POTION_RED;
|
||||
break;
|
||||
case BR_CHOICE_BOTTLE_GREENPOTION:
|
||||
brItems[18] = ITEM_POTION_GREEN;
|
||||
break;
|
||||
case BR_CHOICE_BOTTLE_BLUEPOTION:
|
||||
brItems[18] = ITEM_POTION_BLUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_BUNNYHOOD] == BR_CHOICE_BUNNYHOOD_YES) {
|
||||
brItems[23] = ITEM_MASK_BUNNY;
|
||||
}
|
||||
|
||||
for (int item = 0; item < ARRAY_COUNT(gSaveContext.inventory.items); item++) {
|
||||
gSaveContext.inventory.items[item] = brItems[item];
|
||||
}
|
||||
|
||||
// Set consumable counts
|
||||
std::array<s8, 16> brAmmo = { 5, 5, 10, 10, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_AMMO] == BR_CHOICE_AMMO_FULL) {
|
||||
brAmmo = { 10, 20, 20, 30, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
} else if (gSaveContext.bossRushOptions[BR_OPTIONS_AMMO] == BR_CHOICE_AMMO_MAXED) {
|
||||
brAmmo = { 30, 40, 40, 50, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
}
|
||||
|
||||
for (int ammo = 0; ammo < ARRAY_COUNT(gSaveContext.inventory.ammo); ammo++) {
|
||||
gSaveContext.inventory.ammo[ammo] = brAmmo[ammo];
|
||||
}
|
||||
|
||||
// Equipment
|
||||
gSaveContext.inventory.equipment |= 1 << 0; // Kokiri Sword
|
||||
gSaveContext.inventory.equipment |= 1 << 1; // Master Sword
|
||||
gSaveContext.inventory.equipment |= 1 << 4; // Deku Shield
|
||||
gSaveContext.inventory.equipment |= 1 << 6; // Mirror Shield
|
||||
gSaveContext.inventory.equipment |= 1 << 9; // Goron Tunic
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_BGS] == BR_CHOICE_BGS_YES) {
|
||||
gSaveContext.inventory.equipment |= 1 << 2; // Biggoron Sword
|
||||
gSaveContext.bgsFlag = 1;
|
||||
}
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_HOVERBOOTS] == BR_CHOICE_HOVERBOOTS_YES) {
|
||||
gSaveContext.inventory.equipment |= 1 << 14; // Hover Boots
|
||||
}
|
||||
|
||||
// Upgrades
|
||||
uint8_t upgradeLevel = 1;
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_AMMO] == BR_CHOICE_AMMO_MAXED) {
|
||||
upgradeLevel = 3;
|
||||
}
|
||||
Inventory_ChangeUpgrade(UPG_QUIVER, upgradeLevel);
|
||||
Inventory_ChangeUpgrade(UPG_BOMB_BAG, upgradeLevel);
|
||||
Inventory_ChangeUpgrade(UPG_BULLET_BAG, upgradeLevel);
|
||||
Inventory_ChangeUpgrade(UPG_STICKS, upgradeLevel);
|
||||
Inventory_ChangeUpgrade(UPG_NUTS, upgradeLevel);
|
||||
|
||||
// Set flags and Link's age based on chosen settings.
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_ADULT ||
|
||||
gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_GANONDORF_GANON) {
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE);
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN);
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY);
|
||||
if (gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_GANONDORF_GANON) {
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_FOREST_TEMPLE);
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE);
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE);
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE);
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE);
|
||||
}
|
||||
gSaveContext.linkAge = LINK_AGE_ADULT;
|
||||
BossRush_SetEquipment(LINK_AGE_ADULT);
|
||||
} else {
|
||||
gSaveContext.linkAge = LINK_AGE_CHILD;
|
||||
BossRush_SetEquipment(LINK_AGE_CHILD);
|
||||
}
|
||||
}
|
||||
|
||||
void BossRush_SetEquipment(uint8_t linkAge) {
|
||||
|
||||
std::array<u8, 8> brButtonItems;
|
||||
std::array<u8, 7> brCButtonSlots;
|
||||
|
||||
// Set Child Equipment.
|
||||
if (linkAge == LINK_AGE_CHILD) {
|
||||
brButtonItems = {
|
||||
ITEM_SWORD_KOKIRI, ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE
|
||||
};
|
||||
|
||||
brCButtonSlots = { SLOT_STICK, SLOT_NUT, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE };
|
||||
|
||||
Inventory_ChangeEquipment(EQUIP_SWORD, PLAYER_SWORD_KOKIRI);
|
||||
Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_DEKU);
|
||||
// Set Adult equipment.
|
||||
} else {
|
||||
brButtonItems = { ITEM_SWORD_MASTER, ITEM_BOW, ITEM_HAMMER, ITEM_BOMB,
|
||||
ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE };
|
||||
|
||||
brCButtonSlots = { SLOT_BOW, SLOT_HAMMER, SLOT_BOMB, SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE };
|
||||
|
||||
Inventory_ChangeEquipment(EQUIP_SWORD, PLAYER_SWORD_MASTER);
|
||||
Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_MIRROR);
|
||||
Inventory_ChangeEquipment(EQUIP_TUNIC, PLAYER_TUNIC_GORON + 1); // Game expects tunic + 1, don't ask me why.
|
||||
}
|
||||
|
||||
// Button Items
|
||||
for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.buttonItems); button++) {
|
||||
gSaveContext.equips.buttonItems[button] = brButtonItems[button];
|
||||
}
|
||||
|
||||
// C buttons
|
||||
for (int button = 0; button < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); button++) {
|
||||
gSaveContext.equips.cButtonSlots[button] = brCButtonSlots[button];
|
||||
}
|
||||
}
|
20
soh/soh/Enhancements/boss-rush/BossRush.h
Normal file
20
soh/soh/Enhancements/boss-rush/BossRush.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "BossRushTypes.h"
|
||||
#include "variables.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void BossRush_SpawnBlueWarps(PlayState* play);
|
||||
void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ);
|
||||
void BossRush_HandleBlueWarpHeal(PlayState* play);
|
||||
void BossRush_InitSave();
|
||||
void BossRush_SetEquipment(uint8_t linkAge);
|
||||
void BossRush_HandleCompleteBoss(PlayState* play);
|
||||
const char* BossRush_GetSettingName(uint8_t optionIndex, uint8_t language);
|
||||
const char* BossRush_GetSettingChoiceName(uint8_t optionIndex, uint8_t choiceIndex, uint8_t language);
|
||||
uint8_t BossRush_GetSettingOptionsAmount(uint8_t optionIndex);
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
91
soh/soh/Enhancements/boss-rush/BossRushTypes.h
Normal file
91
soh/soh/Enhancements/boss-rush/BossRushTypes.h
Normal file
@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#define BOSSRUSH_OPTIONS_AMOUNT 12
|
||||
#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
|
||||
} 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;
|
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
const char* Interface_ReplaceSpecialCharacters(char text[]);
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
@ -1,6 +1,9 @@
|
||||
#include "CustomMessageManager.h"
|
||||
#include "CustomMessageInterfaceAddon.h"
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
@ -148,6 +151,24 @@ void CustomMessage::ReplaceSpecialCharacters() {
|
||||
}
|
||||
}
|
||||
|
||||
const char* Interface_ReplaceSpecialCharacters(char text[]) {
|
||||
std::string textString(text);
|
||||
|
||||
for (auto specialCharacterPair : textBoxSpecialCharacters) {
|
||||
size_t start_pos = 0;
|
||||
std::string textBoxSpecialCharacterString = ""s;
|
||||
textBoxSpecialCharacterString += specialCharacterPair.second;
|
||||
while ((start_pos = textString.find(specialCharacterPair.first, start_pos)) != std::string::npos) {
|
||||
textString.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString);
|
||||
start_pos += textBoxSpecialCharacterString.length();
|
||||
}
|
||||
}
|
||||
|
||||
char* textChar = new char[textString.length() + 1];
|
||||
strcpy(textChar, textString.c_str());
|
||||
return textChar;
|
||||
}
|
||||
|
||||
void CustomMessage::ReplaceColors() {
|
||||
for (std::string* str : { &english, &french, &german }) {
|
||||
for (auto colorPair : colors) {
|
||||
|
@ -1087,7 +1087,7 @@ void DrawFlagsTab() {
|
||||
|
||||
for (int i = 0; i < flagTables.size(); i++) {
|
||||
const FlagTable& flagTable = flagTables[i];
|
||||
if (flagTable.flagTableType == RANDOMIZER_INF && !gSaveContext.n64ddFlag) {
|
||||
if (flagTable.flagTableType == RANDOMIZER_INF && !gSaveContext.n64ddFlag && !gSaveContext.isBossRush) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -798,6 +798,7 @@ void SetupDisplayNames() {
|
||||
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_TWINROVA], "Twinrova Defeated: ");
|
||||
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GANONDORF], "Ganondorf Defeated: ");
|
||||
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GANON], "Ganon Defeated: ");
|
||||
strcpy(itemTimestampDisplayName[TIMESTAMP_BOSSRUSH_FINISH], "Boss Rush Finished: ");
|
||||
strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_GREG], "Greg Found: ");
|
||||
}
|
||||
|
||||
@ -808,39 +809,50 @@ void SetupDisplayColors() {
|
||||
case ITEM_KOKIRI_EMERALD:
|
||||
case ITEM_SONG_SARIA:
|
||||
case ITEM_MEDALLION_FOREST:
|
||||
case TIMESTAMP_DEFEAT_GOHMA:
|
||||
case TIMESTAMP_DEFEAT_PHANTOM_GANON:
|
||||
case TIMESTAMP_FOUND_GREG:
|
||||
itemTimestampDisplayColor[i] = COLOR_GREEN;
|
||||
break;
|
||||
case ITEM_SONG_BOLERO:
|
||||
case ITEM_GORON_RUBY:
|
||||
case ITEM_MEDALLION_FIRE:
|
||||
case TIMESTAMP_DEFEAT_KING_DODONGO:
|
||||
case TIMESTAMP_DEFEAT_VOLVAGIA:
|
||||
itemTimestampDisplayColor[i] = COLOR_RED;
|
||||
break;
|
||||
case ITEM_SONG_SERENADE:
|
||||
case ITEM_ZORA_SAPPHIRE:
|
||||
case ITEM_MEDALLION_WATER:
|
||||
case TIMESTAMP_DEFEAT_BARINADE:
|
||||
case TIMESTAMP_DEFEAT_MORPHA:
|
||||
itemTimestampDisplayColor[i] = COLOR_BLUE;
|
||||
break;
|
||||
case ITEM_SONG_LULLABY:
|
||||
case ITEM_SONG_NOCTURNE:
|
||||
case ITEM_MEDALLION_SHADOW:
|
||||
case TIMESTAMP_DEFEAT_BONGO_BONGO:
|
||||
itemTimestampDisplayColor[i] = COLOR_PURPLE;
|
||||
break;
|
||||
case ITEM_SONG_EPONA:
|
||||
case ITEM_SONG_REQUIEM:
|
||||
case ITEM_MEDALLION_SPIRIT:
|
||||
case TIMESTAMP_DEFEAT_TWINROVA:
|
||||
itemTimestampDisplayColor[i] = COLOR_ORANGE;
|
||||
break;
|
||||
case ITEM_SONG_SUN:
|
||||
case ITEM_SONG_PRELUDE:
|
||||
case ITEM_MEDALLION_LIGHT:
|
||||
case ITEM_ARROW_LIGHT:
|
||||
case TIMESTAMP_DEFEAT_GANONDORF:
|
||||
case TIMESTAMP_DEFEAT_GANON:
|
||||
itemTimestampDisplayColor[i] = COLOR_YELLOW;
|
||||
break;
|
||||
case ITEM_SONG_STORMS:
|
||||
itemTimestampDisplayColor[i] = COLOR_GREY;
|
||||
break;
|
||||
case ITEM_SONG_TIME:
|
||||
case TIMESTAMP_BOSSRUSH_FINISH:
|
||||
itemTimestampDisplayColor[i] = COLOR_LIGHT_BLUE;
|
||||
break;
|
||||
default:
|
||||
|
@ -33,6 +33,7 @@ typedef enum {
|
||||
/* 0xA7 */ TIMESTAMP_DEFEAT_TWINROVA, // z_boss_tw.c
|
||||
/* 0xA8 */ TIMESTAMP_DEFEAT_GANONDORF, // z_boss_ganon.c
|
||||
/* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c
|
||||
/* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c
|
||||
/* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c
|
||||
/* 0xAB */ TIMESTAMP_MAX
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <libultraship/bridge.h>
|
||||
#include "game-interactor/GameInteractor.h"
|
||||
#include "tts/tts.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRushTypes.h"
|
||||
|
||||
extern "C" {
|
||||
#include <z64.h>
|
||||
@ -440,8 +441,13 @@ void RegisterHyperBosses() {
|
||||
actor->id == ACTOR_BOSS_GANON || // Ganondorf
|
||||
actor->id == ACTOR_BOSS_GANON2; // Ganon
|
||||
|
||||
uint8_t hyperBossesActive =
|
||||
CVarGetInteger("gHyperBosses", 0) ||
|
||||
(gSaveContext.isBossRush &&
|
||||
gSaveContext.bossRushOptions[BR_OPTIONS_HYPERBOSSES] == BR_CHOICE_HYPERBOSSES_YES);
|
||||
|
||||
// Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses.
|
||||
if (CVarGetInteger("gHyperBosses", 0) && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) {
|
||||
if (hyperBossesActive && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) {
|
||||
// Barinade needs to be updated in sequence to avoid unintended behaviour.
|
||||
if (actor->id == ACTOR_BOSS_VA) {
|
||||
// params -1 is BOSSVA_BODY
|
||||
|
@ -88,7 +88,6 @@ void Ctx_ReadSaveFile(uintptr_t addr, void* dramAddr, size_t size);
|
||||
void Ctx_WriteSaveFile(uintptr_t addr, void* dramAddr, size_t size);
|
||||
|
||||
uint64_t GetPerfCounter();
|
||||
uint64_t GetUnixTimestamp();
|
||||
struct SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, SkelAnime* skelAnime);
|
||||
void ResourceMgr_UnregisterSkeleton(SkelAnime* skelAnime);
|
||||
void ResourceMgr_ClearSkeletons();
|
||||
@ -149,4 +148,12 @@ void SaveManager_ThreadPoolWait();
|
||||
int32_t GetGIID(uint32_t itemID);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
uint64_t GetUnixTimestamp();
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "macros.h"
|
||||
#include <variables.h>
|
||||
#include <Hooks.h>
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
#define NOGDI // avoid various windows defines that conflict with things in z64.h
|
||||
@ -583,6 +584,10 @@ void SaveManager::InitFileNormal() {
|
||||
gSaveContext.pendingSale = ITEM_NONE;
|
||||
gSaveContext.pendingSaleMod = MOD_NONE;
|
||||
|
||||
if (gSaveContext.isBossRush) {
|
||||
BossRush_InitSave();
|
||||
}
|
||||
|
||||
//RANDOTODO (ADD ITEMLOCATIONS TO GSAVECONTEXT)
|
||||
}
|
||||
|
||||
@ -748,7 +753,8 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se
|
||||
|
||||
// SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded
|
||||
void SaveManager::SaveSection(int fileNum, int sectionID, bool threaded) {
|
||||
if (fileNum == 0xFF) {
|
||||
// Don't save in Boss rush.
|
||||
if (fileNum == 0xFF || fileNum == 0xFE) {
|
||||
return;
|
||||
}
|
||||
// Don't save a nonexistent section
|
||||
|
@ -76,6 +76,10 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) {
|
||||
gSaveContext.worldMapArea = 0;
|
||||
OTRScene_ExecuteCommands(play, (LUS::Scene*)play->sceneSegment);
|
||||
Play_InitEnvironment(play, play->skyboxId);
|
||||
// Unpause the timer for Boss Rush when the scene loaded isn't the Chamber of Sages.
|
||||
if (gSaveContext.isBossRush && play->sceneNum != SCENE_KENJYANOMA) {
|
||||
gSaveContext.isBossRushPaused = 0;
|
||||
}
|
||||
/* auto data = static_cast<LUS::Vertex*>(LUS::Context::GetInstance()
|
||||
->GetResourceManager()
|
||||
->LoadResource("object_link_child\\object_link_childVtx_01FE08")
|
||||
|
@ -56,7 +56,8 @@ void KaleidoScopeCall_Update(PlayState* play) {
|
||||
KaleidoMgrOverlay* kaleidoScopeOvl = &gKaleidoMgrOverlayTable[KALEIDO_OVL_KALEIDO_SCOPE];
|
||||
PauseContext* pauseCtx = &play->pauseCtx;
|
||||
|
||||
if (!gSaveContext.sohStats.gameComplete) {
|
||||
if (!gSaveContext.sohStats.gameComplete &&
|
||||
(!gSaveContext.isBossRush || !gSaveContext.isBossRushPaused)) {
|
||||
gSaveContext.sohStats.pauseTimer++;
|
||||
}
|
||||
|
||||
|
@ -177,6 +177,10 @@ void Font_LoadChar(Font* font, u8 character, u16 codePointIndex) {
|
||||
memcpy(&font->charTexBuf[codePointIndex], fntTbl[character], strlen(fntTbl[character]) + 1);
|
||||
}
|
||||
|
||||
void* Font_FetchCharTexture(u8 character) {
|
||||
return fntTbl[character];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a message box icon from message_static, such as the ending triangle/square or choice arrow into the
|
||||
* icon buffer.
|
||||
|
@ -709,6 +709,10 @@ f32 sFontWidths[144] = {
|
||||
14.0f, // ?
|
||||
};
|
||||
|
||||
f32 Message_GetCharacterWidth(unsigned char characterIndex) {
|
||||
return sFontWidths[characterIndex] * (R_TEXT_CHAR_SCALE / 100.0f);
|
||||
}
|
||||
|
||||
u16 Message_DrawItemIcon(PlayState* play, u16 itemId, Gfx** p, u16 i) {
|
||||
s32 pad;
|
||||
Gfx* gfx = *p;
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
#include "libultraship/bridge.h"
|
||||
#include "soh/Enhancements/gameplaystats.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRushTypes.h"
|
||||
#include "soh/Enhancements/custom-message/CustomMessageInterfaceAddon.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
@ -882,7 +884,8 @@ void func_80083108(PlayState* play) {
|
||||
Interface_ChangeAlpha(12);
|
||||
}
|
||||
}
|
||||
} else if (play->sceneNum == SCENE_KENJYANOMA) {
|
||||
// Don't hide the HUD in the Chamber of Sages when in Boss Rush.
|
||||
} else if (play->sceneNum == SCENE_KENJYANOMA && !gSaveContext.isBossRush) {
|
||||
Interface_ChangeAlpha(1);
|
||||
} else if (play->sceneNum == SCENE_TURIBORI) {
|
||||
gSaveContext.unk_13E7 = 2;
|
||||
@ -4942,7 +4945,10 @@ void Interface_Draw(PlayState* play) {
|
||||
PosX_RC = PosX_RC_ori;
|
||||
}
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, rColor.r, rColor.g, rColor.b, interfaceCtx->magicAlpha);
|
||||
// Draw Rupee icon. Hide in Boss Rush.
|
||||
if (!gSaveContext.isBossRush) {
|
||||
OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gRupeeCounterIconTex, 16, 16, PosX_RC, PosY_RC, 16, 16, 1 << 10, 1 << 10);
|
||||
}
|
||||
|
||||
switch (play->sceneNum) {
|
||||
case SCENE_BMORI1:
|
||||
@ -5057,10 +5063,12 @@ void Interface_Draw(PlayState* play) {
|
||||
svar2 = rupeeDigitsFirst[CUR_UPG_VALUE(UPG_WALLET)];
|
||||
svar5 = rupeeDigitsCount[CUR_UPG_VALUE(UPG_WALLET)];
|
||||
|
||||
// Draw Rupee Counter. Hide in Boss Rush.
|
||||
if (!gSaveContext.isBossRush) {
|
||||
for (svar1 = 0, svar3 = 16; svar1 < svar5; svar1++, svar2++, svar3 += 8) {
|
||||
OVERLAY_DISP =
|
||||
Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[svar2]]), 8, 16,
|
||||
PosX_RC+svar3, PosY_RC, 8, 16, 1 << 10, 1 << 10);
|
||||
OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[svar2]]),
|
||||
8, 16, PosX_RC + svar3, PosY_RC, 8, 16, 1 << 10, 1 << 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -5983,7 +5991,8 @@ void Interface_Draw(PlayState* play) {
|
||||
void Interface_DrawTotalGameplayTimer(PlayState* play) {
|
||||
// Draw timer based on the Gameplay Stats total time.
|
||||
|
||||
if (CVarGetInteger("gGameplayStats.ShowIngameTimer", 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2) {
|
||||
if ((gSaveContext.isBossRush && gSaveContext.bossRushOptions[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES) ||
|
||||
(CVarGetInteger("gGameplayStats.ShowIngameTimer", 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2)) {
|
||||
|
||||
s32 X_Margins_Timer = 0;
|
||||
if (CVarGetInteger("gIGTUseMargins", 0) != 0) {
|
||||
@ -6065,6 +6074,8 @@ void Interface_DrawTotalGameplayTimer(PlayState* play) {
|
||||
// Draw regular text. Change color based on if the timer is paused, running or the game is completed.
|
||||
if (gSaveContext.sohStats.gameComplete) {
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, 255);
|
||||
} else if (gSaveContext.isBossRushPaused) {
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 150, 150, 150, 255);
|
||||
} else {
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255);
|
||||
}
|
||||
@ -6483,3 +6494,66 @@ void Interface_Update(PlayState* play) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Interface_DrawTextCharacter(GraphicsContext* gfx, int16_t x, int16_t y, void* texture, uint16_t colorR,
|
||||
uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow) {
|
||||
|
||||
int32_t scale = R_TEXT_CHAR_SCALE * textScale;
|
||||
int32_t sCharTexSize = (scale / 100.0f) * 16.0f;
|
||||
int32_t sCharTexScale = 1024.0f / (scale / 100.0f);
|
||||
|
||||
OPEN_DISPS(gfx);
|
||||
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
|
||||
gDPLoadTextureBlock_4b(POLY_OPA_DISP++, texture, G_IM_FMT_I, FONT_CHAR_TEX_WIDTH, FONT_CHAR_TEX_HEIGHT, 0,
|
||||
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
|
||||
G_TX_NOLOD);
|
||||
|
||||
if (textShadow) {
|
||||
// Draw drop shadow
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, colorA);
|
||||
gSPTextureRectangle(POLY_OPA_DISP++, (x + R_TEXT_DROP_SHADOW_OFFSET) << 2, (y + R_TEXT_DROP_SHADOW_OFFSET) << 2,
|
||||
(x + R_TEXT_DROP_SHADOW_OFFSET + sCharTexSize) << 2,
|
||||
(y + R_TEXT_DROP_SHADOW_OFFSET + sCharTexSize) << 2, G_TX_RENDERTILE, 0, 0, sCharTexScale,
|
||||
sCharTexScale);
|
||||
}
|
||||
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
|
||||
// Draw normal text
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, colorR, colorG, colorB, colorA);
|
||||
gSPTextureRectangle(POLY_OPA_DISP++, x << 2, y << 2, (x + sCharTexSize) << 2, (y + sCharTexSize) << 2,
|
||||
G_TX_RENDERTILE, 0, 0, sCharTexScale, sCharTexScale);
|
||||
|
||||
CLOSE_DISPS(gfx);
|
||||
}
|
||||
|
||||
uint16_t Interface_DrawTextLine(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR,
|
||||
uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow) {
|
||||
|
||||
uint16_t textureIndex;
|
||||
uint16_t kerningOffset = 0;
|
||||
uint16_t lineOffset = 0;
|
||||
void* texture;
|
||||
const char* processedText = Interface_ReplaceSpecialCharacters(text);
|
||||
uint8_t textLength = strlen(processedText);
|
||||
|
||||
for (uint16_t i = 0; i < textLength; i++) {
|
||||
if (processedText[i] == '\n') {
|
||||
lineOffset += 15 * textScale;
|
||||
kerningOffset = 0;
|
||||
} else {
|
||||
textureIndex = processedText[i] - 32;
|
||||
|
||||
if (textureIndex != 0) {
|
||||
texture = Font_FetchCharTexture(textureIndex);
|
||||
Interface_DrawTextCharacter(gfx, x + kerningOffset, y + lineOffset, texture, colorR, colorG, colorB,
|
||||
colorA, textScale, textShadow);
|
||||
}
|
||||
kerningOffset += (uint16_t)(Message_GetCharacterWidth(textureIndex) * textScale);
|
||||
}
|
||||
}
|
||||
|
||||
return kerningOffset;
|
||||
}
|
||||
|
@ -1170,7 +1170,8 @@ void Play_Update(PlayState* play) {
|
||||
|
||||
play->gameplayFrames++;
|
||||
// Gameplay stat tracking
|
||||
if (!gSaveContext.sohStats.gameComplete) {
|
||||
if (!gSaveContext.sohStats.gameComplete &&
|
||||
(!gSaveContext.isBossRush || (gSaveContext.isBossRush && !gSaveContext.isBossRushPaused))) {
|
||||
gSaveContext.sohStats.playTimer++;
|
||||
gSaveContext.sohStats.sceneTimer++;
|
||||
gSaveContext.sohStats.roomTimer++;
|
||||
|
@ -29,6 +29,10 @@ void Sram_InitDebugSave(void) {
|
||||
Save_InitFile(true);
|
||||
}
|
||||
|
||||
void Sram_InitBossRushSave(void) {
|
||||
Save_InitFile(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy save currently on the buffer to Save Context and complete various tasks to open the save.
|
||||
* This includes:
|
||||
|
@ -274,7 +274,8 @@ void BgBreakwall_Wait(BgBreakwall* this, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
if (this->collider.base.acFlags & 2 || blueFireArrowHit) {
|
||||
// Break the floor immediately in Boss Rush so the player can jump in the hole immediately.
|
||||
if (this->collider.base.acFlags & 2 || blueFireArrowHit || gSaveContext.isBossRush) {
|
||||
Vec3f effectPos;
|
||||
s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
#include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
@ -1346,6 +1347,7 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) {
|
||||
this->cameraAt.y = camera->at.y;
|
||||
this->cameraAt.z = camera->at.z;
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_KING_DODONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
break;
|
||||
case 5:
|
||||
tempSin = Math_SinS(this->actor.shape.rot.y - 0x1388) * 150.0f;
|
||||
@ -1630,11 +1632,13 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) {
|
||||
|
||||
if (this->unk_1DA == 820) {
|
||||
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART,
|
||||
Math_SinS(this->actor.shape.rot.y) * -50.0f + this->actor.world.pos.x,
|
||||
this->actor.world.pos.y,
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_Spawn(
|
||||
&play->actorCtx, play, ACTOR_ITEM_B_HEART,
|
||||
Math_SinS(this->actor.shape.rot.y) * -50.0f + this->actor.world.pos.x, this->actor.world.pos.y,
|
||||
Math_CosS(this->actor.shape.rot.y) * -50.0f + this->actor.world.pos.z, 0, 0, 0, 0, true);
|
||||
}
|
||||
}
|
||||
if (this->unk_1DA == 600) {
|
||||
camera = Play_GetCamera(play, MAIN_CAM);
|
||||
camera->eye = this->cameraEye;
|
||||
@ -1647,8 +1651,11 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) {
|
||||
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE);
|
||||
func_80064534(play, &play->csCtx);
|
||||
func_8002DF54(play, &this->actor, 7);
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f,
|
||||
-3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD);
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD);
|
||||
} else {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, WARP_DUNGEON_ADULT, false);
|
||||
}
|
||||
this->skelAnime.playSpeed = 0.0f;
|
||||
Flags_SetClear(play, play->roomCtx.curRoom.num);
|
||||
}
|
||||
|
@ -913,7 +913,7 @@ void BossFd_Fly(BossFd* this, PlayState* play) {
|
||||
this->actionFunc = BossFd_Wait;
|
||||
this->actor.world.pos.y -= 1000.0f;
|
||||
}
|
||||
if (this->timers[0] == 7) {
|
||||
if (this->timers[0] == 7 && !gSaveContext.isBossRush) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x,
|
||||
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
#include "vt.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
@ -788,8 +789,13 @@ void BossFd2_Death(BossFd2* this, PlayState* play) {
|
||||
this->deathCamera = 0;
|
||||
func_80064534(play, &play->csCtx);
|
||||
func_8002DF54(play, &this->actor, 7);
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f,
|
||||
0, 0, 0, WARP_DUNGEON_ADULT);
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0,
|
||||
0, WARP_DUNGEON_ADULT);
|
||||
} else {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0,
|
||||
WARP_DUNGEON_ADULT, true);
|
||||
}
|
||||
Flags_SetClear(play, play->roomCtx.curRoom.num);
|
||||
}
|
||||
break;
|
||||
@ -894,6 +900,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_VOLVAGIA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
} else if (damage) {
|
||||
BossFd2_SetupDamaged(this, play);
|
||||
this->work[FD2_DAMAGE_FLASH_TIMER] = 10;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h"
|
||||
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -569,7 +570,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
Play_ChangeCameraStatus(play, this->csCamIndex, CAM_STAT_ACTIVE);
|
||||
this->csCamFov = 60.0f;
|
||||
|
||||
if (gSaveContext.eventChkInf[7] & 0x100 || gSaveContext.n64ddFlag) {
|
||||
if (gSaveContext.eventChkInf[7] & 0x100 || gSaveContext.n64ddFlag || gSaveContext.isBossRush) {
|
||||
// watched cutscene already, skip most of it
|
||||
this->csState = 17;
|
||||
this->csTimer = 0;
|
||||
@ -580,7 +581,9 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
BossGanon_SetIntroCsCamera(this, 11);
|
||||
this->unk_198 = 2;
|
||||
this->timers[2] = 110;
|
||||
if (!(gSaveContext.isBossRush && gSaveContext.bossRushOptions[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_NEVER)) {
|
||||
gSaveContext.healthAccumulator = 0x140;
|
||||
}
|
||||
Audio_QueueSeqCmd(NA_BGM_STOP);
|
||||
} else {
|
||||
this->useOpenHand = true;
|
||||
@ -901,7 +904,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
this->csTimer = 0;
|
||||
this->csCamFov = 60.0f;
|
||||
BossGanon_SetIntroCsCamera(this, 12);
|
||||
if (!gSaveContext.n64ddFlag) {
|
||||
if (!gSaveContext.n64ddFlag && !gSaveContext.isBossRush) {
|
||||
Message_StartTextbox(play, 0x70CB, NULL);
|
||||
}
|
||||
}
|
||||
@ -925,7 +928,9 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
|
||||
this->csState = 19;
|
||||
this->csTimer = 0;
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Message_StartTextbox(play, 0x70CC, NULL);
|
||||
}
|
||||
Animation_MorphToPlayOnce(&this->skelAnime, &gGanondorfRaiseHandStartAnim, -5.0f);
|
||||
this->triforceType = GDF_TRIFORCE_DORF;
|
||||
this->fwork[GDF_TRIFORCE_SCALE] = 10.0f;
|
||||
@ -967,7 +972,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
|
||||
if ((this->csTimer > 80) && (Message_GetState(&play->msgCtx) == TEXT_STATE_NONE)) {
|
||||
// In rando, skip past dark waves section straight to title card phase of the cutscene.
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
if (gSaveContext.n64ddFlag || gSaveContext.isBossRush) {
|
||||
this->timers[2] = 30;
|
||||
this->csCamAt.x = this->unk_1FC.x - 10.0f;
|
||||
this->csCamAt.y = this->unk_1FC.y + 30.0f;
|
||||
@ -1274,19 +1279,16 @@ void BossGanon_DeathAndTowerCutscene(BossGanon* this, PlayState* play) {
|
||||
this->actor.shape.yOffset = -7000.0f;
|
||||
|
||||
this->actor.shape.rot.y = 0;
|
||||
// In rando, skip Ganondorf dying and go straight to next scene.
|
||||
// Commented out for potential future use.
|
||||
// Skip Ganondorf dying and go straight to next scene.
|
||||
// The cutscene skip met a mixed reaction, so until we figure out a better way of doing it,
|
||||
// it will stay not-skipped.
|
||||
/*if (!gSaveContext.n64ddFlag) {
|
||||
// it will stay not-skipped outside of Boss Rush (originally implemented for randomizer).
|
||||
if (!gSaveContext.isBossRush) {
|
||||
this->csState = 1;
|
||||
this->csTimer = 0;
|
||||
} else {
|
||||
this->csState = 9;
|
||||
this->csTimer = 170;
|
||||
}*/
|
||||
this->csState = 1;
|
||||
this->csTimer = 0;
|
||||
}
|
||||
this->useOpenHand = true;
|
||||
// fallthrough
|
||||
case 1:
|
||||
@ -1537,7 +1539,7 @@ void BossGanon_DeathAndTowerCutscene(BossGanon* this, PlayState* play) {
|
||||
|
||||
if (this->csTimer == 180) {
|
||||
play->sceneLoadFlag = 0x14;
|
||||
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE)) {
|
||||
if ((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE) || gSaveContext.isBossRush)) {
|
||||
Flags_SetEventChkInf(0xC7);
|
||||
play->nextEntranceIndex = 0x517;
|
||||
}
|
||||
@ -1560,7 +1562,7 @@ void BossGanon_DeathAndTowerCutscene(BossGanon* this, PlayState* play) {
|
||||
sBossGanonZelda = (EnZl3*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_ZL3, 0.0f,
|
||||
6000.0f, 0.0f, 0, 0, 0, 0x2000);
|
||||
|
||||
if (!gSaveContext.n64ddFlag) {
|
||||
if (!gSaveContext.n64ddFlag && !gSaveContext.isBossRush) {
|
||||
this->csState = 101;
|
||||
} else {
|
||||
this->skelAnime.playSpeed = 1.0f;
|
||||
@ -1688,7 +1690,7 @@ void BossGanon_DeathAndTowerCutscene(BossGanon* this, PlayState* play) {
|
||||
// fallthrough
|
||||
case 104:
|
||||
// In rando, fade out the white here as the earlier part is skipped.
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
if (gSaveContext.n64ddFlag || gSaveContext.isBossRush) {
|
||||
Math_ApproachZeroF(&this->whiteFillAlpha, 1.0f, 10.0f);
|
||||
}
|
||||
|
||||
@ -1710,7 +1712,7 @@ void BossGanon_DeathAndTowerCutscene(BossGanon* this, PlayState* play) {
|
||||
|
||||
if (this->csTimer == 50) {
|
||||
// In rando, skip the rest of the cutscene after the crystal around Zelda dissapears.
|
||||
if (!gSaveContext.n64ddFlag) {
|
||||
if (!gSaveContext.n64ddFlag && !gSaveContext.isBossRush) {
|
||||
sBossGanonZelda->unk_3C8 = 4;
|
||||
} else {
|
||||
this->csState = 108;
|
||||
@ -2811,6 +2813,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) {
|
||||
Audio_QueueSeqCmd(0x100100FF);
|
||||
this->screenFlashTimer = 4;
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANONDORF] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
} else {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "objects/object_ganon_anime3/object_ganon_anime3.h"
|
||||
#include "objects/object_geff/object_geff.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -233,7 +234,7 @@ void func_808FD5F4(BossGanon2* this, PlayState* play) {
|
||||
sBossGanon2Zelda->actor.shape.rot.y = -0x7000;
|
||||
|
||||
// In rando, skip past the cutscene to the part where the player takes control again.
|
||||
if (!gSaveContext.n64ddFlag) {
|
||||
if (!gSaveContext.n64ddFlag && !gSaveContext.isBossRush) {
|
||||
this->csState = 1;
|
||||
this->csTimer = 0;
|
||||
} else {
|
||||
@ -1681,6 +1682,7 @@ void func_8090120C(BossGanon2* this, PlayState* play) {
|
||||
(player->swordState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) {
|
||||
func_80064520(play, &play->csCtx);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
gSaveContext.sohStats.gameComplete = true;
|
||||
this->unk_39E = Play_CreateSubCamera(play);
|
||||
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h"
|
||||
#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h"
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
@ -958,12 +959,12 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) {
|
||||
case DEATH_THROES:
|
||||
switch (this->work[GND_ACTION_STATE]) {
|
||||
case DEATH_SPASM:
|
||||
if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME]) && !gSaveContext.n64ddFlag) {
|
||||
if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME]) && !gSaveContext.n64ddFlag && !gSaveContext.isBossRush) {
|
||||
this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonAirDamageAnim);
|
||||
Animation_Change(&this->skelAnime, &gPhantomGanonAirDamageAnim, 0.5f, 0.0f,
|
||||
this->fwork[GND_END_FRAME], ANIMMODE_ONCE_INTERP, 0.0f);
|
||||
this->work[GND_ACTION_STATE] = DEATH_LIMP;
|
||||
} else if (gSaveContext.n64ddFlag) {
|
||||
} else if (gSaveContext.n64ddFlag || gSaveContext.isBossRush) {
|
||||
// Skip to death scream animation and move ganondrof to middle
|
||||
this->deathState = DEATH_SCREAM;
|
||||
this->timers[0] = 50;
|
||||
@ -990,7 +991,7 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) {
|
||||
bodyDecayLevel = 1;
|
||||
break;
|
||||
}
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
if (gSaveContext.n64ddFlag || gSaveContext.isBossRush) {
|
||||
break;
|
||||
}
|
||||
Math_ApproachS(&this->actor.shape.rot.y, this->work[GND_VARIANCE_TIMER] * -100, 5, 0xBB8);
|
||||
@ -1087,8 +1088,8 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) {
|
||||
bodyDecayLevel = 10;
|
||||
if (this->timers[0] == 150) {
|
||||
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X,
|
||||
GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y,
|
||||
GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true);
|
||||
}
|
||||
|
||||
Math_ApproachZeroF(&this->cameraEye.y, 0.05f, 1.0f); // GND_BOSSROOM_CENTER_Y + 33.0f
|
||||
@ -1104,8 +1105,10 @@ void BossGanondrof_Death(BossGanondrof* this, PlayState* play) {
|
||||
this->deathCamera = 0;
|
||||
func_80064534(play, &play->csCtx);
|
||||
func_8002DF54(play, &this->actor, 7);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, GND_BOSSROOM_CENTER_X,
|
||||
GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z + 200.0f, 0, 0, 0, 0, true);
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y,
|
||||
GND_BOSSROOM_CENTER_Z + 200.0f, 0, 0, 0, 0, true);
|
||||
}
|
||||
this->actor.child = &horse->actor;
|
||||
this->killActor = true;
|
||||
horse->killActor = true;
|
||||
@ -1246,6 +1249,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) {
|
||||
BossGanondrof_SetupDeath(this, play);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "overlays/actors/ovl_En_Goma/z_en_goma.h"
|
||||
#include "overlays/actors/ovl_Door_Shutter/z_door_shutter.h"
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
@ -1117,9 +1118,11 @@ void BossGoma_Defeated(BossGoma* this, PlayState* play) {
|
||||
this->timer = 70;
|
||||
this->decayingProgress = 0;
|
||||
this->subCameraFollowSpeed = 0.0f;
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x,
|
||||
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
@ -1149,8 +1152,13 @@ void BossGoma_Defeated(BossGoma* this, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, childPos.x,
|
||||
this->actor.world.pos.y, childPos.z, 0, 0, 0, WARP_DUNGEON_CHILD);
|
||||
} else {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, childPos.x, this->actor.world.pos.y,
|
||||
childPos.z, 0, 0, 0, WARP_DUNGEON_ADULT, false);
|
||||
}
|
||||
Flags_SetClear(play, play->roomCtx.curRoom.num);
|
||||
}
|
||||
|
||||
@ -1834,6 +1842,7 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) {
|
||||
BossGoma_SetupDefeated(this, play);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GOHMA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
}
|
||||
|
||||
this->invincibilityFrames = 10;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "vt.h"
|
||||
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -1115,11 +1116,16 @@ void BossMo_Tentacle(BossMo* this, PlayState* play) {
|
||||
BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)play->specialEffects, &spD4, &spE0,
|
||||
((300 - indS1) * .0015f) + 0.13f);
|
||||
}
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1,
|
||||
this->actor.world.pos.x, -280.0f, this->actor.world.pos.z, 0, 0, 0,
|
||||
WARP_DUNGEON_ADULT);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x + 200.0f,
|
||||
-280.0f, this->actor.world.pos.z, 0, 0, 0, 0, true);
|
||||
} else {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, this->actor.world.pos.x, -280.0f,
|
||||
this->actor.world.pos.z, 0, 0, 0, WARP_DUNGEON_ADULT, true);
|
||||
}
|
||||
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR);
|
||||
Flags_SetClear(play, play->roomCtx.curRoom.num);
|
||||
}
|
||||
@ -1788,6 +1794,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) {
|
||||
((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) {
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_MORPHA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
|
||||
this->csState = MO_DEATH_START;
|
||||
sMorphaTent1->drawActor = false;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h"
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_DRAGGED_BY_HOOKSHOT)
|
||||
|
||||
@ -1201,11 +1202,13 @@ void BossSst_HeadFinish(BossSst* this, PlayState* play) {
|
||||
Flags_SetClear(play, play->roomCtx.curRoom.num);
|
||||
}
|
||||
} else if (this->effects[0].alpha == 0) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z, 0,
|
||||
0, 0, WARP_DUNGEON_ADULT, true);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z, 0, 0, 0,
|
||||
WARP_DUNGEON_ADULT, true);
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART,
|
||||
(Math_SinS(this->actor.shape.rot.y) * 200.0f) + ROOM_CENTER_X, ROOM_CENTER_Y,
|
||||
Math_CosS(this->actor.shape.rot.y) * 200.0f + ROOM_CENTER_Z, 0, 0, 0, 0, true);
|
||||
}
|
||||
BossSst_SetCameraTargets(1.0f, 7);
|
||||
this->effectMode = BONGO_NULL;
|
||||
} else if (this->timer == 0) {
|
||||
@ -2561,6 +2564,7 @@ void BossSst_HeadCollisionCheck(BossSst* this, PlayState* play) {
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
BossSst_HeadSetupDeath(this, play);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BONGO_BONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
} else {
|
||||
BossSst_HeadSetupDamage(this);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "objects/object_tw/object_tw.h"
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -2364,7 +2365,7 @@ void BossTw_DeathCSMsgSfx(BossTw* this, PlayState* play) {
|
||||
sp35 = 0;
|
||||
|
||||
// Skip ahead to last part of the cutscene in rando
|
||||
if (this->work[CS_TIMER_2] == 10 && gSaveContext.n64ddFlag) {
|
||||
if (this->work[CS_TIMER_2] == 10 && (gSaveContext.n64ddFlag || gSaveContext.isBossRush)) {
|
||||
this->work[CS_TIMER_2] = 860;
|
||||
}
|
||||
|
||||
@ -2549,7 +2550,7 @@ void BossTw_DeathCSMsgSfx(BossTw* this, PlayState* play) {
|
||||
|
||||
// Add seperate timings for the "beam" that opens and closes around the sisters
|
||||
// Needed because we skip ahead in cutscene timer value so it never gets called otherwise
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
if (gSaveContext.n64ddFlag || gSaveContext.isBossRush) {
|
||||
if (this->work[CS_TIMER_2] < 900) {
|
||||
Math_ApproachF(&this->workf[UNK_F18], 255.0f, 0.1f, 5.0f);
|
||||
} else if (this->work[CS_TIMER_2] > 910) {
|
||||
@ -2794,9 +2795,14 @@ void BossTw_TwinrovaDeathCS(BossTw* this, PlayState* play) {
|
||||
func_80064534(play, &play->csCtx);
|
||||
func_8002DF54(play, &this->actor, 7);
|
||||
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR);
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f,
|
||||
0.0f, 0, 0, 0, WARP_DUNGEON_ADULT);
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0,
|
||||
0, 0, WARP_DUNGEON_ADULT);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -600.0f, 230.f, 0.0f, 0, 0, 0, 0, true);
|
||||
} else {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, 0, 0,
|
||||
WARP_DUNGEON_ADULT, true);
|
||||
}
|
||||
this->actor.world.pos.y = -2000.0f;
|
||||
this->workf[UNK_F18] = 0.0f;
|
||||
sKoumePtr->visible = sKotakePtr->visible = false;
|
||||
@ -5287,6 +5293,7 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) {
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_TWINROVA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,10 @@
|
||||
#include "objects/object_bv/object_bv.h"
|
||||
#include "overlays/actors/ovl_En_Boom/z_en_boom.h"
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
@ -1396,6 +1398,7 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) {
|
||||
BossVa_SetupBodyDeath(this, play);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BARINADE] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
BossRush_HandleCompleteBoss(play);
|
||||
return;
|
||||
}
|
||||
this->actor.speedXZ = -10.0f;
|
||||
@ -1650,8 +1653,10 @@ void BossVa_BodyDeath(BossVa* this, PlayState* play) {
|
||||
func_8002DF54(play, &this->actor, 7);
|
||||
sCsState++;
|
||||
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.world.pos.x,
|
||||
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0, true);
|
||||
}
|
||||
|
||||
for (i = 2, sp7C = 2; i > 0; i--) {
|
||||
if (Math_Vec3f_DistXYZ(&sWarpPos[i], &player->actor.world.pos) <
|
||||
@ -1660,8 +1665,13 @@ void BossVa_BodyDeath(BossVa* this, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!gSaveContext.isBossRush) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_RU1, sWarpPos[sp7C].x, sWarpPos[sp7C].y,
|
||||
sWarpPos[sp7C].z, 0, 0, 0, 0, true);
|
||||
} else {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, sWarpPos[sp7C].x, sWarpPos[sp7C].y,
|
||||
sWarpPos[sp7C].z, 0, 0, 0, WARP_DUNGEON_ADULT, false);
|
||||
}
|
||||
}
|
||||
case DEATH_FINISH:
|
||||
Rand_CenteredFloat(0.5f);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "overlays/actors/ovl_En_Elf/z_en_elf.h"
|
||||
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
|
||||
#include "objects/object_sa/object_sa.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
|
||||
#include "vt.h"
|
||||
|
||||
@ -253,11 +254,19 @@ void func_8098E960(DemoSa* this, PlayState* play) {
|
||||
|
||||
if ((gSaveContext.chamberCutsceneNum == 0) && (gSaveContext.sceneSetupIndex < 4)) {
|
||||
player = GET_PLAYER(play);
|
||||
if (!gSaveContext.isBossRush) {
|
||||
this->action = 1;
|
||||
play->csCtx.segment = D_8099010C;
|
||||
gSaveContext.cutsceneTrigger = 2;
|
||||
Item_Give(play, ITEM_MEDALLION_FOREST);
|
||||
player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000;
|
||||
} else {
|
||||
this->action = 1;
|
||||
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
|
||||
player->actor.world.rot.y = player->actor.shape.rot.y = -5461 + 0x8000;
|
||||
}
|
||||
BossRush_SpawnBlueWarps(play);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#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"
|
||||
|
||||
#define FLAGS 0
|
||||
|
||||
@ -251,8 +252,13 @@ void DoorWarp1_SetupBlueCrystal(DoorWarp1* this, PlayState* play) {
|
||||
-255;
|
||||
}
|
||||
|
||||
if (!gSaveContext.isBossRush) {
|
||||
play->envCtx.adjFogNear = -500;
|
||||
this->warpTimer = 30;
|
||||
} else {
|
||||
play->envCtx.adjFogNear = 0;
|
||||
this->warpTimer = 0;
|
||||
}
|
||||
this->unk_1B8 = 4000;
|
||||
DoorWarp1_SetupAction(this, DoorWarp1_BlueCrystal);
|
||||
}
|
||||
@ -293,7 +299,11 @@ void DoorWarp1_SetPlayerPos(DoorWarp1* this, PlayState* play) {
|
||||
|
||||
player->actor.velocity.y = 0.0f;
|
||||
player->actor.world.pos.x = this->actor.world.pos.x;
|
||||
if (!gSaveContext.isBossRush) {
|
||||
player->actor.world.pos.y = this->actor.world.pos.y + 55.0f;
|
||||
} else {
|
||||
player->actor.world.pos.y = this->actor.world.pos.y;
|
||||
}
|
||||
player->actor.world.pos.z = this->actor.world.pos.z;
|
||||
}
|
||||
|
||||
@ -313,8 +323,12 @@ void func_80999214(DoorWarp1* this, PlayState* play) {
|
||||
|
||||
Math_SmoothStepToF(&this->crystalAlpha, 255.0f, 0.2f, 5.0f, 0.1f);
|
||||
|
||||
if (!gSaveContext.isBossRush) {
|
||||
darkness = (f32)(40 - this->warpTimer) / 40.0f;
|
||||
darkness = CLAMP_MIN(darkness, 0);
|
||||
} else {
|
||||
darkness = 0.0f;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
play->envCtx.adjAmbientColor[i] = play->envCtx.adjFogColor[i] = play->envCtx.adjLight1Color[i] =
|
||||
@ -352,8 +366,10 @@ void func_80999348(DoorWarp1* this, PlayState* play) {
|
||||
void DoorWarp1_FloatPlayer(DoorWarp1* this, PlayState* play) {
|
||||
Player* player = GET_PLAYER(play);
|
||||
|
||||
if (!gSaveContext.isBossRush) {
|
||||
player->actor.gravity = -0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
void DoorWarp1_PurpleCrystal(DoorWarp1* this, PlayState* play) {
|
||||
if (this->warpTimer != 0) {
|
||||
@ -744,6 +760,11 @@ 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 (gSaveContext.isBossRush) {
|
||||
BossRush_HandleBlueWarpHeal(play);
|
||||
}
|
||||
|
||||
player = GET_PLAYER(play);
|
||||
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
@ -805,7 +826,9 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) {
|
||||
this->warpTimer++;
|
||||
|
||||
if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) {
|
||||
if (play->sceneNum == SCENE_MORIBOSSROOM) {
|
||||
if (gSaveContext.isBossRush) {
|
||||
BossRush_HandleBlueWarp(play, this->actor.world.pos.x, this->actor.world.pos.z);
|
||||
} else if (play->sceneNum == SCENE_MORIBOSSROOM) {
|
||||
if (!(gSaveContext.eventChkInf[4] & 0x100)) {
|
||||
gSaveContext.eventChkInf[4] |= 0x100;
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_FOREST_TEMPLE);
|
||||
|
@ -202,6 +202,11 @@ void EnBox_Init(Actor* thisx, PlayState* play2) {
|
||||
if (play->sceneNum == SCENE_BMORI1 && this->dyna.actor.params == 10222) {
|
||||
this->movementFlags = ENBOX_MOVE_IMMOBILE;
|
||||
}
|
||||
|
||||
// Delete chests in Boss Rush. Mainly for the chest in King Dodongo's boss room.
|
||||
if (gSaveContext.isBossRush) {
|
||||
EnBox_SetupAction(this, EnBox_Destroy);
|
||||
}
|
||||
}
|
||||
|
||||
void EnBox_Destroy(Actor* thisx, PlayState* play) {
|
||||
|
@ -277,6 +277,11 @@ void EnKusa_Destroy(Actor* thisx, PlayState* play2) {
|
||||
}
|
||||
|
||||
void EnKusa_SetupWaitObject(EnKusa* this) {
|
||||
// Kill bushes in Boss Rush. Used in Gohma's arena.
|
||||
if (gSaveContext.isBossRush) {
|
||||
Actor_Kill(this);
|
||||
}
|
||||
|
||||
EnKusa_SetupAction(this, EnKusa_WaitObject);
|
||||
}
|
||||
|
||||
|
@ -221,6 +221,11 @@ void ObjTsubo_WaterBreak(ObjTsubo* this, PlayState* play) {
|
||||
}
|
||||
|
||||
void ObjTsubo_SetupWaitForObject(ObjTsubo* this) {
|
||||
// Remove pots in Boss Rush. Present in Barinade's and Ganondorf's arenas.
|
||||
if (gSaveContext.isBossRush) {
|
||||
Actor_Kill(this);
|
||||
}
|
||||
|
||||
this->actionFunc = ObjTsubo_WaitForObject;
|
||||
}
|
||||
|
||||
|
@ -4437,7 +4437,8 @@ s32 func_80839800(Player* this, PlayState* play) {
|
||||
if ((this->doorType != PLAYER_DOORTYPE_NONE) &&
|
||||
(!(this->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) ||
|
||||
((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)))) {
|
||||
if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (func_8084F9A0 == this->func_674)) {
|
||||
// Disable doors in Boss Rush so the player can't leave the boss rooms backwards.
|
||||
if ((CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (func_8084F9A0 == this->func_674)) && !gSaveContext.isBossRush) {
|
||||
doorActor = this->doorActor;
|
||||
|
||||
if (this->doorType <= PLAYER_DOORTYPE_AJAR) {
|
||||
|
@ -61,6 +61,10 @@ typedef enum {
|
||||
CM_START_QUEST_MENU,
|
||||
CM_QUEST_TO_MAIN,
|
||||
CM_NAME_ENTRY_TO_QUEST_MENU,
|
||||
CM_ROTATE_TO_BOSS_RUSH_MENU,
|
||||
CM_BOSS_RUSH_MENU,
|
||||
CM_START_BOSS_RUSH_MENU,
|
||||
CM_BOSS_RUSH_TO_QUEST,
|
||||
} ConfigMode;
|
||||
|
||||
typedef enum {
|
||||
@ -168,6 +172,13 @@ typedef enum {
|
||||
/* 99 */ FS_KBD_BTN_NONE = 99
|
||||
} KeyboardButton;
|
||||
|
||||
typedef enum {
|
||||
/* 00 */ FS_QUEST_NORMAL,
|
||||
/* 01 */ FS_QUEST_MASTER,
|
||||
/* 02 */ FS_QUEST_RANDOMIZER,
|
||||
/* 03 */ FS_QUEST_BOSSRUSH,
|
||||
} FileSelectQuest;
|
||||
|
||||
void FileChoose_SetupCopySource(GameState* thisx);
|
||||
void FileChoose_SelectCopySource(GameState* thisx);
|
||||
void FileChoose_SetupCopyDest1(GameState* thisx);
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "file_choose.h"
|
||||
#include "file_choose.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -11,28 +11,20 @@
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
#include "soh_assets.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
|
||||
|
||||
#define NORMAL_QUEST 0
|
||||
#define MASTER_QUEST 1
|
||||
#define RANDOMIZER_QUEST 2
|
||||
#define MIN_QUEST (ResourceMgr_GameHasOriginal() ? NORMAL_QUEST : MASTER_QUEST)
|
||||
u8 getMaxQuest() {
|
||||
if ((strnlen(CVarGetString("gSpoilerLog", ""), 1) != 0)) {
|
||||
return RANDOMIZER_QUEST;
|
||||
#define MIN_QUEST (ResourceMgr_GameHasOriginal() ? FS_QUEST_NORMAL : FS_QUEST_MASTER)
|
||||
#define MAX_QUEST FS_QUEST_BOSSRUSH
|
||||
u8 hasRandomizerQuest() {
|
||||
if (strnlen(CVarGetString("gSpoilerLog", ""), 1) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ResourceMgr_GameHasMasterQuest()) {
|
||||
return MASTER_QUEST;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return NORMAL_QUEST;
|
||||
}
|
||||
#define MAX_QUEST getMaxQuest()
|
||||
|
||||
void FileChoose_DrawTextureI8(GraphicsContext* gfxCtx, const void* texture, s16 texWidth, s16 texHeight, s16 rectLeft, s16 rectTop,
|
||||
s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) {
|
||||
s16 rectWidth, s16 rectHeight, s16 dsdx, s16 dtdy) {
|
||||
OPEN_DISPS(gfxCtx);
|
||||
gDPLoadTextureBlock(POLY_OPA_DISP++, texture, G_IM_FMT_I, G_IM_SIZ_8b, texWidth, texHeight, 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);
|
||||
@ -305,7 +297,7 @@ void DrawSeedHashSprites(FileChooseContext* this) {
|
||||
// Draw icons on the main menu, when a rando file is selected, and when quest selection is set to rando
|
||||
if ((this->configMode == CM_MAIN_MENU &&
|
||||
(this->selectMode != SM_CONFIRM_FILE || Save_GetSaveMetaInfo(this->selectedFileIndex)->randoSave == 1)) ||
|
||||
(this->configMode == CM_QUEST_MENU && this->questType[this->buttonIndex] == RANDOMIZER_QUEST)) {
|
||||
(this->configMode == CM_QUEST_MENU && this->questType[this->buttonIndex] == FS_QUEST_RANDOMIZER)) {
|
||||
|
||||
if (this->fileInfoAlpha[this->selectedFileIndex] > 0) {
|
||||
// Use file info alpha to match fading
|
||||
@ -415,24 +407,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
|
||||
if (!Save_GetSaveMetaInfo(this->buttonIndex)->valid) {
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
this->prevConfigMode = this->configMode;
|
||||
if (MIN_QUEST != MAX_QUEST) {
|
||||
this->configMode = CM_ROTATE_TO_QUEST_MENU;
|
||||
} else {
|
||||
this->configMode = CM_ROTATE_TO_NAME_ENTRY;
|
||||
gSaveContext.isMasterQuest = MIN_QUEST == MASTER_QUEST;
|
||||
this->questType[this->buttonIndex] = MIN_QUEST;
|
||||
CVarSetInteger("gOnFileSelectNameEntry", 1);
|
||||
this->kbdButton = FS_KBD_BTN_NONE;
|
||||
this->charPage = FS_CHAR_PAGE_ENG;
|
||||
this->kbdX = 0;
|
||||
this->kbdY = 0;
|
||||
this->charIndex = 0;
|
||||
this->charBgAlpha = 0;
|
||||
this->newFileNameCharCount = CVarGetInteger("gLinkDefaultName", 0) ? 4 : 0;
|
||||
this->nameEntryBoxPosX = 120;
|
||||
this->nameEntryBoxAlpha = 0;
|
||||
memcpy(Save_GetSaveMetaInfo(this->buttonIndex)->playerName, CVarGetInteger("gLinkDefaultName", 0) ? &linkName : &emptyName, 8);
|
||||
}
|
||||
this->logoAlpha = 0;
|
||||
} else if(!FileChoose_IsSaveCompatible(Save_GetSaveMetaInfo(this->buttonIndex))) {
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
@ -588,6 +563,19 @@ void FileChoose_StartQuestMenu(GameState* thisx) {
|
||||
}
|
||||
}
|
||||
|
||||
void FileChoose_StartBossRushMenu(GameState* thisx) {
|
||||
FileChooseContext* this = (FileChooseContext*)thisx;
|
||||
|
||||
this->logoAlpha -= 25;
|
||||
this->bossRushUIAlpha = 0;
|
||||
this->bossRushArrowOffset = 0;
|
||||
|
||||
if (this->logoAlpha >= 0) {
|
||||
this->logoAlpha = 0;
|
||||
this->configMode = CM_BOSS_RUSH_MENU;
|
||||
}
|
||||
}
|
||||
|
||||
void FileChoose_UpdateQuestMenu(GameState* thisx) {
|
||||
static u8 emptyName[] = { 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E };
|
||||
static u8 linkName[] = { 0x15, 0x2C, 0x31, 0x2E, 0x3E, 0x3E, 0x3E, 0x3E };
|
||||
@ -595,31 +583,30 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
|
||||
FileChooseContext* this = (FileChooseContext*)thisx;
|
||||
Input* input = &this->state.input[0];
|
||||
s8 i = 0;
|
||||
bool dpad = CVarGetInteger("gDpadText", 0);(dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN | BTN_DUP));
|
||||
bool dpad = CVarGetInteger("gDpadText", 0);
|
||||
|
||||
FileChoose_UpdateRandomizer();
|
||||
|
||||
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))) {
|
||||
this->questType[this->buttonIndex] += 1;
|
||||
if (this->questType[this->buttonIndex] == MASTER_QUEST && !ResourceMgr_GameHasMasterQuest()) {
|
||||
// the only case not handled by the MIN/MAX_QUEST logic below. This will either put it at
|
||||
// above MAX_QUEST in which case it will wrap back around, or it will put it on MAX_QUEST
|
||||
// in which case if MAX_QUEST even is that number it will be a valid selection that won't
|
||||
// crash.
|
||||
while ((this->questType[this->buttonIndex] == FS_QUEST_MASTER && !ResourceMgr_GameHasMasterQuest()) ||
|
||||
(this->questType[this->buttonIndex] == FS_QUEST_RANDOMIZER && !hasRandomizerQuest())) {
|
||||
// If Master Quest is selected without a Master Quest OTR present or when Randomizer Quest is
|
||||
// selected without a loaded Randomizer seed, skip past it.
|
||||
this->questType[this->buttonIndex] += 1;
|
||||
}
|
||||
} else if (this->stickRelX < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT))) {
|
||||
this->questType[this->buttonIndex] -= 1;
|
||||
if (this->questType[this->buttonIndex] == MASTER_QUEST && !ResourceMgr_GameHasMasterQuest()) {
|
||||
// the only case not handled by the MIN/MAX_QUEST logic below. This will either put it at
|
||||
// below MIN_QUEST in which case it will wrap back around, or it will put it on MIN_QUEST
|
||||
// in which case if MIN_QUEST even is that number it will be a valid selection that won't
|
||||
// crash.
|
||||
while ((this->questType[this->buttonIndex] == FS_QUEST_MASTER && !ResourceMgr_GameHasMasterQuest()) ||
|
||||
(this->questType[this->buttonIndex] == FS_QUEST_RANDOMIZER && !hasRandomizerQuest())) {
|
||||
// If Master Quest is selected without a Master Quest OTR present or when Randomizer Quest is
|
||||
// selected without a loaded Randomizer seed, skip past it.
|
||||
this->questType[this->buttonIndex] -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If current buttonIndex is higher or lower than the min/max value, wrap around.
|
||||
if (this->questType[this->buttonIndex] > MAX_QUEST) {
|
||||
this->questType[this->buttonIndex] = MIN_QUEST;
|
||||
} else if (this->questType[this->buttonIndex] < MIN_QUEST) {
|
||||
@ -630,9 +617,19 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
|
||||
}
|
||||
|
||||
if (CHECK_BTN_ALL(input->press.button, BTN_A)) {
|
||||
|
||||
gSaveContext.isMasterQuest = this->questType[this->buttonIndex] == FS_QUEST_MASTER;
|
||||
gSaveContext.n64ddFlag = this->questType[this->buttonIndex] == FS_QUEST_RANDOMIZER;
|
||||
gSaveContext.isBossRush = this->questType[this->buttonIndex] == FS_QUEST_BOSSRUSH;
|
||||
gSaveContext.isBossRushPaused = false;
|
||||
|
||||
if (this->questType[this->buttonIndex] == FS_QUEST_BOSSRUSH) {
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
this->prevConfigMode = this->configMode;
|
||||
this->configMode = CM_ROTATE_TO_BOSS_RUSH_MENU;
|
||||
return;
|
||||
} else {
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
gSaveContext.isMasterQuest = this->questType[this->buttonIndex] == MASTER_QUEST;
|
||||
gSaveContext.n64ddFlag = this->questType[this->buttonIndex] == RANDOMIZER_QUEST;
|
||||
osSyncPrintf("Selected Dungeon Quest: %d\n", gSaveContext.isMasterQuest);
|
||||
this->prevConfigMode = this->configMode;
|
||||
this->configMode = CM_ROTATE_TO_NAME_ENTRY;
|
||||
@ -650,6 +647,7 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
|
||||
memcpy(Save_GetSaveMetaInfo(this->buttonIndex)->playerName, CVarGetInteger("gLinkDefaultName", 0) ? &linkName : &emptyName, 8);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
|
||||
this->configMode = CM_QUEST_TO_MAIN;
|
||||
@ -657,6 +655,93 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
|
||||
}
|
||||
}
|
||||
|
||||
void FileChoose_UpdateBossRushMenu(GameState* thisx) {
|
||||
FileChoose_UpdateStickDirectionPromptAnim(thisx);
|
||||
FileChooseContext* this = (FileChooseContext*)thisx;
|
||||
Input* input = &this->state.input[0];
|
||||
bool dpad = CVarGetInteger("gDpadText", 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) > BOSSRUSH_OPTIONS_AMOUNT - 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 = BOSSRUSH_OPTIONS_AMOUNT - 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, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
}
|
||||
|
||||
// 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.bossRushOptions[this->bossRushIndex] + 1) == BossRush_GetSettingOptionsAmount(this->bossRushIndex)) {
|
||||
gSaveContext.bossRushOptions[this->bossRushIndex] = 0;
|
||||
} else {
|
||||
gSaveContext.bossRushOptions[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.bossRushOptions[this->bossRushIndex] - 1) < 0) {
|
||||
gSaveContext.bossRushOptions[this->bossRushIndex] = BossRush_GetSettingOptionsAmount(this->bossRushIndex) - 1;
|
||||
} else {
|
||||
gSaveContext.bossRushOptions[this->bossRushIndex]--;
|
||||
}
|
||||
}
|
||||
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
}
|
||||
|
||||
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, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
this->buttonIndex = 0xFE;
|
||||
this->menuMode = FS_MENU_MODE_SELECT;
|
||||
this->selectMode = SM_FADE_OUT;
|
||||
this->prevConfigMode = this->configMode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update function for `CM_UNUSED_31`
|
||||
*/
|
||||
@ -689,10 +774,7 @@ void FileChoose_RotateToNameEntry(GameState* thisx) {
|
||||
|
||||
this->windowRot += VREG(16);
|
||||
|
||||
if (MIN_QUEST == MAX_QUEST && this->windowRot >= 314.0f) {
|
||||
this->windowRot = 314.0f;
|
||||
this->configMode = CM_START_NAME_ENTRY;
|
||||
} else if (this->windowRot >= 628.0f) {
|
||||
if (this->windowRot >= 628.0f) {
|
||||
this->windowRot = 628.0f;
|
||||
this->configMode = CM_START_NAME_ENTRY;
|
||||
}
|
||||
@ -719,8 +801,7 @@ void FileChoose_RotateToOptions(GameState* thisx) {
|
||||
*/
|
||||
void FileChoose_RotateToMain(GameState* thisx) {
|
||||
FileChooseContext* this = (FileChooseContext*)thisx;
|
||||
if (this->configMode == CM_QUEST_TO_MAIN || (MIN_QUEST == MAX_QUEST && this->configMode == CM_NAME_ENTRY_TO_MAIN && this->prevConfigMode != CM_MAIN_MENU) ||
|
||||
this->configMode == CM_OPTIONS_TO_MAIN) {
|
||||
if (this->configMode == CM_QUEST_TO_MAIN || this->configMode == CM_OPTIONS_TO_MAIN) {
|
||||
this->windowRot -= VREG(16);
|
||||
|
||||
if (this->windowRot <= 0.0f) {
|
||||
@ -732,7 +813,7 @@ void FileChoose_RotateToMain(GameState* thisx) {
|
||||
if (this->configMode == CM_NAME_ENTRY_TO_MAIN && this->prevConfigMode == CM_MAIN_MENU) {
|
||||
this->windowRot += VREG(16);
|
||||
|
||||
if (this->windowRot >= 942.0f || (MIN_QUEST == MAX_QUEST && this->windowRot >= 628.0f)) {
|
||||
if (this->windowRot >= 942.0f) {
|
||||
this->windowRot = 0.0f;
|
||||
this->configMode = CM_MAIN_MENU;
|
||||
}
|
||||
@ -742,7 +823,7 @@ void FileChoose_RotateToMain(GameState* thisx) {
|
||||
void FileChoose_RotateToQuest(GameState* thisx) {
|
||||
FileChooseContext* this = (FileChooseContext*)thisx;
|
||||
|
||||
if (this->configMode == CM_NAME_ENTRY_TO_QUEST_MENU) {
|
||||
if (this->configMode == CM_NAME_ENTRY_TO_QUEST_MENU || this->configMode == CM_BOSS_RUSH_TO_QUEST) {
|
||||
this->windowRot -= VREG(16);
|
||||
|
||||
if (this->windowRot <= 314.0f) {
|
||||
@ -759,6 +840,17 @@ void FileChoose_RotateToQuest(GameState* thisx) {
|
||||
}
|
||||
}
|
||||
|
||||
void FileChoose_RotateToBossRush(GameState* thisx) {
|
||||
FileChooseContext* this = (FileChooseContext*)thisx;
|
||||
|
||||
this->windowRot += VREG(16);
|
||||
|
||||
if (this->windowRot >= 628.0f) {
|
||||
this->windowRot = 628.0f;
|
||||
this->configMode = CM_START_BOSS_RUSH_MENU;
|
||||
}
|
||||
}
|
||||
|
||||
static void (*gConfigModeUpdateFuncs[])(GameState*) = {
|
||||
FileChoose_StartFadeIn, FileChoose_FinishFadeIn,
|
||||
FileChoose_UpdateMainMenu, FileChoose_SetupCopySource,
|
||||
@ -783,6 +875,8 @@ static void (*gConfigModeUpdateFuncs[])(GameState*) = {
|
||||
FileChoose_UnusedCMDelay, FileChoose_RotateToQuest,
|
||||
FileChoose_UpdateQuestMenu, FileChoose_StartQuestMenu,
|
||||
FileChoose_RotateToMain, FileChoose_RotateToQuest,
|
||||
FileChoose_RotateToBossRush, FileChoose_UpdateBossRushMenu,
|
||||
FileChoose_StartBossRushMenu, FileChoose_RotateToQuest,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1376,6 +1470,18 @@ const char* FileChoose_GetQuestChooseTitleTexName(Language lang) {
|
||||
}
|
||||
}
|
||||
|
||||
const char* FileChoose_GetBossRushOptionsTitleTexName(Language lang) {
|
||||
switch (lang) {
|
||||
case LANGUAGE_ENG:
|
||||
default:
|
||||
return gFileSelBossRushSettingsENGText;
|
||||
case LANGUAGE_FRA:
|
||||
return gFileSelBossRushSettingsFRAText;
|
||||
case LANGUAGE_GER:
|
||||
return gFileSelBossRushSettingsGERText;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw most window contents including buttons, labels, and icons.
|
||||
* Does not include anything from the keyboard and settings windows.
|
||||
@ -1388,11 +1494,26 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
s16 quadVtxIndex;
|
||||
s16 isActive;
|
||||
s16 pad;
|
||||
char* tex = (this->configMode == CM_QUEST_MENU || this->configMode == CM_ROTATE_TO_NAME_ENTRY ||
|
||||
this->configMode == CM_START_QUEST_MENU || this->configMode == CM_QUEST_TO_MAIN ||
|
||||
this->configMode == CM_NAME_ENTRY_TO_QUEST_MENU)
|
||||
? FileChoose_GetQuestChooseTitleTexName(gSaveContext.language)
|
||||
: sTitleLabels[gSaveContext.language][this->titleLabel];
|
||||
char* tex;
|
||||
|
||||
switch (this->configMode) {
|
||||
case CM_QUEST_MENU:
|
||||
case CM_ROTATE_TO_NAME_ENTRY:
|
||||
case CM_START_QUEST_MENU:
|
||||
case CM_QUEST_TO_MAIN:
|
||||
case CM_NAME_ENTRY_TO_QUEST_MENU:
|
||||
case CM_ROTATE_TO_BOSS_RUSH_MENU:
|
||||
tex = FileChoose_GetQuestChooseTitleTexName(gSaveContext.language);
|
||||
break;
|
||||
case CM_BOSS_RUSH_MENU:
|
||||
case CM_START_BOSS_RUSH_MENU:
|
||||
case CM_BOSS_RUSH_TO_QUEST:
|
||||
tex = FileChoose_GetBossRushOptionsTitleTexName(gSaveContext.language);
|
||||
break;
|
||||
default:
|
||||
tex = sTitleLabels[gSaveContext.language][this->titleLabel];
|
||||
break;
|
||||
}
|
||||
|
||||
OPEN_DISPS(this->state.gfxCtx);
|
||||
|
||||
@ -1412,7 +1533,6 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
if ((this->configMode == CM_QUEST_MENU) || (this->configMode == CM_START_QUEST_MENU) ||
|
||||
this->configMode == CM_NAME_ENTRY_TO_QUEST_MENU) {
|
||||
// draw control stick prompts.
|
||||
if (MIN_QUEST != MAX_QUEST) {
|
||||
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,
|
||||
@ -1437,9 +1557,8 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
this->stickRightPrompt.stickColorG, this->stickRightPrompt.stickColorB,
|
||||
this->stickRightPrompt.stickColorA, this->stickRightPrompt.stickTexX,
|
||||
this->stickRightPrompt.stickTexY, this->stickRightPrompt.z, 0, 0, 1.0f, 1.0f);
|
||||
}
|
||||
switch (this->questType[this->buttonIndex]) {
|
||||
case NORMAL_QUEST:
|
||||
case FS_QUEST_NORMAL:
|
||||
default:
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->logoAlpha);
|
||||
FileChoose_DrawTextureI8(this->state.gfxCtx, gTitleTheLegendOfTextTex, 72, 8, 156, 108, 72, 8, 1024, 1024);
|
||||
@ -1447,7 +1566,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
FileChoose_DrawImageRGBA32(this->state.gfxCtx, 160, 135, gTitleZeldaShieldLogoTex, 160, 160);
|
||||
break;
|
||||
|
||||
case MASTER_QUEST:
|
||||
case FS_QUEST_MASTER:
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->logoAlpha);
|
||||
FileChoose_DrawTextureI8(this->state.gfxCtx, gTitleTheLegendOfTextTex, 72, 8, 156, 108, 72, 8, 1024, 1024);
|
||||
FileChoose_DrawTextureI8(this->state.gfxCtx, gTitleOcarinaOfTimeTMTextTex, 96, 8, 154, 163, 96, 8, 1024, 1024);
|
||||
@ -1455,7 +1574,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
FileChoose_DrawImageRGBA32(this->state.gfxCtx, 182, 180, gTitleMasterQuestSubtitleTex, 128, 32);
|
||||
break;
|
||||
|
||||
case RANDOMIZER_QUEST:
|
||||
case FS_QUEST_RANDOMIZER:
|
||||
DrawSeedHashSprites(this);
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->logoAlpha);
|
||||
FileChoose_DrawTextureI8(this->state.gfxCtx, gTitleTheLegendOfTextTex, 72, 8, 156, 108, 72, 8, 1024, 1024);
|
||||
@ -1463,8 +1582,77 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
|
||||
FileChoose_DrawImageRGBA32(this->state.gfxCtx, 160, 135, ResourceMgr_GameHasOriginal() ? gTitleZeldaShieldLogoTex : gTitleZeldaShieldLogoMQTex, 160, 160);
|
||||
FileChoose_DrawImageRGBA32(this->state.gfxCtx, 182, 180, gTitleRandomizerSubtitleTex, 128, 32);
|
||||
break;
|
||||
|
||||
case FS_QUEST_BOSSRUSH:
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->logoAlpha);
|
||||
FileChoose_DrawTextureI8(this->state.gfxCtx, gTitleTheLegendOfTextTex, 72, 8, 156, 108, 72, 8, 1024, 1024);
|
||||
FileChoose_DrawTextureI8(this->state.gfxCtx, gTitleOcarinaOfTimeTMTextTex, 96, 8, 154, 163, 96, 8, 1024, 1024);
|
||||
FileChoose_DrawImageRGBA32(this->state.gfxCtx, 160, 135, ResourceMgr_GameHasOriginal() ? gTitleZeldaShieldLogoTex : gTitleZeldaShieldLogoMQTex, 160, 160);
|
||||
FileChoose_DrawImageRGBA32(this->state.gfxCtx, 182, 180, gTitleBossRushSubtitleTex, 128, 32);
|
||||
break;
|
||||
}
|
||||
} else if (this->configMode != CM_ROTATE_TO_NAME_ENTRY) {
|
||||
} else if (this->configMode == CM_BOSS_RUSH_MENU) {
|
||||
|
||||
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 (BOSSRUSH_OPTIONS_AMOUNT - 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, gSaveContext.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.bossRushOptions[i], gSaveContext.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_ROTATE_TO_NAME_ENTRY && this->configMode != CM_START_BOSS_RUSH_MENU &&
|
||||
this->configMode != CM_ROTATE_TO_BOSS_RUSH_MENU && this->configMode != CM_BOSS_RUSH_TO_QUEST) {
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->titleAlpha[1]);
|
||||
gDPLoadTextureBlock(POLY_OPA_DISP++, sTitleLabels[gSaveContext.language][this->nextTitleLabel], G_IM_FMT_IA,
|
||||
@ -1702,7 +1890,7 @@ void FileChoose_ConfigModeDraw(GameState* thisx) {
|
||||
if (this->windowRot != 0) {
|
||||
if (this->configMode == CM_ROTATE_TO_QUEST_MENU ||
|
||||
(this->configMode >= CM_MAIN_TO_OPTIONS && this->configMode <= CM_OPTIONS_TO_MAIN) ||
|
||||
MIN_QUEST == MAX_QUEST || this->configMode == CM_QUEST_TO_MAIN) {
|
||||
this->configMode == CM_QUEST_TO_MAIN) {
|
||||
Matrix_RotateX(this->windowRot / 100.0f, MTXMODE_APPLY);
|
||||
} else {
|
||||
Matrix_RotateX((this->windowRot - 942.0f) / 100.0f, MTXMODE_APPLY);
|
||||
@ -1736,11 +1924,7 @@ void FileChoose_ConfigModeDraw(GameState* thisx) {
|
||||
|
||||
Matrix_Translate(0.0f, 0.0f, -93.6f, MTXMODE_NEW);
|
||||
Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY);
|
||||
if (MIN_QUEST == MAX_QUEST) {
|
||||
Matrix_RotateX((this->windowRot - 314.0f) / 100.0f, MTXMODE_APPLY);
|
||||
} else {
|
||||
Matrix_RotateX((this->windowRot - 628.0f) / 100.0f, MTXMODE_APPLY);
|
||||
}
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(this->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
|
||||
@ -1790,7 +1974,7 @@ void FileChoose_ConfigModeDraw(GameState* thisx) {
|
||||
// draw quest menu
|
||||
if ((this->configMode == CM_QUEST_MENU) || (this->configMode == CM_ROTATE_TO_QUEST_MENU) ||
|
||||
(this->configMode == CM_ROTATE_TO_NAME_ENTRY) || this->configMode == CM_QUEST_TO_MAIN ||
|
||||
this->configMode == CM_NAME_ENTRY_TO_QUEST_MENU) {
|
||||
this->configMode == CM_NAME_ENTRY_TO_QUEST_MENU || this->configMode == CM_ROTATE_TO_BOSS_RUSH_MENU) {
|
||||
// window
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
|
||||
@ -1819,6 +2003,36 @@ void FileChoose_ConfigModeDraw(GameState* thisx) {
|
||||
FileChoose_DrawWindowContents(&this->state);
|
||||
}
|
||||
|
||||
// Draw Boss Rush Options Menu
|
||||
if (this->configMode == CM_BOSS_RUSH_MENU || this->configMode == CM_ROTATE_TO_BOSS_RUSH_MENU ||
|
||||
this->configMode == CM_START_BOSS_RUSH_MENU || this->configMode == CM_BOSS_RUSH_TO_QUEST) {
|
||||
// window
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2],
|
||||
this->windowAlpha);
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0);
|
||||
|
||||
Matrix_Translate(0.0f, 0.0f, -93.6f, MTXMODE_NEW);
|
||||
Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY);
|
||||
Matrix_RotateX((this->windowRot - 628.0f) / 100.0f, MTXMODE_APPLY);
|
||||
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(this->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
|
||||
gSPVertex(POLY_OPA_DISP++, &this->windowVtx[0], 32, 0);
|
||||
gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow1DL);
|
||||
|
||||
gSPVertex(POLY_OPA_DISP++, &this->windowVtx[32], 32, 0);
|
||||
gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow2DL);
|
||||
|
||||
gSPVertex(POLY_OPA_DISP++, &this->windowVtx[64], 16, 0);
|
||||
gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow3DL);
|
||||
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
|
||||
FileChoose_DrawWindowContents(&this->state);
|
||||
}
|
||||
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
FileChoose_SetView(this, 0.0f, 0.0f, 64.0f);
|
||||
|
||||
@ -1920,6 +2134,8 @@ void FileChoose_ConfirmFile(GameState* thisx) {
|
||||
if (this->confirmButtonIndex == FS_BTN_CONFIRM_YES) {
|
||||
func_800AA000(300.0f, 180, 20, 100);
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
// Reset Boss Rush because it's only ever saved in memory.
|
||||
gSaveContext.isBossRush = 0;
|
||||
this->selectMode = SM_FADE_OUT;
|
||||
func_800F6964(0xF);
|
||||
} else {
|
||||
@ -2033,25 +2249,27 @@ void FileChoose_LoadGame(GameState* thisx) {
|
||||
u16 swordEquipMask;
|
||||
s32 pad;
|
||||
|
||||
if ((this->buttonIndex == FS_BTN_SELECT_FILE_1 && CVarGetInteger("gDebugEnabled", 0)) || this->buttonIndex == 0xFF) {
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
gSaveContext.fileNum = this->buttonIndex;
|
||||
gSaveContext.gameMode = 0;
|
||||
|
||||
if ((this->buttonIndex == FS_BTN_SELECT_FILE_1 && CVarGetInteger("gDebugEnabled", 0)) || this->buttonIndex == 0xFF) {
|
||||
if (this->buttonIndex == 0xFF) {
|
||||
Sram_InitDebugSave();
|
||||
} else {
|
||||
Sram_OpenSave();
|
||||
}
|
||||
gSaveContext.gameMode = 0;
|
||||
SET_NEXT_GAMESTATE(&this->state, Select_Init, SelectContext);
|
||||
this->state.running = false;
|
||||
} else {
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
||||
gSaveContext.fileNum = this->buttonIndex;
|
||||
if (this->buttonIndex == 0xFE) {
|
||||
Sram_InitBossRushSave();
|
||||
} else {
|
||||
Sram_OpenSave();
|
||||
gSaveContext.gameMode = 0;
|
||||
SET_NEXT_GAMESTATE(&this->state, Play_Init, PlayState);
|
||||
this->state.running = false;
|
||||
}
|
||||
SET_NEXT_GAMESTATE(&this->state, Play_Init, PlayState);
|
||||
}
|
||||
|
||||
this->state.running = false;
|
||||
|
||||
Randomizer_LoadSettings("");
|
||||
Randomizer_LoadHintLocations("");
|
||||
@ -2573,6 +2791,9 @@ void FileChoose_InitContext(GameState* thisx) {
|
||||
this->arrowAnimTween = 0;
|
||||
this->stickAnimTween = 0;
|
||||
|
||||
this->bossRushIndex = 0;
|
||||
this->bossRushOffset = 0;
|
||||
|
||||
ShrinkWindow_SetVal(0);
|
||||
|
||||
gSaveContext.skyboxTime = 0;
|
||||
|
@ -1508,8 +1508,14 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA);
|
||||
gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha);
|
||||
|
||||
POLY_KAL_DISP =
|
||||
KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][0], 48, 16, 12);
|
||||
if (!gSaveContext.isBossRush) {
|
||||
POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(
|
||||
POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][0], 48, 16, 12);
|
||||
} else {
|
||||
// Show "No" twice in Boss Rush because the player can't save within it.
|
||||
POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(
|
||||
POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][1], 48, 16, 12);
|
||||
}
|
||||
|
||||
POLY_KAL_DISP =
|
||||
KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][1], 48, 16, 16);
|
||||
@ -3629,7 +3635,9 @@ void KaleidoScope_Update(PlayState* play)
|
||||
case 6:
|
||||
switch (pauseCtx->unk_1E4) {
|
||||
case 0:
|
||||
if (CHECK_BTN_ALL(input->press.button, BTN_START)) {
|
||||
// Boss Rush skips past the "Save?" window when pressing B while paused.
|
||||
if (CHECK_BTN_ALL(input->press.button, BTN_START) ||
|
||||
(CHECK_BTN_ALL(input->press.button, BTN_B) && gSaveContext.isBossRush)) {
|
||||
if (CVarGetInteger("gCheatEasyPauseBufferEnabled", 0) || CVarGetInteger("gCheatEasyInputBufferingEnabled", 0)) {
|
||||
CVarSetInteger("gPauseBufferBlockInputFrame", 9);
|
||||
}
|
||||
@ -4015,7 +4023,11 @@ void KaleidoScope_Update(PlayState* play)
|
||||
VREG(88) = 66;
|
||||
WREG(2) = 0;
|
||||
pauseCtx->alpha = 255;
|
||||
if (!gSaveContext.isBossRush) {
|
||||
pauseCtx->state = 0xE;
|
||||
} else {
|
||||
pauseCtx->state = 0xF;
|
||||
}
|
||||
gSaveContext.deaths++;
|
||||
if (gSaveContext.deaths > 999) {
|
||||
gSaveContext.deaths = 999;
|
||||
@ -4059,7 +4071,7 @@ void KaleidoScope_Update(PlayState* play)
|
||||
|
||||
case 0x10:
|
||||
if (CHECK_BTN_ALL(input->press.button, BTN_A) || CHECK_BTN_ALL(input->press.button, BTN_START)) {
|
||||
if (pauseCtx->promptChoice == 0) {
|
||||
if (pauseCtx->promptChoice == 0 && !gSaveContext.isBossRush) {
|
||||
Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0,
|
||||
&D_801333E8);
|
||||
Play_SaveSceneFlags(play);
|
||||
@ -4132,7 +4144,7 @@ void KaleidoScope_Update(PlayState* play)
|
||||
R_PAUSE_MENU_MODE = 0;
|
||||
func_800981B8(&play->objectCtx);
|
||||
func_800418D0(&play->colCtx, play);
|
||||
if (pauseCtx->promptChoice == 0) {
|
||||
if (pauseCtx->promptChoice == 0 && !gSaveContext.isBossRush) {
|
||||
Play_TriggerRespawn(play);
|
||||
gSaveContext.respawnFlag = -2;
|
||||
// In ER, handle death warp to last entrance from grottos
|
||||
@ -4156,6 +4168,7 @@ void KaleidoScope_Update(PlayState* play)
|
||||
osSyncPrintf(VT_RST);
|
||||
} else {
|
||||
play->state.running = 0;
|
||||
gSaveContext.isBossRush = false;
|
||||
SET_NEXT_GAMESTATE(&play->state, Opening_Init, OpeningContext);
|
||||
GameInteractor_ExecuteOnExitGame(gSaveContext.fileNum);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user