Merge pull request #3468 from briaguya-ai/delta-to-dev

MacReady Delta -> develop
This commit is contained in:
aMannus 2023-11-30 00:07:38 +01:00 committed by GitHub
commit 19cede44c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 858 additions and 328 deletions

View file

@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
project(Ship VERSION 8.0.2 LANGUAGES C CXX) project(Ship VERSION 8.0.3 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Charlie" CACHE STRING "") set(PROJECT_BUILD_NAME "MacReady Delta" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "") set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)

@ -1 +1 @@
Subproject commit 1b41afa9eaedb19a9073f5dd1eca2cda4dea59c8 Subproject commit 4600eedcc18f496319c99e07b8b2b4f11a0f6e64

View file

@ -79,7 +79,7 @@
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x6238"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x91E8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61E8"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61E8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/>
</File> </File>
</Root> </Root>

View file

@ -1,14 +1,14 @@
<Root> <Root>
<File Name="nes_message_data_static"> <File Name="nes_message_data_static">
<Text Name="nes_message_data_static" CodeOffset="0xF6910"/> <Text Name="nes_message_data_static" CodeOffset="0xF68F0"/>
</File> </File>
<File Name="ger_message_data_static"> <File Name="ger_message_data_static">
<Text Name="ger_message_data_static" CodeOffset="0xF6910" LangOffset="0xFAB38"/> <Text Name="ger_message_data_static" CodeOffset="0xF68F0" LangOffset="0xFAB18"/>
</File> </File>
<File Name="fra_message_data_static"> <File Name="fra_message_data_static">
<Text Name="fra_message_data_static" CodeOffset="0xF6910" LangOffset="0xFCC48"/> <Text Name="fra_message_data_static" CodeOffset="0xF68F0" LangOffset="0xFCC28"/>
</File> </File>
<File Name="staff_message_data_static"> <File Name="staff_message_data_static">
<Text Name="staff_message_data_static" CodeOffset="0xFED58"/> <Text Name="staff_message_data_static" CodeOffset="0xFED38"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1210" RangeStart="0x6238" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1210" RangeStart="0x6238" RangeEnd="0x9238">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x6238"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61E8" RangeEnd="0x91E8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61E8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x8089E470" RangeStart="0x61C8" RangeEnd="0x91C8"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x8089E470" RangeStart="0x61C8" RangeEnd="0x91C8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61C8"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61C8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61C8" RangeEnd="0x91C8"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61C8" RangeEnd="0x91C8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61C8"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61C8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/>
</File> </File>
</Root> </Root>

View file

@ -304,6 +304,7 @@ namespace GameControlEditor {
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select " DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
"certain items."); "certain items.");
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false); UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false);
DrawHelpIcon("Hold the assigned button to change the maximum walking speed\nTo change the assigned button, go into the Ports tabs above"); DrawHelpIcon("Hold the assigned button to change the maximum walking speed\nTo change the assigned button, go into the Ports tabs above");
if (CVarGetInteger("gEnableWalkModify", 0)) { if (CVarGetInteger("gEnableWalkModify", 0)) {
@ -315,6 +316,7 @@ namespace GameControlEditor {
UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %d %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true); UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %d %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
window->EndGroupPanelPublic(0); window->EndGroupPanelPublic(0);
} }
ImGui::EndDisabled();
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL"); UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up"); DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");

View file

