diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53b6b0ac386..9ae0360d8bd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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")
-project(Ship VERSION 8.0.2 LANGUAGES C CXX)
-set(PROJECT_BUILD_NAME "MacReady Charlie" CACHE STRING "")
+project(Ship VERSION 8.0.3 LANGUAGES C CXX)
+set(PROJECT_BUILD_NAME "MacReady Delta" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
diff --git a/libultraship b/libultraship
index f717dd265af..59427a67bf9 160000
--- a/libultraship
+++ b/libultraship
@@ -1 +1 @@
-Subproject commit f717dd265aff2eff359de26915d8ad4e498ffdaf
+Subproject commit 59427a67bf9af060a4928bb72e3acce3b0782177
diff --git a/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 b/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0
index 3637534b99f..4f60b10fe4b 100644
--- a/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0
+++ b/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0
@@ -1,72 +1,72 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 b/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1
index ef51d7124c0..80931181f14 100644
--- a/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1
+++ b/soh/assets/custom/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1
@@ -1,107 +1,107 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 b/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0
index d9a5075af2b..366463fd9cc 100644
--- a/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0
+++ b/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0
@@ -1,72 +1,72 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 b/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1
index 8c2f954e7c3..8085ae95ab8 100644
--- a/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1
+++ b/soh/assets/custom/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1
@@ -1,96 +1,96 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_blue_mat b/soh/assets/custom/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_blue_mat
index 18d8436b2ba..6ba4d88f9b2 100644
--- a/soh/assets/custom/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_blue_mat
+++ b/soh/assets/custom/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_blue_mat
@@ -8,7 +8,7 @@
-
+
diff --git a/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 b/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0
index 5122e290427..90dc38784f0 100644
--- a/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0
+++ b/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0
@@ -1,72 +1,72 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 b/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1
index 988bfb67356..ef4ea40e42a 100644
--- a/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1
+++ b/soh/assets/custom/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1
@@ -1,410 +1,410 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_Green_mat b/soh/assets/custom/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_Green_mat
index 35d57dfeae7..94418f24fc5 100644
--- a/soh/assets/custom/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_Green_mat
+++ b/soh/assets/custom/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_Green_mat
@@ -8,7 +8,7 @@
-
+
diff --git a/soh/assets/custom/textures/parameter_static/gTriforcePiece.rgba32.png b/soh/assets/custom/textures/parameter_static/gTriforcePiece.rgba32.png
index cc67b6a133d..eace659af29 100644
Binary files a/soh/assets/custom/textures/parameter_static/gTriforcePiece.rgba32.png and b/soh/assets/custom/textures/parameter_static/gTriforcePiece.rgba32.png differ
diff --git a/soh/assets/xml/GC_MQ_D/objects/object_mo.xml b/soh/assets/xml/GC_MQ_D/objects/object_mo.xml
index f25286df87c..f31a345d6a4 100644
--- a/soh/assets/xml/GC_MQ_D/objects/object_mo.xml
+++ b/soh/assets/xml/GC_MQ_D/objects/object_mo.xml
@@ -79,7 +79,7 @@
-
+
diff --git a/soh/assets/xml/GC_MQ_D/overlays/ovl_Boss_Dodongo.xml b/soh/assets/xml/GC_MQ_D/overlays/ovl_Boss_Dodongo.xml
index f2ee5b7b8c0..0e2c84f578b 100644
--- a/soh/assets/xml/GC_MQ_D/overlays/ovl_Boss_Dodongo.xml
+++ b/soh/assets/xml/GC_MQ_D/overlays/ovl_Boss_Dodongo.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/soh/assets/xml/GC_MQ_PAL_F/objects/object_mo.xml b/soh/assets/xml/GC_MQ_PAL_F/objects/object_mo.xml
index 2a6247c9f5c..b4528ef3176 100644
--- a/soh/assets/xml/GC_MQ_PAL_F/objects/object_mo.xml
+++ b/soh/assets/xml/GC_MQ_PAL_F/objects/object_mo.xml
@@ -8,7 +8,7 @@
-
+
@@ -69,17 +69,17 @@
-
+
-
+
-
+
diff --git a/soh/assets/xml/GC_MQ_PAL_F/overlays/ovl_Boss_Dodongo.xml b/soh/assets/xml/GC_MQ_PAL_F/overlays/ovl_Boss_Dodongo.xml
index 0e1303c17d7..8a1c4ee94cc 100644
--- a/soh/assets/xml/GC_MQ_PAL_F/overlays/ovl_Boss_Dodongo.xml
+++ b/soh/assets/xml/GC_MQ_PAL_F/overlays/ovl_Boss_Dodongo.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/soh/assets/xml/GC_MQ_PAL_F/text/message_data_static.xml b/soh/assets/xml/GC_MQ_PAL_F/text/message_data_static.xml
index 400a27e27d0..5241e3ef766 100644
--- a/soh/assets/xml/GC_MQ_PAL_F/text/message_data_static.xml
+++ b/soh/assets/xml/GC_MQ_PAL_F/text/message_data_static.xml
@@ -1,14 +1,14 @@
-
+
-
+
-
+
-
+
diff --git a/soh/assets/xml/GC_NMQ_D/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_D/objects/object_mo.xml
index 2a6247c9f5c..b4528ef3176 100644
--- a/soh/assets/xml/GC_NMQ_D/objects/object_mo.xml
+++ b/soh/assets/xml/GC_NMQ_D/objects/object_mo.xml
@@ -8,7 +8,7 @@
-
+
@@ -69,17 +69,17 @@
-
+
-
+
-
+
diff --git a/soh/assets/xml/GC_NMQ_D/overlays/ovl_Boss_Dodongo.xml b/soh/assets/xml/GC_NMQ_D/overlays/ovl_Boss_Dodongo.xml
index f6a52f04433..d31e4abcc60 100644
--- a/soh/assets/xml/GC_NMQ_D/overlays/ovl_Boss_Dodongo.xml
+++ b/soh/assets/xml/GC_NMQ_D/overlays/ovl_Boss_Dodongo.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/soh/assets/xml/GC_NMQ_PAL_F/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_PAL_F/objects/object_mo.xml
index 2a6247c9f5c..b4528ef3176 100644
--- a/soh/assets/xml/GC_NMQ_PAL_F/objects/object_mo.xml
+++ b/soh/assets/xml/GC_NMQ_PAL_F/objects/object_mo.xml
@@ -8,7 +8,7 @@
-
+
@@ -69,17 +69,17 @@
-
+
-
+
-
+
diff --git a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Boss_Dodongo.xml b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Boss_Dodongo.xml
index f2ee5b7b8c0..67d85d16896 100644
--- a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Boss_Dodongo.xml
+++ b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Boss_Dodongo.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
diff --git a/soh/assets/xml/N64_PAL_10/objects/object_mo.xml b/soh/assets/xml/N64_PAL_10/objects/object_mo.xml
index 2a6247c9f5c..b4528ef3176 100644
--- a/soh/assets/xml/N64_PAL_10/objects/object_mo.xml
+++ b/soh/assets/xml/N64_PAL_10/objects/object_mo.xml
@@ -8,7 +8,7 @@
-
+
@@ -69,17 +69,17 @@
-
+
-
+
-
+
diff --git a/soh/assets/xml/N64_PAL_10/overlays/ovl_Boss_Dodongo.xml b/soh/assets/xml/N64_PAL_10/overlays/ovl_Boss_Dodongo.xml
index 8f0c7612c2e..5e4975bc1d4 100644
--- a/soh/assets/xml/N64_PAL_10/overlays/ovl_Boss_Dodongo.xml
+++ b/soh/assets/xml/N64_PAL_10/overlays/ovl_Boss_Dodongo.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/soh/assets/xml/N64_PAL_11/objects/object_mo.xml b/soh/assets/xml/N64_PAL_11/objects/object_mo.xml
index 2a6247c9f5c..b4528ef3176 100644
--- a/soh/assets/xml/N64_PAL_11/objects/object_mo.xml
+++ b/soh/assets/xml/N64_PAL_11/objects/object_mo.xml
@@ -8,7 +8,7 @@
-
+
@@ -69,17 +69,17 @@
-
+
-
+
-
+
diff --git a/soh/assets/xml/N64_PAL_11/overlays/ovl_Boss_Dodongo.xml b/soh/assets/xml/N64_PAL_11/overlays/ovl_Boss_Dodongo.xml
index cd9069e13f2..d6f77448d1e 100644
--- a/soh/assets/xml/N64_PAL_11/overlays/ovl_Boss_Dodongo.xml
+++ b/soh/assets/xml/N64_PAL_11/overlays/ovl_Boss_Dodongo.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/soh/soh/Enhancements/controls/GameControlEditor.cpp b/soh/soh/Enhancements/controls/GameControlEditor.cpp
index 935935c4801..eb69f3cc852 100644
--- a/soh/soh/Enhancements/controls/GameControlEditor.cpp
+++ b/soh/soh/Enhancements/controls/GameControlEditor.cpp
@@ -312,6 +312,7 @@ namespace GameControlEditor {
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
"certain items.");
UIWidgets::Spacer(0);
+ ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
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");
if (CVarGetInteger("gEnableWalkModify", 0)) {
@@ -323,6 +324,7 @@ namespace GameControlEditor {
UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %d %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
window->EndGroupPanelPublic(0);
}
+ ImGui::EndDisabled();
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp
index 0c5ba8ed390..6b5b5d7a4df 100644
--- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp
+++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp
@@ -1485,6 +1485,7 @@ void Draw_Placements(){
}
void DrawSillyTab() {
+ ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (CVarGetInteger("gLetItSnow", 0)) {
if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
@@ -1569,6 +1570,7 @@ void DrawSillyTab() {
CVarClear("gCosmetics.Kak_Windmill_Speed.Changed");
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
diff --git a/soh/soh/Enhancements/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h
index 529ebed81ed..c8461bbf1a3 100644
--- a/soh/soh/Enhancements/enhancementTypes.h
+++ b/soh/soh/Enhancements/enhancementTypes.h
@@ -1,3 +1,6 @@
+#ifndef _ENHANCEMENT_TYPES_H_
+#define _ENHANCEMENT_TYPES_H_
+
typedef enum {
WARP_MODE_OVERRIDE_OFF,
WARP_MODE_OVERRIDE_MQ_AS_VANILLA,
@@ -74,3 +77,5 @@ typedef enum {
DEKU_STICK_UNBREAKABLE,
DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE,
} DekuStickType;
+
+#endif
diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp
index a8242f963ec..34066c7abe6 100644
--- a/soh/soh/Enhancements/mods.cpp
+++ b/soh/soh/Enhancements/mods.cpp
@@ -48,6 +48,7 @@ void ReloadSceneTogglingLinkAge() {
void RegisterInfiniteMoney() {
GameInteractor::Instance->RegisterGameHook([]() {
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
@@ -58,6 +59,7 @@ void RegisterInfiniteMoney() {
void RegisterInfiniteHealth() {
GameInteractor::Instance->RegisterGameHook([]() {
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
if (gSaveContext.health < gSaveContext.healthCapacity) {
gSaveContext.health = gSaveContext.healthCapacity;
@@ -68,6 +70,7 @@ void RegisterInfiniteHealth() {
void RegisterInfiniteAmmo() {
GameInteractor::Instance->RegisterGameHook([]() {
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
// Deku Sticks
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
@@ -104,6 +107,7 @@ void RegisterInfiniteAmmo() {
void RegisterInfiniteMagic() {
GameInteractor::Instance->RegisterGameHook([]() {
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
@@ -114,6 +118,7 @@ void RegisterInfiniteMagic() {
void RegisterInfiniteNayrusLove() {
GameInteractor::Instance->RegisterGameHook([]() {
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
gSaveContext.nayrusLoveTimer = 0x44B;
}
@@ -122,7 +127,7 @@ void RegisterInfiniteNayrusLove() {
void RegisterMoonJumpOnL() {
GameInteractor::Instance->RegisterGameHook([]() {
- if (!gPlayState) return;
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@@ -137,7 +142,7 @@ void RegisterMoonJumpOnL() {
void RegisterInfiniteISG() {
GameInteractor::Instance->RegisterGameHook([]() {
- if (!gPlayState) return;
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gEzISG", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@@ -149,7 +154,7 @@ void RegisterInfiniteISG() {
//Permanent quick put away (QPA) glitched damage value
void RegisterEzQPA() {
GameInteractor::Instance->RegisterGameHook([]() {
- if (!gPlayState) return;
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gEzQPA", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@@ -161,7 +166,7 @@ void RegisterEzQPA() {
void RegisterUnrestrictedItems() {
GameInteractor::Instance->RegisterGameHook([]() {
- if (!gPlayState) return;
+ if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
@@ -189,14 +194,16 @@ void RegisterFreezeTime() {
/// Switches Link's age and respawns him at the last entrance he entered.
void RegisterSwitchAge() {
GameInteractor::Instance->RegisterGameHook([]() {
+ if (!GameInteractor::IsSaveLoaded()) {
+ CVarClear("gSwitchAge");
+ return;
+ }
static bool warped = false;
static Vec3f playerPos;
static int16_t playerYaw;
static RoomContext* roomCtx;
static s32 roomNum;
- if (!gPlayState) return;
-
if (CVarGetInteger("gSwitchAge", 0) && !warped) {
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
@@ -216,7 +223,7 @@ void RegisterSwitchAge() {
func_80097534(gPlayState, roomCtx); // load map for new room (unloading the previous room)
}
warped = false;
- CVarSetInteger("gSwitchAge", 0);
+ CVarClear("gSwitchAge");
}
});
}
@@ -225,7 +232,8 @@ void RegisterSwitchAge() {
void RegisterOcarinaTimeTravel() {
GameInteractor::Instance->RegisterGameHook([]() {
- if (!gPlayState) {
+ if (!GameInteractor::IsSaveLoaded()) {
+ CVarClear("gTimeTravel");
return;
}
@@ -1033,8 +1041,16 @@ void RegisterRandomizedEnemySizes() {
Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast(refActor);
- // Only apply to enemies and bosses. Exclude the 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 wobbly platforms in Jabu because they need to act like platforms.
+ // 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;
}
@@ -1043,9 +1059,8 @@ void RegisterRandomizedEnemySizes() {
uint8_t bigActor = rand() % 2;
- // Big actor. Dodongo and Volvagia are always smaller because they're impossible when bigger.
- if (bigActor && actor->id != ACTOR_BOSS_DODONGO && actor->id != ACTOR_BOSS_FD &&
- actor->id != ACTOR_BOSS_FD2) {
+ // Big actor
+ if (bigActor && !smallOnlyEnemy) {
randomNumber = rand() % 200;
// Between 100% and 300% size.
randomScale = 1.0f + (randomNumber / 100);
diff --git a/soh/soh/Enhancements/presets.h b/soh/soh/Enhancements/presets.h
index f0f586e0031..22c9dd7db3d 100644
--- a/soh/soh/Enhancements/presets.h
+++ b/soh/soh/Enhancements/presets.h
@@ -217,6 +217,31 @@ const std::vector enhancementsCvars = {
"gFixTexturesOOB",
"gIvanCoopModeEnabled",
"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 cheatCvars = {
@@ -269,7 +294,23 @@ const std::vector cheatCvars = {
"gNoRedeadFreeze",
"gBombTimerMultiplier",
"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 randomizerCvars = {
@@ -399,6 +440,15 @@ const std::vector randomizerCvars = {
"gRandomizeGregHint",
"gRandoManualSeedEntry",
"gRandomizerSettingsEnabled",
+ "gRandomizeTriforceHuntTotalPieces",
+ "gRandomizeTriforceHuntRequiredPieces",
+ "gRandomizeTriforceHunt",
+ "gRandomizeShuffleMasterSword",
+ "gRandomizeSariaHint",
+ "gRandomizeRupeeNames",
+ "gRandomizeFrogsHint",
+ "gRandoRelevantNavi",
+ "gRandoQuestItemFanfares",
};
const std::vector vanillaPlusPresetEntries = {
diff --git a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp
index 577de1c1958..0ab29d8692d 100644
--- a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp
@@ -24,11 +24,11 @@ void LocationTable_Init() {
//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_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_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_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_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(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(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_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);
@@ -53,7 +53,7 @@ void LocationTable_Init() {
//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_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_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);
@@ -73,7 +73,7 @@ void LocationTable_Init() {
//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_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_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);
@@ -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);
//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_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_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_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(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(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_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_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);
@@ -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_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_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_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_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(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_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_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_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_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_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(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(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_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_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_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
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);
//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_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);
diff --git a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp
index 088ba506756..62042246c36 100644
--- a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp
@@ -1,5 +1,6 @@
#include "playthrough.hpp"
+#include
#include
#include "custom_messages.hpp"
#include "fill.hpp"
@@ -8,6 +9,7 @@
#include "random.hpp"
#include "spoiler_log.hpp"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
+#include "variables.h"
namespace Playthrough {
@@ -39,6 +41,10 @@ int Playthrough_Init(uint32_t seed, std::unordered_map{}(std::to_string(Settings::seed) + settingsStr);
Random_Init(finalHash);
Settings::hash = std::to_string(finalHash);
diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp
index 1705f9776d8..d9342e92ae9 100644
--- a/soh/soh/Enhancements/randomizer/randomizer.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer.cpp
@@ -361,37 +361,35 @@ std::unordered_map SpoilerfileSettingNameToEn
{ "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 GCC push_options
#pragma GCC optimize ("O0")
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
- if (strcmp(spoilerFileName, "") != 0) {
- std::ifstream spoilerFileStream(sanitize(spoilerFileName));
- if (!spoilerFileStream) {
- return false;
- } else {
+ try {
+ if (strcmp(spoilerFileName, "") != 0) {
+ std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
+ if (!spoilerFileStream) {
+ return false;
+ }
+
+ json spoilerFileJson;
+ spoilerFileStream >> spoilerFileJson;
+
+ if (!spoilerFileJson.contains("version") || !spoilerFileJson.contains("finalSeed")) {
+ return false;
+ }
+
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 optimize("", on)
@@ -660,7 +658,7 @@ void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
}
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
- std::ifstream spoilerFileStream(sanitize(spoilerFileName));
+ std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream)
return;
@@ -1294,7 +1292,7 @@ std::string FormatJsonHintText(std::string jsonHint) {
}
void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
- std::ifstream spoilerFileStream(sanitize(spoilerFileName));
+ std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream)
return;
@@ -1395,7 +1393,7 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
}
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
- std::ifstream spoilerFileStream(sanitize(spoilerFileName));
+ std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
@@ -1416,7 +1414,7 @@ void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
}
void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
- std::ifstream spoilerFileStream(sanitize(spoilerFileName));
+ std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
@@ -1496,7 +1494,7 @@ int16_t Randomizer::GetVanillaMerchantPrice(RandomizerCheck check) {
}
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
- std::ifstream spoilerFileStream(sanitize(spoilerFileName));
+ std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream)
return;
@@ -1559,7 +1557,7 @@ void Randomizer::ParseItemLocationsFile(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) {
return;
}
@@ -2548,6 +2546,7 @@ std::map rcToRandomizerInf = {
{ RC_LH_CHILD_FISHING, RAND_INF_CHILD_FISHING },
{ RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING },
{ 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) {
@@ -3144,7 +3143,9 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
}
+ ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
DrawPresetSelector(PRESET_TYPE_RANDOMIZER);
+ ImGui::EndDisabled();
UIWidgets::Spacer(0);
UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, "");
@@ -3167,13 +3168,17 @@ void RandomizerSettingsWindow::DrawElement() {
}
UIWidgets::Spacer(0);
+ ImGui::BeginDisabled(CVarGetInteger("gRandomizerDontGenerateSpoiler", 0) && gSaveContext.gameMode != GAMEMODE_FILE_SELECT);
if (ImGui::Button("Generate Randomizer")) {
GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : "");
}
+ ImGui::EndDisabled();
UIWidgets::Spacer(0);
- std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
- ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
+ if (!CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
+ std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
+ ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
+ }
// RANDOTODO settings presets
// std::string presetfilepath = CVarGetString("gLoadedPreset", "");
@@ -3181,6 +3186,8 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator();
+ ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
+
ImGuiWindow* window = ImGui::GetCurrentWindow();
static ImVec2 cellPadding(8.0f, 8.0f);
@@ -5224,6 +5231,8 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::EndTabBar();
}
+
+ ImGui::EndDisabled();
if (disableEditingRandoSettings) {
UIWidgets::ReEnableComponent("");
diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp
index d7f99edfb9c..d551108615b 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp
@@ -39,7 +39,7 @@ std::map 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_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_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_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),
@@ -238,7 +238,7 @@ std::map 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_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_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_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),
diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
index 6123c1a2797..003c8c135a9 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
@@ -73,21 +73,12 @@ bool showLinksPocket;
bool fortressFast;
bool fortressNormal;
-bool bypassRandoCheck = true;
// persistent during gameplay
bool initialized;
bool doAreaScroll;
bool previousShowHidden = false;
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 startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_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)"
bool optCollapseAll; // A bool that will collapse all checks once
bool optExpandAll; // A bool that will expand all checks once
-RandomizerCheck lastItemGetCheck = RC_UNKNOWN_CHECK;
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
RandomizerCheckArea previousArea = RCAREA_INVALID;
RandomizerCheckArea currentArea = RCAREA_INVALID;
-std::vector checkAreas;
-std::vector itemsReceived;
OSContPad* trackerButtonsPressed;
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 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 };
-void SetLastItemGetRC(RandomizerCheck rc) {
- lastItemGetCheck = rc;
-}
-
void DefaultCheckData(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED;
gSaveContext.checkTrackerData[rc].skipped = 0;
@@ -253,9 +237,6 @@ void SetCheckCollected(RandomizerCheck rc) {
} else {
gSaveContext.checkTrackerData[rc].skipped = false;
}
- if (!checkAreas.empty()) {
- checkAreas.erase(checkAreas.begin());
- }
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
doAreaScroll = true;
@@ -360,32 +341,18 @@ bool vector_contains_scene(std::vector vec, const int16_t scene) {
std::vector 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) {
- if (HasItemBeenCollected(rco.rc) && gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_COLLECTED &&
- gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_SAVED) {
- SetCheckCollected(rco.rc);
- 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 ClearAreaChecksAndTotals() {
+ for (auto& [rcArea, vec] : checksByArea) {
+ vec.clear();
+ areaChecksGotten[rcArea] = 0;
}
}
void SetShopSeen(uint32_t sceneNum, bool prices) {
RandomizerCheck start = startingShopItem.find(sceneNum)->second;
+ if (sceneNum == SCENE_POTION_SHOP_KAKARIKO && !LINK_IS_ADULT) {
+ return;
+ }
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) {
start = RC_KAK_BAZAAR_ITEM_1;
}
@@ -509,12 +476,6 @@ void CheckTrackerLoadGame(int32_t fileNum) {
UpdateInventoryChecks();
}
-void CheckTrackerDialogClosed() {
- if (messageCloseCheck) {
- messageCloseCheck = false;
- }
-}
-
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
return;
@@ -536,10 +497,6 @@ void CheckTrackerTransition(uint32_t sceneNum) {
if (!GameInteractor::IsSaveLoaded()) {
return;
}
- gSaveContext;
- if (transitionCheck) {
- transitionCheck = false;
- }
doAreaScroll = true;
previousArea = currentArea;
currentArea = GetCheckArea();
@@ -560,29 +517,13 @@ void CheckTrackerFrame() {
if (!GameInteractor::IsSaveLoaded()) {
return;
}
- if (!checkAreas.empty() && !transitionCheck && !messageCloseCheck && !pendingSaleCheck) {
- for (int i = 0; i < 10; i++) {
- if (CheckByArea()) {
- checkCounter = 0;
- break;
- } else {
- checkCounter++;
- }
- }
- if (checkLoops > 15) {
- checkAreas.erase(checkAreas.begin());
- checkLoops = 0;
+ // TODO: Move to OnAmmoChange hook once it gets added.
+ if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED &&
+ gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) {
+ if (BEANS_BOUGHT >= 10) {
+ SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
}
}
- if (savedFrames > 0 && !pendingSaleCheck && !messageCloseCheck) {
- savedFrames--;
- }
-}
-
-void CheckTrackerSaleEnd(GetItemEntry giEntry) {
- if (pendingSaleCheck) {
- pendingSaleCheck = false;
- }
}
void CheckTrackerItemReceive(GetItemEntry giEntry) {
@@ -593,7 +534,7 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
// Vanilla special item checks
if (!IS_RANDO) {
if (giEntry.itemId == ITEM_SHIELD_DEKU) {
- SetCheckCollected(RC_KF_SHOP_ITEM_3);
+ SetCheckCollected(RC_KF_SHOP_ITEM_1);
return;
}else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) {
SetCheckCollected(RC_QUEEN_GOHMA);
@@ -622,16 +563,19 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
} else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) {
SetCheckCollected(RC_GIFT_FROM_SAGES);
return;
- } else if (giEntry.itemId == ITEM_SONG_LULLABY) {
- SetCheckCollected(RC_SONG_FROM_IMPA);
- return;
} else if (giEntry.itemId == ITEM_SONG_EPONA) {
SetCheckCollected(RC_SONG_FROM_MALON);
return;
} else if (giEntry.itemId == ITEM_SONG_SARIA) {
SetCheckCollected(RC_SONG_FROM_SARIA);
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);
return;
} else if (giEntry.itemId == ITEM_SONG_TIME) {
@@ -658,42 +602,127 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
} else if (giEntry.itemId == ITEM_SONG_PRELUDE) {
SetCheckCollected(RC_SHEIK_AT_TEMPLE);
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);
- return;
- } else if (giEntry.itemId == ITEM_WEIRD_EGG) {
- SetCheckCollected(RC_HC_MALON_EGG);
- return;
- } else if (giEntry.itemId == ITEM_BEAN) {
- SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
- return;
- }
+ }*/
}
- auto checkArea = GetCheckArea();
- if (gSaveContext.pendingSale != ITEM_NONE) {
- pendingSaleCheck = true;
- checkAreas.push_back(checkArea);
+}
+
+void CheckTrackerSceneFlagSet(int16_t sceneNum, int16_t flagType, int32_t flag) {
+ if (flagType != FLAG_SCENE_TREASURE && flagType != FLAG_SCENE_COLLECTIBLE) {
return;
}
- if (scene == SCENE_DESERT_COLOSSUS && (gSaveContext.entranceIndex == 485 || gSaveContext.entranceIndex == 489)) {
- checkAreas.push_back(RCAREA_SPIRIT_TEMPLE);
+ if (sceneNum == SCENE_GRAVEYARD && flag == 0x19 && flagType == FLAG_SCENE_COLLECTIBLE) { // Gravedigging tour special case
+ SetCheckCollected(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR);
return;
}
- if (GET_PLAYER(gPlayState) == nullptr) {
- transitionCheck = true;
- 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;
+ }
+ }
+}
+
+void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
+ 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 (gPlayState->msgCtx.msgMode != MSGMODE_NONE) {
- checkAreas.push_back(checkArea);
- messageCloseCheck = true;
+ if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_NONE) {
return;
}
- if (IS_RANDO || (!IS_RANDO && giEntry.getItemCategory != ITEM_CATEGORY_JUNK)) {
- checkAreas.push_back(checkArea);
- checkCollected = true;
+ for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
+ if ((!IS_RANDO && ((rcObj.vOrMQ == RCVORMQ_MQ && !IS_MASTER_QUEST) ||
+ (rcObj.vOrMQ == RCVORMQ_VANILLA && IS_MASTER_QUEST))) ||
+ (IS_RANDO && ((OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
+ rcObj.vOrMQ == RCVORMQ_VANILLA) ||
+ !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
+ rcObj.vOrMQ == RCVORMQ_MQ))) {
+ continue;
+ }
+ SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
+ SpoilerCollectionCheckType scCheckType = scCheck.type;
+ if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF &&
+ (scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT ||
+ scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
+ scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW ||
+ 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) {
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
- if (gameSave || savedFrames > 0) {
+ if (gameSave) {
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
UpdateAllOrdering();
UpdateInventoryChecks();
@@ -730,9 +759,6 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveTrackerData(saveContext, sectionID, fullSave);
- if (fullSave) {
- savedFrames = 40;
- }
}
void LoadFile() {
@@ -748,14 +774,9 @@ void LoadFile() {
void Teardown() {
initialized = false;
- for (auto& [rcArea, vec] : checksByArea) {
- vec.clear();
- areaChecksGotten[rcArea] = 0;
- }
+ ClearAreaChecksAndTotals();
checksByArea.clear();
areasSpoiled = 0;
- checkCollected = false;
- checkLoops = 0;
lastLocationChecked = RC_UNKNOWN_CHECK;
}
@@ -925,7 +946,11 @@ void CheckTrackerWindow::DrawElement() {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 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 (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
@@ -1151,8 +1176,8 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
}
else if (rcObj.vanillaCompletion) {
return (rcObj.vOrMQ == RCVORMQ_BOTH ||
- rcObj.vOrMQ == RCVORMQ_MQ && OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) ||
- rcObj.vOrMQ == RCVORMQ_VANILLA && !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) ||
+ (rcObj.vOrMQ == RCVORMQ_MQ && IS_MASTER_QUEST) ||
+ (rcObj.vOrMQ == RCVORMQ_VANILLA && !IS_MASTER_QUEST) ||
rcObj.rc == RC_GIFT_FROM_SAGES) && rcObj.rc != RC_LINKS_POCKET;
}
return false;
@@ -1541,10 +1566,11 @@ void CheckTrackerWindow::InitElement() {
Teardown();
});
GameInteractor::Instance->RegisterGameHook(CheckTrackerItemReceive);
- GameInteractor::Instance->RegisterGameHook(CheckTrackerSaleEnd);
GameInteractor::Instance->RegisterGameHook(CheckTrackerFrame);
GameInteractor::Instance->RegisterGameHook(CheckTrackerTransition);
GameInteractor::Instance->RegisterGameHook(CheckTrackerShopSlotChange);
+ GameInteractor::Instance->RegisterGameHook(CheckTrackerSceneFlagSet);
+ GameInteractor::Instance->RegisterGameHook(CheckTrackerFlagSet);
LocationTable_Init();
}
diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h
index b2193f696e0..a8928cca3e4 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h
+++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h
@@ -48,10 +48,7 @@ void Teardown();
void UpdateAllOrdering();
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
void InitTrackerData(bool isDebug);
-void SetLastItemGetRC(RandomizerCheck rc);
RandomizerCheckArea GetCheckArea();
-void CheckTrackerDialogClosed();
-void ToggleShopRightChecks();
void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
} // namespace CheckTracker
diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c
index f2f1a8a3f35..e6ec3fef3fa 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c
+++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c
@@ -76,7 +76,7 @@ static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
static s8 hasCopiedEntranceTable = 0;
static s8 hasModifiedEntranceTable = 0;
-void Entrance_SetEntranceDiscovered(u16 entranceIndex);
+void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance);
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
@@ -276,13 +276,13 @@ s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) {
return nextEntranceIndex;
}
- Entrance_SetEntranceDiscovered(nextEntranceIndex);
+ Entrance_SetEntranceDiscovered(nextEntranceIndex, false);
EntranceTracker_SetLastEntranceOverride(nextEntranceIndex);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex));
}
s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) {
- Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex]);
+ Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex], false);
EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex]));
}
@@ -784,7 +784,7 @@ u8 Entrance_GetIsEntranceDiscovered(u16 entranceIndex) {
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
// 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)) {
@@ -796,14 +796,20 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit;
- // Set connected
- for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
- if (entranceIndex == gSaveContext.entranceOverrides[i].index) {
- Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination);
- break;
+
+ // Set reverse entrance when not decoupled
+ if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {
+ for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
+ 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);
+ }
}
diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.h b/soh/soh/Enhancements/randomizer/randomizer_entrance.h
index c00f29701b5..c71b2493ab6 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_entrance.h
+++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.h
@@ -63,7 +63,7 @@ void Entrance_OverrideSpawnScene(int32_t sceneNum, int32_t spawn);
int32_t Entrance_OverrideSpawnSceneRoom(int32_t sceneNum, int32_t spawn, int32_t room);
void Entrance_EnableFW(void);
uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex);
-void Entrance_SetEntranceDiscovered(uint16_t entranceIndex);
+void Entrance_SetEntranceDiscovered(uint16_t entranceIndex, uint8_t isReversedEntrance);
#ifdef __cplusplus
}
#endif
diff --git a/soh/soh/Enhancements/randomizer/randomizer_grotto.c b/soh/soh/Enhancements/randomizer/randomizer_grotto.c
index 408dc01601e..78e429fb229 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_grotto.c
+++ b/soh/soh/Enhancements/randomizer/randomizer_grotto.c
@@ -140,7 +140,7 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
// If Link hits a grotto exit, load the entrance index from the grotto exit list
// based on the current grotto ID
if (nextEntranceIndex == 0x7FFF) {
- 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);
nextEntranceIndex = grottoExitList[grottoId];
}
@@ -211,7 +211,7 @@ void Grotto_OverrideActorEntrance(Actor* thisx) {
if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) {
// 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);
index = grottoLoadList[index];
diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp
index b7703d0a5b9..6949577e173 100644
--- a/soh/soh/OTRGlobals.cpp
+++ b/soh/soh/OTRGlobals.cpp
@@ -49,6 +49,9 @@
#include "Fonts.h"
#include
#include "Enhancements/custom-message/CustomMessageManager.h"
+#include "Enhancements/presets.h"
+#include "util.h"
+#include
#if not defined (__SWITCH__) && not defined(__WIIU__)
#include "Extractor/Extract.h"
@@ -253,15 +256,26 @@ OTRGlobals::OTRGlobals() {
OTRFiles.push_back(sohOtrPath);
}
std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName);
+ std::vector patchOTRs = {};
if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) {
if (std::filesystem::is_directory(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")) {
- 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 ValidHashes = {
OOT_PAL_MQ,
OOT_NTSC_JP_MQ,
@@ -1048,6 +1062,10 @@ extern "C" void InitOTR() {
tm *tm_now = localtime(&now);
CVarRegisterInteger("gLetItSnow", 1);
+ CVarRegisterInteger("gCosmetics.Consumable_Hearts.Changed", 1);
+ CVarRegisterColor("gCosmetics.Consumable_Hearts.Value", Color_RGBA8{ 255, 158, 0, 255 });
+ CVarRegisterInteger("gCosmetics.Consumable_Magic.Changed", 1);
+ CVarRegisterColor("gCosmetics.Consumable_Magic.Value", Color_RGBA8{ 255, 0, 0, 255 });
srand(now);
#ifdef ENABLE_CROWD_CONTROL
@@ -2562,6 +2580,70 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
gfx_register_blended_texture(name, mask, replacement);
}
-extern "C" void CheckTracker_OnMessageClose() {
- CheckTracker::CheckTrackerDialogClosed();
+// #region SOH [TODO] Ideally this should move to being event based, it's currently run every frame on the file select screen
+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().c_str());
+ } else if (value.is_number_integer()) {
+ CVarSetInteger(path.c_str(), value.get());
+ } else if (value.is_number_float()) {
+ CVarSetFloat(path.c_str(), value.get());
+ }
+ }
+
+ 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::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();
+
+ gui->SaveConsoleVariablesOnNextTick();
+
+ uint32_t finalHash = boost::hash_32{}(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
diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h
index 9b42e689550..88c57ced336 100644
--- a/soh/soh/OTRGlobals.h
+++ b/soh/soh/OTRGlobals.h
@@ -175,6 +175,7 @@ void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose();
+void SoH_ProcessDroppedFiles();
int32_t GetGIID(uint32_t itemID);
#endif
diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp
index 10af75ca840..6cb37092868 100644
--- a/soh/soh/SohMenuBar.cpp
+++ b/soh/soh/SohMenuBar.cpp
@@ -492,6 +492,8 @@ extern std::shared_ptr mGameplayStatsWindow;
void DrawEnhancementsMenu() {
if (ImGui::BeginMenu("Enhancements"))
{
+ ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
+
DrawPresetSelector(PRESET_TYPE_ENHANCEMENTS);
UIWidgets::PaddedSeparator();
@@ -605,7 +607,7 @@ void DrawEnhancementsMenu() {
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::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::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);
@@ -1066,6 +1068,9 @@ void DrawEnhancementsMenu() {
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::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();
}
@@ -1201,6 +1206,8 @@ void DrawEnhancementsMenu() {
UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f);
+ ImGui::EndDisabled();
+
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
@@ -1242,6 +1249,8 @@ void DrawEnhancementsMenu() {
void DrawCheatsMenu() {
if (ImGui::BeginMenu("Cheats"))
{
+ ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
+
if (ImGui::BeginMenu("Infinite...")) {
UIWidgets::EnhancementCheckbox("Money", "gInfiniteMoney");
UIWidgets::PaddedEnhancementCheckbox("Health", "gInfiniteHealth", true, false);
@@ -1396,6 +1405,8 @@ void DrawCheatsMenu() {
}
UIWidgets::Tooltip("Clears the cutscene pointer to a value safe for wrong warps.");
+ ImGui::EndDisabled();
+
ImGui::EndMenu();
}
}
@@ -1409,6 +1420,8 @@ extern std::shared_ptr mDLViewerWindow;
void DrawDeveloperToolsMenu() {
if (ImGui::BeginMenu("Developer Tools")) {
+ ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
+
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");
if (CVarGetInteger("gDebugEnabled", 0)) {
@@ -1481,6 +1494,8 @@ void DrawDeveloperToolsMenu() {
ImGui::PopStyleVar(3);
ImGui::PopStyleColor(1);
+ ImGui::EndDisabled();
+
ImGui::EndMenu();
}
}
diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp
index 62e1d2dce23..856432cbbd2 100644
--- a/soh/soh/util.cpp
+++ b/soh/soh/util.cpp
@@ -2,6 +2,7 @@
#include
#include
+#include
std::vector sceneNames = {
"Inside the Deku Tree",
@@ -318,3 +319,20 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
strncpy(destination, source.c_str(), size - 1);
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;
+}
diff --git a/soh/soh/util.h b/soh/soh/util.h
index bdbfcd777fb..db5af8636a3 100644
--- a/soh/soh/util.h
+++ b/soh/soh/util.h
@@ -12,4 +12,6 @@ namespace SohUtils {
// 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
void CopyStringToCharArray(char* destination, std::string source, size_t size);
+
+ std::string Sanitize(std::string stringValue);
} // namespace SohUtils
diff --git a/soh/soh/z_message_OTR.cpp b/soh/soh/z_message_OTR.cpp
index f90e8bb9370..6bbd1eca9bd 100644
--- a/soh/soh/z_message_OTR.cpp
+++ b/soh/soh/z_message_OTR.cpp
@@ -72,6 +72,9 @@ MessageTableEntry* OTRMessage_LoadTable(const char* filePath, bool isNES) {
_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;
}
@@ -104,6 +107,9 @@ extern "C" void OTRMessage_Init()
sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str();
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);
diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c
index da0f5f55fe2..7bb3803fcc6 100644
--- a/soh/src/code/z_message_PAL.c
+++ b/soh/src/code/z_message_PAL.c
@@ -3384,7 +3384,7 @@ void Message_Update(PlayState* play) {
}
sLastPlayedSong = 0xFF;
osSyncPrintf("OCARINA_MODE=%d chk_ocarina_no=%d\n", play->msgCtx.ocarinaMode, msgCtx->unk_E3F2);
- CheckTracker_OnMessageClose();
+ // TODO: OnMessageClose hook
break;
case MSGMODE_PAUSED:
break;
diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c
index 9d26561c98e..43381c7d69a 100644
--- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c
+++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c
@@ -17,6 +17,10 @@ void gfx_texture_cache_clear();
#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_Destroy(Actor* thisx, PlayState* play);
void BossDodongo_Update(Actor* thisx, PlayState* play);
@@ -70,8 +74,8 @@ static u8 sMaskTexLava[32 * 64] = { { 0 } };
static u32* sLavaFloorModifiedTexRaw = NULL;
static u32* sLavaWavyTexRaw = NULL;
-static u16 sLavaFloorModifiedTex[4096];
-static u16 sLavaWavyTex[2048];
+static u16 sLavaFloorModifiedTex[LAVA_TEX_SIZE];
+static u16 sLavaWavyTex[LAVA_TEX_SIZE];
static u8 hasRegisteredBlendedHook = 0;
@@ -105,7 +109,7 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
// 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(sLavaFloorLavaTex)) {
+ if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) {
u32* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
size_t lavaSize = ResourceGetSizeByName(sLavaFloorLavaTex);
size_t floorSize = ResourceGetSizeByName(gDodongosCavernBossLavaFloorTex);
@@ -127,14 +131,13 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
// Register the blended effect for the raw texture
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTexRaw);
} else {
- u16* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
- memcpy(sLavaFloorModifiedTex, lavaTex, sizeof(sLavaFloorModifiedTex));
-
// When KD is dead, just immediately copy the rock texture
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
u16* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
- size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex);
- memcpy(sLavaFloorModifiedTex, rockTex, rockSize);
+ memcpy(sLavaFloorModifiedTex, rockTex, sizeof(sLavaFloorModifiedTex));
+ } else {
+ u16* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
+ memcpy(sLavaFloorModifiedTex, lavaTex, sizeof(sLavaFloorModifiedTex));
}
// Register the blended effect for the non-raw texture
@@ -183,7 +186,7 @@ void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
// 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 / 32;
+ 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));
@@ -1166,14 +1169,14 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) {
s16 i2;
// Get the scale based on the original texture size
- u16 widthScale = width / 32;
- u16 heightScale = height / 64;
+ u16 widthScale = width / LAVA_TEX_WIDTH;
+ u16 heightScale = height / LAVA_TEX_HEIGHT;
u32 size = width * height;
for (i2 = 0; i2 < 20; i2++) {
- s16 new_var = this->unk_1C2 & 0x7FF;
+ 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 % 32) * widthScale) + ((new_var / 32) * width * heightScale);
+ 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++) {
diff --git a/soh/src/overlays/actors/ovl_En_ChristmasTree/z_en_christmastree.c b/soh/src/overlays/actors/ovl_En_ChristmasTree/z_en_christmastree.c
index 66514fd4bd9..32a2cf81f50 100644
--- a/soh/src/overlays/actors/ovl_En_ChristmasTree/z_en_christmastree.c
+++ b/soh/src/overlays/actors/ovl_En_ChristmasTree/z_en_christmastree.c
@@ -131,7 +131,7 @@ void EnChristmasTree_HandleEndTitle(EnChristmasTree* this, PlayState* play) {
// Hide player so he's not visible in the final screen. Also move him so target arrow on tree dissapears.
player->actor.scale.x = player->actor.scale.y = player->actor.scale.z = 0.00001f;
- player->actor.world.pos.y = -200.0f;
+ player->actor.world.pos.z = 500.0f;
// Hide HUD
Interface_ChangeAlpha(1);
diff --git a/soh/src/overlays/actors/ovl_En_Du/z_en_du.c b/soh/src/overlays/actors/ovl_En_Du/z_en_du.c
index 3a98074b146..40454811e17 100644
--- a/soh/src/overlays/actors/ovl_En_Du/z_en_du.c
+++ b/soh/src/overlays/actors/ovl_En_Du/z_en_du.c
@@ -96,6 +96,26 @@ static AnimationInfo sAnimationInfo[] = {
{ &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) {
this->actionFunc = actionFunc;
}
@@ -256,7 +276,13 @@ void func_809FE040(EnDu* this) {
if (this->unk_1E6 >= 8) {
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]);
+ }
}
}
@@ -272,7 +298,13 @@ void func_809FE104(EnDu* this) {
if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) {
this->unk_1E6++;
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]);
+ }
}
}
}
@@ -466,7 +498,13 @@ void func_809FE890(EnDu* this, PlayState* play) {
}
if (csAction->action == 7 || csAction->action == 8) {
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;
if (this->unk_1EA == 7) {
diff --git a/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c
index f1dff358a61..393e10a918b 100644
--- a/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c
+++ b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c
@@ -211,6 +211,13 @@ void EnInsect_Init(Actor* thisx, PlayState* play2) {
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++;
} else {
rand = Rand_ZeroOne();
@@ -394,9 +401,6 @@ void func_80A7CAD0(EnInsect* this, PlayState* play) {
}
void func_80A7CBC8(EnInsect* this) {
- if (CVarGetInteger("gNoBugsDespawn", 0) != 0) {
- return;
- }
this->unk_31A = 60;
func_80A7BF58(this);
this->skelAnime.playSpeed = 1.9f;
diff --git a/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c
index e7cbb0fc37f..a7f96df2be5 100644
--- a/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c
+++ b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c
@@ -314,7 +314,9 @@ void EnSth_GiveReward(EnSth* this, PlayState* play) {
this->actor.parent = NULL;
EnSth_SetupAction(this, EnSth_RewardObtainedTalk);
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 {
EnSth_GivePlayerItem(this, play);
}
diff --git a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c
index 3f0fabdf6c5..bf737cc47f1 100644
--- a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c
+++ b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c
@@ -409,7 +409,7 @@ s32 EnTk_ChooseReward(EnTk* this) {
f32 luck;
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;
}
@@ -625,10 +625,8 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
this->currentReward = EnTk_ChooseReward(this);
- // merging in dampe tour fix seems messy, so i'm just wrapping this whole thing
- // in an n64dd check for now
- if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
- if (this->currentReward == 3) {
+ if (this->currentReward == 3) {
+ if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
/*
* Upgrade the purple rupee reward to the heart piece if this
* is the first grand prize dig.
@@ -636,37 +634,31 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
if (!Flags_GetItemGetInf(ITEMGETINF_1C) && !(IS_RANDO || CVarGetInteger("gDampeWin", 0))) {
Flags_SetItemGetInf(ITEMGETINF_1C);
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;
}
}
-
- if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && this->currentReward == 4) {
- Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0,
- 0, 0, 0x1F06, true);
- this->heartPieceSpawned = 1;
- } else {
- Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
- }
- } 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;
- }
+ /*
+ * 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;
}
+ }
+ 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 {
EnItem00* reward = Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
if (this->currentReward == 4) {
reward->collectibleFlag = COLLECTFLAG_GRAVEDIGGING_HEART_PIECE;
diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
index 9c83f9322cd..9e6150593d2 100644
--- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
+++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
@@ -1,6 +1,7 @@
#include "file_choose.h"
#include
+#include
#include "textures/title_static/title_static.h"
#include "textures/parameter_static/parameter_static.h"
@@ -1024,7 +1025,7 @@ void FileChoose_UpdateRandomizer() {
return;
}
- if (!SpoilerFileExists(CVarGetString("gSpoilerLog", ""))) {
+ if (!SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && !CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
CVarSetString("gSpoilerLog", "");
fileSelectSpoilerFileLoaded = false;
}
@@ -1051,6 +1052,10 @@ void FileChoose_UpdateRandomizer() {
Randomizer_LoadMasterQuestDungeons(fileLoc);
Randomizer_LoadEntranceOverrides(fileLoc, silent);
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];
bool dpad = CVarGetInteger("gDpadText", 0);
+ SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer();
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;
bool dpad = CVarGetInteger("gDpadText", 0);
+ SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer();
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);
Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA);
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;
}
diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c
index 74460691056..665c95b81af 100644
--- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c
+++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c
@@ -1054,8 +1054,9 @@ void KaleidoScope_UpdateItemEquip(PlayState* play) {
//Fix for Equip Dupe
if (pauseCtx->equipTargetItem == ITEM_BOW) {
- if ((gSaveContext.equips.buttonItems[otherButtonIndex] >= ITEM_BOW_ARROW_FIRE) &&
- (gSaveContext.equips.buttonItems[otherButtonIndex] <= ITEM_BOW_ARROW_LIGHT)) {
+ if (gSaveContext.equips.buttonItems[otherButtonIndex] >= ITEM_BOW_ARROW_FIRE &&
+ gSaveContext.equips.buttonItems[otherButtonIndex] <= ITEM_BOW_ARROW_LIGHT &&
+ !CVarGetInteger("gSeparateArrows", 0)) {
gSaveContext.equips.buttonItems[otherButtonIndex] = gSaveContext.equips.buttonItems[targetButtonIndex];
gSaveContext.equips.cButtonSlots[otherSlotIndex] = gSaveContext.equips.cButtonSlots[pauseCtx->equipTargetCBtn];
Interface_LoadItemIcon2(play, otherButtonIndex);