@ -1485,6 +1485,7 @@ void Draw_Placements(){
} }
void DrawSillyTab() { void DrawSillyTab() {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (CVarGetInteger("gLetItSnow", 0)) { if (CVarGetInteger("gLetItSnow", 0)) {
if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) { if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
@ -1569,6 +1570,7 @@ void DrawSillyTab() {
CVarClear("gCosmetics.Kak_Windmill_Speed.Changed"); CVarClear("gCosmetics.Kak_Windmill_Speed.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
ImGui::EndDisabled();
} }
// Copies the RGB values from one cosmetic option to another, multiplied by the passed in amount, this // Copies the RGB values from one cosmetic option to another, multiplied by the passed in amount, this

View file

@ -1,3 +1,6 @@
#ifndef _ENHANCEMENT_TYPES_H_
#define _ENHANCEMENT_TYPES_H_
typedef enum { typedef enum {
WARP_MODE_OVERRIDE_OFF, WARP_MODE_OVERRIDE_OFF,
WARP_MODE_OVERRIDE_MQ_AS_VANILLA, WARP_MODE_OVERRIDE_MQ_AS_VANILLA,
@ -74,3 +77,5 @@ typedef enum {
DEKU_STICK_UNBREAKABLE, DEKU_STICK_UNBREAKABLE,
DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE, DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE,
} DekuStickType; } DekuStickType;
#endif

View file

@ -194,6 +194,8 @@ public:
DEFINE_HOOK(OnSetGameLanguage, void()); DEFINE_HOOK(OnSetGameLanguage, void());
DEFINE_HOOK(OnAssetAltChange, void());
// Helpers // Helpers
static bool IsSaveLoaded(); static bool IsSaveLoaded();
static bool IsGameplayPaused(); static bool IsGameplayPaused();

View file

@ -181,3 +181,9 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode) {
void GameInteractor_ExecuteOnSetGameLanguage() { void GameInteractor_ExecuteOnSetGameLanguage() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>(); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
} }
// MARK: - System
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnAssetAltChange>(fn);
}

View file

@ -56,6 +56,10 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode);
// MARK: - Game // MARK: - Game
void GameInteractor_ExecuteOnSetGameLanguage(); void GameInteractor_ExecuteOnSetGameLanguage();
// MARK: - System
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void));
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -47,6 +47,7 @@ void ReloadSceneTogglingLinkAge() {
void RegisterInfiniteMoney() { void RegisterInfiniteMoney() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteMoney", 0) != 0) { if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) { if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
@ -57,6 +58,7 @@ void RegisterInfiniteMoney() {
void RegisterInfiniteHealth() { void RegisterInfiniteHealth() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteHealth", 0) != 0) { if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
if (gSaveContext.health < gSaveContext.healthCapacity) { if (gSaveContext.health < gSaveContext.healthCapacity) {
gSaveContext.health = gSaveContext.healthCapacity; gSaveContext.health = gSaveContext.healthCapacity;
@ -67,6 +69,7 @@ void RegisterInfiniteHealth() {
void RegisterInfiniteAmmo() { void RegisterInfiniteAmmo() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) { if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
// Deku Sticks // Deku Sticks
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) { if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
@ -103,6 +106,7 @@ void RegisterInfiniteAmmo() {
void RegisterInfiniteMagic() { void RegisterInfiniteMagic() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteMagic", 0) != 0) { if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) { if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30; gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
@ -113,6 +117,7 @@ void RegisterInfiniteMagic() {
void RegisterInfiniteNayrusLove() { void RegisterInfiniteNayrusLove() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteNayru", 0) != 0) { if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
gSaveContext.nayrusLoveTimer = 0x44B; gSaveContext.nayrusLoveTimer = 0x44B;
} }
@ -121,7 +126,7 @@ void RegisterInfiniteNayrusLove() {
void RegisterMoonJumpOnL() { void RegisterMoonJumpOnL() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) { if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
@ -136,7 +141,7 @@ void RegisterMoonJumpOnL() {
void RegisterInfiniteISG() { void RegisterInfiniteISG() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gEzISG", 0) != 0) { if (CVarGetInteger("gEzISG", 0) != 0) {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
@ -148,7 +153,7 @@ void RegisterInfiniteISG() {
//Permanent quick put away (QPA) glitched damage value //Permanent quick put away (QPA) glitched damage value
void RegisterEzQPA() { void RegisterEzQPA() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gEzQPA", 0) != 0) { if (CVarGetInteger("gEzQPA", 0) != 0) {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
@ -160,7 +165,7 @@ void RegisterEzQPA() {
void RegisterUnrestrictedItems() { void RegisterUnrestrictedItems() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gNoRestrictItems", 0) != 0) { if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong; u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
@ -188,14 +193,16 @@ void RegisterFreezeTime() {
/// Switches Link's age and respawns him at the last entrance he entered. /// Switches Link's age and respawns him at the last entrance he entered.
void RegisterSwitchAge() { void RegisterSwitchAge() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) {
CVarClear("gSwitchAge");
return;
}
static bool warped = false; static bool warped = false;
static Vec3f playerPos; static Vec3f playerPos;
static int16_t playerYaw; static int16_t playerYaw;
static RoomContext* roomCtx; static RoomContext* roomCtx;
static s32 roomNum; static s32 roomNum;
if (!gPlayState) return;
if (CVarGetInteger("gSwitchAge", 0) && !warped) { if (CVarGetInteger("gSwitchAge", 0) && !warped) {
playerPos = GET_PLAYER(gPlayState)->actor.world.pos; playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y; playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
@ -215,7 +222,7 @@ void RegisterSwitchAge() {
func_80097534(gPlayState, roomCtx); // load map for new room (unloading the previous room) func_80097534(gPlayState, roomCtx); // load map for new room (unloading the previous room)
} }
warped = false; warped = false;
CVarSetInteger("gSwitchAge", 0); CVarClear("gSwitchAge");
} }
}); });
} }
@ -224,7 +231,8 @@ void RegisterSwitchAge() {
void RegisterOcarinaTimeTravel() { void RegisterOcarinaTimeTravel() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
if (!gPlayState) { if (!GameInteractor::IsSaveLoaded()) {
CVarClear("gTimeTravel");
return; return;
} }
@ -1028,8 +1036,16 @@ void RegisterRandomizedEnemySizes() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor); Actor* actor = static_cast<Actor*>(refActor);
// Only apply to enemies and bosses. Exclude the wobbly platforms in Jabu because they need to act like platforms. // Exclude wobbly platforms in Jabu because they need to act like platforms.
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || actor->id == ACTOR_EN_BROB) { // Exclude Dead Hand hands and Bongo Bongo main body because they make the fights (near) impossible.
uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || (actor->id == ACTOR_BOSS_SST && actor->params == -1);
// Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger.
uint8_t smallOnlyEnemy =
actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || ACTOR_EN_DH;
// Only apply to enemies and bosses.
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || excludedEnemy) {
return; return;
} }
@ -1038,9 +1054,8 @@ void RegisterRandomizedEnemySizes() {
uint8_t bigActor = rand() % 2; uint8_t bigActor = rand() % 2;
// Big actor. Dodongo and Volvagia are always smaller because they're impossible when bigger. // Big actor
if (bigActor && actor->id != ACTOR_BOSS_DODONGO && actor->id != ACTOR_BOSS_FD && if (bigActor && !smallOnlyEnemy) {
actor->id != ACTOR_BOSS_FD2) {
randomNumber = rand() % 200; randomNumber = rand() % 200;
// Between 100% and 300% size. // Between 100% and 300% size.
randomScale = 1.0f + (randomNumber / 100); randomScale = 1.0f + (randomNumber / 100);

View file

@ -218,6 +218,31 @@ const std::vector<const char*> enhancementsCvars = {
"gFixTexturesOOB", "gFixTexturesOOB",
"gIvanCoopModeEnabled", "gIvanCoopModeEnabled",
"gEnemySpawnsOverWaterboxes", "gEnemySpawnsOverWaterboxes",
"gTreeStickDrops",
"gShadowTag",
"gRandomizedEnemySizes",
"gRandomizedEnemies",
"gMirroredWorldMode",
"gMirroredWorld",
"gHyperEnemies",
"gHookshotableReticle",
"gHideBunnyHood",
"gFixVineFall",
"gFileSelectMoreInfo",
"gEnemyHealthBar",
"gBushDropFix",
"gAllDogsRichard",
"gAddTraps.enabled",
"gAddTraps.Ammo",
"gAddTraps.Bomb",
"gAddTraps.Burn",
"gAddTraps.Ice",
"gAddTraps.Kill",
"gAddTraps.Knock",
"gAddTraps.Shock",
"gAddTraps.Speed",
"gAddTraps.Tele",
"gAddTraps.Void",
}; };
const std::vector<const char*> cheatCvars = { const std::vector<const char*> cheatCvars = {
@ -270,7 +295,23 @@ const std::vector<const char*> cheatCvars = {
"gNoRedeadFreeze", "gNoRedeadFreeze",
"gBombTimerMultiplier", "gBombTimerMultiplier",
"gNoFishDespawn", "gNoFishDespawn",
"gNoBugsDespawn" "gNoBugsDespawn",
"gWalkModifierDoesntChangeJump",
"gStatsEnabled",
"gSaveStatesEnabled",
"gSaveStatePromise",
"gRegEditEnabled",
"gPreset0",
"gPreset1",
"gDekuStickCheat",
"gDebugWarpScreenTranslation",
"gDebugSaveFileMode",
"gCosmetics.Link_BodyScale.Changed",
"gCosmetics.Link_BodyScale.Value",
"gCosmetics.Link_HeadScale.Changed",
"gCosmetics.Link_HeadScale.Value",
"gCosmetics.Link_SwordScale.Changed",
"gCosmetics.Link_SwordScale.Value",
}; };
const std::vector<const char*> randomizerCvars = { const std::vector<const char*> randomizerCvars = {
@ -400,6 +441,15 @@ const std::vector<const char*> randomizerCvars = {
"gRandomizeGregHint", "gRandomizeGregHint",
"gRandoManualSeedEntry", "gRandoManualSeedEntry",
"gRandomizerSettingsEnabled", "gRandomizerSettingsEnabled",
"gRandomizeTriforceHuntTotalPieces",
"gRandomizeTriforceHuntRequiredPieces",
"gRandomizeTriforceHunt",
"gRandomizeShuffleMasterSword",
"gRandomizeSariaHint",
"gRandomizeRupeeNames",
"gRandomizeFrogsHint",
"gRandoRelevantNavi",
"gRandoQuestItemFanfares",
}; };
const std::vector<PresetEntry> vanillaPlusPresetEntries = { const std::vector<PresetEntry> vanillaPlusPresetEntries = {

View file

@ -24,11 +24,11 @@ void LocationTable_Init() {
//Lost Woods //Lost Woods
locationTable[LW_NEAR_SHORTCUTS_GROTTO_CHEST] = ItemLocation::Chest (RC_LW_NEAR_SHORTCUTS_GROTTO_CHEST, 0x3E, 0x14, "LW Near Shortcuts Grotto Chest", LW_NEAR_SHORTCUTS_GROTTO_CHEST, BLUE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_NEAR_SHORTCUTS_GROTTO_CHEST] = ItemLocation::Chest (RC_LW_NEAR_SHORTCUTS_GROTTO_CHEST, 0x3E, 0x14, "LW Near Shortcuts Grotto Chest", LW_NEAR_SHORTCUTS_GROTTO_CHEST, BLUE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_SKULL_KID] = ItemLocation::Base (RC_LW_SKULL_KID, 0x5B, "LW Skull Kid", LW_SKULL_KID, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(30), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_SKULL_KID] = ItemLocation::Base (RC_LW_SKULL_KID, 0x5B, "LW Skull Kid", LW_SKULL_KID, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(22), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_TRADE_COJIRO] = ItemLocation::Base (RC_LW_TRADE_COJIRO, 0x5B, "LW Trade Cojiro", LW_TRADE_COJIRO, ODD_MUSHROOM, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TRADE_COJIRO] = ItemLocation::Base (RC_LW_TRADE_COJIRO, 0x5B, "LW Trade Cojiro", LW_TRADE_COJIRO, ODD_MUSHROOM, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_TRADE_ODD_POTION] = ItemLocation::Base (RC_LW_TRADE_ODD_POTION, 0x5B, "LW Trade Odd Potion", LW_TRADE_ODD_POTION, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(57), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TRADE_ODD_POTION] = ItemLocation::Base (RC_LW_TRADE_ODD_POTION, 0x5B, "LW Trade Odd Potion", LW_TRADE_ODD_POTION, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(49), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_OCARINA_MEMORY_GAME] = ItemLocation::Base (RC_LW_OCARINA_MEMORY_GAME, 0x5B, "LW Ocarina Memory Game", LW_OCARINA_MEMORY_GAME, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(31), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_OCARINA_MEMORY_GAME] = ItemLocation::Base (RC_LW_OCARINA_MEMORY_GAME, 0x5B, "LW Ocarina Memory Game", LW_OCARINA_MEMORY_GAME, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(23), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_TARGET_IN_WOODS] = ItemLocation::Base (RC_LW_TARGET_IN_WOODS, 0x5B, "LW Target in Woods", LW_TARGET_IN_WOODS, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(21), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TARGET_IN_WOODS] = ItemLocation::Base (RC_LW_TARGET_IN_WOODS, 0x5B, "LW Target in Woods", LW_TARGET_IN_WOODS, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, 0x5B, "LW Deku Scrub Near Deku Theater Right",LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, 0x5B, "LW Deku Scrub Near Deku Theater Right",LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, 0x5B, "LW Deku Scrub Near Deku Theater Left", LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, BUY_DEKU_STICK_1, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, 0x5B, "LW Deku Scrub Near Deku Theater Left", LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, BUY_DEKU_STICK_1, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_DEKU_SCRUB_NEAR_BRIDGE] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_BRIDGE, 0x5B, "LW Deku Scrub Near Bridge", LW_DEKU_SCRUB_NEAR_BRIDGE, PROGRESSIVE_STICK_UPGRADE, {Category::cDekuScrub, Category::cDekuScrubUpgrades}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_NEAR_BRIDGE] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_BRIDGE, 0x5B, "LW Deku Scrub Near Bridge", LW_DEKU_SCRUB_NEAR_BRIDGE, PROGRESSIVE_STICK_UPGRADE, {Category::cDekuScrub, Category::cDekuScrubUpgrades}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
@ -53,7 +53,7 @@ void LocationTable_Init() {
//Lake Hylia //Lake Hylia
locationTable[LH_CHILD_FISHING] = ItemLocation::Base (RC_LH_CHILD_FISHING, 0x49, "LH Child Fishing", LH_CHILD_FISHING, PIECE_OF_HEART, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_CHILD_FISHING] = ItemLocation::Base (RC_LH_CHILD_FISHING, 0x49, "LH Child Fishing", LH_CHILD_FISHING, PIECE_OF_HEART, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_ADULT_FISHING] = ItemLocation::Base (RC_LH_ADULT_FISHING, 0x49, "LH Adult Fishing", LH_ADULT_FISHING, PROGRESSIVE_SCALE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_ADULT_FISHING] = ItemLocation::Base (RC_LH_ADULT_FISHING, 0x49, "LH Adult Fishing", LH_ADULT_FISHING, PROGRESSIVE_SCALE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_LAB_DIVE] = ItemLocation::Base (RC_LH_LAB_DIVE, 0x38, "LH Lab Dive", LH_LAB_DIVE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(24), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_LAB_DIVE] = ItemLocation::Base (RC_LH_LAB_DIVE, 0x38, "LH Lab Dive", LH_LAB_DIVE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(16), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_TRADE_FROG] = ItemLocation::Base (RC_LH_TRADE_FROG, 0x38, "LH Lab Trade Eyeball Frog", LH_TRADE_FROG, EYEDROPS, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_TRADE_FROG] = ItemLocation::Base (RC_LH_TRADE_FROG, 0x38, "LH Lab Trade Eyeball Frog", LH_TRADE_FROG, EYEDROPS, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_UNDERWATER_ITEM] = ItemLocation::Base (RC_LH_UNDERWATER_ITEM, 0x57, "LH Underwater Item", LH_UNDERWATER_ITEM, RUTOS_LETTER, {}, SpoilerCollectionCheck::EventChkInf(0x31), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_UNDERWATER_ITEM] = ItemLocation::Base (RC_LH_UNDERWATER_ITEM, 0x57, "LH Underwater Item", LH_UNDERWATER_ITEM, RUTOS_LETTER, {}, SpoilerCollectionCheck::EventChkInf(0x31), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_SUN] = ItemLocation::Base (RC_LH_SUN, 0x57, "LH Sun", LH_SUN, FIRE_ARROWS, {}, SpoilerCollectionCheck::Chest(0x57, 0x1F), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_SUN] = ItemLocation::Base (RC_LH_SUN, 0x57, "LH Sun", LH_SUN, FIRE_ARROWS, {}, SpoilerCollectionCheck::Chest(0x57, 0x1F), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
@ -73,7 +73,7 @@ void LocationTable_Init() {
//Gerudo Fortress //Gerudo Fortress
locationTable[GF_CHEST] = ItemLocation::Chest (RC_GF_CHEST, 0x5D, 0x00, "GF Chest", GF_CHEST, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_CHEST] = ItemLocation::Chest (RC_GF_CHEST, 0x5D, 0x00, "GF Chest", GF_CHEST, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_HBA_1000_POINTS] = ItemLocation::Base (RC_GF_HBA_1000_POINTS, 0x5D, "GF HBA 1000 Points", GF_HBA_1000_POINTS, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x08), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_HBA_1000_POINTS] = ItemLocation::Base (RC_GF_HBA_1000_POINTS, 0x5D, "GF HBA 1000 Points", GF_HBA_1000_POINTS, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x08), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_HBA_1500_POINTS] = ItemLocation::Base (RC_GF_HBA_1500_POINTS, 0x5D, "GF HBA 1500 Points", GF_HBA_1500_POINTS, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::ItemGetInf(7), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_HBA_1500_POINTS] = ItemLocation::Base (RC_GF_HBA_1500_POINTS, 0x5D, "GF HBA 1500 Points", GF_HBA_1500_POINTS, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::ItemGetInf(15), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_GERUDO_MEMBERSHIP_CARD] = ItemLocation::Base (RC_GF_GERUDO_MEMBERSHIP_CARD, 0x0C, "GF Gerudo Membership Card", GF_GERUDO_MEMBERSHIP_CARD, GERUDO_MEMBERSHIP_CARD, {}, SpoilerCollectionCheck::GerudoToken(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_GERUDO_MEMBERSHIP_CARD] = ItemLocation::Base (RC_GF_GERUDO_MEMBERSHIP_CARD, 0x0C, "GF Gerudo Membership Card", GF_GERUDO_MEMBERSHIP_CARD, GERUDO_MEMBERSHIP_CARD, {}, SpoilerCollectionCheck::GerudoToken(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_NORTH_F1_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F1_CARPENTER, 0x0C, 0x0C, "GF North F1 Carpenter", GF_NORTH_F1_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_NORTH_F1_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F1_CARPENTER, 0x0C, 0x0C, "GF North F1 Carpenter", GF_NORTH_F1_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_NORTH_F2_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F2_CARPENTER, 0x0C, 0x0A, "GF North F2 Carpenter", GF_NORTH_F2_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_NORTH_F2_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F2_CARPENTER, 0x0C, 0x0A, "GF North F2 Carpenter", GF_NORTH_F2_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
@ -90,12 +90,12 @@ void LocationTable_Init() {
locationTable[COLOSSUS_DEKU_SCRUB_GROTTO_FRONT] = ItemLocation::GrottoScrub(RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, 0xFD, "Colossus Deku Scrub Grotto Front", COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, BUY_GREEN_POTION, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[COLOSSUS_DEKU_SCRUB_GROTTO_FRONT] = ItemLocation::GrottoScrub(RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, 0xFD, "Colossus Deku Scrub Grotto Front", COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, BUY_GREEN_POTION, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
//Market //Market
locationTable[MARKET_TREASURE_CHEST_GAME_REWARD] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_REWARD, 0x10, "MK Treasure Chest Game Reward", MARKET_TREASURE_CHEST_GAME_REWARD, TREASURE_GAME_HEART, {}, SpoilerCollectionCheck::ItemGetInf(19), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_TREASURE_CHEST_GAME_REWARD] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_REWARD, 0x10, "MK Treasure Chest Game Reward", MARKET_TREASURE_CHEST_GAME_REWARD, TREASURE_GAME_HEART, {}, SpoilerCollectionCheck::ItemGetInf(27), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_BOMBCHU_BOWLING_FIRST_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, 0x4B, "MK Bombchu Bowling First Prize", MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, PROGRESSIVE_BOMB_BAG, {}, SpoilerCollectionCheck::ItemGetInf(25), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_BOMBCHU_BOWLING_FIRST_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, 0x4B, "MK Bombchu Bowling First Prize", MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, PROGRESSIVE_BOMB_BAG, {}, SpoilerCollectionCheck::ItemGetInf(17), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_BOMBCHU_BOWLING_SECOND_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, 0x4B, "MK Bombchu Bowling Second Prize", MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(26), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_BOMBCHU_BOWLING_SECOND_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, 0x4B, "MK Bombchu Bowling Second Prize", MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(18), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_BOMBCHU_BOWLING_BOMBCHUS] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_BOMBCHUS, 0x4B, "MK Bombchu Bowling Bombchus", NONE, BOMBCHU_DROP, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_BOMBCHU_BOWLING_BOMBCHUS] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_BOMBCHUS, 0x4B, "MK Bombchu Bowling Bombchus", NONE, BOMBCHU_DROP, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_LOST_DOG] = ItemLocation::Base (RC_MARKET_LOST_DOG, 0x35, "MK Lost Dog", MARKET_LOST_DOG, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x09), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_LOST_DOG] = ItemLocation::Base (RC_MARKET_LOST_DOG, 0x35, "MK Lost Dog", MARKET_LOST_DOG, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x09), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_MARKET_SHOOTING_GALLERY_REWARD, 0x42, "MK Shooting Gallery", MARKET_SHOOTING_GALLERY_REWARD, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(5), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_MARKET_SHOOTING_GALLERY_REWARD, 0x42, "MK Shooting Gallery", MARKET_SHOOTING_GALLERY_REWARD, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(13), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_10_BIG_POES] = ItemLocation::Base (RC_MARKET_10_BIG_POES, 0x4D, "MK 10 Big Poes", MARKET_10_BIG_POES, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_10_BIG_POES] = ItemLocation::Base (RC_MARKET_10_BIG_POES, 0x4D, "MK 10 Big Poes", MARKET_10_BIG_POES, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_1] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_1, 0x10, 0x01, "MK Chest Game First Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_1, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_1] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_1, 0x10, 0x01, "MK Chest Game First Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_1, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_2] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_2, 0x10, 0x03, "MK Chest Game Second Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_2, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_2] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_2, 0x10, 0x03, "MK Chest Game Second Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_2, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
@ -115,14 +115,14 @@ void LocationTable_Init() {
locationTable[KAK_30_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_30_GOLD_SKULLTULA_REWARD, 0x50, "Kak 30 Gold Skulltula Reward", KAK_30_GOLD_SKULLTULA_REWARD, PROGRESSIVE_WALLET, {}, SpoilerCollectionCheck::EventChkInf(0xDC), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_30_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_30_GOLD_SKULLTULA_REWARD, 0x50, "Kak 30 Gold Skulltula Reward", KAK_30_GOLD_SKULLTULA_REWARD, PROGRESSIVE_WALLET, {}, SpoilerCollectionCheck::EventChkInf(0xDC), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_40_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_40_GOLD_SKULLTULA_REWARD, 0x50, "Kak 40 Gold Skulltula Reward", KAK_40_GOLD_SKULLTULA_REWARD, BOMBCHU_10, {}, SpoilerCollectionCheck::EventChkInf(0xDD), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_40_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_40_GOLD_SKULLTULA_REWARD, 0x50, "Kak 40 Gold Skulltula Reward", KAK_40_GOLD_SKULLTULA_REWARD, BOMBCHU_10, {}, SpoilerCollectionCheck::EventChkInf(0xDD), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_50_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_50_GOLD_SKULLTULA_REWARD, 0x50, "Kak 50 Gold Skulltula Reward", KAK_50_GOLD_SKULLTULA_REWARD, PIECE_OF_HEART, {}, SpoilerCollectionCheck::EventChkInf(0xDE), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_50_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_50_GOLD_SKULLTULA_REWARD, 0x50, "Kak 50 Gold Skulltula Reward", KAK_50_GOLD_SKULLTULA_REWARD, PIECE_OF_HEART, {}, SpoilerCollectionCheck::EventChkInf(0xDE), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_100_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_100_GOLD_SKULLTULA_REWARD, 0x50, "Kak 100 Gold Skulltula Reward", KAK_100_GOLD_SKULLTULA_REWARD, HUGE_RUPEE, {}, SpoilerCollectionCheck::EventChkInf(0xDF), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_100_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_100_GOLD_SKULLTULA_REWARD, 0x50, "Kak 100 Gold Skulltula Reward", KAK_100_GOLD_SKULLTULA_REWARD, HUGE_RUPEE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(21), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(56), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(48), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cMerchant}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cMerchant}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {}, SpoilerCollectionCheck::ItemGetInf(36), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {}, SpoilerCollectionCheck::ItemGetInf(44), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::ItemGetInf(4), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::ItemGetInf(12), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(38), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(46), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_IMPAS_HOUSE_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_IMPAS_HOUSE_FREESTANDING_POH, 0x37, 0x01, "Kak Impas House Freestanding PoH", KAK_IMPAS_HOUSE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_IMPAS_HOUSE_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_IMPAS_HOUSE_FREESTANDING_POH, 0x37, 0x01, "Kak Impas House Freestanding PoH", KAK_IMPAS_HOUSE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_WINDMILL_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_WINDMILL_FREESTANDING_POH, 0x48, 0x01, "Kak Windmill Freestanding PoH", KAK_WINDMILL_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_WINDMILL_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_WINDMILL_FREESTANDING_POH, 0x48, 0x01, "Kak Windmill Freestanding PoH", KAK_WINDMILL_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
@ -133,7 +133,7 @@ void LocationTable_Init() {
locationTable[GRAVEYARD_HOOKSHOT_CHEST] = ItemLocation::Chest (RC_GRAVEYARD_HOOKSHOT_CHEST, 0x48, 0x00, "GY Hookshot Chest", GRAVEYARD_HOOKSHOT_CHEST, PROGRESSIVE_HOOKSHOT, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_HOOKSHOT_CHEST] = ItemLocation::Chest (RC_GRAVEYARD_HOOKSHOT_CHEST, 0x48, 0x00, "GY Hookshot Chest", GRAVEYARD_HOOKSHOT_CHEST, PROGRESSIVE_HOOKSHOT, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[GRAVEYARD_DAMPE_RACE_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, 0x48, 0x07, "GY Dampe Race Freestanding PoH", GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_DAMPE_RACE_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, 0x48, 0x07, "GY Dampe Race Freestanding PoH", GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[GRAVEYARD_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_FREESTANDING_POH, 0x53, 0x04, "GY Freestanding PoH", GRAVEYARD_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_FREESTANDING_POH, 0x53, 0x04, "GY Freestanding PoH", GRAVEYARD_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {}, SpoilerCollectionCheck::Gravedigger(0x53, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {}, SpoilerCollectionCheck::Gravedigger(0x53, 0x19), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
//Death Mountain //Death Mountain
locationTable[DMT_CHEST] = ItemLocation::Chest (RC_DMT_CHEST, 0x60, 0x01, "DMT Chest", DMT_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); locationTable[DMT_CHEST] = ItemLocation::Chest (RC_DMT_CHEST, 0x60, 0x01, "DMT Chest", DMT_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN);
@ -191,7 +191,7 @@ void LocationTable_Init() {
locationTable[ZF_BOTTOM_FREESTANDING_POH] = ItemLocation::Collectable(RC_ZF_BOTTOM_FREESTANDING_POH, 0x59, 0x14, "ZF Bottom Freestanding PoH", ZF_BOTTOM_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); locationTable[ZF_BOTTOM_FREESTANDING_POH] = ItemLocation::Collectable(RC_ZF_BOTTOM_FREESTANDING_POH, 0x59, 0x14, "ZF Bottom Freestanding PoH", ZF_BOTTOM_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN);
//Lon Lon Ranch //Lon Lon Ranch
locationTable[LLR_TALONS_CHICKENS] = ItemLocation::Base (RC_LLR_TALONS_CHICKENS, 0x4C, "LLR Talons Chickens", LLR_TALONS_CHICKENS, BOTTLE_WITH_MILK, {}, SpoilerCollectionCheck::ItemGetInf(10), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_TALONS_CHICKENS] = ItemLocation::Base (RC_LLR_TALONS_CHICKENS, 0x4C, "LLR Talons Chickens", LLR_TALONS_CHICKENS, BOTTLE_WITH_MILK, {}, SpoilerCollectionCheck::ItemGetInf(2), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
locationTable[LLR_FREESTANDING_POH] = ItemLocation::Collectable(RC_LLR_FREESTANDING_POH, 0x4C, 0x01, "LLR Freestanding PoH", LLR_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_FREESTANDING_POH] = ItemLocation::Collectable(RC_LLR_FREESTANDING_POH, 0x4C, 0x01, "LLR Freestanding PoH", LLR_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
locationTable[LLR_DEKU_SCRUB_GROTTO_LEFT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_LEFT, 0xFC, "LLR Deku Scrub Grotto Left", LLR_DEKU_SCRUB_GROTTO_LEFT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_DEKU_SCRUB_GROTTO_LEFT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_LEFT, 0xFC, "LLR Deku Scrub Grotto Left", LLR_DEKU_SCRUB_GROTTO_LEFT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
locationTable[LLR_DEKU_SCRUB_GROTTO_RIGHT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_RIGHT, 0xFC, "LLR Deku Scrub Grotto Right", LLR_DEKU_SCRUB_GROTTO_RIGHT, BUY_BOMBS_535, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_DEKU_SCRUB_GROTTO_RIGHT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_RIGHT, 0xFC, "LLR Deku Scrub Grotto Right", LLR_DEKU_SCRUB_GROTTO_RIGHT, BUY_BOMBS_535, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);

View file

@ -1,5 +1,6 @@
#include "playthrough.hpp" #include "playthrough.hpp"
#include <libultraship/libultraship.h>
#include <boost_custom/container_hash/hash_32.hpp> #include <boost_custom/container_hash/hash_32.hpp>
#include "custom_messages.hpp" #include "custom_messages.hpp"
#include "fill.hpp" #include "fill.hpp"
@ -8,6 +9,7 @@
#include "random.hpp" #include "random.hpp"
#include "spoiler_log.hpp" #include "spoiler_log.hpp"
#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "variables.h"
namespace Playthrough { namespace Playthrough {
@ -39,6 +41,10 @@ int Playthrough_Init(uint32_t seed, std::unordered_map<RandomizerSettingKey, uin
} }
} }
if (CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
settingsStr += (char*)gBuildVersion;
}
uint32_t finalHash = boost::hash_32<std::string>{}(std::to_string(Settings::seed) + settingsStr); uint32_t finalHash = boost::hash_32<std::string>{}(std::to_string(Settings::seed) + settingsStr);
Random_Init(finalHash); Random_Init(finalHash);
Settings::hash = std::to_string(finalHash); Settings::hash = std::to_string(finalHash);

View file

@ -361,37 +361,35 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Shuffle Dungeon Quest:Ganon's Castle", RSK_MQ_GANONS_CASTLE }, { "Shuffle Dungeon Quest:Ganon's Castle", RSK_MQ_GANONS_CASTLE },
}; };
std::string sanitize(std::string stringValue) {
// Add backslashes.
for (auto i = stringValue.begin();;) {
auto const pos = std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
if (pos == stringValue.end()) {
break;
}
i = std::next(stringValue.insert(pos, '\\'), 2);
}
// Removes others.
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) {
return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end());
return stringValue;
}
#pragma optimize("", off) #pragma optimize("", off)
#pragma GCC push_options #pragma GCC push_options
#pragma GCC optimize ("O0") #pragma GCC optimize ("O0")
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
if (strcmp(spoilerFileName, "") != 0) { try {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); if (strcmp(spoilerFileName, "") != 0) {
if (!spoilerFileStream) { std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
return false; if (!spoilerFileStream) {
} else { return false;
}
json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;
if (!spoilerFileJson.contains("version") || !spoilerFileJson.contains("finalSeed")) {
return false;
}
return true; return true;
} }
}
return false; return false;
} catch (std::exception& e) {
SPDLOG_ERROR("Error checking if spoiler file exists: {}", e.what());
return false;
} catch (...) {
SPDLOG_ERROR("Error checking if spoiler file exists");
return false;
}
} }
#pragma GCC pop_options #pragma GCC pop_options
#pragma optimize("", on) #pragma optimize("", on)
@ -660,7 +658,7 @@ void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
} }
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
return; return;
@ -1294,7 +1292,7 @@ std::string FormatJsonHintText(std::string jsonHint) {
} }
void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) { void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
return; return;
@ -1395,7 +1393,7 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
} }
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) { void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) { if (!spoilerFileStream) {
return; return;
} }
@ -1416,7 +1414,7 @@ void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
} }
void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) { void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) { if (!spoilerFileStream) {
return; return;
} }
@ -1496,7 +1494,7 @@ int16_t Randomizer::GetVanillaMerchantPrice(RandomizerCheck check) {
} }
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) { void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
return; return;
@ -1559,7 +1557,7 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
} }
void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent) { void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) { if (!spoilerFileStream) {
return; return;
} }
@ -2548,6 +2546,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
{ RC_LH_CHILD_FISHING, RAND_INF_CHILD_FISHING }, { RC_LH_CHILD_FISHING, RAND_INF_CHILD_FISHING },
{ RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING }, { RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING },
{ RC_MARKET_10_BIG_POES, RAND_INF_10_BIG_POES }, { RC_MARKET_10_BIG_POES, RAND_INF_10_BIG_POES },
{ RC_KAK_100_GOLD_SKULLTULA_REWARD, RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD },
}; };
RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) { RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) {
@ -3144,7 +3143,9 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
} }
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
DrawPresetSelector(PRESET_TYPE_RANDOMIZER); DrawPresetSelector(PRESET_TYPE_RANDOMIZER);
ImGui::EndDisabled();
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, ""); UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, "");
@ -3167,13 +3168,17 @@ void RandomizerSettingsWindow::DrawElement() {
} }
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gRandomizerDontGenerateSpoiler", 0) && gSaveContext.gameMode != GAMEMODE_FILE_SELECT);
if (ImGui::Button("Generate Randomizer")) { if (ImGui::Button("Generate Randomizer")) {
GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : ""); GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : "");
} }
ImGui::EndDisabled();
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
std::string spoilerfilepath = CVarGetString("gSpoilerLog", ""); if (!CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str()); std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
}
// RANDOTODO settings presets // RANDOTODO settings presets
// std::string presetfilepath = CVarGetString("gLoadedPreset", ""); // std::string presetfilepath = CVarGetString("gLoadedPreset", "");
@ -3181,6 +3186,8 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
ImGuiWindow* window = ImGui::GetCurrentWindow(); ImGuiWindow* window = ImGui::GetCurrentWindow();
static ImVec2 cellPadding(8.0f, 8.0f); static ImVec2 cellPadding(8.0f, 8.0f);
@ -5225,6 +5232,8 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::EndTabBar(); ImGui::EndTabBar();
} }
ImGui::EndDisabled();
if (disableEditingRandoSettings) { if (disableEditingRandoSettings) {
UIWidgets::ReEnableComponent(""); UIWidgets::ReEnableComponent("");

View file

@ -39,7 +39,7 @@ std::map<RandomizerCheck, RandomizerCheckObject> rcObjects = {
RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x01, GI_STICKS_1, "Deku Scrub Near Deku Theater Left", "LW Deku Scrub Near Deku Theater Left", false), RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x01, GI_STICKS_1, "Deku Scrub Near Deku Theater Left", "LW Deku Scrub Near Deku Theater Left", false),
RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x09, GI_STICK_UPGRADE_20, "Deku Scrub Near Bridge", "LW Deku Scrub Near Bridge", true), RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x09, GI_STICK_UPGRADE_20, "Deku Scrub Near Bridge", "LW Deku Scrub Near Bridge", true),
RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_REAR, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x03,0xF5), GI_SEEDS_30, "Deku Scrub Grotto Rear", "LW Deku Scrub Grotto Rear", false), RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_REAR, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x03,0xF5), GI_SEEDS_30, "Deku Scrub Grotto Rear", "LW Deku Scrub Grotto Rear", false),
RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_FRONT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x0A,0xF5), GI_NUT_UPGRADE_30, "Deku Scrub Grotto Front", "LW Deku Scrub Grotto Front", false), RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_FRONT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x0A,0xF5), GI_NUT_UPGRADE_30, "Deku Scrub Grotto Front", "LW Deku Scrub Grotto Front", true),
RC_OBJECT(RC_DEKU_THEATER_SKULL_MASK, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Skull Mask", "Deku Theater Skull Mask", true), RC_OBJECT(RC_DEKU_THEATER_SKULL_MASK, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Skull Mask", "Deku Theater Skull Mask", true),
RC_OBJECT(RC_DEKU_THEATER_MASK_OF_TRUTH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Mask of Truth", "Deku Theater Mask of Truth", true), RC_OBJECT(RC_DEKU_THEATER_MASK_OF_TRUTH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Mask of Truth", "Deku Theater Mask of Truth", true),
RC_OBJECT(RC_LW_GS_BEAN_PATCH_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_LOST_WOODS, ACTOR_EN_SI, SCENE_LOST_WOODS, 27905, GI_SKULL_TOKEN, "GS Bean Patch Near Bridge", "LW GS Bean Patch Near Bridge", true), RC_OBJECT(RC_LW_GS_BEAN_PATCH_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_LOST_WOODS, ACTOR_EN_SI, SCENE_LOST_WOODS, 27905, GI_SKULL_TOKEN, "GS Bean Patch Near Bridge", "LW GS Bean Patch Near Bridge", true),
@ -238,7 +238,7 @@ std::map<RandomizerCheck, RandomizerCheckObject> rcObjects = {
RC_OBJECT(RC_GRAVEYARD_HOOKSHOT_CHEST, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_BOX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 4352, GI_HOOKSHOT, "Hookshot Chest", "GY Hookshot Chest", true), RC_OBJECT(RC_GRAVEYARD_HOOKSHOT_CHEST, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_BOX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 4352, GI_HOOKSHOT, "Hookshot Chest", "GY Hookshot Chest", true),
RC_OBJECT(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_WINDMILL_AND_DAMPES_GRAVE, 1798, GI_HEART_PIECE, "Dampe Race Freestanding PoH", "GY Dampe Race Freestanding PoH", true), RC_OBJECT(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_WINDMILL_AND_DAMPES_GRAVE, 1798, GI_HEART_PIECE, "Dampe Race Freestanding PoH", "GY Dampe Race Freestanding PoH", true),
RC_OBJECT(RC_GRAVEYARD_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 1030, GI_HEART_PIECE, "Freestanding PoH", "GY Freestanding PoH", true), RC_OBJECT(RC_GRAVEYARD_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 1030, GI_HEART_PIECE, "Freestanding PoH", "GY Freestanding PoH", true),
RC_OBJECT(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 7942, GI_HEART_PIECE, "Dampe Gravedigging Tour", "GY Dampe Gravedigging Tour", true), RC_OBJECT(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 6406, GI_HEART_PIECE, "Dampe Gravedigging Tour", "GY Dampe Gravedigging Tour", true),
RC_OBJECT(RC_GRAVEYARD_GS_WALL, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 20608, GI_SKULL_TOKEN, "GS Wall", "Graveyard GS Wall", true), RC_OBJECT(RC_GRAVEYARD_GS_WALL, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 20608, GI_SKULL_TOKEN, "GS Wall", "Graveyard GS Wall", true),
RC_OBJECT(RC_GRAVEYARD_GS_BEAN_PATCH, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 28673, GI_SKULL_TOKEN, "GS Bean Patch", "Graveyard GS Bean Patch", true), RC_OBJECT(RC_GRAVEYARD_GS_BEAN_PATCH, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 28673, GI_SKULL_TOKEN, "GS Bean Patch", "Graveyard GS Bean Patch", true),
RC_OBJECT(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCVORMQ_BOTH, RCTYPE_SONG_LOCATION, RCAREA_GRAVEYARD, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, "Song from Composers Grave", "Song from Composers Grave", true), RC_OBJECT(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCVORMQ_BOTH, RCTYPE_SONG_LOCATION, RCAREA_GRAVEYARD, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, "Song from Composers Grave", "Song from Composers Grave", true),

View file

@ -73,21 +73,12 @@ bool showLinksPocket;
bool fortressFast; bool fortressFast;
bool fortressNormal; bool fortressNormal;
bool bypassRandoCheck = true;
// persistent during gameplay // persistent during gameplay
bool initialized; bool initialized;
bool doAreaScroll; bool doAreaScroll;
bool previousShowHidden = false; bool previousShowHidden = false;
bool hideShopRightChecks = true; bool hideShopRightChecks = true;
bool checkCollected = false;
int checkLoops = 0;
int checkCounter = 0;
u16 savedFrames = 0;
bool messageCloseCheck = false;
bool pendingSaleCheck = false;
bool transitionCheck = false;
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 }, std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 }, { SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
{ SCENE_POTION_SHOP_MARKET, RC_MARKET_POTION_SHOP_ITEM_1 }, { SCENE_POTION_SHOP_MARKET, RC_MARKET_POTION_SHOP_ITEM_1 },
@ -118,12 +109,9 @@ bool showVOrMQ;
s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)" s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)"
bool optCollapseAll; // A bool that will collapse all checks once bool optCollapseAll; // A bool that will collapse all checks once
bool optExpandAll; // A bool that will expand all checks once bool optExpandAll; // A bool that will expand all checks once
RandomizerCheck lastItemGetCheck = RC_UNKNOWN_CHECK;
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK; RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
RandomizerCheckArea previousArea = RCAREA_INVALID; RandomizerCheckArea previousArea = RCAREA_INVALID;
RandomizerCheckArea currentArea = RCAREA_INVALID; RandomizerCheckArea currentArea = RCAREA_INVALID;
std::vector<RandomizerCheckArea> checkAreas;
std::vector<GetItemEntry> itemsReceived;
OSContPad* trackerButtonsPressed; OSContPad* trackerButtonsPressed;
void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0); void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0);
@ -194,10 +182,6 @@ Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; // Green
std::vector<uint32_t> buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L, std::vector<uint32_t> buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L,
BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
void SetLastItemGetRC(RandomizerCheck rc) {
lastItemGetCheck = rc;
}
void DefaultCheckData(RandomizerCheck rc) { void DefaultCheckData(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED; gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED;
gSaveContext.checkTrackerData[rc].skipped = 0; gSaveContext.checkTrackerData[rc].skipped = 0;
@ -253,9 +237,6 @@ void SetCheckCollected(RandomizerCheck rc) {
} else { } else {
gSaveContext.checkTrackerData[rc].skipped = false; gSaveContext.checkTrackerData[rc].skipped = false;
} }
if (!checkAreas.empty()) {
checkAreas.erase(checkAreas.begin());
}
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
doAreaScroll = true; doAreaScroll = true;
@ -360,32 +341,18 @@ bool vector_contains_scene(std::vector<SceneID> vec, const int16_t scene) {
std::vector<SceneID> skipScenes = {SCENE_GANON_BOSS, SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, SCENE_GANON_BOSS, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR}; std::vector<SceneID> skipScenes = {SCENE_GANON_BOSS, SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, SCENE_GANON_BOSS, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR};
bool EvaluateCheck(RandomizerCheckObject rco) { void ClearAreaChecksAndTotals() {
if (HasItemBeenCollected(rco.rc) && gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_COLLECTED && for (auto& [rcArea, vec] : checksByArea) {
gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_SAVED) { vec.clear();
SetCheckCollected(rco.rc); areaChecksGotten[rcArea] = 0;
return true;
}
return false;
}
bool CheckByArea(RandomizerCheckArea area = RCAREA_INVALID) {
if (area == RCAREA_INVALID) {
area = checkAreas.front();
}
if (area != RCAREA_INVALID) {
auto areaChecks = checksByArea.find(area)->second;
if (checkCounter >= areaChecks.size()) {
checkCounter = 0;
checkLoops++;
}
auto rco = areaChecks.at(checkCounter);
return EvaluateCheck(rco);
} }
} }
void SetShopSeen(uint32_t sceneNum, bool prices) { void SetShopSeen(uint32_t sceneNum, bool prices) {
RandomizerCheck start = startingShopItem.find(sceneNum)->second; RandomizerCheck start = startingShopItem.find(sceneNum)->second;
if (sceneNum == SCENE_POTION_SHOP_KAKARIKO && !LINK_IS_ADULT) {
return;
}
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) { if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) {
start = RC_KAK_BAZAAR_ITEM_1; start = RC_KAK_BAZAAR_ITEM_1;
} }
@ -509,12 +476,6 @@ void CheckTrackerLoadGame(int32_t fileNum) {
UpdateInventoryChecks(); UpdateInventoryChecks();
} }
void CheckTrackerDialogClosed() {
if (messageCloseCheck) {
messageCloseCheck = false;
}
}
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) { void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
if (gPlayState->sceneNum == SCENE_HAPPY_MASK_SHOP) { // Happy Mask Shop is not used in rando, so is not tracked if (gPlayState->sceneNum == SCENE_HAPPY_MASK_SHOP) { // Happy Mask Shop is not used in rando, so is not tracked
return; return;
@ -536,10 +497,6 @@ void CheckTrackerTransition(uint32_t sceneNum) {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded()) {
return; return;
} }
gSaveContext;
if (transitionCheck) {
transitionCheck = false;
}
doAreaScroll = true; doAreaScroll = true;
previousArea = currentArea; previousArea = currentArea;
currentArea = GetCheckArea(); currentArea = GetCheckArea();
@ -560,28 +517,12 @@ void CheckTrackerFrame() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded()) {
return; return;
} }
if (!checkAreas.empty() && !transitionCheck && !messageCloseCheck && !pendingSaleCheck) { // TODO: Move to OnAmmoChange hook once it gets added.
for (int i = 0; i < 10; i++) { if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED &&
if (CheckByArea()) { gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) {
checkCounter = 0; if (BEANS_BOUGHT >= 10) {
break; SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
} else {
checkCounter++;
}
} }
if (checkLoops > 15) {
checkAreas.erase(checkAreas.begin());
checkLoops = 0;
}
}
if (savedFrames > 0 && !pendingSaleCheck && !messageCloseCheck) {
savedFrames--;
}
}
void CheckTrackerSaleEnd(GetItemEntry giEntry) {
if (pendingSaleCheck) {
pendingSaleCheck = false;
} }
} }
@ -593,7 +534,7 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
// Vanilla special item checks // Vanilla special item checks
if (!IS_RANDO) { if (!IS_RANDO) {
if (giEntry.itemId == ITEM_SHIELD_DEKU) { if (giEntry.itemId == ITEM_SHIELD_DEKU) {
SetCheckCollected(RC_KF_SHOP_ITEM_3); SetCheckCollected(RC_KF_SHOP_ITEM_1);
return; return;
}else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) { }else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) {
SetCheckCollected(RC_QUEEN_GOHMA); SetCheckCollected(RC_QUEEN_GOHMA);
@ -622,16 +563,19 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
} else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) { } else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) {
SetCheckCollected(RC_GIFT_FROM_SAGES); SetCheckCollected(RC_GIFT_FROM_SAGES);
return; return;
} else if (giEntry.itemId == ITEM_SONG_LULLABY) {
SetCheckCollected(RC_SONG_FROM_IMPA);
return;
} else if (giEntry.itemId == ITEM_SONG_EPONA) { } else if (giEntry.itemId == ITEM_SONG_EPONA) {
SetCheckCollected(RC_SONG_FROM_MALON); SetCheckCollected(RC_SONG_FROM_MALON);
return; return;
} else if (giEntry.itemId == ITEM_SONG_SARIA) { } else if (giEntry.itemId == ITEM_SONG_SARIA) {
SetCheckCollected(RC_SONG_FROM_SARIA); SetCheckCollected(RC_SONG_FROM_SARIA);
return; return;
} else if (giEntry.itemId == ITEM_SONG_SUN) { } else if (giEntry.itemId == ITEM_BEAN) {
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
return;
} else if (giEntry.itemId == ITEM_BRACELET) {
SetCheckCollected(RC_GC_DARUNIAS_JOY);
return;
}/* else if (giEntry.itemId == ITEM_SONG_SUN) {
SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB); SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB);
return; return;
} else if (giEntry.itemId == ITEM_SONG_TIME) { } else if (giEntry.itemId == ITEM_SONG_TIME) {
@ -658,42 +602,127 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
} else if (giEntry.itemId == ITEM_SONG_PRELUDE) { } else if (giEntry.itemId == ITEM_SONG_PRELUDE) {
SetCheckCollected(RC_SHEIK_AT_TEMPLE); SetCheckCollected(RC_SHEIK_AT_TEMPLE);
return; return;
} else if (giEntry.itemId == ITEM_BRACELET) { }*/
SetCheckCollected(RC_GC_DARUNIAS_JOY); }
return; }
} else if (giEntry.itemId == ITEM_LETTER_ZELDA) {
SetCheckCollected(RC_HC_ZELDAS_LETTER); void CheckTrackerSceneFlagSet(int16_t sceneNum, int16_t flagType, int32_t flag) {
return; if (flagType != FLAG_SCENE_TREASURE && flagType != FLAG_SCENE_COLLECTIBLE) {
} else if (giEntry.itemId == ITEM_WEIRD_EGG) { return;
SetCheckCollected(RC_HC_MALON_EGG); }
return; if (sceneNum == SCENE_GRAVEYARD && flag == 0x19 && flagType == FLAG_SCENE_COLLECTIBLE) { // Gravedigging tour special case
} else if (giEntry.itemId == ITEM_BEAN) { SetCheckCollected(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR);
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN); return;
}
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
if (!IsVisibleInCheckTracker(rcObj)) {
continue;
}
SpoilerCollectionCheckType checkMatchType = flagType == FLAG_SCENE_TREASURE ? SpoilerCollectionCheckType::SPOILER_CHK_CHEST : SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE;
SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
if (scCheck.scene == sceneNum && scCheck.flag == flag && scCheck.type == checkMatchType) {
SetCheckCollected(rc);
return; return;
} }
} }
auto checkArea = GetCheckArea(); }
if (gSaveContext.pendingSale != ITEM_NONE) {
pendingSaleCheck = true; void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
checkAreas.push_back(checkArea); SpoilerCollectionCheckType checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_NONE;
switch (flagType) {
case FLAG_GS_TOKEN:
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA;
break;
case FLAG_EVENT_CHECK_INF:
if ((flag == EVENTCHKINF_CARPENTERS_FREE(0) || flag == EVENTCHKINF_CARPENTERS_FREE(1) ||
flag == EVENTCHKINF_CARPENTERS_FREE(2) || flag == EVENTCHKINF_CARPENTERS_FREE(3))
&& GET_EVENTCHKINF_CARPENTERS_FREE_ALL()) {
SetCheckCollected(RC_GF_GERUDO_MEMBERSHIP_CARD);
return;
}
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF;
break;
case FLAG_INF_TABLE:
if (flag == INFTABLE_190) {
SetCheckCollected(RC_GF_HBA_1000_POINTS);
return;
} else if (flag == INFTABLE_11E) {
SetCheckCollected(RC_GC_ROLLING_GORON_AS_CHILD);
return;
} else if (flag == INFTABLE_GORON_CITY_DOORS_UNLOCKED) {
SetCheckCollected(RC_GC_ROLLING_GORON_AS_ADULT);
return;
} else if (flag == INFTABLE_139) {
SetCheckCollected(RC_ZD_KING_ZORA_THAWED);
return;
} else if (flag == INFTABLE_191) {
SetCheckCollected(RC_MARKET_LOST_DOG);
return;
}
if (!IS_RANDO) {
if (flag == INFTABLE_192) {
SetCheckCollected(RC_LW_DEKU_SCRUB_NEAR_BRIDGE);
return;
} else if (flag == INFTABLE_193) {
SetCheckCollected(RC_LW_DEKU_SCRUB_GROTTO_FRONT);
return;
}
}
break;
case FLAG_ITEM_GET_INF:
if (!IS_RANDO) {
if (flag == ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE) {
SetCheckCollected(RC_DEKU_THEATER_SKULL_MASK);
return;
} else if (flag == ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) {
SetCheckCollected(RC_DEKU_THEATER_MASK_OF_TRUTH);
return;
} else if (flag == ITEMGETINF_0B) {
SetCheckCollected(RC_HF_DEKU_SCRUB_GROTTO);
return;
}
}
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF;
break;
case FLAG_RANDOMIZER_INF:
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF;
break;
}
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_NONE) {
return; return;
} }
if (scene == SCENE_DESERT_COLOSSUS && (gSaveContext.entranceIndex == 485 || gSaveContext.entranceIndex == 489)) { for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
checkAreas.push_back(RCAREA_SPIRIT_TEMPLE); if ((!IS_RANDO && ((rcObj.vOrMQ == RCVORMQ_MQ && !IS_MASTER_QUEST) ||
return; (rcObj.vOrMQ == RCVORMQ_VANILLA && IS_MASTER_QUEST))) ||
} (IS_RANDO && ((OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
if (GET_PLAYER(gPlayState) == nullptr) { rcObj.vOrMQ == RCVORMQ_VANILLA) ||
transitionCheck = true; !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
return; rcObj.vOrMQ == RCVORMQ_MQ))) {
} continue;
if (gPlayState->msgCtx.msgMode != MSGMODE_NONE) { }
checkAreas.push_back(checkArea); SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
messageCloseCheck = true; SpoilerCollectionCheckType scCheckType = scCheck.type;
return; if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF &&
} (scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT ||
if (IS_RANDO || (!IS_RANDO && giEntry.getItemCategory != ITEM_CATEGORY_JUNK)) { scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
checkAreas.push_back(checkArea); scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW ||
checkCollected = true; scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SCRUB ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF)) {
if (flag == OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc)) {
SetCheckCollected(rc);
return;
}
continue;
}
int16_t checkFlag = scCheck.flag;
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA) {
checkFlag = rcObj.actorParams;
}
if (checkFlag == flag && scCheck.type == checkMatchType) {
SetCheckCollected(rc);
return;
}
} }
} }
@ -711,7 +740,7 @@ void InitTrackerData(bool isDebug) {
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) { void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) { SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) { if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
if (gameSave || savedFrames > 0) { if (gameSave) {
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED; gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
UpdateAllOrdering(); UpdateAllOrdering();
UpdateInventoryChecks(); UpdateInventoryChecks();
@ -730,9 +759,6 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveTrackerData(saveContext, sectionID, fullSave); SaveTrackerData(saveContext, sectionID, fullSave);
if (fullSave) {
savedFrames = 40;
}
} }
void LoadFile() { void LoadFile() {
@ -748,14 +774,9 @@ void LoadFile() {
void Teardown() { void Teardown() {
initialized = false; initialized = false;
for (auto& [rcArea, vec] : checksByArea) { ClearAreaChecksAndTotals();
vec.clear();
areaChecksGotten[rcArea] = 0;
}
checksByArea.clear(); checksByArea.clear();
areasSpoiled = 0; areasSpoiled = 0;
checkCollected = false;
checkLoops = 0;
lastLocationChecked = RC_UNKNOWN_CHECK; lastLocationChecked = RC_UNKNOWN_CHECK;
} }
@ -925,7 +946,11 @@ void CheckTrackerWindow::DrawElement() {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f,
extraColor.b / 255.0f, extraColor.a / 255.0f)); extraColor.b / 255.0f, extraColor.a / 255.0f));
isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0); isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0) || !IS_RANDO ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_NONE ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SELECTION ||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12);
if (isThisAreaSpoiled) { if (isThisAreaSpoiled) {
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) { if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
@ -1151,8 +1176,8 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
} }
else if (rcObj.vanillaCompletion) { else if (rcObj.vanillaCompletion) {
return (rcObj.vOrMQ == RCVORMQ_BOTH || return (rcObj.vOrMQ == RCVORMQ_BOTH ||
rcObj.vOrMQ == RCVORMQ_MQ && OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) || (rcObj.vOrMQ == RCVORMQ_MQ && IS_MASTER_QUEST) ||
rcObj.vOrMQ == RCVORMQ_VANILLA && !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) || (rcObj.vOrMQ == RCVORMQ_VANILLA && !IS_MASTER_QUEST) ||
rcObj.rc == RC_GIFT_FROM_SAGES) && rcObj.rc != RC_LINKS_POCKET; rcObj.rc == RC_GIFT_FROM_SAGES) && rcObj.rc != RC_LINKS_POCKET;
} }
return false; return false;
@ -1541,10 +1566,11 @@ void CheckTrackerWindow::InitElement() {
Teardown(); Teardown();
}); });
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSaleEnd>(CheckTrackerSaleEnd);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(CheckTrackerSceneFlagSet);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(CheckTrackerFlagSet);
LocationTable_Init(); LocationTable_Init();
} }

View file

@ -48,10 +48,7 @@ void Teardown();
void UpdateAllOrdering(); void UpdateAllOrdering();
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj); bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
void InitTrackerData(bool isDebug); void InitTrackerData(bool isDebug);
void SetLastItemGetRC(RandomizerCheck rc);
RandomizerCheckArea GetCheckArea(); RandomizerCheckArea GetCheckArea();
void CheckTrackerDialogClosed();
void ToggleShopRightChecks();
void UpdateCheck(uint32_t, RandomizerCheckTrackerData); void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
} // namespace CheckTracker } // namespace CheckTracker

View file

@ -90,7 +90,7 @@ static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
static s8 hasCopiedEntranceTable = 0; static s8 hasCopiedEntranceTable = 0;
static s8 hasModifiedEntranceTable = 0; static s8 hasModifiedEntranceTable = 0;
void Entrance_SetEntranceDiscovered(u16 entranceIndex); void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance);
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) { u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0 return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
@ -290,13 +290,13 @@ s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) {
return nextEntranceIndex; return nextEntranceIndex;
} }
Entrance_SetEntranceDiscovered(nextEntranceIndex); Entrance_SetEntranceDiscovered(nextEntranceIndex, false);
EntranceTracker_SetLastEntranceOverride(nextEntranceIndex); EntranceTracker_SetLastEntranceOverride(nextEntranceIndex);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex)); return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex));
} }
s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) { s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) {
Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex]); Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex], false);
EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]); EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex])); return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex]));
} }
@ -799,7 +799,7 @@ u8 Entrance_GetIsEntranceDiscovered(u16 entranceIndex) {
return 0; return 0;
} }
void Entrance_SetEntranceDiscovered(u16 entranceIndex) { void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) {
// Skip if already set to save time from setting the connected entrance or // Skip if already set to save time from setting the connected entrance or
// if this entrance is outside of the randomized entrance range (i.e. is a dynamic entrance) // if this entrance is outside of the randomized entrance range (i.e. is a dynamic entrance)
if (entranceIndex > MAX_ENTRANCE_RANDO_USED_INDEX || Entrance_GetIsEntranceDiscovered(entranceIndex)) { if (entranceIndex > MAX_ENTRANCE_RANDO_USED_INDEX || Entrance_GetIsEntranceDiscovered(entranceIndex)) {
@ -811,14 +811,20 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) { if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex)); u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit; gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit;
// Set connected
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) { // Set reverse entrance when not decoupled
if (entranceIndex == gSaveContext.entranceOverrides[i].index) { if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination); for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
break; if (entranceIndex == gSaveContext.entranceOverrides[i].index) {
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination, true);
break;
}
} }
} }
} }
// Save entrancesDiscovered
Save_SaveSection(SECTION_ID_ENTRANCES); // Save entrancesDiscovered when it is not the reversed entrance
if (!isReversedEntrance) {
Save_SaveSection(SECTION_ID_ENTRANCES);
}
} }

View file

@ -109,7 +109,7 @@ void Entrance_OverrideSpawnScene(int32_t sceneNum, int32_t spawn);
int32_t Entrance_OverrideSpawnSceneRoom(int32_t sceneNum, int32_t spawn, int32_t room); int32_t Entrance_OverrideSpawnSceneRoom(int32_t sceneNum, int32_t spawn, int32_t room);
void Entrance_EnableFW(void); void Entrance_EnableFW(void);
uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex); uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex);
void Entrance_SetEntranceDiscovered(uint16_t entranceIndex); void Entrance_SetEntranceDiscovered(uint16_t entranceIndex, uint8_t isReversedEntrance);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -140,7 +140,7 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
// If Link hits a grotto exit, load the entrance index from the grotto exit list // If Link hits a grotto exit, load the entrance index from the grotto exit list
// based on the current grotto ID // based on the current grotto ID
if (nextEntranceIndex == ENTR_RETURN_GROTTO) { if (nextEntranceIndex == ENTR_RETURN_GROTTO) {
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId); Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId, false);
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId); EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId);
nextEntranceIndex = grottoExitList[grottoId]; nextEntranceIndex = grottoExitList[grottoId];
} }
@ -211,7 +211,7 @@ void Grotto_OverrideActorEntrance(Actor* thisx) {
if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) { if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) {
// Find the override for the matching index from the grotto Load List // Find the override for the matching index from the grotto Load List
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_LOAD_START + index); Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_LOAD_START + index, false);
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_LOAD_START + index); EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_LOAD_START + index);
index = grottoLoadList[index]; index = grottoLoadList[index];

View file

@ -50,6 +50,9 @@
#include "Fonts.h" #include "Fonts.h"
#include <Utils/StringHelper.h> #include <Utils/StringHelper.h>
#include "Enhancements/custom-message/CustomMessageManager.h" #include "Enhancements/custom-message/CustomMessageManager.h"
#include "Enhancements/presets.h"
#include "util.h"
#include <boost_custom/container_hash/hash_32.hpp>
#if not defined (__SWITCH__) && not defined(__WIIU__) #if not defined (__SWITCH__) && not defined(__WIIU__)
#include "Extractor/Extract.h" #include "Extractor/Extract.h"
@ -254,15 +257,26 @@ OTRGlobals::OTRGlobals() {
OTRFiles.push_back(sohOtrPath); OTRFiles.push_back(sohOtrPath);
} }
std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName); std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName);
std::vector<std::string> patchOTRs = {};
if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) { if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) {
if (std::filesystem::is_directory(patchesPath)) { if (std::filesystem::is_directory(patchesPath)) {
for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) { for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath, std::filesystem::directory_options::follow_directory_symlink)) {
if (StringHelper::IEquals(p.path().extension().string(), ".otr")) { if (StringHelper::IEquals(p.path().extension().string(), ".otr")) {
OTRFiles.push_back(p.path().generic_string()); patchOTRs.push_back(p.path().generic_string());
} }
} }
} }
} }
std::sort(patchOTRs.begin(), patchOTRs.end(), [](const std::string& a, const std::string& b) {
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
[](char c1, char c2) {
return std::tolower(c1) < std::tolower(c2);
}
);
});
OTRFiles.insert(OTRFiles.end(), patchOTRs.begin(), patchOTRs.end());
std::unordered_set<uint32_t> ValidHashes = { std::unordered_set<uint32_t> ValidHashes = {
OOT_PAL_MQ, OOT_PAL_MQ,
OOT_NTSC_JP_MQ, OOT_NTSC_JP_MQ,
@ -1234,6 +1248,7 @@ extern "C" void Graph_StartFrame() {
case KbScancode::LUS_KB_TAB: { case KbScancode::LUS_KB_TAB: {
// Toggle HD Assets // Toggle HD Assets
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0)); CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
ShouldClearTextureCacheAtEndOfFrame = true; ShouldClearTextureCacheAtEndOfFrame = true;
break; break;
} }
@ -1433,6 +1448,14 @@ extern "C" void ResourceMgr_DirtyDirectory(const char* resName) {
LUS::Context::GetInstance()->GetResourceManager()->DirtyDirectory(resName); LUS::Context::GetInstance()->GetResourceManager()->DirtyDirectory(resName);
} }
extern "C" void ResourceMgr_UnloadResource(const char* resName) {
std::string path = resName;
if (path.substr(0, 7) == "__OTR__") {
path = path.substr(7);
}
auto res = LUS::Context::GetInstance()->GetResourceManager()->UnloadResource(path);
}
// OTRTODO: There is probably a more elegant way to go about this... // OTRTODO: There is probably a more elegant way to go about this...
// Kenix: This is definitely leaking memory when it's called. // Kenix: This is definitely leaking memory when it's called.
extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) { extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) {
@ -1459,6 +1482,27 @@ extern "C" uint8_t ResourceMgr_FileExists(const char* filePath) {
return ExtensionCache.contains(path); return ExtensionCache.contains(path);
} }
extern "C" uint8_t ResourceMgr_FileAltExists(const char* filePath) {
std::string path = filePath;
if (path.substr(0, 7) == "__OTR__") {
path = path.substr(7);
}
if (path.substr(0, 4) != "alt/") {
path = "alt/" + path;
}
return ExtensionCache.contains(path);
}
// Unloads a resource if an alternate version exists when alt assets are enabled
// The resource is only removed from the internal cache to prevent it from used in the next resource lookup
extern "C" void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName) {
if (CVarGetInteger("gAltAssets", 0) && ResourceMgr_FileAltExists((char*) resName)) {
ResourceMgr_UnloadResource((char*) resName);
}
}
extern "C" void ResourceMgr_LoadFile(const char* resName) { extern "C" void ResourceMgr_LoadFile(const char* resName) {
LUS::Context::GetInstance()->GetResourceManager()->LoadResource(resName); LUS::Context::GetInstance()->GetResourceManager()->LoadResource(resName);
} }
@ -1498,6 +1542,11 @@ extern "C" char* ResourceMgr_LoadFileFromDisk(const char* filePath) {
return data; return data;
} }
extern "C" uint8_t ResourceMgr_TexIsRaw(const char* texPath) {
auto res = std::static_pointer_cast<LUS::Texture>(GetResourceByNameHandlingMQ(texPath));
return res->Flags & TEX_FLAG_LOAD_AS_RAW;
}
extern "C" uint8_t ResourceMgr_ResourceIsBackground(char* texPath) { extern "C" uint8_t ResourceMgr_ResourceIsBackground(char* texPath) {
auto res = GetResourceByNameHandlingMQ(texPath); auto res = GetResourceByNameHandlingMQ(texPath);
return res->GetInitData()->Type == LUS::ResourceType::SOH_Background; return res->GetInitData()->Type == LUS::ResourceType::SOH_Background;
@ -1583,6 +1632,11 @@ extern "C" void ResourceMgr_PushCurrentDirectory(char* path)
extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path) extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path)
{ {
// When an alt resource exists for the DL, we need to unload the original asset
// to clear the cache so the alt asset will be loaded instead
// OTRTODO: If Alt loading over original cache is fixed, this line can most likely be removed
ResourceMgr_UnloadOriginalWhenAltExists(path);
auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path)); auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path));
return (Gfx*)&res->Instructions[0]; return (Gfx*)&res->Instructions[0];
} }
@ -2548,6 +2602,70 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
gfx_register_blended_texture(name, mask, replacement); gfx_register_blended_texture(name, mask, replacement);
} }
extern "C" void CheckTracker_OnMessageClose() { // #region SOH [TODO] Ideally this should move to being event based, it's currently run every frame on the file select screen
CheckTracker::CheckTrackerDialogClosed(); extern "C" void SoH_ProcessDroppedFiles() {
const char* droppedFile = CVarGetString("gDroppedFile", "");
if (CVarGetInteger("gNewFileDropped", 0) && strcmp(droppedFile, "") != 0) {
try {
std::ifstream configStream(SohUtils::Sanitize(droppedFile));
if (!configStream) {
return;
}
nlohmann::json configJson;
configStream >> configJson;
if (!configJson.contains("CVars")) {
return;
}
clearCvars(enhancementsCvars);
clearCvars(cheatCvars);
clearCvars(randomizerCvars);
// Flatten everything under CVars into a single array
auto cvars = configJson["CVars"].flatten();
for (auto& [key, value] : cvars.items()) {
// Replace slashes with dots in key, and remove leading dot
std::string path = key;
std::replace(path.begin(), path.end(), '/', '.');
if (path[0] == '.') {
path.erase(0, 1);
}
if (value.is_string()) {
CVarSetString(path.c_str(), value.get<std::string>().c_str());
} else if (value.is_number_integer()) {
CVarSetInteger(path.c_str(), value.get<int>());
} else if (value.is_number_float()) {
CVarSetFloat(path.c_str(), value.get<float>());
}
}
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGuiWindow("Console")->Hide();
gui->GetGuiWindow("Actor Viewer")->Hide();
gui->GetGuiWindow("Collision Viewer")->Hide();
gui->GetGuiWindow("Save Editor")->Hide();
gui->GetGuiWindow("Display List Viewer")->Hide();
gui->GetGuiWindow("Stats")->Hide();
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();
gui->SaveConsoleVariablesOnNextTick();
uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
} catch (std::exception& e) {
SPDLOG_ERROR("Failed to load config file: {}", e.what());
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
} catch (...) {
SPDLOG_ERROR("Failed to load config file");
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
}
}
} }
// #endregion

View file

@ -89,11 +89,15 @@ uint32_t ResourceMgr_GetGameVersion(int index);
uint32_t ResourceMgr_GetGamePlatform(int index); uint32_t ResourceMgr_GetGamePlatform(int index);
uint32_t ResourceMgr_GetGameRegion(int index); uint32_t ResourceMgr_GetGameRegion(int index);
void ResourceMgr_LoadDirectory(const char* resName); void ResourceMgr_LoadDirectory(const char* resName);
void ResourceMgr_UnloadResource(const char* resName);
char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize); char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
uint8_t ResourceMgr_FileExists(const char* resName); uint8_t ResourceMgr_FileExists(const char* resName);
uint8_t ResourceMgr_FileAltExists(const char* resName);
void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName);
char* GetResourceDataByNameHandlingMQ(const char* path); char* GetResourceDataByNameHandlingMQ(const char* path);
void ResourceMgr_LoadFile(const char* resName); void ResourceMgr_LoadFile(const char* resName);
char* ResourceMgr_LoadFileFromDisk(const char* filePath); char* ResourceMgr_LoadFileFromDisk(const char* filePath);
uint8_t ResourceMgr_TexIsRaw(const char* texPath);
uint8_t ResourceMgr_ResourceIsBackground(char* texPath); uint8_t ResourceMgr_ResourceIsBackground(char* texPath);
char* ResourceMgr_LoadJPEG(char* data, size_t dataSize); char* ResourceMgr_LoadJPEG(char* data, size_t dataSize);
uint16_t ResourceMgr_LoadTexWidthByName(char* texPath); uint16_t ResourceMgr_LoadTexWidthByName(char* texPath);
@ -174,6 +178,7 @@ void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement); void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void SaveManager_ThreadPoolWait(); void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose(); void CheckTracker_OnMessageClose();
void SoH_ProcessDroppedFiles();
int32_t GetGIID(uint32_t itemID); int32_t GetGIID(uint32_t itemID);
#endif #endif

View file

@ -493,6 +493,8 @@ extern std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow;
void DrawEnhancementsMenu() { void DrawEnhancementsMenu() {
if (ImGui::BeginMenu("Enhancements")) if (ImGui::BeginMenu("Enhancements"))
{ {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
DrawPresetSelector(PRESET_TYPE_ENHANCEMENTS); DrawPresetSelector(PRESET_TYPE_ENHANCEMENTS);
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
@ -608,7 +610,7 @@ void DrawEnhancementsMenu() {
UIWidgets::PaddedEnhancementCheckbox("Nuts explode bombs", "gNutsExplodeBombs", true, false); UIWidgets::PaddedEnhancementCheckbox("Nuts explode bombs", "gNutsExplodeBombs", true, false);
UIWidgets::Tooltip("Makes nuts explode bombs, similar to how they interact with bombchus. This does not affect bombflowers."); UIWidgets::Tooltip("Makes nuts explode bombs, similar to how they interact with bombchus. This does not affect bombflowers.");
UIWidgets::PaddedEnhancementCheckbox("Equip Multiple Arrows at Once", "gSeparateArrows", true, false); UIWidgets::PaddedEnhancementCheckbox("Equip Multiple Arrows at Once", "gSeparateArrows", true, false);
UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots"); UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots. (Note this will disable the behaviour of the 'Equip Dupe' glitch)");
UIWidgets::PaddedEnhancementCheckbox("Bow as Child/Slingshot as Adult", "gBowSlingShotAmmoFix", true, false); UIWidgets::PaddedEnhancementCheckbox("Bow as Child/Slingshot as Adult", "gBowSlingShotAmmoFix", true, false);
UIWidgets::Tooltip("Allows child to use bow with arrows.\nAllows adult to use slingshot with seeds.\n\nRequires glitches or 'Timeless Equipment' cheat to equip."); UIWidgets::Tooltip("Allows child to use bow with arrows.\nAllows adult to use slingshot with seeds.\n\nRequires glitches or 'Timeless Equipment' cheat to equip.");
UIWidgets::PaddedEnhancementCheckbox("Better Farore's Wind", "gBetterFW", true, false); UIWidgets::PaddedEnhancementCheckbox("Better Farore's Wind", "gBetterFW", true, false);
@ -918,6 +920,7 @@ void DrawEnhancementsMenu() {
if (ImGui::BeginMenu("Mods")) { if (ImGui::BeginMenu("Mods")) {
if (UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", "gAltAssets", false, false)) { if (UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", "gAltAssets", false, false)) {
ShouldClearTextureCacheAtEndOfFrame = true; ShouldClearTextureCacheAtEndOfFrame = true;
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
} }
UIWidgets::Tooltip("Toggle between standard assets and alternate assets. Usually mods will indicate if this setting has to be used or not."); UIWidgets::Tooltip("Toggle between standard assets and alternate assets. Usually mods will indicate if this setting has to be used or not.");
UIWidgets::PaddedEnhancementCheckbox("Disable Bomb Billboarding", "gDisableBombBillboarding", true, false); UIWidgets::PaddedEnhancementCheckbox("Disable Bomb Billboarding", "gDisableBombBillboarding", true, false);
@ -1076,6 +1079,9 @@ void DrawEnhancementsMenu() {
UIWidgets::Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges."); UIWidgets::Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges.");
UIWidgets::PaddedEnhancementCheckbox("Fix Link's eyes open while sleeping", "gFixEyesOpenWhileSleeping", true, false); UIWidgets::PaddedEnhancementCheckbox("Fix Link's eyes open while sleeping", "gFixEyesOpenWhileSleeping", true, false);
UIWidgets::Tooltip("Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping."); UIWidgets::Tooltip("Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping.");
UIWidgets::PaddedEnhancementCheckbox("Fix Darunia dancing too fast", "gEnhancements.FixDaruniaDanceSpeed",
true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true);
UIWidgets::Tooltip("Fixes Darunia's dancing speed so he dances to the beat of Saria's Song, like in vanilla.");
ImGui::EndMenu(); ImGui::EndMenu();
} }
@ -1211,6 +1217,8 @@ void DrawEnhancementsMenu() {
UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f);
ImGui::EndDisabled();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
@ -1252,6 +1260,8 @@ void DrawEnhancementsMenu() {
void DrawCheatsMenu() { void DrawCheatsMenu() {
if (ImGui::BeginMenu("Cheats")) if (ImGui::BeginMenu("Cheats"))
{ {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (ImGui::BeginMenu("Infinite...")) { if (ImGui::BeginMenu("Infinite...")) {
UIWidgets::EnhancementCheckbox("Money", "gInfiniteMoney"); UIWidgets::EnhancementCheckbox("Money", "gInfiniteMoney");
UIWidgets::PaddedEnhancementCheckbox("Health", "gInfiniteHealth", true, false); UIWidgets::PaddedEnhancementCheckbox("Health", "gInfiniteHealth", true, false);
@ -1406,6 +1416,8 @@ void DrawCheatsMenu() {
} }
UIWidgets::Tooltip("Clears the cutscene pointer to a value safe for wrong warps."); UIWidgets::Tooltip("Clears the cutscene pointer to a value safe for wrong warps.");
ImGui::EndDisabled();
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }
@ -1420,6 +1432,8 @@ extern std::shared_ptr<ValueViewerWindow> mValueViewerWindow;
void DrawDeveloperToolsMenu() { void DrawDeveloperToolsMenu() {
if (ImGui::BeginMenu("Developer Tools")) { if (ImGui::BeginMenu("Developer Tools")) {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled"); UIWidgets::EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled");
UIWidgets::Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right, and open the debug menu with L on the pause screen"); UIWidgets::Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right, and open the debug menu with L on the pause screen");
if (CVarGetInteger("gDebugEnabled", 0)) { if (CVarGetInteger("gDebugEnabled", 0)) {
@ -1498,6 +1512,8 @@ void DrawDeveloperToolsMenu() {
ImGui::PopStyleVar(3); ImGui::PopStyleVar(3);
ImGui::PopStyleColor(1); ImGui::PopStyleColor(1);
ImGui::EndDisabled();
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }
@ -1646,4 +1662,4 @@ void SohMenuBar::DrawElement() {
ImGui::EndMenuBar(); ImGui::EndMenuBar();
} }
} }
} // namespace SohGui } // namespace SohGui

View file

@ -260,7 +260,7 @@ void Audio_osWritebackDCache(void* mem, s32 size)
s32 osAiSetFrequency(u32 freq) s32 osAiSetFrequency(u32 freq)
{ {
return 1;
} }
s32 osEPiStartDma(OSPiHandle* handle, OSIoMesg* mb, s32 direction) s32 osEPiStartDma(OSPiHandle* handle, OSIoMesg* mb, s32 direction)

View file

@ -2,6 +2,7 @@
#include <string.h> #include <string.h>
#include <vector> #include <vector>
#include <algorithm>
std::vector<std::string> sceneNames = { std::vector<std::string> sceneNames = {
"Inside the Deku Tree", "Inside the Deku Tree",
@ -318,3 +319,20 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
strncpy(destination, source.c_str(), size - 1); strncpy(destination, source.c_str(), size - 1);
destination[size - 1] = '\0'; destination[size - 1] = '\0';
} }
std::string SohUtils::Sanitize(std::string stringValue) {
// Add backslashes.
for (auto i = stringValue.begin();;) {
auto const pos = std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
if (pos == stringValue.end()) {
break;
}
i = std::next(stringValue.insert(pos, '\\'), 2);
}
// Removes others.
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) {
return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end());
return stringValue;
}

View file

@ -12,4 +12,6 @@ namespace SohUtils {
// Copies a string and ensures the destination is null terminated if the source string is larger than size // Copies a string and ensures the destination is null terminated if the source string is larger than size
// Only up to size-1 characters are copied from the source string // Only up to size-1 characters are copied from the source string
void CopyStringToCharArray(char* destination, std::string source, size_t size); void CopyStringToCharArray(char* destination, std::string source, size_t size);
std::string Sanitize(std::string stringValue);
} // namespace SohUtils } // namespace SohUtils

View file

@ -72,6 +72,9 @@ MessageTableEntry* OTRMessage_LoadTable(const char* filePath, bool isNES) {
_message_0xFFFC_nes = (char*)file->messages[i].msg.c_str(); _message_0xFFFC_nes = (char*)file->messages[i].msg.c_str();
} }
// Assert that the first message starts at the first text ID
assert(table[0].textId == 0x0001);
return table; return table;
} }
@ -104,6 +107,9 @@ extern "C" void OTRMessage_Init()
sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str(); sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str();
sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size(); sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size();
} }
// Assert staff credits start at the first credits ID
assert(sStaffMessageEntryTablePtr[0].textId == 0x0500);
} }
CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID); CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID);

View file

@ -3384,7 +3384,7 @@ void Message_Update(PlayState* play) {
} }
sLastPlayedSong = 0xFF; sLastPlayedSong = 0xFF;
osSyncPrintf("OCARINA_MODE=%d chk_ocarina_no=%d\n", play->msgCtx.ocarinaMode, msgCtx->unk_E3F2); osSyncPrintf("OCARINA_MODE=%d chk_ocarina_no=%d\n", play->msgCtx.ocarinaMode, msgCtx->unk_E3F2);
CheckTracker_OnMessageClose(); // TODO: OnMessageClose hook
break; break;
case MSGMODE_PAUSED: case MSGMODE_PAUSED:
break; break;

View file

@ -1526,13 +1526,13 @@ void Inventory_SwapAgeEquipment(void) {
} else { } else {
// When becoming child, set swordless flag if player doesn't have kokiri sword // When becoming child, set swordless flag if player doesn't have kokiri sword
// Only in rando to keep swordless link bugs in vanilla // Only in rando to keep swordless link bugs in vanilla
if (IS_RANDO && (EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0) { if (IS_RANDO && CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
Flags_SetInfTable(INFTABLE_SWORDLESS); Flags_SetInfTable(INFTABLE_SWORDLESS);
} }
// When using enhancements, set swordless flag if player doesn't have kokiri sword or hasn't equipped a sword yet. // When using enhancements, set swordless flag if player doesn't have kokiri sword or hasn't equipped a sword yet.
// Then set the child equips button items to item none to ensure kokiri sword is not equipped // Then set the child equips button items to item none to ensure kokiri sword is not equipped
if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) && ((EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0 || Flags_GetInfTable(INFTABLE_SWORDLESS))) { if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) && (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0 || Flags_GetInfTable(INFTABLE_SWORDLESS))) {
Flags_SetInfTable(INFTABLE_SWORDLESS); Flags_SetInfTable(INFTABLE_SWORDLESS);
gSaveContext.childEquips.buttonItems[0] = ITEM_NONE; gSaveContext.childEquips.buttonItems[0] = ITEM_NONE;
} }
@ -1568,7 +1568,7 @@ void Inventory_SwapAgeEquipment(void) {
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment; gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4)); gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
// Equips kokiri sword in the inventory screen only if kokiri sword exists in inventory and a sword has been equipped already // Equips kokiri sword in the inventory screen only if kokiri sword exists in inventory and a sword has been equipped already
if (!((EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0) && !Flags_GetInfTable(INFTABLE_SWORDLESS)) { if (!(CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) && !Flags_GetInfTable(INFTABLE_SWORDLESS)) {
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4); gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
} }
} else if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) { } else if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) {
@ -1598,7 +1598,7 @@ void Inventory_SwapAgeEquipment(void) {
(only kokiri tunic/boots equipped, no sword, no C-button items, no D-Pad items). (only kokiri tunic/boots equipped, no sword, no C-button items, no D-Pad items).
When becoming child, set swordless flag if player doesn't have kokiri sword When becoming child, set swordless flag if player doesn't have kokiri sword
Only in rando to keep swordless link bugs in vanilla*/ Only in rando to keep swordless link bugs in vanilla*/
if (EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment == 0) { if (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
Flags_SetInfTable(INFTABLE_SWORDLESS); Flags_SetInfTable(INFTABLE_SWORDLESS);
} }

View file

@ -277,6 +277,11 @@ void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 h
bg->b.imagePal = 0; bg->b.imagePal = 0;
bg->b.imageFlip = CVarGetInteger("gMirroredWorld", 0) ? G_BG_FLAG_FLIPS : 0; bg->b.imageFlip = CVarGetInteger("gMirroredWorld", 0) ? G_BG_FLAG_FLIPS : 0;
// When an alt resource exists for the background, we need to unload the original asset
// to clear the cache so the alt asset will be loaded instead
// OTRTODO: If Alt loading over original cache is fixed, this line can most likely be removed
ResourceMgr_UnloadOriginalWhenAltExists((char*) source);
if (ResourceMgr_ResourceIsBackground((char*) source)) { if (ResourceMgr_ResourceIsBackground((char*) source)) {
char* blob = (char*) ResourceGetDataByName((char *) source); char* blob = (char*) ResourceGetDataByName((char *) source);
swapAndConvertJPEG(blob); swapAndConvertJPEG(blob);

View file

@ -417,7 +417,12 @@ void func_800AEFC8(SkyboxContext* skyboxCtx, s16 skyboxId) {
s32 j; s32 j;
s32 phi_s3 = 0; s32 phi_s3 = 0;
if (skyboxId == SKYBOX_BAZAAR || (skyboxId > SKYBOX_HOUSE_KAKARIKO && skyboxId <= SKYBOX_BOMBCHU_SHOP)) { //! @bug All shops only provide 2 faces for their sky box. Mask shop is missing from the condition
// meaning that the Mask shop will calculate 4 faces
// This effect is not noticed as the faces are behind the camera, but will cause a crash in SoH.
// SOH General: We have added the Mask shop to this check so only the 2 expected faces are calculated.
if (skyboxId == SKYBOX_BAZAAR || skyboxId == SKYBOX_HAPPY_MASK_SHOP ||
(skyboxId >= SKYBOX_KOKIRI_SHOP && skyboxId <= SKYBOX_BOMBCHU_SHOP)) {
for (j = 0, i = 0; i < 2; i++, j += 2) { for (j = 0, i = 0; i < 2; i++, j += 2) {
phi_s3 = func_800ADBB0(skyboxCtx, skyboxCtx->roomVtx, phi_s3, D_8012AEBC[i].unk_0, D_8012AEBC[i].unk_4, phi_s3 = func_800ADBB0(skyboxCtx, skyboxCtx->roomVtx, phi_s3, D_8012AEBC[i].unk_0, D_8012AEBC[i].unk_4,
D_8012AEBC[i].unk_8, D_8012AEBC[i].unk_C, D_8012AEBC[i].unk_10, i, j); D_8012AEBC[i].unk_8, D_8012AEBC[i].unk_C, D_8012AEBC[i].unk_10, i, j);

View file

@ -59,8 +59,14 @@ void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyb
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[2]); gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[2]);
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[3]); gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[3]);
if (skyboxId != SKYBOX_BAZAAR) { //! @bug All shops only provide 2 faces for their sky box. Mask shop is missing from the condition
if (skyboxId <= SKYBOX_HOUSE_KAKARIKO || skyboxId > SKYBOX_BOMBCHU_SHOP) { // meaning that the Mask shop will render the previously loaded sky box values, or uninitialized data.
// This effect is not noticed as the faces are behind the camera, but will cause a crash in SoH.
// SOH General: We have added the Mask shop to this check so only the 2 expected faces are rendered.
if (skyboxId != SKYBOX_BAZAAR && skyboxId != SKYBOX_HAPPY_MASK_SHOP) {
if (skyboxId < SKYBOX_KOKIRI_SHOP || skyboxId > SKYBOX_BOMBCHU_SHOP) {
// Skip remaining faces for most shop skyboxes
gDPPipeSync(POLY_OPA_DISP++); gDPPipeSync(POLY_OPA_DISP++);
gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[2]); gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[2]);
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[4]); gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[4]);

View file

@ -5,9 +5,21 @@
#include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h" #include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include <stdlib.h> // malloc
#include <string.h> // memcpy
// OTRTODO: Replace usage of this method when we can clear the cache
// for a single texture without the need of a DL opcode in the render code
void gfx_texture_cache_clear();
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
#define LAVA_TEX_WIDTH 32
#define LAVA_TEX_HEIGHT 64
#define LAVA_TEX_SIZE 2048
void BossDodongo_Init(Actor* thisx, PlayState* play); void BossDodongo_Init(Actor* thisx, PlayState* play);
void BossDodongo_Destroy(Actor* thisx, PlayState* play); void BossDodongo_Destroy(Actor* thisx, PlayState* play);
void BossDodongo_Update(Actor* thisx, PlayState* play); void BossDodongo_Update(Actor* thisx, PlayState* play);
@ -59,6 +71,13 @@ static u8 sMaskTex8x8[8 * 8] = { { 0 } };
static u8 sMaskTex8x32[8 * 32] = { { 0 } }; static u8 sMaskTex8x32[8 * 32] = { { 0 } };
static u8 sMaskTexLava[32 * 64] = { { 0 } }; static u8 sMaskTexLava[32 * 64] = { { 0 } };
static u32* sLavaFloorModifiedTexRaw = NULL;
static u32* sLavaWavyTexRaw = NULL;
static u16 sLavaFloorModifiedTex[LAVA_TEX_SIZE];
static u16 sLavaWavyTex[LAVA_TEX_SIZE];
static u8 hasRegisteredBlendedHook = 0;
static InitChainEntry sInitChain[] = { static InitChainEntry sInitChain[] = {
ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE),
ICHAIN_S8(naviEnemyId, 0x0C, ICHAIN_CONTINUE), ICHAIN_S8(naviEnemyId, 0x0C, ICHAIN_CONTINUE),
@ -66,6 +85,69 @@ static InitChainEntry sInitChain[] = {
ICHAIN_F32(targetArrowOffset, 8200.0f, ICHAIN_STOP), ICHAIN_F32(targetArrowOffset, 8200.0f, ICHAIN_STOP),
}; };
void BossDodongo_RegisterBlendedLavaTextureUpdate() {
// Not in scene so there is nothing to do
if (gPlayState == NULL || gPlayState->sceneNum != SCENE_DODONGOS_CAVERN_BOSS) {
return;
}
// Free old textures
if (sLavaFloorModifiedTexRaw != NULL) {
free(sLavaFloorModifiedTexRaw);
sLavaFloorModifiedTexRaw = NULL;
}
if (sLavaWavyTexRaw != NULL) {
free(sLavaWavyTexRaw);
sLavaWavyTexRaw = NULL;
}
// Unload original textures to bypass cache result for lookups
ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorLavaTex);
ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorRockTex);
ResourceMgr_UnloadOriginalWhenAltExists(gDodongosCavernBossLavaFloorTex);
// When the texture is HD (raw) we need to work with u32 values for RGBA32
// Otherwise the original asset is u16 for RGBA16
if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) {
u32* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
size_t lavaSize = ResourceGetSizeByName(sLavaFloorLavaTex);
size_t floorSize = ResourceGetSizeByName(gDodongosCavernBossLavaFloorTex);
sLavaFloorModifiedTexRaw = malloc(lavaSize);
sLavaWavyTexRaw = malloc(floorSize);
memcpy(sLavaFloorModifiedTexRaw, lavaTex, lavaSize);
// When KD is dead, just immediately copy the rock texture
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
u32* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex);
memcpy(sLavaFloorModifiedTexRaw, rockTex, rockSize);
}
memcpy(sLavaWavyTexRaw, sLavaFloorModifiedTexRaw, floorSize);
// Register the blended effect for the raw texture
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTexRaw);
} else {
// When KD is dead, just immediately copy the rock texture
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
u16* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
memcpy(sLavaFloorModifiedTex, rockTex, sizeof(sLavaFloorModifiedTex));
} else {
u16* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
memcpy(sLavaFloorModifiedTex, lavaTex, sizeof(sLavaFloorModifiedTex));
}
// Register the blended effect for the non-raw texture
memcpy(sLavaWavyTex, sLavaFloorModifiedTex, sizeof(sLavaWavyTex));
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTex);
}
gfx_texture_cache_clear();
}
void func_808C12C4(u8* arg1, s16 arg2) { void func_808C12C4(u8* arg1, s16 arg2) {
if (arg2[arg1] != 0) { if (arg2[arg1] != 0) {
sMaskTex8x16[arg2 / 2] = 1; sMaskTex8x16[arg2 / 2] = 1;
@ -86,12 +168,51 @@ void func_808C12C4(u8* arg1, s16 arg2) {
} }
} }
void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) { // Same as func_808C1554 but works with u32 values for RGBA32 raw textures
arg0 = GetResourceDataByNameHandlingMQ(arg0); void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
floorTex = ResourceGetDataByName(floorTex); u16 width = ResourceGetTexWidthByName(arg0);
s32 size = ResourceGetTexHeightByName(arg0) * width;
u16* temp_s3 = SEGMENTED_TO_VIRTUAL(arg0); u32* temp_s3 = sLavaWavyTexRaw;
u16* temp_s1 = SEGMENTED_TO_VIRTUAL(floorTex); u32* temp_s1 = sLavaFloorModifiedTexRaw;
s32 i;
s32 i2;
u32* sp54 = malloc(size * sizeof(u32)); // Match the size for lava floor tex
s32 temp;
s32 temp2;
// Multiplier is used to try to scale the wavy effect to match the scale of the HD texture
// Applying sqrt(multiplier) to arg3 is to control how many pixels move left/right for the selected row
// Applying to arg2 and M_PI help to space out the wave effect
// It's not perfect but close enough
u16 multiplier = width / LAVA_TEX_WIDTH;
for (i = 0; i < size; i += width) {
temp = sinf((((i / width) + (s32)(((arg2 * multiplier) * 50.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier));
for (i2 = 0; i2 < width; i2++) {
sp54[i + ((temp + i2) & (width - 1))] = temp_s1[i + i2];
}
}
for (i = 0; i < width; i++) {
temp = sinf(((i + (s32)(((arg2 * multiplier) * 80.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier));
temp *= width;
for (i2 = 0; i2 < size; i2 += width) {
temp2 = (temp + i2) & (size - 1);
temp_s3[i + temp2] = sp54[i + i2];
}
}
free(sp54);
// Need to clear the cache after updating sLavaWavyTexRaw
gfx_texture_cache_clear();
}
// Modified to support CPU modified texture with the resource system
// Used for the original non-raw asset working with u16 values
void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
u16* temp_s3 = sLavaWavyTex;
u16* temp_s1 = sLavaFloorModifiedTex;
s16 i; s16 i;
s16 i2; s16 i2;
u16 sp54[2048]; u16 sp54[2048];
@ -112,6 +233,9 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
temp_s3[i + temp2] = sp54[i + i2]; temp_s3[i + temp2] = sp54[i + i2];
} }
} }
// Need to clear the cache after updating sLavaWavyTex
gfx_texture_cache_clear();
} }
void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) { void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) {
@ -187,27 +311,21 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items); Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items);
if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead
u16* LavaFloorTex = ResourceGetDataByName(gDodongosCavernBossLavaFloorTex); // SOH [General]
u16* LavaFloorRockTex = ResourceGetDataByName(sLavaFloorRockTex); // Applying the "cooled off" lava rock CPU modified texture for re-visiting the scene
temp_s1_3 = SEGMENTED_TO_VIRTUAL(LavaFloorTex); // is now handled by BossDodongo_RegisterBlendedLavaTextureUpdate below
temp_s2 = SEGMENTED_TO_VIRTUAL(LavaFloorRockTex);
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f,
-3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD); -3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD);
Actor_Spawn(&play->actorCtx, play, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, 0x6000, true); Actor_Spawn(&play->actorCtx, play, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, 0x6000, true);
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0, true); Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0, true);
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
sMaskTexLava[i] = 1;
}
} else {
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
sMaskTexLava[i] = 0;
}
} }
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
// #region SOH [General]
// Init mask values for all blended textures
for (int i = 0; i < ARRAY_COUNT(sMaskTex8x16); i++) { for (int i = 0; i < ARRAY_COUNT(sMaskTex8x16); i++) {
sMaskTex8x16[i] = 0; sMaskTex8x16[i] = 0;
} }
@ -223,6 +341,12 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
for (int i = 0; i < ARRAY_COUNT(sMaskTex32x16); i++) { for (int i = 0; i < ARRAY_COUNT(sMaskTex32x16); i++) {
sMaskTex32x16[i] = 0; sMaskTex32x16[i] = 0;
} }
// Set all true for the lava as it will always replace the scene texture
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
sMaskTexLava[i] = 1;
}
// Register all blended textures
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015890, sMaskTex8x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015890, sMaskTex8x16, NULL);
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_017210, sMaskTex8x32, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_017210, sMaskTex8x32, NULL);
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015D90, sMaskTex16x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015D90, sMaskTex16x16, NULL);
@ -234,10 +358,14 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL);
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL);
// OTRTODO: This is causing OOB memory reads with HD assets BossDodongo_RegisterBlendedLavaTextureUpdate();
// commenting this out means the lava will stay lava even after beating king d
// // Register alt listener to update the blended lava for the replacement texture based on alt path
// Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaFloorRockTex); if (!hasRegisteredBlendedHook) {
GameInteractor_RegisterOnAssetAltChange(BossDodongo_RegisterBlendedLavaTextureUpdate);
hasRegisteredBlendedHook = 1;
}
// #endregion
} }
void BossDodongo_Destroy(Actor* thisx, PlayState* play) { void BossDodongo_Destroy(Actor* thisx, PlayState* play) {
@ -1014,21 +1142,64 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) {
} }
} }
// TODO The lave floor bubbles with an effect that modifies the texture. This needs to be recreated shader-side. // The lava bubbles with a wavy effect as a CPU modified texture
//func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224); // This has been done by maintaining copied/modified texture values in the actor code
// The "cooling off" effect for the lava is pre-applied to the lava texture before applying
// the wavy effect. Since this is two effects and closely related to the actor, I've opted
// to handle them here rather than as a shader effect.
//
// Apply the corresponding wavy effect based on the texture being raw or not
if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) {
func_808C1554_Raw(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
} else {
func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
}
} }
// Apply the "cooling off" effect for the lava
if (this->unk_1C6 != 0) { if (this->unk_1C6 != 0) {
u16* ptr1 = ResourceGetDataByName(sLavaFloorLavaTex); // Similar to above, the cooling off effect is a CPU modified texture effect
u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex); // Apply corresponding to the texture being raw or not
s16 i2; if (ResourceMgr_TexIsRaw(sLavaFloorRockTex)) {
u32* ptr1 = sLavaFloorModifiedTexRaw;
u32* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
u16 width = ResourceGetTexWidthByName(sLavaFloorRockTex);
u16 height = ResourceGetTexHeightByName(sLavaFloorRockTex);
s16 i2;
for (i2 = 0; i2 < 20; i2++) { // Get the scale based on the original texture size
s16 new_var = this->unk_1C2 & 0x7FF; u16 widthScale = width / LAVA_TEX_WIDTH;
u16 heightScale = height / LAVA_TEX_HEIGHT;
u32 size = width * height;
sMaskTexLava[new_var] = 1; for (i2 = 0; i2 < 20; i2++) {
this->unk_1C2 += 37; s16 new_var = this->unk_1C2 & (LAVA_TEX_SIZE - 1);
// Compute the index to a scaled position (scaling pseudo x,y as a 1D value)
s32 indexStart = ((new_var % LAVA_TEX_WIDTH) * widthScale) + ((new_var / LAVA_TEX_WIDTH) * width * heightScale);
// From the starting index, apply extra pixels right/down based on the scale
for (size_t j = 0; j < heightScale; j++) {
for (size_t i3 = 0; i3 < widthScale; i3++) {
s32 scaledIndex = (indexStart + i3 + (j * width)) & (size - 1);
ptr1[scaledIndex] = ptr2[scaledIndex];
}
}
this->unk_1C2 += 37;
}
} else {
u16* ptr1 = sLavaFloorModifiedTex;
u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
s16 i2;
for (i2 = 0; i2 < 20; i2++) {
s16 new_var = this->unk_1C2 & 0x7FF;
ptr1[new_var] = ptr2[new_var];
this->unk_1C2 += 37;
}
} }
Math_SmoothStepToF(&this->unk_224, 0.0f, 1.0f, 0.01f, 0.0f); Math_SmoothStepToF(&this->unk_224, 0.0f, 1.0f, 0.01f, 0.0f);
} }

View file

@ -95,6 +95,26 @@ static AnimationInfo sAnimationInfo[] = {
{ &gDaruniaDancingEndAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -6.0f }, { &gDaruniaDancingEndAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -6.0f },
}; };
// #region SOH [Enhancement] Only animations too fast need to be slowed down, otherwise not touched
static AnimationInfo sAnimationInfoFix[] = {
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ &gDaruniaDancingLoop1Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, -10.0f }, //
{ &gDaruniaDancingLoop1Anim, 0.77f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // hop
{ &gDaruniaDancingLoop2Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // from hop to spin
{ &gDaruniaDancingLoop3Anim, 0.77f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // spin
{ NULL },
{ NULL },
{ &gDaruniaDancingLoop4Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // from spin to hop
{ NULL },
};
// #endregion
void EnDu_SetupAction(EnDu* this, EnDuActionFunc actionFunc) { void EnDu_SetupAction(EnDu* this, EnDuActionFunc actionFunc) {
this->actionFunc = actionFunc; this->actionFunc = actionFunc;
} }
@ -255,7 +275,13 @@ void func_809FE040(EnDu* this) {
if (this->unk_1E6 >= 8) { if (this->unk_1E6 >= 8) {
this->unk_1E6 = 0; this->unk_1E6 = 0;
} }
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]); // #region SOH[Enhancement]
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1)) {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, animationIndices[this->unk_1E6]);
// #endregion
} else {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
}
} }
} }
@ -271,7 +297,13 @@ void func_809FE104(EnDu* this) {
if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) {
this->unk_1E6++; this->unk_1E6++;
if (this->unk_1E6 < 4) { if (this->unk_1E6 < 4) {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]); // #region SOH[Enhancement]
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1) && this->unk_1E6 <= 1) {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, animationIndices[this->unk_1E6]);
// #endregion
} else {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
}
} }
} }
} }
@ -465,7 +497,13 @@ void func_809FE890(EnDu* this, PlayState* play) {
} }
if (csAction->action == 7 || csAction->action == 8) { if (csAction->action == 7 || csAction->action == 8) {
this->unk_1E6 = 0; this->unk_1E6 = 0;
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_7); // #region SOH[Enhancement]
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1)) {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, ENDU_ANIM_7);
// #endregion
} else {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_7);
}
} }
this->unk_1EA = csAction->action; this->unk_1EA = csAction->action;
if (this->unk_1EA == 7) { if (this->unk_1EA == 7) {

View file

@ -211,6 +211,13 @@ void EnInsect_Init(Actor* thisx, PlayState* play2) {
func_80A7D39C(this); func_80A7D39C(this);
// For bugs that aren't linked to a soil patch, we remove the "short lived" flag to prevent them from despawning
// And exit early to not increment the "bugs dropped count"
if (CVarGetInteger("gNoBugsDespawn", 0) && this->soilActor == NULL) {
this->unk_314 &= ~4;
return;
}
D_80A7DEB8++; D_80A7DEB8++;
} else { } else {
rand = Rand_ZeroOne(); rand = Rand_ZeroOne();
@ -394,9 +401,6 @@ void func_80A7CAD0(EnInsect* this, PlayState* play) {
} }
void func_80A7CBC8(EnInsect* this) { void func_80A7CBC8(EnInsect* this) {
if (CVarGetInteger("gNoBugsDespawn", 0) != 0) {
return;
}
this->unk_31A = 60; this->unk_31A = 60;
func_80A7BF58(this); func_80A7BF58(this);
this->skelAnime.playSpeed = 1.9f; this->skelAnime.playSpeed = 1.9f;

View file

@ -313,7 +313,9 @@ void EnSth_GiveReward(EnSth* this, PlayState* play) {
this->actor.parent = NULL; this->actor.parent = NULL;
EnSth_SetupAction(this, EnSth_RewardObtainedTalk); EnSth_SetupAction(this, EnSth_RewardObtainedTalk);
gSaveContext.eventChkInf[EVENTCHKINF_SKULLTULA_REWARD_INDEX] |= this->eventFlag; gSaveContext.eventChkInf[EVENTCHKINF_SKULLTULA_REWARD_INDEX] |= this->eventFlag;
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SKULLTULA_REWARD_INDEX << 4) + sEventFlagsShift[this->actor.params]); if (this->eventFlag != 0) {
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SKULLTULA_REWARD_INDEX << 4) + sEventFlagsShift[this->actor.params]);
}
} else { } else {
EnSth_GivePlayerItem(this, play); EnSth_GivePlayerItem(this, play);
} }

View file

@ -408,7 +408,7 @@ s32 EnTk_ChooseReward(EnTk* this) {
f32 luck; f32 luck;
s32 reward; s32 reward;
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, 0x1F) && this->heartPieceSpawned == 0) { if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
return 3; return 3;
} }
@ -624,10 +624,8 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
this->currentReward = EnTk_ChooseReward(this); this->currentReward = EnTk_ChooseReward(this);
// merging in dampe tour fix seems messy, so i'm just wrapping this whole thing if (this->currentReward == 3) {
// in an n64dd check for now if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
if (this->currentReward == 3) {
/* /*
* Upgrade the purple rupee reward to the heart piece if this * Upgrade the purple rupee reward to the heart piece if this
* is the first grand prize dig. * is the first grand prize dig.
@ -635,37 +633,31 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
if (!Flags_GetItemGetInf(ITEMGETINF_1C) && !(IS_RANDO || CVarGetInteger("gDampeWin", 0))) { if (!Flags_GetItemGetInf(ITEMGETINF_1C) && !(IS_RANDO || CVarGetInteger("gDampeWin", 0))) {
Flags_SetItemGetInf(ITEMGETINF_1C); Flags_SetItemGetInf(ITEMGETINF_1C);
this->currentReward = 4; this->currentReward = 4;
} else if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, 0x1F) && this->heartPieceSpawned == 0) { } else if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
this->currentReward = 4; this->currentReward = 4;
} }
} }
/*
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && this->currentReward == 4) { * Upgrade the purple rupee reward to the heart piece if this
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0, * is the first grand prize dig.
0, 0, 0x1F06, true); */
this->heartPieceSpawned = 1; // If vanilla itemGetInf flag is not set, it's impossible for the new flag to be set, so return true.
} else { // Otherwise if the gGravediggingTourFix is enabled and the new flag hasn't been set, return true.
Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]); // If true, spawn the heart piece and set the vanilla itemGetInf flag and new temp clear flag.
if (!heartPieceSpawned &&
(!(gSaveContext.itemGetInf[1] & ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE) ||
CVarGetInteger("gGravediggingTourFix", 0) &&
!Flags_GetCollectible(play, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE))) {
this->currentReward = 4;
gSaveContext.itemGetInf[1] |= ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE;
heartPieceSpawned = true;
} }
}
if (IS_RANDO && this->currentReward == 4) {
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0, 0, 0, 0x1906, true);
this->heartPieceSpawned = 1;
} else { } else {
if (this->currentReward == 3) {
/*
* Upgrade the purple rupee reward to the heart piece if this
* is the first grand prize dig.
*/
// If vanilla itemGetInf flag is not set, it's impossible for the new flag to be set, so return true.
// Otherwise if the gGravediggingTourFix is enabled and the new flag hasn't been set, return true.
// If true, spawn the heart piece and set the vanilla itemGetInf flag and new temp clear flag.
if (!heartPieceSpawned &&
(!(gSaveContext.itemGetInf[1] & ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE) ||
CVarGetInteger("gGravediggingTourFix", 0) &&
!Flags_GetCollectible(play, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE))) {
this->currentReward = 4;
gSaveContext.itemGetInf[1] |= ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE;
heartPieceSpawned = true;
}
}
EnItem00* reward = Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]); EnItem00* reward = Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
if (this->currentReward == 4) { if (this->currentReward == 4) {
reward->collectibleFlag = COLLECTFLAG_GRAVEDIGGING_HEART_PIECE; reward->collectibleFlag = COLLECTFLAG_GRAVEDIGGING_HEART_PIECE;

View file

@ -10474,7 +10474,8 @@ void Player_ProcessSceneCollision(PlayState* play, Player* this) {
sTouchedWallFlags = func_80041DB8(&play->colCtx, this->actor.wallPoly, this->actor.wallBgId); sTouchedWallFlags = func_80041DB8(&play->colCtx, this->actor.wallPoly, this->actor.wallBgId);
if (CVarGetInteger("gFixVineFall", 0)) { // conflicts arise from these two being enabled at once, and with ClimbEverything on, FixVineFall is redundant anyway
if (CVarGetInteger("gFixVineFall", 0) && !CVarGetInteger("gClimbEverything", 0)) {
/* This fixes the "started climbing a wall and then immediately fell off" bug. /* This fixes the "started climbing a wall and then immediately fell off" bug.
* The main idea is if a climbing wall is detected, double-check that it will * The main idea is if a climbing wall is detected, double-check that it will
* still be valid once climbing begins by doing a second raycast with a small * still be valid once climbing begins by doing a second raycast with a small

View file

@ -1,6 +1,7 @@
#include "file_choose.h" #include "file_choose.h"
#include <string.h> #include <string.h>
#include <stdio.h>
#include "textures/title_static/title_static.h" #include "textures/title_static/title_static.h"
#include "textures/parameter_static/parameter_static.h" #include "textures/parameter_static/parameter_static.h"
@ -1024,7 +1025,7 @@ void FileChoose_UpdateRandomizer() {
return; return;
} }
if (!SpoilerFileExists(CVarGetString("gSpoilerLog", ""))) { if (!SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && !CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
CVarSetString("gSpoilerLog", ""); CVarSetString("gSpoilerLog", "");
fileSelectSpoilerFileLoaded = false; fileSelectSpoilerFileLoaded = false;
} }
@ -1051,6 +1052,10 @@ void FileChoose_UpdateRandomizer() {
Randomizer_LoadMasterQuestDungeons(fileLoc); Randomizer_LoadMasterQuestDungeons(fileLoc);
Randomizer_LoadEntranceOverrides(fileLoc, silent); Randomizer_LoadEntranceOverrides(fileLoc, silent);
fileSelectSpoilerFileLoaded = true; fileSelectSpoilerFileLoaded = true;
if (SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
remove(fileLoc);
}
} }
} }
@ -1071,6 +1076,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
Input* input = &this->state.input[0]; Input* input = &this->state.input[0];
bool dpad = CVarGetInteger("gDpadText", 0); bool dpad = CVarGetInteger("gDpadText", 0);
SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer(); FileChoose_UpdateRandomizer();
if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) { if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
@ -1261,6 +1267,7 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
s8 i = 0; s8 i = 0;
bool dpad = CVarGetInteger("gDpadText", 0); bool dpad = CVarGetInteger("gDpadText", 0);
SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer(); FileChoose_UpdateRandomizer();
if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) { if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
@ -3686,4 +3693,7 @@ void FileChoose_Init(GameState* thisx) {
Font_LoadOrderedFont(&this->font); Font_LoadOrderedFont(&this->font);
Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA);
func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1); func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1);
// Originally this was only set when transitioning from the title screen, but gSkipLogoTitle skips that process so we're ensuring it's set here
gSaveContext.gameMode = GAMEMODE_FILE_SELECT;
} }

View file

@ -1054,8 +1054,9 @@ void KaleidoScope_UpdateItemEquip(PlayState* play) {
//Fix for Equip Dupe //Fix for Equip Dupe
if (pauseCtx->equipTargetItem == ITEM_BOW) { if (pauseCtx->equipTargetItem == ITEM_BOW) {
if ((gSaveContext.equips.buttonItems[otherButtonIndex] >= ITEM_BOW_ARROW_FIRE) && if (gSaveContext.equips.buttonItems[otherButtonIndex] >= ITEM_BOW_ARROW_FIRE &&
(gSaveContext.equips.buttonItems[otherButtonIndex] <= ITEM_BOW_ARROW_LIGHT)) { gSaveContext.equips.buttonItems[otherButtonIndex] <= ITEM_BOW_ARROW_LIGHT &&
!CVarGetInteger("gSeparateArrows", 0)) {
gSaveContext.equips.buttonItems[otherButtonIndex] = gSaveContext.equips.buttonItems[targetButtonIndex]; gSaveContext.equips.buttonItems[otherButtonIndex] = gSaveContext.equips.buttonItems[targetButtonIndex];
gSaveContext.equips.cButtonSlots[otherSlotIndex] = gSaveContext.equips.cButtonSlots[pauseCtx->equipTargetCBtn]; gSaveContext.equips.cButtonSlots[otherSlotIndex] = gSaveContext.equips.cButtonSlots[pauseCtx->equipTargetCBtn];
Interface_LoadItemIcon2(play, otherButtonIndex); Interface_LoadItemIcon2(play, otherButtonIndex);