diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c394545900..89f32f74e4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,10 +2,11 @@ cmake_minimum_required(VERSION 3.26.0 FATAL_ERROR) set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.0 LANGUAGES C CXX) +project(Ship VERSION 9.0.5 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) diff --git a/README.md b/README.md index e7f929bad5c..ea1ad76a230 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ Congratulations, you are now sailing with the Ship of Harkinian! Have fun! ### Other shortcuts | Keys | Action | | - | - | -| F1 | Toggle menubar | +| ESC | Toggle menu | +| F2 | Toggle capture mouse input | | F5 | Save state | | F6 | Change state | | F7 | Load state | diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 7277b2dfbda..3511a0caf90 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -4,6 +4,7 @@ set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) project(soh LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") enable_language(OBJCXX) diff --git a/soh/assets/objects/object_mag/object_mag.h b/soh/assets/objects/object_mag/object_mag.h index b470b71dd65..720d0cbe59d 100644 --- a/soh/assets/objects/object_mag/object_mag.h +++ b/soh/assets/objects/object_mag/object_mag.h @@ -18,6 +18,12 @@ static const ALIGN_ASSET(2) char gTitleCopyright19982002Tex[] = dgTitleCopyright #define dgTitleCopyright19982003Tex "__OTR__objects/object_mag/gTitleCopyright19982003Tex" static const ALIGN_ASSET(2) char gTitleCopyright19982003Tex[] = dgTitleCopyright19982003Tex; +#define dgTitleCopyright19982004EngTex "__OTR__objects/object_mag/gTitleCopyright19982004EngTex" +static const ALIGN_ASSET(2) char gTitleCopyright19982004EngTex[] = dgTitleCopyright19982004EngTex; + +#define dgTitleCopyright19982004JpnTex "__OTR__objects/object_mag/gTitleCopyright19982004JpnTex" +static const ALIGN_ASSET(2) char gTitleCopyright19982004JpnTex[] = dgTitleCopyright19982004JpnTex; + #define dgTitleMasterQuestSubtitleTex "__OTR__objects/object_mag/gTitleMasterQuestSubtitleTex" static const ALIGN_ASSET(2) char gTitleMasterQuestSubtitleTex[] = dgTitleMasterQuestSubtitleTex; diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml index b2b082e6b36..6e07fa2cdb6 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml @@ -1,7 +1,7 @@ - + @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml b/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml b/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml index b68d4227ad3..063d801a3f9 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml @@ -1,7 +1,7 @@ - + @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml b/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml index 84467d76d5d..6276dee1da6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml @@ -1,8 +1,8 @@ - - + + @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml b/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml b/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml b/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml b/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml b/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml b/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml b/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml b/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml b/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml b/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml b/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml +++ b/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml b/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml b/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml b/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml b/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml b/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml b/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml b/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml b/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml b/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml b/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml +++ b/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml b/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml b/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml b/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml b/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml b/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml b/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml b/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml b/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml b/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml b/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml +++ b/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/include/functions.h b/soh/include/functions.h index 86e3bf77f6e..971ae5f66b5 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -15,7 +15,7 @@ extern "C" #include #if defined(INCLUDE_GAME_PRINTF) && defined(_DEBUG) -#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, __VA_ARGS__) +#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, ##__VA_ARGS__) #else #define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, ##__VA_ARGS__) #endif @@ -27,6 +27,12 @@ void gSPDisplayListOffset(Gfx* pkt, Gfx* dl, int offset); void gSPVertex(Gfx* pkt, uintptr_t v, int n, int v0); void gSPInvalidateTexCache(Gfx* pkt, uintptr_t texAddr); +void Actor_RefreshLeveledStats(Actor* actor, Player* player); +Gfx* Gfx_TextureIA8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); +Gfx* Gfx_TextureI8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); +void Player_GainExperience(PlayState* play, u16 experience); void cleararena(void); void bootproc(void); @@ -548,7 +554,7 @@ s32 func_800354B4(PlayState* play, Actor* actor, f32 range, s16 arg3, s16 arg4, void func_8003555C(PlayState* play, Vec3f* pos, Vec3f* velocity, Vec3f* accel); void func_800355B8(PlayState* play, Vec3f* pos); u8 func_800355E4(PlayState* play, Collider* collider); -u8 Actor_ApplyDamage(Actor* actor); +u16 Actor_ApplyDamage(Actor* actor); void Actor_SetDropFlag(Actor* actor, ColliderInfo* colBody, s32 freezeFlag); void Actor_SetDropFlagJntSph(Actor* actor, ColliderJntSph* colBody, s32 freezeFlag); void func_80035844(Vec3f* arg0, Vec3f* arg1, Vec3s* arg2, s32 arg3); diff --git a/soh/include/global.h b/soh/include/global.h index b453439fd3f..8a7e92529ad 100644 --- a/soh/include/global.h +++ b/soh/include/global.h @@ -8,6 +8,9 @@ #include "functions.h" #include "variables.h" #include "macros.h" +#include "leveled_overlays.h" +#include "leveled_stat_math.h" +#include "leveled_actor_level_table.h" #include "soh/cvar_prefixes.h" #include "soh/Enhancements/gameconsole.h" #include "soh/Enhancements/gameplaystats.h" diff --git a/soh/include/leveled_actor_level_table.h b/soh/include/leveled_actor_level_table.h new file mode 100644 index 00000000000..7472b50c06c --- /dev/null +++ b/soh/include/leveled_actor_level_table.h @@ -0,0 +1,6 @@ +#ifndef LEVELED_ACTOR_LEVEL_TABLE_H +#define LEVELED_ACTOR_LEVEL_TABLE_H +#include "z64.h" +#endif + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride); \ No newline at end of file diff --git a/soh/include/leveled_overlays.h b/soh/include/leveled_overlays.h new file mode 100644 index 00000000000..ba52a59ea16 --- /dev/null +++ b/soh/include/leveled_overlays.h @@ -0,0 +1,16 @@ +#ifndef LEVELED_OVERLAYS_H +#define LEVELED_OVERLAYS_H +#include "z64.h" +#endif + +void ActorDamageNumber_New(Actor* actor, u16 damage); +void ActorExperienceNumber_New(Actor* actor, u16 experience); +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff); +void ActorDamageNumber_Draw(PlayState* play, Actor* actor); +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor); +void Actor_LevelUpDraw(PlayState* play, Actor* actor); +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b); +void Leveled_OverlayValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 direction, u8 r, u8 g, u8 b, u8 a); +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a); +void Leveled_KaleidoEquip_Stats(PlayState* play); +void Leveled_Interface_DrawNextLevel(PlayState* play); \ No newline at end of file diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h new file mode 100644 index 00000000000..40dfa0c0326 --- /dev/null +++ b/soh/include/leveled_stat_math.h @@ -0,0 +1,34 @@ +#ifndef LEVELED_STAT_MATH_H +#define LEVELED_STAT_MATH_H +#include "z64.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power); +u16 GetActorStat_Attack(u16 attack, u8 power); +u8 GetActorStat_Power(u8 level); +u8 GetActorStat_Courage(u8 level); +u8 GetActorStat_PlayerPower(u8 level); +u8 GetActorStat_PlayerCourage(u8 level); +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level); +u8 GetPlayerStat_BonusHearts(u8 level); +u8 GetPlayerStat_MagicUnits(u8 level); +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level); +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level); +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp); +u16 GetEnemyExperienceReward(u8 level, u16 expRate); +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage); +f32 Leveled_DamageModify(Actor * actor, Actor * attackingActor, f32 attack); +u16 Leveled_GoldSkulltulaExperience(u8 tokens); +void Leveled_SetPlayerModifiedStats(Player * player); +u8 Leveled_GetHealthAttackMultiplier(); +s8 Leveled_GetSceneLevel(s16 sceneId); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/soh/include/z64.h b/soh/include/z64.h index 99835304fea..53b76e8fb07 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -544,6 +544,8 @@ typedef enum { LANGUAGE_MAX } Language; +#define TODO_TRANSLATE "TranslateThis" + // TODO get these properties from the textures themselves #define FONT_CHAR_TEX_WIDTH 16 #define FONT_CHAR_TEX_HEIGHT 16 diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 6d8fd3ca07f..5e911ed6619 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -55,14 +55,14 @@ typedef struct { } DamageTable; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ u8 mass; } CollisionCheckInfoInit; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ s16 cylYShift; @@ -76,8 +76,8 @@ typedef struct { /* 0x12 */ s16 cylHeight; // Used for various purposes /* 0x14 */ s16 cylYShift; // Unused. Purpose inferred from Cylinder16 and CollisionCheck_CylSideVsLineSeg /* 0x16 */ u8 mass; // Used to compute displacement for OC collisions - /* 0x17 */ u8 health; // Note: some actors may use their own health variable instead of this one - /* 0x18 */ u8 damage; // Amount to decrement health by + /* 0x17 */ u16 health; // Note: some actors may use their own health variable instead of this one + /* 0x18 */ u16 damage; // Amount to decrement health by /* 0x19 */ u8 damageEffect; // Stores what effect should occur when hit by a weapon /* 0x1A */ u8 atHitEffect; // Stores what effect should occur when AT connects with an AC /* 0x1B */ u8 acHitEffect; // Stores what effect should occur when AC is touched by an AT @@ -267,8 +267,20 @@ typedef struct Actor { /* 0x138 */ ActorResetFunc reset; /* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom // #region SOH [General] - /* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init + /* */ u16 maximumHealth; // Max health value for use with health bars, set on actor init // #endregion + u16 exp; // Experience + u8 level; // Actor Level + u8 power; // e.g. Strength + u8 courage; // e.g. Defense + s8 powerModifier; // Modifies Power + s8 courageModifier; // Modifies Courage + u16 floatingNumber[7]; + u8 floatingNumberLife[7]; + Vec2f floatingNumberPosition[7]; + Vec2f floatingNumberVelocity[7]; + Mtx floatingDamageNumberMtx; + bool ignoreExpReward; // Actor handles exp reward differently } Actor; // size = 0x14C typedef enum { diff --git a/soh/include/z64player.h b/soh/include/z64player.h index a00d86557c7..992fe5a9611 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -909,7 +909,7 @@ typedef struct Player { /* 0x089A */ s16 floorPitchAlt; // the calculation for this value is bugged and doesn't represent anything meaningful /* 0x089C */ s16 unk_89C; /* 0x089E */ u16 floorSfxOffset; - /* 0x08A0 */ u8 knockbackDamage; + /* 0x08A0 */ u16 knockbackDamage; /* 0x08A1 */ u8 knockbackType; /* 0x08A2 */ s16 knockbackRot; /* 0x08A4 */ f32 knockbackSpeed; diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 751ad0b0f98..a450b0cb23e 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -208,7 +208,7 @@ typedef struct { /* 0x002E */ s16 healthCapacity; // "max_life" /* 0x0030 */ s16 health; // "now_life" /* 0x0032 */ s8 magicLevel; // 0 for no magic/new load, 1 for magic, 2 for double magic - /* 0x0033 */ s8 magic; // current magic available for use + /* 0x0033 */ u8 magic; // current magic available for use /* 0x0034 */ s16 rupees; /* 0x0036 */ u16 swordHealth; /* 0x0038 */ u16 naviTimer; @@ -307,6 +307,11 @@ typedef struct { /* 0x1422 */ s16 sunsSongState; // controls the effects of suns song /* 0x1424 */ s16 healthAccumulator; /* */ ShipSaveContextData ship; + u32 experience; + s16 heartContainers; + s16 healthCapacity2; // Modified max health + u8 magicUnits; // Modified magic units per magic level + s16 showNeededExpTimer; // Shows EXP required in the hud when greater than 0 } SaveContext; // size = 0x1428 typedef enum { diff --git a/soh/soh/Enhancements/Presets/PresetEntries.cpp b/soh/soh/Enhancements/Presets/PresetEntries.cpp index ebf6d4a195e..cf86793e24f 100644 --- a/soh/soh/Enhancements/Presets/PresetEntries.cpp +++ b/soh/soh/Enhancements/Presets/PresetEntries.cpp @@ -46,7 +46,7 @@ const std::vector vanillaPlusPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixZoraHintDialogue"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixVineFall"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixSawSoftlock"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DekuNutUpgradeFix"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixGrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixBrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixDungeonMinimapIcon"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TwoHandedIdle"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("NaviTextFix"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("GerudoWarriorClothingFix"), 1), @@ -82,6 +82,7 @@ const std::vector enhancedPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DisableCritWiggle"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("BetterOwl"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("QuitFishingAtDoor"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("InstantPutaway"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish"), 1), // Skips & Speed-ups PRESET_ENTRY_S32(CVAR_ENHANCEMENT("SkipText"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TextSpeed"), 5), @@ -125,7 +126,7 @@ const std::vector enhancedPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixZoraHintDialogue"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixVineFall"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixSawSoftlock"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DekuNutUpgradeFix"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixGrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixBrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixDungeonMinimapIcon"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TwoHandedIdle"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("NaviTextFix"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("GerudoWarriorClothingFix"), 1), @@ -176,6 +177,7 @@ const std::vector randomizerPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("QuitFishingAtDoor"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("InstantPutaway"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish"), 1), // Skips & Speed-ups PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), 1), @@ -251,7 +253,7 @@ const std::vector randomizerPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixSawSoftlock"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DekuNutUpgradeFix"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixGrokenGiantsKnife"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixBrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixDungeonMinimapIcon"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TwoHandedIdle"), 1), diff --git a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp index 674adcdfbf2..de2e5fc5f43 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp @@ -1,6 +1,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/OTRGlobals.h" +#include "soh/Enhancements/timesaver_hook_handlers.h" extern "C" { #include "macros.h" @@ -12,6 +13,7 @@ extern "C" { #define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get() +extern "C" PlayState* gPlayState; static bool sEnteredBlueWarp = false; /** @@ -124,6 +126,13 @@ void SkipBlueWarp_ShouldPlayBlueWarpCS(GIVanillaBehavior _, bool* should, va_lis */ void SkipBlueWarp_ShouldGiveItem(GIVanillaBehavior _, bool* should, va_list originalArgs) { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { + if (IS_VANILLA) { + if (gPlayState->sceneNum == SCENE_SHADOW_TEMPLE_BOSS) { + TimeSaverQueueItem(RG_SHADOW_MEDALLION); + } else if (gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE_BOSS) { + TimeSaverQueueItem(RG_SPIRIT_MEDALLION); + } + } *should = false; } } diff --git a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp index 97b65a54360..42862d4d99a 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp @@ -93,7 +93,7 @@ void SkipChildRutoInteractions_Register() { if (enRu1->action == 22) { enRu1->action = 27; enRu1->drawConfig = 1; - enRu1->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY; + enRu1->actor.flags &= ~(ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY); Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildSittingAnim, 1.0f, 0.0f, Animation_GetLastFrame((void*)&gRutoChildSittingAnim), ANIMMODE_LOOP, 0.0f); } diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 9865615bda4..6660bee7da3 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -265,6 +265,9 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN } } + auto playingFromMenu = CVarGetInteger(CVAR_AUDIO("Playing"), 0); + auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + // Longest text in Audio Editor ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)"); ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit); @@ -291,10 +294,16 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN const std::string lockedButton = ICON_FA_LOCK + hiddenKey; const std::string unlockedButton = ICON_FA_UNLOCK + hiddenKey; const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue); + const bool isCurrentlyPlaying = currentValue == playingFromMenu || seqData.sequenceId == currentBGM; ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text("%s", seqData.label.c_str()); + if (isCurrentlyPlaying) { + ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Yellow), "%s %s", ICON_FA_PLAY, + seqData.label.c_str()); + } else { + ImGui::Text("%s", seqData.label.c_str()); + } ImGui::TableNextColumn(); ImGui::PushItemWidth(-FLT_MIN); const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; diff --git a/soh/soh/Enhancements/boss-rush/BossRush.cpp b/soh/soh/Enhancements/boss-rush/BossRush.cpp index 76cf5637e06..17ffd70c735 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.cpp +++ b/soh/soh/Enhancements/boss-rush/BossRush.cpp @@ -501,6 +501,10 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } break; } + case VB_PLAY_BLUE_WARP_CS: { + *should = false; + break; + } // Spawn clean blue warps (no ruto, adult animation, etc) case VB_SPAWN_BLUE_WARP: { switch (gPlayState->sceneNum) { diff --git a/soh/soh/Enhancements/controls/InputViewer.cpp b/soh/soh/Enhancements/controls/InputViewer.cpp index a48b1811fe2..1d6695d2538 100644 --- a/soh/soh/Enhancements/controls/InputViewer.cpp +++ b/soh/soh/Enhancements/controls/InputViewer.cpp @@ -151,7 +151,7 @@ void InputViewer::DrawElement() { } ImVec2 mainPos = ImGui::GetWindowPos(); - ImVec2 size = ImGui::GetContentRegionAvail(); + ImVec2 size = ImGui::GetMainViewport()->WorkSize; #ifdef __WIIU__ const float scale = CVarGetFloat(CVAR_INPUT_VIEWER("Scale"), 1.0f) * 2.0f; @@ -584,7 +584,7 @@ void InputViewerSettingsWindow::DrawElement() { } // gInputViewer.Dpad CVarCheckbox("Show D-Pad Layers", CVAR_INPUT_VIEWER("Dpad"), - CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + CheckboxOptions().Color(THEME_COLOR).DefaultValue(false)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Dpad"), 0)) { ImGui::Indent(); CVarCombobox("##DpadOutline", CVAR_INPUT_VIEWER("DpadOutlineMode"), buttonOutlineOptionsVerbose, @@ -593,7 +593,7 @@ void InputViewerSettingsWindow::DrawElement() { } // gInputViewer.Mod1 CVarCheckbox("Show Modifier Button 1 Layers", CVAR_INPUT_VIEWER("Mod1"), - CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + CheckboxOptions().Color(THEME_COLOR).DefaultValue(false)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Mod1"), 0)) { ImGui::Indent(); CVarCombobox("##Mmod1Outline", CVAR_INPUT_VIEWER("Mod1OutlineMode"), buttonOutlineOptionsVerbose, @@ -602,7 +602,7 @@ void InputViewerSettingsWindow::DrawElement() { } // gInputViewer.Mod2 CVarCheckbox("Show Modifier Button 2 Layers", CVAR_INPUT_VIEWER("Mod2"), - CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + CheckboxOptions().Color(THEME_COLOR).DefaultValue(false)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Mod2"), 0)) { ImGui::Indent(); CVarCombobox("##Mod2Outline", CVAR_INPUT_VIEWER("Mod2OutlineMode"), buttonOutlineOptionsVerbose, @@ -652,7 +652,7 @@ void InputViewerSettingsWindow::DrawElement() { "Right Stick Visibility", CVAR_INPUT_VIEWER("RightStick.VisibilityMode"), stickModeOptions, ComboboxOptions() .Color(THEME_COLOR) - .DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .DefaultIndex(STICK_MODE_ALWAYS_HIDDEN) .Tooltip( "Determines the conditions under which the moving layer of the right stick texture is visible.")); @@ -661,7 +661,7 @@ void InputViewerSettingsWindow::DrawElement() { "Right Stick Outline/Background Visibility", CVAR_INPUT_VIEWER("RightStick.OutlineMode"), stickModeOptions, ComboboxOptions() .Color(THEME_COLOR) - .DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .DefaultIndex(STICK_MODE_ALWAYS_HIDDEN) .Tooltip( "Determines the conditions under which the right stick outline/background texture is visible.")); diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 3f77d1ed4c8..41a31f8bed9 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -642,10 +642,14 @@ void SohInputEditorWindow::DrawStickSection(uint8_t port, uint8_t stick, int32_t ImGui::SameLine(); ImGui::BeginGroup(); - DrawStickDirectionLine(ICON_FA_ARROW_UP, port, stick, Ship::UP, color); - DrawStickDirectionLine(ICON_FA_ARROW_DOWN, port, stick, Ship::DOWN, color); - DrawStickDirectionLine(ICON_FA_ARROW_LEFT, port, stick, Ship::LEFT, color); - DrawStickDirectionLine(ICON_FA_ARROW_RIGHT, port, stick, Ship::RIGHT, color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_UP, stick).c_str(), port, stick, Ship::UP, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_DOWN, stick).c_str(), port, stick, Ship::DOWN, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_LEFT, stick).c_str(), port, stick, Ship::LEFT, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_RIGHT, stick).c_str(), port, stick, + Ship::RIGHT, color); ImGui::EndGroup(); ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode(StringHelper::Sprintf("Analog Stick Options##%d", id).c_str())) { @@ -1335,12 +1339,12 @@ void SohInputEditorWindow::DrawOcarinaControlPanel() { ImGui::AlignTextToFramePadding(); ImGui::BulletText("Disable song detection"); - DrawButtonLine(ICON_FA_BAN, 0, BTN_CUSTOM_OCARINA_DISABLE_SONGS); + DrawButtonLine(ICON_FA_BAN "##DisableSongDetection", 0, BTN_CUSTOM_OCARINA_DISABLE_SONGS); ImGui::AlignTextToFramePadding(); ImGui::BulletText("Pitch"); - DrawButtonLine(ICON_FA_ARROW_UP, 0, BTN_CUSTOM_OCARINA_PITCH_UP); - DrawButtonLine(ICON_FA_ARROW_DOWN, 0, BTN_CUSTOM_OCARINA_PITCH_DOWN); + DrawButtonLine(ICON_FA_ARROW_UP "##Pitch", 0, BTN_CUSTOM_OCARINA_PITCH_UP); + DrawButtonLine(ICON_FA_ARROW_DOWN "##Pitch", 0, BTN_CUSTOM_OCARINA_PITCH_DOWN); if (!CVarGetInteger(CVAR_SETTING("CustomOcarina.Enabled"), 0)) { ImGui::EndDisabled(); @@ -1362,10 +1366,10 @@ void SohInputEditorWindow::DrawCameraControlPanel() { .Color(THEME_COLOR) .Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming")); if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { - CVarCheckbox("Allow moving while in first person mode", CVAR_SETTING("MoveInFirstPerson"), + CVarCheckbox("Allow moving while in first-person mode", CVAR_SETTING("MoveInFirstPerson"), CheckboxOptions() .Color(THEME_COLOR) - .Tooltip("Changes the left stick to move the player while in first person mode")); + .Tooltip("Changes the left stick to move the player while in first-person mode")); } CVarCheckbox("Invert Aiming X Axis", CVAR_SETTING("Controls.InvertAimingXAxis"), CheckboxOptions() @@ -1428,7 +1432,9 @@ void SohInputEditorWindow::DrawCameraControlPanel() { CheckboxOptions() .Color(THEME_COLOR) .Tooltip("Enables free look camera control\nNote: You must remap C buttons off of the right stick in the " - "controller config menu, and map the camera stick to the right stick.")); + "controller config menu, and map the camera stick to the right stick.\n" + "Doesn't work in areas were the game locks the camera.\n" + "Scene reload may be necessary to enable.")); CVarCheckbox("Invert Camera X Axis", CVAR_SETTING("FreeLook.InvertXAxis"), CheckboxOptions().Color(THEME_COLOR).Tooltip("Inverts the Camera X Axis in:\n-Free look")); CVarCheckbox( diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index e0f530bc202..8074fa70749 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -2376,10 +2376,12 @@ void CosmeticsEditorWindow::DrawElement() { .Step(0.01f) .Size(ImVec2(300.0f, 0.0f)) .Color(THEME_COLOR)); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Randomize All on New Scene", CVAR_COSMETIC("RandomizeAllOnNewScene"), UIWidgets::CheckboxOptions() .Color(THEME_COLOR) .Tooltip("Enables randomizing all unlocked cosmetics when you enter a new scene.")); + ImGui::EndDisabled(); UIWidgets::CVarCheckbox( "Advanced Mode", CVAR_COSMETIC("AdvancedMode"), UIWidgets::CheckboxOptions() @@ -2408,13 +2410,15 @@ void CosmeticsEditorWindow::DrawElement() { } } } + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (UIWidgets::Button("Randomize All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { CosmeticsEditor_RandomizeAll(); } + ImGui::EndDisabled(); + ImGui::SameLine(); if (UIWidgets::Button("Reset All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { - CVarClearBlock("gCosmetics"); - ApplyOrResetCustomGfxPatches(); + CosmeticsEditor_ResetAll(); } if (UIWidgets::Button("Lock All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { @@ -2432,6 +2436,7 @@ void CosmeticsEditorWindow::DrawElement() { } } + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (UIWidgets::Button("Rainbow All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && @@ -2441,6 +2446,8 @@ void CosmeticsEditorWindow::DrawElement() { } } } + ImGui::EndDisabled(); + ImGui::SameLine(); if (UIWidgets::Button("Un-Rainbow All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { @@ -2470,6 +2477,7 @@ void CosmeticsEditorWindow::DrawElement() { if (ImGui::BeginTabItem("Keys")) { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::Separator(true, true, 2.0f, 2.0f); if (UIWidgets::Button("Give all keys dungeon-specific colors", @@ -2483,6 +2491,7 @@ void CosmeticsEditorWindow::DrawElement() { DrawCosmeticGroup(COSMETICS_GROUP_SMALL_KEYS); DrawCosmeticGroup(COSMETICS_GROUP_BOSS_KEYS); + ImGui::EndDisabled(); ImGui::EndTabItem(); } diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp index 477fe6f0af5..b2b473166d8 100644 --- a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp @@ -281,11 +281,24 @@ void PatchIronKnuckleTextureOverflow() { } } +void PatchBoulderFragment() { + // The boulder fragment renders invisible due to the change made by https://github.com/Kenix3/libultraship/pull/721 + // Until it is known wether this change is approriate or something else should be done to it, the following patches + // adjust the render mode for the DL to not become invisible + ResourceMgr_PatchGfxByName(gBoulderFragmentsDL, "boulderFragmentRenderFix3", 3, + gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_OPA_SURF2)); + ResourceMgr_PatchGfxByName(gBoulderFragmentsDL, "boulderFragmentRenderFix6", 6, + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIA_PRIM2)); +} + void ApplyAuthenticGfxPatches() { + // Overflow textures PatchArrowTipTexture(); PatchDekuStickTextureOverflow(); PatchFreezardTextureOverflow(); PatchIronKnuckleTextureOverflow(); + + PatchBoulderFragment(); } // Patches the Sold Out GI DL to render the texture in the mirror boundary diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp index d4c47865730..a9c19f11f98 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -155,11 +155,12 @@ const std::string CustomMessage::GetFrench(MessageFormat format) const { } const std::string CustomMessage::GetForCurrentLanguage(MessageFormat format) const { - return GetForLanguage((gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language, format); + return GetForLanguage( + ((Language)gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : (Language)gSaveContext.language, format); } const std::string CustomMessage::GetForLanguage(uint8_t language, MessageFormat format) const { - std::string output = messages[language].length() > 0 ? messages[language] : messages[LANGUAGE_ENG]; + std::string output = !messages[language].starts_with(TODO_TRANSLATE) ? messages[language] : messages[LANGUAGE_ENG]; ProcessMessageFormat(output, format); return output; } @@ -273,8 +274,12 @@ void CustomMessage::Replace(std::string&& oldStr, std::string&& newStr) { void CustomMessage::Replace(std::string&& oldStr, CustomMessage newMessage) { for (uint8_t language = 0; language < LANGUAGE_MAX - 1; language++) { size_t position = messages[language].find(oldStr); + std::string newMsg = newMessage.messages[language]; + if (language != LANGUAGE_ENG && newMsg == TODO_TRANSLATE) { + newMsg = newMessage.messages[LANGUAGE_ENG]; + } while (position != std::string::npos) { - messages[language].replace(position, oldStr.length(), newMessage.messages[language]); + messages[language].replace(position, oldStr.length(), newMsg); position = messages[language].find(oldStr); } } diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h index 71ae423b549..6b14c8060e1 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -6,6 +6,7 @@ #include #include "../../../include/z64item.h" +#include "../../../include/z64.h" #include "../../../include/message_data_textbox_types.h" #include "../randomizer/3drando/text.hpp" @@ -46,6 +47,7 @@ class CustomMessage { TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); CustomMessage(std::string english_, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); + // RANDOTODO trying to declare this with capital and type causes ambiguity with the first signature CustomMessage(std::string english_, std::vector colors_, std::vector capital_ = {}, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); CustomMessage(Text text, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); @@ -203,7 +205,7 @@ class CustomMessage { void CleanString(std::string& str) const; private: - std::vector messages = { "", "", "" }; + std::vector messages = { "", TODO_TRANSLATE, TODO_TRANSLATE }; TextBoxType type = TEXTBOX_TYPE_BLACK; TextBoxPosition position = TEXTBOX_POS_BOTTOM; std::vector colors = {}; diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index db5cc824f7a..91c0d4c1cc3 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -11,6 +11,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/cosmetics/CosmeticsEditor.h" #include "soh/Enhancements/audio/AudioEditor.h" +#include "soh/Enhancements/randomizer/logic.h" #define Path _Path #define PATH_HACK @@ -1450,6 +1451,52 @@ static bool SfxHandler(std::shared_ptr Console, const std::vector return 0; } +static bool AvailableChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { + const auto& logic = Rando::Context::GetInstance()->GetLogic(); + bool enabled = false; + + if (args.size() == 1) { + enabled = !logic->ACProcessUndiscoveredExits; + } else { + try { + enabled = std::stoi(args[1]); + } catch (std::invalid_argument const& ex) { + ERROR_MESSAGE("[SOH] Enable should be 0 or 1"); + return 1; + } + } + + logic->ACProcessUndiscoveredExits = enabled; + INFO_MESSAGE("[SOH] Available Checks - Process Undiscovered Exits %s", + logic->ACProcessUndiscoveredExits ? "enabled" : "disabled"); + + CheckTracker::RecalculateAvailableChecks(); + return 0; +} + +static bool AvailableChecksRecalculateHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { + RandomizerRegion startingRegion = RR_ROOT; + + if (args.size() > 1) { + try { + startingRegion = static_cast(std::stoi(args[1])); + } catch (std::invalid_argument const& ex) { + ERROR_MESSAGE("[SOH] Region should be a number"); + return 1; + } + + if (startingRegion <= RR_NONE || startingRegion >= RR_MAX) { + ERROR_MESSAGE("[SOH] Region should be between 1 and %d", RR_MAX - 1); + return 1; + } + } + + CheckTracker::RecalculateAvailableChecks(startingRegion); + return 0; +} + void DebugConsole_Init(void) { // Console CMD_REGISTER("file_select", { FileSelectHandler, "Returns to the file select." }); @@ -1708,5 +1755,15 @@ void DebugConsole_Init(void) { { "group_name", Ship::ArgumentType::TEXT, true }, } }); + CMD_REGISTER("acpue", { AvailableChecksProcessUndiscoveredExitsHandler, + "Available Checks - Process Undiscovered Exits", + { { "enable", Ship::ArgumentType::NUMBER, true } } }); + + CMD_REGISTER("acr", { AvailableChecksRecalculateHandler, + "Available Checks - Recalculate", + { + { "starting_region", Ship::ArgumentType::NUMBER, true }, + } }); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } diff --git a/soh/soh/Enhancements/debugger/MessageViewer.cpp b/soh/soh/Enhancements/debugger/MessageViewer.cpp index fef875e94b6..c018b001707 100644 --- a/soh/soh/Enhancements/debugger/MessageViewer.cpp +++ b/soh/soh/Enhancements/debugger/MessageViewer.cpp @@ -2,6 +2,7 @@ #include "soh/SohGui/UIWidgets.hpp" #include "soh/SohGui/SohGui.hpp" +#include "soh/SohGui/SohMenu.h" #include "soh/OTRGlobals.h" #include @@ -26,6 +27,7 @@ void MessageViewer::InitElement() { } void MessageViewer::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::Text("Table ID"); ImGui::SameLine(); PushStyleInput(THEME_COLOR); @@ -56,22 +58,12 @@ void MessageViewer::DrawElement() { memset(mTextIdBuf, 0, sizeof(char) * MAX_STRING_SIZE); } PopStyleCheckbox(); - ImGui::Text("Language"); - ImGui::SameLine(); - PushStyleCombobox(THEME_COLOR); - if (ImGui::BeginCombo("##Language", mLanguages[mLanguage])) { - // ReSharper disable CppDFAUnreachableCode - for (size_t i = 0; i < mLanguages.size(); i++) { - if (strlen(mLanguages[i]) > 0) { - if (ImGui::Selectable(mLanguages[i], i == mLanguage)) { - mLanguage = i; - } - } - } - ImGui::EndCombo(); - } - PopStyleCombobox(); - UIWidgets::InsertHelpHoverText("Which language to load from the selected text ID"); + SohGui::SohMenu::UpdateLanguageMap(SohGui::languages); + UIWidgets::Combobox("Language", &mLanguage, SohGui::languages, + UIWidgets::ComboboxOptions() + .Color(THEME_COLOR) + .DefaultIndex(0) + .Tooltip("Which language to load from the selected text ID")); PushStyleButton(THEME_COLOR); if (ImGui::Button("Display Message##ExistingMessage")) { mDisplayExistingMessageClicked = true; @@ -87,6 +79,7 @@ void MessageViewer::DrawElement() { mDisplayCustomMessageClicked = true; } PopStyleButton(); + ImGui::EndDisabled(); } void MessageViewer::UpdateElement() { @@ -196,6 +189,11 @@ void MessageDebug_StartTextBox(const char* tableId, uint16_t textId, uint8_t lan R_TEXT_CHAR_SCALE = 75; R_TEXT_LINE_SPACING = 12; R_TEXT_INIT_XPOS = 65; + if (language == LANGUAGE_JPN) { + R_TEXT_CHAR_SCALE = 88; + R_TEXT_LINE_SPACING = 18; + R_TEXT_INIT_XPOS = 65; + } char* buffer = font->msgBuf; msgCtx->textId = textId; if (strlen(tableId) == 0) { @@ -207,10 +205,8 @@ void MessageDebug_StartTextBox(const char* tableId, uint16_t textId, uint8_t lan constexpr int maxBufferSize = sizeof(font->msgBuf); const CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(tableId, textId); font->charTexBuf[0] = (messageEntry.GetTextBoxType() << 4) | messageEntry.GetTextBoxPosition(); - switch (language) { - font->msgLength = - SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetForLanguage(language), maxBufferSize); - } + font->msgLength = + SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetForLanguage(language), maxBufferSize); msgCtx->msgLength = static_cast(font->msgLength); } msgCtx->textBoxProperties = font->charTexBuf[0]; diff --git a/soh/soh/Enhancements/debugger/MessageViewer.h b/soh/soh/Enhancements/debugger/MessageViewer.h index 20924a0b612..83e290114dc 100644 --- a/soh/soh/Enhancements/debugger/MessageViewer.h +++ b/soh/soh/Enhancements/debugger/MessageViewer.h @@ -4,7 +4,7 @@ #ifdef __cplusplus #include "GuiWindow.h" -#include +#include extern "C" { #endif /** @@ -41,7 +41,6 @@ class MessageViewer : public Ship::GuiWindow { void DisplayCustomMessage() const; static constexpr uint16_t MAX_STRING_SIZE = 1024; - static constexpr std::array mLanguages = { "English", "German", "French" }; static constexpr int HEXADECIMAL = 0; static constexpr int DECIMAL = 1; char* mTableIdBuf; @@ -49,7 +48,7 @@ class MessageViewer : public Ship::GuiWindow { char* mTextIdBuf; uint16_t mTextId; int mTextIdBase = HEXADECIMAL; - size_t mLanguage = LANGUAGE_ENG; + int32_t mLanguage = LANGUAGE_ENG; char* mCustomMessageBuf; std::string mCustomMessageString; bool mDisplayExistingMessageClicked = false; diff --git a/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp b/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp index d5090e7e593..19be376ed7c 100644 --- a/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp +++ b/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp @@ -12,6 +12,7 @@ void SohConsoleWindow::UpdateElement() { } void SohConsoleWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::PushStyleInput(THEME_COLOR); // Small font (13) to match hardcoded width values in the LUS window.. set large font after below TODO addressed ImGui::PushFont(OTRGlobals::Instance->fontMonoSmall); @@ -27,4 +28,5 @@ void SohConsoleWindow::DrawElement() { ImGui::PopFont(); UIWidgets::PopStyleInput(); + ImGui::EndDisabled(); } diff --git a/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp b/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp index 06d81577d6e..def23caccb1 100644 --- a/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp +++ b/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp @@ -10,7 +10,9 @@ void SohGfxDebuggerWindow::UpdateElement() { } void SohGfxDebuggerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); GfxDebuggerWindow::DrawElement(); ImGui::PopFont(); + ImGui::EndDisabled(); } diff --git a/soh/soh/Enhancements/debugger/actorViewer.cpp b/soh/soh/Enhancements/debugger/actorViewer.cpp index 8ae940ffb99..1acf780647d 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -849,6 +849,7 @@ void ActorViewer_AddTagForAllActors() { } void ActorViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); static Actor* display; static Actor empty{}; static Actor* fetch = NULL; @@ -922,6 +923,10 @@ void ActorViewerWindow::DrawElement() { ImGui::Text("Name: %s", ActorDB::Instance->RetrieveEntry(display->id).name.c_str()); ImGui::Text("Description: %s", GetActorDescription(display->id).c_str()); ImGui::Text("Category: %s", acMapping[display->category]); + if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { + ImGui::Text("Level: %d", display->level); + ImGui::Text("EXP: %d", display->exp); + } ImGui::Text("ID: %d", display->id); ImGui::Text("Parameters: %d", display->params); }, @@ -957,7 +962,7 @@ void ActorViewerWindow::DrawElement() { if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { PushStyleInput(THEME_COLOR); - ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health); + ImGui::InputScalar("Enemy Health", ImGuiDataType_U16, &display->colChkInfo.health); PopStyleInput(); UIWidgets::InsertHelpHoverText("Some actors might not use this!"); } @@ -1186,6 +1191,7 @@ void ActorViewerWindow::DrawElement() { actors.clear(); } } + ImGui::EndDisabled(); } void ActorViewerWindow::InitElement() { diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp index e3abba484ec..352d4b9626e 100644 --- a/soh/soh/Enhancements/debugger/colViewer.cpp +++ b/soh/soh/Enhancements/debugger/colViewer.cpp @@ -58,6 +58,7 @@ using namespace UIWidgets; // Draws the ImGui window for the collision viewer void ColViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); CheckboxOptions checkOpt = CheckboxOptions().Color(THEME_COLOR); ComboboxOptions comboOpt = ComboboxOptions().Color(THEME_COLOR); CVarCheckbox("Enabled", CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), checkOpt); @@ -139,6 +140,7 @@ void ColViewerWindow::DrawElement() { UIWidgets::Tooltip(colorHelpText.c_str()); } PopStyleHeader(); + ImGui::EndDisabled(); } // Calculates the normal for a triangle at the 3 specified points diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 534201f8e7a..2d3185f4687 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -20,6 +20,7 @@ extern "C" { #include "functions.h" #include "macros.h" #include "soh/cvar_prefixes.h" +#include "leveled_stat_math.h" extern PlayState* gPlayState; #include "textures/icon_item_static/icon_item_static.h" @@ -171,16 +172,22 @@ void DrawInfoTab() { PopStyleInput(); if (ImGui::IsItemDeactivated()) { gSaveContext.healthCapacity = healthIntermediary; + + Player* player = GET_PLAYER(gPlayState); + gSaveContext.healthCapacity2 = + GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, player->actor.level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; } Tooltip("Maximum health. 16 units per full heart"); - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; // Clamp health to new max + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; // Clamp health to new max } int32_t health = (int32_t)gSaveContext.health; if (SliderInt("Health", &health, intSliderOptionsBase.Tooltip("Current health. 16 units per full heart") .Min(0) - .Max(gSaveContext.healthCapacity))) { + .Max(gSaveContext.healthCapacity2))) { gSaveContext.health = (int16_t)health; } @@ -196,7 +203,7 @@ void DrawInfoTab() { gSaveContext.isMagicAcquired = gSaveContext.magicLevel > 0; gSaveContext.isDoubleMagicAcquired = gSaveContext.magicLevel == 2; } - gSaveContext.magicCapacity = gSaveContext.magicLevel * 0x30; // Set to get the bar drawn in the UI + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; // Set to get the bar drawn in the UI if (gSaveContext.magic > gSaveContext.magicCapacity) { gSaveContext.magic = gSaveContext.magicCapacity; // Clamp magic to new max } @@ -206,7 +213,7 @@ void DrawInfoTab() { intSliderOptionsBase.Min(0) .Max(gSaveContext.magicCapacity) .Tooltip("Current magic. 48 units per magic level"))) { - gSaveContext.magic = (int8_t)magic; + gSaveContext.magic = (uint8_t)magic; } PushStyleInput(THEME_COLOR); @@ -849,9 +856,19 @@ void DrawFlagsTab() { DrawGroupWithBorder( [&]() { - size_t selectedGsMap = 0; + PushStyleCombobox(THEME_COLOR); + static size_t selectedGsMap = 0; ImGui::Text("Gold Skulltulas"); - Combobox("Map##Gold Skulltulas", &selectedGsMap, gsMapping, comboboxOptionsBase.Tooltip("")); + if (ImGui::BeginCombo("##GSMap", gsMapping[selectedGsMap])) { + for (size_t index = 0; index < gsMapping.size(); index++) { + if (ImGui::Selectable(gsMapping[index])) { + selectedGsMap = index; + } + } + + ImGui::EndCombo(); + } + PopStyleCombobox(); // TODO We should write out descriptions for each one... ugh ImGui::AlignTextToFramePadding(); @@ -1655,6 +1672,7 @@ void ResetBaseOptions() { void SaveEditorWindow::DrawElement() { PushStyleTabs(THEME_COLOR); ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (ImGui::BeginTabBar("SaveContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { ResetBaseOptions(); @@ -1696,6 +1714,7 @@ void SaveEditorWindow::DrawElement() { ImGui::EndTabBar(); } + ImGui::EndDisabled(); ImGui::PopFont(); PopStyleTabs(); } diff --git a/soh/soh/Enhancements/debugger/dlViewer.cpp b/soh/soh/Enhancements/debugger/dlViewer.cpp index 7cedcdf26d2..a02aa7d95df 100644 --- a/soh/soh/Enhancements/debugger/dlViewer.cpp +++ b/soh/soh/Enhancements/debugger/dlViewer.cpp @@ -89,6 +89,7 @@ void PerformDisplayListSearch() { } void DLViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); // Debounce the search field as listing otr files is expensive UIWidgets::PushStyleInput(THEME_COLOR); ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); @@ -122,6 +123,7 @@ void DLViewerWindow::DrawElement() { if (activeDisplayList == "") { ImGui::PopFont(); + ImGui::EndDisabled(); return; } @@ -131,6 +133,8 @@ void DLViewerWindow::DrawElement() { if (res->GetInitData()->Type != static_cast(Fast::ResourceType::DisplayList)) { ImGui::Text("Resource type is not a Display List. Please choose another."); + ImGui::PopFont(); + ImGui::EndDisabled(); return; } @@ -325,13 +329,10 @@ void DLViewerWindow::DrawElement() { } ImGui::EndGroup(); } - } catch (const std::exception& e) { - ImGui::Text("Error displaying DL instructions."); - ImGui::PopFont(); - return; - } + } catch (const std::exception& e) { ImGui::Text("Error displaying DL instructions."); } ImGui::PopFont(); + ImGui::EndDisabled(); } void DLViewerWindow::InitElement() { diff --git a/soh/soh/Enhancements/debugger/hookDebugger.cpp b/soh/soh/Enhancements/debugger/hookDebugger.cpp index 11386228278..146db2ddc9b 100644 --- a/soh/soh/Enhancements/debugger/hookDebugger.cpp +++ b/soh/soh/Enhancements/debugger/hookDebugger.cpp @@ -77,6 +77,7 @@ void DrawHookRegisteringInfos(const char* hookName) { } void HookDebuggerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); #ifndef __cpp_lib_source_location ImGui::TextColored(yellow, "Some features of the Hook Debugger are unavailable because SoH was compiled " "without \"\" support " @@ -93,6 +94,7 @@ void HookDebuggerWindow::DrawElement() { } ImGui::PopFont(); + ImGui::EndDisabled(); } void HookDebuggerWindow::InitElement() { diff --git a/soh/soh/Enhancements/debugger/valueViewer.cpp b/soh/soh/Enhancements/debugger/valueViewer.cpp index 1ea11744423..4544979cb53 100644 --- a/soh/soh/Enhancements/debugger/valueViewer.cpp +++ b/soh/soh/Enhancements/debugger/valueViewer.cpp @@ -154,6 +154,7 @@ void RegisterValueViewerHooks() { RegisterShipInitFunc initFunc(RegisterValueViewerHooks, { CVAR_NAME }); void ValueViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Enable Printing", CVAR_NAME, UIWidgets::CheckboxOptions().Color(THEME_COLOR)); ImGui::BeginGroup(); @@ -275,6 +276,7 @@ void ValueViewerWindow::DrawElement() { } ImGui::EndGroup(); } + ImGui::EndDisabled(); } void ValueViewerWindow::InitElement() { diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index 61113158413..227a841276e 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -11,8 +11,13 @@ extern "C" { #include +#include "src/overlays/actors/ovl_En_Rr/z_en_rr.h" } +#define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies") +#define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF +#define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT) + const char* enemyCVarList[] = { CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"), CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"), @@ -120,7 +125,6 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { ACTOR_EN_CROW, 0 }, // Guay { ACTOR_EN_FIREFLY, 4 }, // Ice Keese { ACTOR_EN_ST, 2 }, // Skulltula (invisible) - { ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing) { ACTOR_EN_FIREFLY, 2 }, // Regular Keese { ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large) { ACTOR_EN_RR, 0 }, // Like-Like @@ -141,6 +145,7 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { ACTOR_EN_TP, -1 }, // Electric Tailpasaran { ACTOR_EN_BW, 0 }, // Torch Slug { ACTOR_EN_WALLMAS, 1 }, // Wallmaster + { ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing) { ACTOR_EN_WF, 1 }, // Wolfos (white) { ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba @@ -270,15 +275,7 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po // Get randomized enemy ID and parameter. uint32_t seed = play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params; - EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed); - - int8_t timesRandomized = 1; - - // While randomized enemy isn't allowed in certain situations, randomize again. - while (!IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, randomEnemy)) { - randomEnemy = GetRandomizedEnemyEntry(seed + timesRandomized); - timesRandomized++; - } + EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play); *actorId = randomEnemy.id; *params = randomEnemy.params; @@ -318,41 +315,44 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po return 1; } -std::vector selectedEnemyList; +static std::vector selectedEnemyList; void GetSelectedEnemies() { selectedEnemyList.clear(); - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM) { - for (int i = 0; i < 49; i++) { - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) { - selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); - } else if (CVarGetInteger(enemyCVarList[i], 1)) { - selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); - } - } - if (selectedEnemyList.size() == 0) { - selectedEnemyList.push_back(randomizedEnemySpawnTable[0]); - } - } else { - for (int i = 0; i < 49; i++) { + for (int i = 0; i < 49; i++) { + if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) { + selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); + } else if (CVarGetInteger(enemyCVarList[i], 1)) { selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); } } + if (selectedEnemyList.size() == 0) { + selectedEnemyList.push_back(randomizedEnemySpawnTable[0]); + } } -EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) { +EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) { + std::vector filteredEnemyList = {}; if (selectedEnemyList.size() == 0) { GetSelectedEnemies(); } - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) { + for (EnemyEntry enemy : selectedEnemyList) { + if (IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, enemy)) { + filteredEnemyList.push_back(enemy); + } + } + if (filteredEnemyList.size() == 0) { + filteredEnemyList = selectedEnemyList; + } + if (CVAR_ENEMY_RANDOMIZER_VALUE == ENEMY_RANDOMIZER_RANDOM_SEEDED) { uint32_t finalSeed = seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt); Random_Init(finalSeed); - uint32_t randomNumber = Random(0, RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE); - return selectedEnemyList[randomNumber]; + uint32_t randomNumber = Random(0, filteredEnemyList.size()); + return filteredEnemyList[randomNumber]; } else { - uint32_t randomSelectedEnemy = Random(0, selectedEnemyList.size()); - return selectedEnemyList[randomSelectedEnemy]; + uint32_t randomSelectedEnemy = Random(0, filteredEnemyList.size()); + return filteredEnemyList[randomSelectedEnemy]; } } @@ -433,11 +433,9 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) { // Shell Blade & Spike - Child Link can't kill these with sword or Deku Stick. // Arwing & Dark Link - Both go out of bounds way too easily, softlocking the player. // Wallmaster - Not easily visible, often makes players think they're softlocked and that there's no enemies left. - // Club Moblin - Many issues with them falling or placing out of bounds. Maybe fixable in the future? bool enemiesToExcludeClearRooms = enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB || enemy.id == ACTOR_EN_NY || enemy.id == ACTOR_EN_CLEAR_TAG || - enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 || - enemy.id == ACTOR_EN_MB; + enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2; // Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms. bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI; @@ -538,3 +536,45 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) { return 1; } } + +void RegisterEnemyRandomizer() { + // prevent dark link from triggering a voidout + COND_VB_SHOULD(VB_TRIGGER_VOIDOUT, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + Actor* actor = va_arg(args, Actor*); + + if (actor->category != ACTORCAT_PLAYER) { + *should = false; + Actor_Kill(actor); + } + }); + + // prevent dark link dealing fall damage to the player + COND_VB_SHOULD(VB_RECIEVE_FALL_DAMAGE, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + Actor* actor = va_arg(args, Actor*); + + if (actor->category != ACTORCAT_PLAYER) { + *should = false; + } + }); + + // prevent dark link from interfering with HESS/recoil/etc when at more than 100 away from him + COND_VB_SHOULD(VB_TORCH2_HANDLE_CLANKING, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + Actor* darkLink = va_arg(args, Actor*); + + if (darkLink->xzDistToPlayer > 100.0f) { + *should = false; + } + }); + + // prevent dark link from being grabbed by like likes and therefore grabbing the player + COND_VB_SHOULD(VB_LIKE_LIKE_GRAB_PLAYER, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + EnRr* likeLike = va_arg(args, EnRr*); + + if (!(likeLike->collider1.base.oc != NULL && likeLike->collider1.base.oc->category == ACTORCAT_PLAYER) && + !(likeLike->collider2.base.oc != NULL && likeLike->collider2.base.oc->category == ACTORCAT_PLAYER)) { + *should = false; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME }); \ No newline at end of file diff --git a/soh/soh/Enhancements/enemyrandomizer.h b/soh/soh/Enhancements/enemyrandomizer.h index 4c4fd55763e..6f80503df96 100644 --- a/soh/soh/Enhancements/enemyrandomizer.h +++ b/soh/soh/Enhancements/enemyrandomizer.h @@ -1,6 +1,7 @@ #pragma once #include +#include "item-tables/ItemTableTypes.h" typedef struct EnemyEntry { int16_t id; @@ -11,7 +12,7 @@ typedef struct EnemyEntry { bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX); bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy); -EnemyEntry GetRandomizedEnemyEntry(uint32_t seed); +EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play); extern const char* enemyCVarList[]; extern const char* enemyNameList[]; diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index 95455926b34..0abb8e803a3 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -189,7 +189,7 @@ void ModifyGravity::_Remove() { GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() { if (!GameInteractor::IsSaveLoaded()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; - } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) || + } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity2) || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))) { return GameInteractionEffectQueryResult::NotPossible; } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 44d0e643a6b..ecd970b3120 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -15,6 +15,7 @@ DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry)); DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry)); DEFINE_HOOK(OnTransitionEnd, (int16_t sceneNum)); DEFINE_HOOK(OnSceneInit, (int16_t sceneNum)); +DEFINE_HOOK(AfterSceneCommands, (int16_t sceneNum)); DEFINE_HOOK(OnSceneFlagSet, (int16_t sceneNum, int16_t flagType, int16_t flag)); DEFINE_HOOK(OnSceneFlagUnset, (int16_t sceneNum, int16_t flagType, int16_t flag)); DEFINE_HOOK(OnFlagSet, (int16_t flagType, int16_t flag)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 9231da8438b..5e52bed7153 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -45,12 +45,18 @@ void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum) { GameInteractor::Instance->ExecuteHooksForFilter(sceneNum); } -void GameInteractor_ExecuteOnSceneInitHooks(int16_t sceneNum) { +void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum) { GameInteractor::Instance->ExecuteHooks(sceneNum); GameInteractor::Instance->ExecuteHooksForID(sceneNum, sceneNum); GameInteractor::Instance->ExecuteHooksForFilter(sceneNum); } +void GameInteractor_ExecuteAfterSceneCommands(int16_t sceneNum) { + GameInteractor::Instance->ExecuteHooks(sceneNum); + GameInteractor::Instance->ExecuteHooksForID(sceneNum, sceneNum); + GameInteractor::Instance->ExecuteHooksForFilter(sceneNum); +} + void GameInteractor_ExecuteOnSceneFlagSet(int16_t sceneNum, int16_t flagType, int16_t flag) { GameInteractor::Instance->ExecuteHooks(sceneNum, flagType, flag); GameInteractor::Instance->ExecuteHooksForFilter(sceneNum, flagType, flag); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 33c8d2681bf..b1f9195b4bb 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -18,6 +18,7 @@ void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry); void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); +void GameInteractor_ExecuteAfterSceneCommands(int16_t sceneNum); void GameInteractor_ExecuteOnSceneFlagSet(int16_t sceneNum, int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnSceneFlagUnset(int16_t sceneNum, int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnFlagSet(int16_t flagType, int16_t flag); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index ac99859b54c..a090b96d7db 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -18,12 +18,14 @@ extern PlayState* gPlayState; void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { gSaveContext.healthCapacity += amount * 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + } void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { // Full single magic = 48 // Full double magic = 96 - int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * 48; + int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; @@ -46,17 +48,17 @@ void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { void GameInteractor::RawAction::HealOrDamagePlayer(int16_t hearts) { if (hearts > 0) { - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); } else if (hearts < 0) { Player* player = GET_PLAYER(gPlayState); - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); player->invincibilityTimer = 28; } } void GameInteractor::RawAction::SetPlayerHealth(int16_t hearts) { - gSaveContext.health = hearts * 0x10; + gSaveContext.health = hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } void GameInteractor::RawAction::SetLinkInvisibility(bool active) { diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 1c516fc6eab..e624515b45a 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -474,6 +474,14 @@ typedef enum { // - `*BgHeavyBlock` VB_FREEZE_LINK_FOR_BLOCK_THROW, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_FREEZE_LINK_FOR_FOREST_PILLARS, + // #### `result` // ```c // true @@ -946,6 +954,14 @@ typedef enum { // - `*VBFishingData` VB_GIVE_RANDO_FISHING_PRIZE, + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - '*Fishing' + VB_GIVE_RANDO_GLITCH_FISHING_PRIZE, + // #### `result` // ```c // true @@ -1350,6 +1366,14 @@ typedef enum { // - `*BgTreemouth` VB_PLAY_DEKU_TREE_INTRO_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*DemoKekkai` + VB_PLAY_DISPEL_BARRIER_CS, + // #### `result` // ```c // true @@ -1414,6 +1438,15 @@ typedef enum { // - None VB_PLAY_FIRE_ARROW_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnHeishi2` + // - `bool` (clearCamera - true if the code clears a sub-camera, false otherwise) + VB_PLAY_GATE_OPENING_OR_CLOSING_CS, + // #### `result` // ```c // true @@ -1978,6 +2011,38 @@ typedef enum { // #### `args` // - `*EnWonderTalk2` VB_WONDER_TALK, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Actor` + VB_TRIGGER_VOIDOUT, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Actor` + VB_TORCH2_HANDLE_CLANKING, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Actor` + VB_RECIEVE_FALL_DAMAGE, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnRr` + VB_LIKE_LIKE_GRAB_PLAYER, } GIVanillaBehavior; #endif diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 2c357fd34f6..bfa9575a823 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -99,6 +99,13 @@ void SwitchAge() { gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST; gPlayState->linkAgeOnLoad ^= 1; + // Discover adult/child spawns + if (gPlayState->linkAgeOnLoad == LINK_AGE_ADULT) { + Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false); + } else { + Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false); + } + static HOOK_ID hookId = 0; hookId = REGISTER_VB_SHOULD(VB_INFLICT_VOID_DAMAGE, { *should = false; @@ -205,7 +212,8 @@ void UpdatePermanentHeartLossState() { uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = false; } } @@ -220,9 +228,11 @@ void RegisterPermanentHeartLoss() { if (!CVarGetInteger(CVAR_ENHANCEMENT("PermanentHeartLoss"), 0) || !GameInteractor::IsSaveLoaded()) return; - if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity2 - gSaveContext.health >= heartUnits) { gSaveContext.healthCapacity -= 16; - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = true; } }); @@ -820,6 +830,7 @@ void UpdateHurtContainerModeState(bool newState) { } else { gSaveContext.healthCapacity = 48 + ((getHeartPieces + getHeartContainers) * 16); } + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); } void RegisterHurtContainerModeHandler() { @@ -872,9 +883,11 @@ void RegisterRandomizedEnemySizes() { // Scale the health based on a smaller factor than randomScale float healthScalingFactor = 0.8f; // Adjust this factor as needed float scaledHealth = actor->colChkInfo.health * (randomScale * healthScalingFactor); + float scaledExp = actor->exp * (randomScale * healthScalingFactor); // Ensure the scaled health doesn't go below zero actor->colChkInfo.health = fmax(scaledHealth, 1.0f); + actor->exp = fmax(scaledExp, 1.0f); } else { return; } diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index e5e22ad1d63..f2b0e782c1e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -161,7 +161,7 @@ static void ValidateOtherEntrance(GetAccessibleLocationsStruct& gals) { } // If we are not shuffling the guard house, add the key so we can properly check for poe merchant access if (gals.validatedStartingRegion && gals.foundTempleOfTime && - !ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL)) { + ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF)) { Rando::StaticData::RetrieveItem(RG_GUARD_HOUSE_KEY).ApplyEffect(); } } @@ -209,11 +209,18 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer bool stopOnBeatable = false, bool addToPlaythrough = false) { auto ctx = Rando::Context::GetInstance(); for (auto& exit : region->exits) { + int16_t entranceIndex = exit.GetIndex(); + if (!logic->ACProcessUndiscoveredExits && logic->CalculatingAvailableChecks && + ctx->GetOption(RSK_SHUFFLE_ENTRANCES).Get() && exit.IsShuffled() && entranceIndex != -1 && + !Entrance_GetIsEntranceDiscovered(entranceIndex)) { + continue; + } + Region* exitRegion = exit.GetConnectedRegion(); // Update Time of Day Access for the exit if (UpdateToDAccess(&exit, exitRegion)) { gals.logicUpdated = true; - if (!gals.sphereZeroComplete) { + if (!gals.sphereZeroComplete || logic->AreCheckingBigPoes) { if (!gals.foundTempleOfTime || !gals.validatedStartingRegion) { ValidateOtherEntrance(gals); } @@ -421,18 +428,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals Rando::ItemLocation* location = ctx->GetItemLocation(loc); RandomizerGet locItem = location->GetPlacedRandomizerGet(); - if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) { - if (gals.calculatingAvailableChecks) { - gals.accessibleLocations.push_back(loc); - StopPerformanceTimer(PT_LOCATION_LOGIC); - return false; - } - + if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, logic->CalculatingAvailableChecks)) { location->AddToPool(); - if (locItem == RG_NONE) { + if (locItem == RG_NONE || logic->CalculatingAvailableChecks) { gals.accessibleLocations.push_back(loc); // Empty location, consider for placement - } else { + } + if (locItem != RG_NONE) { // If ignore has a value, we want to check if the item location should be considered or not // This is necessary due to the below preprocessing for playthrough generation if (ignore != RG_NONE) { @@ -528,11 +530,32 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize // Return any of the targetLocations that are accessible in logic std::vector ReachabilitySearch(const std::vector& targetLocations, RandomizerGet ignore /* = RG_NONE*/, - bool calculatingAvailableChecks /* = false */) { + bool calculatingAvailableChecks /* = false */, + RandomizerRegion startingRegion /* = RR_ROOT */) { auto ctx = Rando::Context::GetInstance(); GetAccessibleLocationsStruct gals(0); - gals.calculatingAvailableChecks = calculatingAvailableChecks; ResetLogic(ctx, gals, !calculatingAvailableChecks); + if (startingRegion != RR_ROOT) { + gals.regionPool.insert(gals.regionPool.begin(), startingRegion); + + const auto& region = RegionTable(startingRegion); + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) { + region->childDay = true; + } else { + region->adultDay = true; + } + if (region->timePass) { + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) { + region->childNight = true; + } else { + region->adultNight = true; + } + } + } + if (calculatingAvailableChecks) { + logic->Reset(false); + logic->CalculatingAvailableChecks = true; + } do { gals.InitLoop(); for (size_t i = 0; i < gals.regionPool.size(); i++) { @@ -622,9 +645,11 @@ void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAcce RegionTable(RR_ROOT)->adultDay = true; } else if (checkPoeCollectorAccess) { // If we are not shuffling the guard house, add the key so we can properly check for poe merchant access - if (!ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL)) { + if (ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF)) { Rando::StaticData::RetrieveItem(RG_GUARD_HOUSE_KEY).ApplyEffect(); } + RegionTable(RR_ROOT)->adultNight = true; + RegionTable(RR_ROOT)->adultDay = true; } else { ApplyAllAdvancmentItems(); } diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index bb013cc1395..e8124c0dce0 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -34,8 +34,6 @@ struct GetAccessibleLocationsStruct { std::vector itemSphere; std::list entranceSphere; - bool calculatingAvailableChecks = false; - GetAccessibleLocationsStruct(int _maxGsCount){ regionPool = {RR_ROOT}; gsCount = 0; @@ -58,16 +56,17 @@ struct GetAccessibleLocationsStruct { void ClearProgress(); void VanillaFill(); int Fill(); +void SetAreas(); std::vector GetEmptyLocations(std::vector allowedLocations); void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE, bool stopOnBeatable = false, bool addToPlaythrough = false); -std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false); +std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false, RandomizerRegion startingRegion=RR_ROOT); void GeneratePlaythrough(); bool CheckBeatable(RandomizerGet ignore=RG_NONE); -void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); \ No newline at end of file +void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 5d880f04777..0670f913990 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -1458,257 +1458,257 @@ void StaticData::HintTable_Init() { ---------------------------*/ hintTextTable[RHT_JUNK01] = HintText(CustomMessage("They say you must read the names of \"Special Deal\" shop items carefully.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les \"Offres spéciales\" sont parfois trompeuses... Lisez les attentivement!")); hintTextTable[RHT_JUNK02] = HintText(CustomMessage("They say that Zelda is a poor leader.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Zelda ne ferait pas un bon monarque.")); hintTextTable[RHT_JUNK03] = HintText(CustomMessage("These hints can be quite useful. This is an exception.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ces indices sont très utiles, à l'exception de celui-ci.")); hintTextTable[RHT_JUNK04] = HintText(CustomMessage("They say that the Lizalfos in Dodongo's Cavern like to play in lava.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les Lézalfos de la Caverne Dodongo aiment patauger dans la lave.")); hintTextTable[RHT_JUNK05] = HintText(CustomMessage("They say that all the Zora drowned in Wind Waker.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les Zoras se sont noyés dans Wind Waker.")); hintTextTable[RHT_JUNK06] = HintText(CustomMessage("If Gorons eat rocks, does that mean I'm in danger?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ne dis pas au Gorons que je suis ici. Ils mangent des roches, tu sais!")); hintTextTable[RHT_JUNK07] = HintText(CustomMessage("'Member when Ganon was a blue pig?^I 'member.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Dans mon temps, Ganon était un cochon bleu...^Pff! Les jeunes de nos jours, et leur Ganondorf!")); hintTextTable[RHT_JUNK08] = HintText(CustomMessage("One who does not have Triforce can't go in.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ceux sans Triforce doivent rebrousser chemin.")); hintTextTable[RHT_JUNK09] = HintText(CustomMessage("Save your future, end the Happy Mask Salesman.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, tu t'éviteras des jours de malheur si tu vaincs le vendeur de masques...")); hintTextTable[RHT_JUNK10] = HintText(CustomMessage("Glitches are a pathway to many abilities some consider to be... Unnatural.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Les glitchs sont un moyen d'acquérir de nombreuses facultés considérées par certains comme... contraire ")); hintTextTable[RHT_JUNK11] = HintText(CustomMessage("I'm stoned. Get it?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Allez, roche, papier, ciseau...&Roche.")); hintTextTable[RHT_JUNK12] = HintText(CustomMessage("Hoot! Hoot! Would you like me to repeat that?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Hou hou! Veux-tu que je répète tout ça?")); hintTextTable[RHT_JUNK13] = HintText(CustomMessage("Gorons are stupid. They eat rocks.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Les Gorons sont des vraies têtes dures.")); hintTextTable[RHT_JUNK14] = HintText(CustomMessage("They say that Lon Lon Ranch prospered under Ingo.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le Ranch Lon Lon était plus prospère sous Ingo.")); hintTextTable[RHT_JUNK15] = HintText(CustomMessage("They say without the Lens of Truth, the Treasure Chest Mini-Game is a 1 out of 32 chance.^Good luck!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Gagner la Chasse-aux-Trésors est 1 chance sur 32.^Bonne chance!")); hintTextTable[RHT_JUNK16] = HintText(CustomMessage("Use bombs wisely.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Utilise les bombes avec précaution.")); hintTextTable[RHT_JUNK17] = HintText(CustomMessage("They say that players who select the \"ON\" option for \"MOTION CONTROL\" are the real \"Zelda players!\"", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ceux qui utilisent les contrôles gyroscopiques sont les VRAIS joueurs.")); hintTextTable[RHT_JUNK18] = HintText(CustomMessage("L2P @.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Arrête de lire les indices et joue comme un grand, @.")); hintTextTable[RHT_JUNK19] = HintText(CustomMessage("I bet you'd like to have more bombs.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Je parie que tu veux plus de bombes.")); hintTextTable[RHT_JUNK20] = HintText(CustomMessage("When all else fails, use Fire.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Quand rien ne marche, utilise le feu.")); hintTextTable[RHT_JUNK21] = HintText(CustomMessage("Here's a hint, @. Don't be bad.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, la #Triforce# n'est pas dans le jeu... Duh!")); hintTextTable[RHT_JUNK22] = HintText(CustomMessage("Game Over. Return of Ganon.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Partie terminée. RETour de Ganon.")); hintTextTable[RHT_JUNK23] = HintText(CustomMessage("May the way of the Hero lead to the Triforce.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Que le chemin du héros te mène à la Triforce.")); hintTextTable[RHT_JUNK24] = HintText(CustomMessage("Can't find an item? Scan an Amiibo.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tu cherches de quoi? Utilise un Amiibo!")); hintTextTable[RHT_JUNK25] = HintText(CustomMessage("They say this game has just a few glitches.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ce jeu est complètement exempt de glitchs.")); hintTextTable[RHT_JUNK26] = HintText(CustomMessage("BRRING BRRING This is Ulrira. Wrong number?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "DRING DRING!! Pépé le Ramollo à l'appareil... Quoi? Faux numéro?")); hintTextTable[RHT_JUNK27] = HintText(CustomMessage("Tingle Tingle Kooloo Limpah!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tingle! Tingle! Kooloolin... Pah!")); hintTextTable[RHT_JUNK28] = HintText(CustomMessage("L is real 2401", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "L is real 2401")); hintTextTable[RHT_JUNK29] = HintText(CustomMessage("They say that Ganondorf will appear in the next Mario Tennis.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Ganondorf sera la nouvelle recrue dans Mario Tennis.")); hintTextTable[RHT_JUNK30] = HintText(CustomMessage("They say Medigoron sells the earliest Breath of the Wild demo.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Medigoron vend une démo de #Breath of the Wild#.")); hintTextTable[RHT_JUNK31] = HintText(CustomMessage("Can you move me? I don't get great service here.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Peux-tu me déplacer? J'ai pas une bonne réception ici.")); hintTextTable[RHT_JUNK32] = HintText(CustomMessage("They say if you use Strength on the truck, you can find Mew.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #Mew# se trouve dessous le camion... Duh!")); hintTextTable[RHT_JUNK33] = HintText(CustomMessage("I'm a helpful hint Gossip Stone!^See, I'm helping.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Salut! Je suis une pierre de bons conseils!^Tiens, tu vois? J'aide bien, hein?")); hintTextTable[RHT_JUNK34] = HintText(CustomMessage("Dear @, please come to the castle. I've baked a cake for you.&Yours truly, Princess Zelda.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Mon très cher @:&Viens vite au château, je t'ai préparé&un délicieux gâteau...^À bientôt, Princesse Zelda")); hintTextTable[RHT_JUNK35] = HintText(CustomMessage("They say all toasters toast toast.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les grille-pains grillent du pain.")); hintTextTable[RHT_JUNK36] = HintText(CustomMessage("You thought it would be a useful hint, but it was me, junk hint!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tu t'attendais à un bon indice... Mais c'était moi, un mauvais indice!")); hintTextTable[RHT_JUNK37] = HintText(CustomMessage("They say that quest guidance can be found at a talking rock.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, des #indices# se trouvent auprès d'une pierre parlante... Duh!")); hintTextTable[RHT_JUNK38] = HintText(CustomMessage("They say that the final item you're looking for can be found somewhere in Hyrule.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le #dernier objet# se trouve quelque part dans Hyrule... Duh!")); hintTextTable[RHT_JUNK39] = HintText(CustomMessage("Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.")); hintTextTable[RHT_JUNK40] = HintText(CustomMessage("They say that Barinade fears Deku Nuts.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Barinade a la frousse des noix Mojo.")); hintTextTable[RHT_JUNK41] = HintText(CustomMessage("They say that Flare Dancers do not fear Goron-crafted blades.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le danse-flamme n'a pas peur des armes de Goron.")); hintTextTable[RHT_JUNK42] = HintText(CustomMessage("They say that Morpha is easily trapped in a corner.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Morpha est facilement coincé.")); hintTextTable[RHT_JUNK43] = HintText(CustomMessage("They say that Bongo Bongo really hates the cold.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Bongo Bongo a facilement froid aux doigts.")); hintTextTable[RHT_JUNK44] = HintText(CustomMessage("They say that your sword is most powerful when you put it away.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ton épée est à pleine puissance quand tu la rengaines.")); hintTextTable[RHT_JUNK45] = HintText(CustomMessage("They say that bombing the hole Volvagia last flew into can be rewarding.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le trou où se creuse Volvagia est vulnérable aux bombes.")); hintTextTable[RHT_JUNK46] = HintText(CustomMessage("They say that invisible ghosts can be exposed with Deku Nuts.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, des fantômes invisibles apparaissent avec des noix Mojo.")); hintTextTable[RHT_JUNK47] = HintText(CustomMessage("They say that the real Phantom Ganon is bright and loud.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le vrai spectre de Ganon est clair et bruyant.")); hintTextTable[RHT_JUNK48] = HintText(CustomMessage("They say that walking backwards is very fast.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, tu fais marche arrière très rapidement pour un héros.")); hintTextTable[RHT_JUNK49] = HintText(CustomMessage("They say Ingo is not very good at planning ahead.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Ingo ne fait pas un très bon geôlier.")); hintTextTable[RHT_JUNK50] = HintText(CustomMessage("You found a spiritual Stone! By which I mean, I worship Nayru.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Vous avez trouvé une Pierre Ancestrale! En effet, je vénère la déesse Hylia.")); hintTextTable[RHT_JUNK51] = HintText(CustomMessage("Open your eyes.^Open your eyes.^Wake up, @.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Réveille-toi...^Réveille-toi.^Ouvre les yeux, @.")); hintTextTable[RHT_JUNK52] = HintText(CustomMessage("They say that the Nocturne of Shadow can bring you very close to Ganon.", - /*german*/ "", - /*french*/ "Selon moi, le nocturne de l'ombre peut t'amener très près de Ganon.")); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, le Nocturne de l'Ombre peut t'amener très près de Ganon.")); hintTextTable[RHT_JUNK53] = HintText(CustomMessage("They say that Twinrova always casts the same spell the first three times.", - /*german*/ "", - /*french*/ "Selon moi, Twinrova lance toujours les mêmes trois premiers sorts.")); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, le Duo Maléfique lance toujours les mêmes trois premiers sorts.")); hintTextTable[RHT_JUNK54] = HintText(CustomMessage("They say that the nightly builds may be unstable.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les \"nightly builds\" peuvent être instables.")); hintTextTable[RHT_JUNK55] = HintText(CustomMessage("You're playing a Randomizer. I'm randomized!^Here's a random number: #4#.&Enjoy your Randomizer!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tu joues à un randomizer. Je suis aléatoire!^Voici un nombre aléatoire: #4#.&Bonne partie!")); hintTextTable[RHT_JUNK56] = HintText(CustomMessage("They say Ganondorf's bolts can be reflected with glass or steel.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les éclairs de Ganon se reflètent sur l'acier et le verre.")); hintTextTable[RHT_JUNK57] = HintText(CustomMessage("They say Ganon's tail is vulnerable to nuts, arrows, swords, explosives, hammers...^...sticks, seeds, " "boomerangs...^...rods, shovels, iron balls, angry bees...", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, la queue de Ganon est vulnérable aux noix, flèches, épées, bombes, marteaux...^...bâtons, " "graines, boomerangs...^...baguettes, pelles, boulets de fer, abeilles enragées...")); hintTextTable[RHT_JUNK58] = HintText(CustomMessage("They say that you're wasting time reading this hint, but I disagree. Talk to me again!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi... tu sais quoi? Parle-moi encore, et je te le dirai!")); hintTextTable[RHT_JUNK59] = HintText(CustomMessage("They say Ganondorf knows where to find the instrument of his doom.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Ganondorf sait où il a caché son point faible.")); hintTextTable[RHT_JUNK60] = HintText(CustomMessage("I heard @ is pretty good at Zelda.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Apparemment, @ est super bon à Zelda.")); hintTextTable[RHT_JUNK61] = HintText(CustomMessage("Hi @, we've been trying to reach you about your car's extended warranty. ", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Bonjour, @. Vous avez une voiture? Vous savez, nous offrons des assurances abordables...")); hintTextTable[RHT_JUNK62] = HintText(CustomMessage("They say that it's actually possible to beat the running man.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, il est possible de battre le coureur.&Donc, tu prends ton arc, et...")); hintTextTable[RHT_JUNK63] = HintText(CustomMessage("They say this hint makes more sense in other languages.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ces indices auraient pu être mieux traduits... Duh!")); // ^ Junk hints above are from 3drando @@ -1721,36 +1721,36 @@ void StaticData::HintTable_Init() { "Erreur 0x69a504:&Traduction manquante^C'est de la faute à Purple Hato!&J'vous jure!" hintTextTable[RHT_JUNK64] = HintText(CustomMessage("They say Greg is special.", - /*german*/ "", - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, Greg est spécial.")); hintTextTable[RHT_JUNK65] = HintText(CustomMessage("They say the longer the Goron's neck, the wiser they are.", - /*german*/ "", - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, plus le cou des Gorons est long, plus ils sont sage." )); hintTextTable[RHT_JUNK66] = HintText(CustomMessage("They say this ship is what all true gamers strive for.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, cette version du port est ce pour quoi luttent tous les vrais gamers.")); hintTextTable[RHT_JUNK67] = HintText(CustomMessage("They say that Glowsticks can be found in the Raveyard.", - /*german*/ "", - /*french*/ "On peut trouver des Bâtons Lumineux sur le dancefloor du cimetière.")); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, on peut trouver des Bâtons Lumineux sur le dancefloor du cimetière.")); hintTextTable[RHT_JUNK68] = HintText(CustomMessage("They say @'s uncle works for Nintendo.", - /*german*/ "", - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, l'oncle de @ travaille chez Nintendo.")); hintTextTable[RHT_JUNK69] = HintText(CustomMessage("They say pulling all gravestones in the graveyard leads to something magical.", - /*german*/ "", - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, tirer toutes les tombes du Cimetière déclanche un truc magique.")); hintTextTable[RHT_JUNK70] = HintText(CustomMessage("They say holding L while pausing makes you win the game.", - /*german*/ "", - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, maintenir L pendant que vous appuyez sur START vous permet de terminer le jeu.")); hintTextTable[RHT_JUNK71] = HintText(CustomMessage("They say @'s body is ready.", - /*german*/ "", - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, ce junk hint ne se traduirait pas bien en français.")); /*-------------------------- | DUNGEON HINT TEXT | @@ -1976,27 +1976,27 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_BRIDGE_OPEN_HINT] = HintText(CustomMessage("$lThe awakened ones have #already created a bridge# to the castle where the evil dwells.^", /*german*/ "$lDie Weisen haben #bereits&eine Brücke zum Portal von&Ganons Schloß gelegt#...^", /*french*/ "$lLes êtres de sagesse ont#déjà créé un pont# vers le repaire du mal.^", - {QM_LBLUE})); + {QM_LBLUE}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$lLos sabios #ya habrán creado un puente#&al castillo, de donde emana el mal.^ hintTextTable[RHT_BRIDGE_VANILLA_HINT] = HintText(CustomMessage("$6The awakened ones require the #Shadow and Spirit Medallions# as well as the #Light Arrows#.^", /*german*/ "$6Die Weisen werden darauf warten, daß der Held das #Amulett des Schattens, Amulett der Geister# und die #Licht-Pfeile# sammelt.^", /*french*/ "$6Les êtres de sagesse attendront le héros muni des #Médaillons de l'Ombre et l'Esprit# et des #Flèches de Lumière#.^", - {QM_RED, QM_YELLOW})); + {QM_RED, QM_YELLOW}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$6Los sabios aguardarán a que el héroe obtenga tanto el #Medallón de las Sombras y el del Espíritu# junto // a la #flecha de luz#.^ hintTextTable[RHT_BRIDGE_STONES_HINT] = HintText(CustomMessage("$0The awakened ones will await for the Hero to collect #[[d]] Spiritual Stone||s|#.^", /*german*/ "$0Die Weisen werden darauf warten, daß der Held #[[d]] |Heiligen Stein|Heilige Steine|# sammelt.^", /*french*/ "$0Les êtres de sagesse attendront le héros muni de #[[d]] |Pierre Ancestrale|Pierres Ancestrales|#.^", - {QM_BLUE})); + {QM_BLUE}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$0Los sabios aguardarán a que el héroe&obtenga #[[d]] |piedra espiritual|piedras espirituales|#.^ hintTextTable[RHT_BRIDGE_MEDALLIONS_HINT] = HintText(CustomMessage("$8The awakened ones will await for the Hero to collect #[[d]] Medallion||s|#.^", /*german*/ "$8Die Weisen werden darauf warten, daß der Held #[[d]] Amulett||e|# sammelt.^", /*french*/ "$8Les êtres de sagesse attendront le héros muni de #[[d]] Médaillon||s|#.^", - {QM_RED})); + {QM_RED}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$8Los sabios aguardarán a que el héroe&obtenga #[[d]] |medallón|medallones|#.^ hintTextTable[RHT_BRIDGE_REWARDS_HINT] = HintText(CustomMessage("$CThe awakened ones will await for the Hero to collect #[[d]]# |#Spiritual Stone# or #Medallion#|" @@ -2005,24 +2005,24 @@ void StaticData::HintTable_Init() { "#Heilige Steine# oder #Amulette#| sammelt.^", /*french*/ "$CLes êtres de sagesse attendront le héros muni de #[[d]]# |#Pierre Ancestrale# ou #Médaillon#" "|#Pierres Ancestrales# ou #Médaillons#|.^", - {QM_YELLOW, QM_BLUE, QM_RED})); + {QM_YELLOW, QM_BLUE, QM_RED}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$CLos sabios aguardarán a que el héroe obtenga #[[d]]# |#piedra espiritual# o #medallón#| //#piedras espirtuales# y #medallones#|.^ hintTextTable[RHT_BRIDGE_DUNGEONS_HINT] = HintText(CustomMessage("$mThe awakened ones will await for the Hero to conquer #[[d]] Dungeon||s|#.^", /*german*/ "$mDie Weisen werden darauf warten, daß der Held #[[d]] Labyrinth||e|# abschließt.^", /*french*/ "$mLes êtres de sagesse attendront la conquête de #[[d]] Donjon||s|#.^", - {QM_PINK})); + {QM_PINK}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$mLos sabios aguardarán a que el héroe complete #[[d]] mazmorra||s|#.^ hintTextTable[RHT_BRIDGE_TOKENS_HINT] = HintText(CustomMessage("$sThe awakened ones will await for the Hero to collect #[[d]] Gold Skulltula Token||s|#.^", /*german*/ "$sDie Weisen werden darauf warten, daß der Held #[[d]] Skulltula-Symbol||e|# sammelt.^", /*french*/ "$sLes êtres de sagesse attendront le héros muni de #[[d]] Symbole||s| de Skulltula d'or#.^", - {QM_YELLOW})); + {QM_YELLOW}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$sLos sabios aguardarán a que el héroe obtenga #[[d]] símbolo||s| de skulltula dorada#.^ - hintTextTable[RHT_BRIDGE_GREG_HINT] = HintText(CustomMessage("$gThe awakened ones will await for the Hero to find #Greg#.^", - {QM_GREEN})); + hintTextTable[RHT_BRIDGE_GREG_HINT] = HintText(CustomMessage("$gThe awakened ones will await for the Hero to find #Greg#.^", TODO_TRANSLATE, TODO_TRANSLATE, + {QM_GREEN}, {}, TEXTBOX_TYPE_BLUE)); /*-------------------------- @@ -2176,22 +2176,22 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_CHILD_ALTAR_STONES] = HintText(CustomMessage("3 Spiritual Stones found in Hyrule...^$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^", /*german*/ "Drei Heilige Steine, zu finden in Hyrule...$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^", /*french*/ "Les trois Pierres Ancestrales cachées&dans Hyrule...$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^", - {QM_GREEN, QM_RED, QM_BLUE}, {true, true, true})); + {QM_GREEN, QM_RED, QM_BLUE}, {true, true, true}, TEXTBOX_TYPE_BLUE)); // /*spanish*/ Tres piedras espirituales halladas por Hyrule...$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^ hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTOPEN] = HintText(CustomMessage("$oYe who may become a Hero...&The path to the future is open...", /*german*/ "$oJener auf dem Weg des Helden...&Der Pfad zur Zukunft sei geöffnet...", - /*french*/ "$oÀ celui qui a quête de devenir&héros...&Le futur vous accueille béant...")); + /*french*/ "$oÀ celui qui a quête de devenir&héros...&Le futur vous accueille béant...", TEXTBOX_TYPE_BLUE)); // /*spanish*/$oPara aquel que se convierta en el héroe...&La puerta al futuro está a su disposición... hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTSONGONLY] = HintText(CustomMessage("$cYe who may become a Hero...&Stand with the Ocarina and&play the Song of Time.", /*german*/ "$cJener auf dem Weg des Helden...&Nehme er seine Okarina zur Hand und&spiele hier die Hymne der Zeit.", - /*french*/ "$cÀ celui qui a quête de devenir&héros...&Portez l'Ocarina et jouez&le chant du temps.")); + /*french*/ "$cÀ celui qui a quête de devenir&héros...&Portez l'Ocarina et jouez&le chant du temps.", TEXTBOX_TYPE_BLUE)); // /*spanish*/$cPara aquel que se convierta en el héroe...&Tome la ocarina y&entone la Canción del Tiempo. hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTCLOSED] = HintText(CustomMessage("$iYe who may become a Hero...&Offer the spiritual stones and&play the Song of Time.", /*german*/ "$iJener mit den drei Heiligen Steinen&nehme seine Okarina zur Hand und&spiele hier die Hymne der Zeit.", - /*french*/ "$iÀ celui qui a quête de devenir&héros... Présentez les Pierres&Ancestrales et jouez&le chant du temps.")); + /*french*/ "$iÀ celui qui a quête de devenir&héros... Présentez les Pierres&Ancestrales et jouez&le chant du temps.", TEXTBOX_TYPE_BLUE)); // /*spanish*/$iPara aquel que se convierta en el héroe...&Tome las piedras espirituales y&entone la Canción del Tiempo. hintTextTable[RHT_ADULT_ALTAR_MEDALLIONS] = HintText(CustomMessage("An awakening voice from the Sacred Realm will call those destined to be Sages, who dwell in the #five temples#.^" @@ -2214,27 +2214,27 @@ void StaticData::HintTable_Init() { ---------------------------*/ hintTextTable[RHT_GANONDORF_HINT_LA_ONLY] = HintText(CustomMessage("Ha ha ha... You'll never beat me by reflecting my lightning bolts and unleashing the arrows from #[[1]]#!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ha ha ha... Pauvre fou! Tu ne pourras jamais me vaincre sans les flèches que j'ai cachées dans #[[1]]#!", {QM_RED})); // /*spanish*/Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de #[[1]]#! hintTextTable[RHT_GANONDORF_HINT_MS_ONLY] = HintText(CustomMessage("Ha ha ha... You'll never defeat me, drop a castle on me and finish me off with the sacred blade from #[[2]]#!", - /*german*/ "", - /*french*/ "", + /*german*/ TODO_TRANSLATE, + /*french*/ "Ha ha ha... Pauvre fou! Tu ne pourras jamais me vaincre sans l'Épée de Légende cachée dans #[[2]]#!", {QM_RED})); hintTextTable[RHT_GANONDORF_HINT_LA_AND_MS] = HintText(CustomMessage("Ha ha ha... You'll never beat me by reflecting my lightning bolts and unleashing the arrows from #[[1]]#!" "^And even if you do, you'll never find the legendary blade hidden in #[[2]]#!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ha ha ha... Pauvre fou! Tu ne pourras jamais me vaincre sans les flèches que j'ai cachées dans #[[1]]#!" - "^Et même si tu les trouves, tu ne touveras jamais l'épée de légende cachée dans #[[2]]#!", + "^Et même si tu les trouves, tu ne touveras jamais l'Épée de Légende cachée dans #[[2]]#!", {QM_RED, QM_RED})); // /*spanish*/Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de #[[1]]#! // ^E incluso si lo haces, nunca encontrarás la espada legendaria escondida en #[[2]]#! hintTextTable[RHT_SHEIK_HINT_LA_ONLY] = HintText(CustomMessage("I overheard Ganondorf say that he misplaced the #Light Arrows# in #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "J'ai entendu dire que Ganondorf aurait caché les #Flèches de Lumière# dans #[[1]]#.", {QM_YELLOW, QM_RED})); @@ -2244,17 +2244,17 @@ void StaticData::HintTable_Init() { {QM_RED, QM_RED})); hintTextTable[RHT_GREG_HINT] = HintText(CustomMessage("By the way, if you're interested, I saw the shiniest #Green Rupee# somewhere in #[[1]]#.^It's said to have #mysterious powers#...^But then, it could just be another regular rupee.&Oh well.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Au fait, si ça t'intéresse, j'ai aperçu le plus éclatant des #Rubis Verts# quelque part à #[[1]]#. On dit qu'il possède des pouvoirs mystérieux... Mais bon, ça pourrait juste être un autre rubis ordinaire.",//RANDOTODO color in mysterious powers {QM_GREEN, QM_RED, QM_RED})); hintTextTable[RHT_SARIA_TALK_HINT] = HintText(CustomMessage("Did you feel the #surge of magic# recently? A mysterious bird told me it came from #[[1]]#.^You should check that place out, @!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "As-tu récemment ressenti une vague de #puissance magique#? Un mystérieux hibou m'a dit qu'elle provenait du #[[1]]#. Tu devrais aller y jeter un coup d'oeil, @!", {QM_GREEN, QM_RED})); hintTextTable[RHT_SARIA_SONG_HINT] = HintText(CustomMessage("Did you feel the #surge of magic# recently? A mysterious bird told me it came from #[[1]]#.^You should check that place out, @!\x0B", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "As-tu récemment ressenti une vague de #puissance magique#? Un mystérieux hibou m'a dit qu'elle provenait du #[[1]]#. Tu devrais aller y jeter un coup d'oeil, @!\x0B", {QM_GREEN, QM_RED}, {}, TEXTBOX_TYPE_BLUE)); @@ -2339,33 +2339,33 @@ void StaticData::HintTable_Init() { ---------------------------*/ hintTextTable[RHT_GANON_JOKE01] = HintText(CustomMessage("Oh! It's @.&I was expecting someone called Sheik.&Do you know what happened to them?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ah, c'est @.&J'attendais un certain Sheik.&Tu sais ce qui lui est arrivé?")); // /*spanish*/¡Oh! Pero si es @.&Estaba esperando a alguien llamado Sheik. ¿Sabes qué puede haberle pasado? hintTextTable[RHT_GANON_JOKE02] = HintText(CustomMessage("I knew I shouldn't have put the key on the other side of my door.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "J'aurais dû garder la clé ici. Hélas...")); // /*spanish*/Sabía que no tendría que haber dejado la llave al otro lado de la puerta. hintTextTable[RHT_GANON_JOKE03] = HintText(CustomMessage("Looks like it's time for a round of tennis.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "C'est l'heure de jouer au tennis.")); // /*spanish*/Parece que es hora de una pachanga de tenis. hintTextTable[RHT_GANON_JOKE04] = HintText(CustomMessage("You'll never deflect my bolts of energy with your sword, then shoot me with those Light Arrows you happen to have.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ne perds pas ton temps à frapper mes éclairs d'énergie avec ton épée et me tirer avec tes flèches de Lumière!")); // /*spanish*/Nunca reflejarás mis esferas de energía con tu espada, para después dispararme con las flechas de luz que tendrás. hintTextTable[RHT_GANON_JOKE05] = HintText(CustomMessage("Why did I leave my trident back in the desert?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Sale bêtise... Et j'ai oublié mon trident dans le désert!")); // /*spanish*/Santa Hylia... ¿Por qué me habré dejado el tridente en el desierto? hintTextTable[RHT_GANON_JOKE06] = HintText(CustomMessage("Zelda is probably going to do something stupid, like send you back to your own timeline.^So this is " "quite meaningless. Do you really want to save this moron?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Même si je suis vaincu... Zelda te renverra dans ton ère, et je reviendrai conquérir!^Telle est la " "prophécie d'Hyrule Historia!")); // /*spanish*/Seguro que Zelda trata de hacer alguna tontería, como enviarte de vuelta a tu línea temporal.^No tiene @@ -2373,30 +2373,30 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_GANON_JOKE07] = HintText(CustomMessage("What about Zelda makes you think&she'd be a better ruler than I?^I saved Lon Lon Ranch,&fed the " "hungry,&and my castle floats.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Zelda ne sera jamais un meilleur monarque que moi!^J'ai un château volant, mes sujets sont des belles " "amazones... et mes Moblins sont clairement plus puissants que jamais!")); // /*spanish*/¿Qué te hace pensar que Zelda gobierna mejor que yo?^Yo he salvado el Rancho Lon Lon,&he alimentado a // los hambrientos&y hasta hago que mi castillo flote. hintTextTable[RHT_GANON_JOKE08] = HintText(CustomMessage("I've learned this spell,&it's really neat,&I'll keep it later&for your treat!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Gamin, ton destin achève,&sous mon sort tu périras!&Cette partie ne fut pas brève,&et cette mort, tu subiras!")); // /*spanish*/Veamos ahora que harás,&la batalla ha de comenzar,&te enviaré de una vez al más allá,&¿listo para // afrontar la verdad? hintTextTable[RHT_GANON_JOKE09] = HintText(CustomMessage("Many tricks are up my sleeve,&to save yourself&you'd better leave!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Sale petit garnement,&tu fais erreur!&C'est maintenant que marque&ta dernière heure!")); // /*spanish*/¿No osarás a mí enfrentarte?&Rimas aparte,&¡voy a matarte! hintTextTable[RHT_GANON_JOKE10] = HintText(CustomMessage("After what you did to Koholint Island, how can you call me the bad guy?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "J'admire ce que tu as fait à l'Île Koholint... Toi et moi, nous devrions faire équipe!")); // /*spanish*/Después de lo que le hiciste a la Isla Koholint, ¿cómo te atreves a llamarme malvado? hintTextTable[RHT_GANON_JOKE11] = HintText(CustomMessage("Today, let's begin down&'The Hero is Defeated' timeline.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Si tu me vaincs, Hyrule sera englouti... mais si tu meurs, on aura A Link to the Past, le meilleur opus " "de la série!")); // /*spanish*/Hoy daremos lugar a la línea temporal del Héroe Derrotado.&¡Prepárate para el culmen de esta saga! @@ -2412,13 +2412,13 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_ISOLATED_PLACE] = HintText(CustomMessage("an Isolated Place")); - hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage(" It's ordinary.", - /*german*/ "&Sieht aus wie immer.", - /*french*/ "&Elle vous semble %rordinaire%w.")); + hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage("&It's %gordinary%w.", + /*german*/ "&Sieht aus %gwie immer%w.", + /*french*/ "&Elle vous semble %gordinaire%w.")); - hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage(" It's masterful!", - /*german*/ "&Man kann darauf die Worte&%r\"Master Quest\"%w entziffern...", - /*french*/ "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus.")); + hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage("&It's %rmasterful%w!", + /*german*/ "&Man kann darauf die Worte %r\"Master_Quest\"%w entziffern...", + /*french*/ "&Étrange... les mots %r\"Master_Quest\"%w sont gravés dessus.")); // clang-format on } diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index 1347945ec3a..75072e72a24 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -124,11 +124,11 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DEKU_TREE_GRASS] = HintText(CustomMessage("They say that some #grass in the Deku Tree# hides #[[1]]#.", - /*german*/ "", - /*french*/ "", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß etwas #Gras im Deku-Baum# #[[1]]# verstecke.", + /*french*/ "Selon moi, de l'#herbe dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_DEKU_TREE] = HintText(CustomMessage("They say that a #crate in the Deku Tree# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste im Deku-Baum# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans l'Arbre Mojo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -303,14 +303,14 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { hintTextTable[RHT_DODONGOS_CAVERN_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in Dodongo's Cavern# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein in Dodongos Höhle# #[[1]]# enthülle.", - /*french*/ "Selon moi, #jouer un air orageux pour une pierre étrange dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DODONGOS_CAVERN_GRASS] = HintText(CustomMessage("They say that some #grass in Dodongo's Cavern# hides #[[1]]#.", - /*german*/ "!!!", - /*french*/ "", {QM_RED, QM_GREEN})); - + /*german*/ "Man erzählt sich, daß etwas #Gras in Dodongos Höhle# #[[1]]# verstecke.", + /*french*/ "Selon moi, de l'#herbe dans la Caverne Dodongo# cache #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_CRATE_DODONGOS_CAVERN] = HintText(CustomMessage("They say that a #crate in Dodongo's Cavern# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste in Dodongos Höhle# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans la Caverne Dodongo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -473,11 +473,11 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, près d'un #ascenceur central dans le Ventre de Jabu-Jabu# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_JABU_JABUS_BELLY_GRASS] = HintText(CustomMessage("They say that some #grass in Jabu Jabu's Belly# hides #[[1]]#.", - /*german*/ "", - /*french*/ "", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß etwas #Gras in Jabu-Jabus Bauch# #[[1]]# verstecke.", + /*french*/ "Selon moi, de l'#herbe dans le Ventre de Jabu-Jabu# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_JABU_JABU] = HintText(CustomMessage("They say that a #crate in Jabu Jabu's Belly# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste in Jabu-Jabus Bauch# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Ventre de Jabu-Jabu# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -706,12 +706,12 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Vase im Waldtempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Temple de la Fôret# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_FOREST_TEMPLE_HEART] = HintText(CustomMessage("They say that a #heart in the Forest Temple# hides #[[1]]#.", - /*german*/ "Man erzählt sich, daß ein #Herz im Waldtempel# #[[1]]# verstecke.", - /*french*/ "Selon moi, un #coeur dans le Temple de la Fôret# cache #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_FOREST_TEMPLE_HEART] = HintText(CustomMessage("They say that a #heart in the Forest Temple# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Herz im Waldtempel# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #coeur dans le Temple de la Fôret# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_FOREST_TEMPLE] = HintText(CustomMessage("They say that a #crate in Forest Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste im Waldtempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de la Fôret# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -911,15 +911,15 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Temple du Feu# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_FIRE_TEMPLE_MQ_LOOP_STALFOS_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a hot arena# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer heißen Arena# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil dans une arène chaude# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_FIRE_TEMPLE_MQ_LOOP_KNUCKLE_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun behind a knight's throne in a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne hinter dem Thron eines Ritters in einem Vulkan# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil derrière le trône d’un chevalier dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_FIRE_TEMPLE] = HintText(CustomMessage("They say that a #crate in Fire Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste im Feuertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple du Feu# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1058,25 +1058,25 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, une #rivière dans le Temple de l'Eau# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_PILAR_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun atop a small pillar before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne auf einer kleinen Säule vor einem Duell mit seinem Schatten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil au sommet d’un petit pilier avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_LEFT_STORM_FAIRY] = HintText(CustomMessage("They say that #calling the rain before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens vor einem Duell mit seinem Schatten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler la pluie avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_RIGHT_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne vor einem Duell mit seinem Schatten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_WATER_TEMPLE] = HintText(CustomMessage("They say that a #crate in Water Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste im Wassertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Eau# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- | SPIRIT TEMPLE | ---------------------------*/ - hintTextTable[RHT_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST] = HintText(CustomMessage("They say that a child conquers a #skull in green fire# in the Spirit Temple to reach #[[1]]#.", + hintTextTable[RHT_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST] = HintText(CustomMessage("They say that a child conquers a #skull in green fire# in the Spirit Temple to reach #[[1]]#.", /*german*/ "Man erzählt sich, daß ein Kind einen #Schädel in grünem Feuer# im Geistertempel erobere, um #[[1]]# zu erreichen.", /*french*/ "Selon moi, le #crâne au halo vert dans le colosse# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, el joven que #baje el puente# del Templo del Espíritu encontrará #[[1]]#. @@ -1311,15 +1311,15 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, une #jarre dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SPIRIT_TEMPLE_HEART] = HintText(CustomMessage("They say that on a #small platform# in the Spirit Temple lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß auf einer #kleinen Plattform# im Geistertempel #[[1]]# läge.", /*french*/ "Selon moi, sur une #petite plateforme# dans le Temple de l'Esprit repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SPIRIT_TEMPLE_MQ_HEART] = HintText(CustomMessage("They say that guarded by a #ring of flame# in the Spirit Temple is #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß sich bewacht von einem #Ring der Flammen# im Geistertempel #[[1]]# befände.", /*french*/ "Selon moi, protégé par un #cercle de flammes# dans le Temple de l'Esprit se trouve #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_SPIRIT_TEMPLE] = HintText(CustomMessage("They say that a #crate in Spirit Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste im Geistertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1574,19 +1574,19 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Temple de l'Ombre# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY] = HintText(CustomMessage("They say that an #calling the rain for a sentry guarding a house of the dead# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens für einen über ein Haus der Toten wachenden Posten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler la pluie pour une sentinelle gardant une maison des morts# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_PIT_STORM_FAIRY] = HintText(CustomMessage("They say that an #calling the rain on a platform suspended above a bottomless pit# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens auf einer über einer bodenlosen Grube hängenden Plattform# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler la pluie sur une plateforme suspendue au-dessus d’un gouffre sans fond# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_WIND_HINT_SUN_FAIRY] = HintText(CustomMessage("They say that an #calling the sun near an invisible chest guarded by the dead# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne nahe einer von Toten bewachten unsichtbaren Truhe# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil près d’un coffre invisible gardé par les morts# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_SHADOW_TEMPLE] = HintText(CustomMessage("They say that a #crate in Shadow Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste im Schattentempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Ombre# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1716,7 +1716,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Puits# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_BOTTOM_OF_THE_WELL_RUPEE] = HintText(CustomMessage("They say that a #hidden path through the floor# in the well leads to #[[1]]#.", - /*german*/ "Man erzählt sich, daß ein #versteckter Pfad durch den Grund# im Brunnen zu #[[1]]# führe.", + /*german*/ "Man erzählt sich, daß ein #versteckter Pfad durch den Boden# im Brunnen zu #[[1]]# führe.", /*french*/ "Selon moi, un #passage caché sous le sol# dans le puits mène à #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_BOTTOM_OF_THE_WELL_BASEMENT_SUN_FAIRY] = HintText(CustomMessage("They say that an #calling the sun a dead end# within the well reveals #[[1]]#.", @@ -1732,8 +1732,8 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, #appeler le soleil dans une impasse# à l’intérieur du puits révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_BOTTOM_OF_THE_WELL_GRASS] = HintText(CustomMessage("They say that some #grass in the Bottom of the Well# hides #[[1]]#.", - /*german*/ "", - /*french*/ "", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß etwas #Gras auf dem Grund des Brunnens# #[[1]]# verstecke.", + /*french*/ "Selon moi, de l'#herbe dans le Puits# cache #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1815,7 +1815,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { hintTextTable[RHT_ICE_CAVERN_HEART] = HintText(CustomMessage("They say that atop on a #frozen pillar# lies #[[1]]#.", /*german*/ "Man erzählt sich, daß auf einer #gefrorenen Säule# #[[1]]# läge.", - /*french*/ "Selon moi, ", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, le dessus d'un #pilier de glace# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ICE_CAVERN_RUPEE] = HintText(CustomMessage("They say that a #rupee in a frozen cavern# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Rubin in einer gefrorenen Kaverne# #[[1]]# verstecke.", @@ -2013,7 +2013,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, dans #une épreuve avec un Dinolfos# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDO_TRAINING_GROUND] = HintText(CustomMessage("They say that a #crate in Gerudo Training Ground# contains #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß eine #Kiste auf dem Gerudo-Trainingsgelände# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Gymnase Gerudo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 11d89d8377f..754709f9729 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -6,7 +6,7 @@ namespace Rando { void StaticData::HintTable_Init_Exclude_Overworld() { // clang-format off - hintTextTable[RHT_KF_KOKIRI_SWORD_CHEST] = HintText(CustomMessage("They say that the #hidden treasure of the Kokiri# is #[[1]]#.", + hintTextTable[RHT_KF_KOKIRI_SWORD_CHEST] = HintText(CustomMessage("They say that the #hidden treasure of the Kokiri# is #[[1]]#.", /*german*/ "Man erzählt sich, daß der #versteckte Schatz der Kokiri# #[[1]]# sei.", /*french*/ "Selon moi, le #trésor des Kokiri# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, el #tesoro oculto de los Kokiri# esconde #[[1]]#. @@ -313,11 +313,11 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*german*/ "Man erzählt sich, daß ein #Tauchexperiment# mit #[[1]]# belohnt würde.", /*french*/ "Selon moi, l'#expérience de plongée# donne #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, #bucear para un experimento# se premia con #[[1]]#. - // RANDOTODO: needs translation + hintTextTable[RHT_ZD_FISH] = HintText(CustomMessage("They say that a #fish by a waterfall# hoards #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Fisch nahe eines Wasserfalls# #[[1]]# horte.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); - // /*spanish*/ Según dicen, #[[1]]#. + /*french*/ "Selon moi, un #poisson près d'une cascade# a #[[1]]#.", {QM_RED, QM_GREEN})); + // /*spanish*/ Según dicen, un #pez junto a una cascada# otorga #[[1]]#. hintTextTable[RHT_GC_ROLLING_GORON_AS_ADULT] = HintText(CustomMessage("They say that #reassuring a young Goron# is rewarded with #[[1]]#.", @@ -1344,12 +1344,12 @@ void StaticData::HintTable_Init_Exclude_Overworld() { hintTextTable[RHT_SFM_DEKU_SCRUB_GROTTO_REAR] = HintText(CustomMessage("They say that a #scrub underground duo# sells #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Deku-Paar im Untergrund# #[[1]]# verkaufe.", - /*french*/ "Selon moi, le #duo de peste Mojo au cœur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, le #duo de peste Mojo au coeur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, un #par de dekus subterráneos# de la pradera sagrada venden #[[1]]#. hintTextTable[RHT_SFM_DEKU_SCRUB_GROTTO_FRONT] = HintText(CustomMessage("They say that a #scrub underground duo# sells #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Deku-Paar im Untergrund# #[[1]]# verkaufe.", - /*french*/ "Selon moi, le #duo de peste Mojo au cœur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, le #duo de peste Mojo au coeur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, un #par de dekus subterráneos# de la pradera sagrada venden #[[1]]#. hintTextTable[RHT_GC_DEKU_SCRUB_GROTTO_LEFT] = HintText(CustomMessage("They say that a #trio of scrubs# sells #[[1]]#.", @@ -1459,42 +1459,50 @@ void StaticData::HintTable_Init_Exclude_Overworld() { hintTextTable[RHT_BEEHIVE_CHEST_GROTTO] = HintText(CustomMessage("They say that a #beehive above a chest# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb einer Truhe# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'un coffre# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un cofre# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_LONELY_SCRUB_GROTTO] = HintText(CustomMessage("They say that a #beehive above a lonely scrub# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb eines einsamen Deku# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'une Peste Mojo solitaire# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un deku solitario# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_SCRUB_PAIR_GROTTO] = HintText(CustomMessage("They say that a #beehive above a pair of scrubs# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb eines Deku-Paars# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'un duo de Pestes Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un par de dekus# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_SCRUB_TRIO_GROTTO] = HintText(CustomMessage("They say that a #beehive above a trio of scrubs# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb eines Deku-Trios# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'un trio de Pestes Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un trío de dekus# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_COW_GROTTO] = HintText(CustomMessage("They say that a #beehive above a cow# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb einer Kuh# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'une vache# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre una vaca# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_IN_FRONT_OF_KING_ZORA] = HintText(CustomMessage("They say that a #beehive in front of the king of the zoras# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock vor dem Zora-König# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche devant le Roi des Zoras# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena delante del rey de los zoras# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_BEHIND_KING_ZORA] = HintText(CustomMessage("They say that a #beehive behind the king of the zoras# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock hinter dem Zora-König# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #rucheau derrière le Roi des Zoras# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena detrás del rey de los zoras# esconde #[[1]]#. - hintTextTable[RHT_POT_KOKIRI_FOREST] = HintText(CustomMessage("They say that a #pot in Kokiri Forest# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_LINKS_HOUSE] = HintText(CustomMessage("They say that the #pot in the hero's house# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", //TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_TWINS_HOUSE] = HintText(CustomMessage("They say that a #pot shared by twins# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", //TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_KNOW_IT_ALL] = HintText(CustomMessage("They say that a #know-it-all bother's pot# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", //TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE update to match hintTextTable[RHT_POT_GERUDO_FORTRESS] = HintText(CustomMessage("They say that a #pot in Gerudo Fortress# contains #[[1]]#.", /*german*/ "Man erzählt sich, daß eine #Vase in der Gerudo-Festung# #[[1]]# enthielte.", @@ -1504,15 +1512,24 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*german*/ "Man erzählt sich, daß eine #Vase in der Gespensterwüste# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Désert Hanté# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_POT_MARKET] = HintText(CustomMessage("They say that a #pot in Market# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_GUARD_HOUSE] = HintText(CustomMessage("They say that a #pot in the Guard House# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_POE_SHOP] = HintText(CustomMessage("They say that a #pot in the Poe Shop# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_ALLY_HOUSE] = HintText(CustomMessage("They say that a #bearded man's pot# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match hintTextTable[RHT_POT_KAKARIKO] = HintText(CustomMessage("They say that a #pot in Kakariko Village# contains #[[1]]#.", /*german*/ "Man erzählt sich, daß eine #Vase in Kakariko# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Village de Cocorico# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_POT_GRAVEYARD] = HintText(CustomMessage("They say that a #pot in Graveyard# contains #[[1]]#.", + hintTextTable[RHT_POT_DAMPE] = HintText(CustomMessage("They say that a #pot in gravekeeper's tomb# contains #[[1]]#.", + //TODO_TRANSLATE check these to make sure they refernce dampe's tomb not the graveyard area /*german*/ "Man erzählt sich, daß eine #Vase auf dem Friedhof# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Cimetière# contient #[[1]]#.", {QM_RED, QM_GREEN})); @@ -1536,13 +1553,17 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*german*/ "Man erzählt sich, daß eine #Vase auf der Lon Lon-Farm# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Ranch Lon Lon# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_POT_HYRULE_FIELD] = HintText(CustomMessage("They say that a #pot in Hyrule Field# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase in der Hylianischen Steppe# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans la Plaine d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_TALONS_HOUSE] = HintText(CustomMessage("They say that a #pot in Talon's Bedroom# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf der Lon Lon-Farm# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans le Ranch Lon Lon# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_WEB_GROTTO] = HintText(CustomMessage("They say that a #pot behind a grotto's webbing# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase in der Hylianischen Steppe# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Plaine d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match - hintTextTable[RHT_POT_HYRULE_CASTLE] = HintText(CustomMessage("They say that a #pot in Hyrule Castle# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase in Schloß Hyrule# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans le Château d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_MUD_WALL_GROTTO] = HintText(CustomMessage("They say that a #pot walled off in a grotto# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase in Schloß Hyrule# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans le Château d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match hintTextTable[RHT_KOKIRI_FOREST_RUPEE] = HintText(CustomMessage("They say that a rupee in a #tranquil forest# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein Rubin in einem #ruhigen Wald# #[[1]]# verstecke.", @@ -1557,463 +1578,501 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*french*/ "Selon moi, un cœur dans la #maison d'une amie chère# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LOST_WOODS_RUPEE] = HintText(CustomMessage("They say that under a #boulder in the woods# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß unter einem #Felsen in den Wäldern# #[[1]]# läge.", /*french*/ "Selon moi, sous un #rocher dans les bois# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LOST_WOODS_SHORTCUT_RUPEE] = HintText(CustomMessage("They say that in a #pool of water in the woods# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß in einem #Wasserbecken in den Wäldern# #[[1]]# läge.", /*french*/ "Selon moi, dans une #mare d'eau dans les bois# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LAKE_HYLIA_RUPEE] = HintText(CustomMessage("They say that just off the #coast of a lake# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß an der #Küste eines Sees# #[[1]]# läge.", /*french*/ "Selon moi, juste au large de la #côte d'un lac# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LABORATORY_RUPEE] = HintText(CustomMessage("They say that at the #bottom of a tank# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß auf dem #Grund eines Reservoirs# #[[1]]# läge.", /*french*/ "Selon moi, au #fond d'un réservoir# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DAMPES_GRAVE_RUPEE] = HintText(CustomMessage("They say that within a #quick-footed spirit's grave# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß innerhalb eines #Grabes eines leichtfüßigen Geistes# #[[1]]# läge.", /*french*/ "Selon moi, dans la #tombe d'un esprit à pieds rapides# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_GERUDO_VALLEY_GROTTO_RUPEE] = HintText(CustomMessage("They say that an Octarok in an #underground spring# guards #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß ein Oktorok in einer #untergründigen Quelle# #[[1]]# bewache.", /*french*/ "Selon moi, un Octrocher dans une #source souterraine# garde #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_RUPEE] = HintText(CustomMessage("They say that beneath a boulder on a #mountain's cliffside# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß unterhalb eines Felsens an einem #abfallenden Felswand eines Berges# #[[1]]# läge.", /*french*/ "Selon moi, sous un rocher sur la #falaise d'une montagne# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_GROTTO_HEART] = HintText(CustomMessage("They say that accompanying a #cow in a small cave# is #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß unterhalb eines Felsens an einem #abfallenden Felswand eines Berges# #[[1]]# läge.", /*french*/ "Selon moi, accompagner une #vache dans une petite grotte# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_GROTTO_RUPEE] = HintText(CustomMessage("They say that accompanying a #cow in a small cave# is #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß das Begleiten einer #Kuh in einer kleinen Höhle# #[[1]]# sei.", /*french*/ "Selon moi, accompagner une #vache dans une petite grotte# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_CRATER_RUPEE] = HintText(CustomMessage("They say that on a #small platform suspended above lava# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß auf einer #oberhalb von Lava hängenden kleinen Plattform# #[[1]]# läge.", /*french*/ "Selon moi, sur une #petite plateforme suspendue au-dessus de la lave# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_ZORAS_RIVER_WATERFALL_RUPEE] = HintText(CustomMessage("They say that beneath a #waterfall feeding a narrow river# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß unterhalb eines #einen schmalen Fluss nährenden Wasserfall# #[[1]]# läge.", /*french*/ "Selon moi, sous une #cascade alimentant une rivière étroite# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_ZORAS_FOUNTAIN_RUPEE] = HintText(CustomMessage("They say that at the bottom of a #partially-frozen spring# lies #[[1]]#.", - /*german*/ "", + /*german*/ "Man erzählt sich, daß auf dem Grund einer #teilweise gefrorenen Quelle# #[[1]]# läge.", /*french*/ "Selon moi, au fond d'une #source partiellement gelée# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_SFM_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a forest meadow# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter einer Waldwiese# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous un bosquet forestier# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a river# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter einem Fluß# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous une rivière# repose #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a a few trees bordering a wide field# rests #[[1]]#.", - /*german*/ "!!!", + hintTextTable[RHT_HF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a few trees bordering a wide field# rests #[[1]]#.", + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter ein weites Feld angrenzenden Bäumen# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous quelques arbres bordant un vaste champ# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath the home of the Zoras# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter der Heimat der Zoras# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous la maison des Zora# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath the home of thieves# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter der Heimat von Dieben# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous la maison des voleurs# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_SHIELD_GRAVE_FAIRY] = HintText(CustomMessage("They say that within #a fountain behind a wall within a grave# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter einer Mauer innerhalb eines Grabes# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine derrière un mur dans une tombe# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_OASIS_FAIRY] = HintText(CustomMessage("They say that #restoring water to a dried oasis# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wiederbringen von Wasser zu einer ausgetrockneten Oase# #[[1]]# enthülle.", /*french*/ "Selon moi, #restaurer l'eau dans un oasis asséché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the riverside# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses am Flußufer# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse au bord de la rivière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near a forest shop# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses nahe des Waldladens# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse près d'un magasin forestier# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near the entrance to the forest# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses nahe des Eingangs zum Wald# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse près de l'entrée de la forêt# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout above a sylvan theatre# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses oberhalb eines waldigen Theaters# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse au-dessus d'un théâtre sylvestre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the lakeside# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses am Seeufer# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse au bord du lac# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the side of a canyon# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses an der Seite einer Schlucht# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse sur le côté d'un canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near a temple of the sand# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses nahe eines Tempels des Sandes# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse près d'un temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout within a graveyard# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses auf einem Friedhof# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse dans un cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout within a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses innerhalb eines Vulkans# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DMT_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the moutainside# reveals #[[1]]#.", - /*german*/ "!!!", + hintTextTable[RHT_DMT_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the mountainside# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses an einem Berghang# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse sur le flanc d'une montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the ouskirts of the market# reveals #[[1]]#.", - /*german*/ "!!!", + hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the outskirts of the market# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rand des Marktes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange en périphérie du marché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the ouskirts of the market# reveals #[[1]]#.", - /*german*/ "!!!", + hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the outskirts of the market# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rand des Marktes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange en périphérie du marché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb eines Vulkans# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb eines Vulkans# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone on a mountain cliff face# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein an einer Bergklippe# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sur une falaise de montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone on a mountain cliff face# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein an einer Bergklippe# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sur une falaise de montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near the temple of the sane# reveals #[[1]]#.", - /*german*/ "!!!", + hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near the temple of the sand# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe des Tempels des Sandes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près du temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near the temple of the sane# reveals #[[1]]#.", - /*german*/ "!!!", + hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near the temple of the sand# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe des Tempels des Sandes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près du temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a canyon waterfall# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines Wasserfalls in einer Schlucht# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'une cascade de canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a canyon waterfall# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines Wasserfalls in einer Schlucht# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'une cascade de canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MAZE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone behind a maze of rock# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein hinter einem Felslabyrinth# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange derrière un labyrinthe de roches# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MAZE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone behind a maze of rock# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein hinter einem Felslabyrinth# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange derrière un labyrinthe de roches# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MEDIGORON_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a blacksmith# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines Schmiedes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un forgeron# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MEDIGORON_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a blacksmith# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines Schmiedes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un forgeron# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the graveyard# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher den Friedhof überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the graveyard# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher den Friedhof überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_MALON_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher den Pfad zum Schloß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant le chemin du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_MALON_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher den Pfad zum Schloß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant le chemin du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_ROCK_WALL_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a secret path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines geheimen Pfades zum Schloß# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un chemin secret vers le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_ROCK_WALL_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a secret path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines geheimen Pfades zum Schloß# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un chemin secret vers le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb des Schloßes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the castle# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous le château révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb des Schloßes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous le château# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near an ancient tree# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines antiken Baumes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un arbre ancien# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near an ancient tree# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines antiken Baumes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un arbre ancien# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest village# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Dorf im Wald überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest village# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Dorf im Wald überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a forest village# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Dorfes im Wald# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a forest village# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Dorfes im Wald# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen See nährenden Fluß überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant la rivière qui alimente un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen See nährenden Fluß überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant la rivière qui alimente un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a perplexing wood# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb eines verwirrenden Waldes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans un bois déroutant# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a perplexing wood# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb eines verwirrenden Waldes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans un bois déroutant# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Waldlabyrinth überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant un labyrinthe forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Waldlabyrinth überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant un labyrinthe forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Versteck im Wald überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surveillant une cachette dans les bois# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Versteck im Wald überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surveillant une cachette dans les bois# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einem aquatischen König zuhört#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange écoutant un roi aquatique# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einem aquatischen König zuhört#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange écoutant un roi aquatique# révèle [[1]].", {QM_RED, QM_GREEN})); - hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the ouskirts of a deap fountain# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); + hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the outskirts of a deep fountain# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rand eines tiefen Brunnens# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange en périphérie d'une profonde fontaine# révèle [[1]].", {QM_RED, QM_GREEN})); - hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the ouskirts of a deap fountain# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); + hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the outskirts of a deep fountain# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rand eines tiefen Brunnens# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange en périphérie d'une profonde fontaine# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen Wächter des Meeres beobachtet#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surveillant un gardien de la mer# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen Wächter des Meeres beobachtet#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surveillant un gardien de la mer# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overwatching a river# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen Fluß überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overwatching a river# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen Fluß überblickt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a waterfall# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Wasserfalls# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous une cascade# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a waterfall# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Wasserfalls# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous une cascade# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone hiding near a cow# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher sich nahe einer Kuh versteckt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange cachée près d'une vache# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone hiding near a cow# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher sich nahe einer Kuh versteckt#, #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange cachée près d'une vache# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb des Eingangs zum Markt# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous l'entrée du marché# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb des Eingangs zum Markt# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous l'entrée du marché# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb von Killeranas bewachten Bäumen# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous des arbres gardés par un Peahat# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb von Killeranas bewachten Bäumen# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous des arbres gardés par un Peahat# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Dorfes am Fuße eines Berges# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous un village au pied d'une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Dorfes am Fuße eines Berges# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous un village au pied d'une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a plateau by a river# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein auf einem Plateau von einem Fluß# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans un plateau près d'une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a plateau by a river# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein auf einem Plateau bei einem Fluß# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans un plateau près d'une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Auswegs des Waldes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous une issue de la forêt# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Auswegs des Waldes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous une issue de la forêt# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Eingangs zu einem Dorf innerhalb eines Berges# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous l'entrée d'un village dans une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Eingangs zu einem Dorf innerhalb eines Berges# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous l'entrée d'un village dans une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within the side of a crater# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb der Seite eines Kraters# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans le côté d'un cratère# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within the side of a crater# reveals #[[1]]#.", - /*german*/ "!!!", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb der Seite eines Kraters# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans le côté d'un cratère# révèle [[1]].", {QM_RED, QM_GREEN})); - hintTextTable[RHT_LH_ISLAND_SUN_FAIRY] = HintText(CustomMessage("They say that #summoning the sun on the lake's island# calls #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_LH_ISLAND_SUN_FAIRY] = HintText(CustomMessage("They say that #summoning the sun on the lake's island# calls #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Beschwören der Sonne auf der Insel eines Sees# #[[1]]# anrufe.", + /*french*/ "Selon moi, #appeler le soleil sur l'île du lac # révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_POND_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling rain to the field's pond# summons #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_HF_POND_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling rain to the field's pond# summons #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens für den Tümpel eines Feldes# #[[1]]# beschwöre.", + /*french*/ "Selon moi, #jouer une mélodie orageuse dans l'étang d'une plaine# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_FENCE_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in a scrub's cave# wakes #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_HF_FENCE_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in a scrub's cave# wakes #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Machen von Regen in der Höhle eines Dekus# #[[1]]# erwecke.", + /*french*/ "Selon moi, #jouer une mélodie orageuse dans la grotte d'une Peste Mojo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DMT_FLAG_SUN_FAIRY] = HintText(CustomMessage("They say that #changing the time in front of the trail's flag# reveals #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_DMT_FLAG_SUN_FAIRY] = HintText(CustomMessage("They say that #changing the time in front of the trail's flag# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Verändern der Zeit im Angesicht der Flagge eines Pfades# #[[1]]# enthülle.", + /*french*/ "Selon moi, #appeler le soleil devant un drapeau du Mont du Péril # révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DMT_COW_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling a storm for a lonely cow# reveals #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_DMT_COW_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling a storm for a lonely cow# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen eines Sturms für eine einsame Kuh# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une vache solitaire# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_LW_SHORTCUT_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in the Lost Woods# reveals #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_LW_SHORTCUT_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in the Lost Woods# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Machen von Regen in den verlorenen Wäldern# #[[1]]# enthülle.", + /*french*/ "Selon moi, #jouer une mélodie orageuse dans les Bois Perdus# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_GF_KITCHEN_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a guarded kitchen# exposes #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_GF_KITCHEN_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a guarded kitchen# exposes #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer bewachten Küche# #[[1]]# freilege.", + /*french*/ "Selon moi, #appeler le soleil dans une cuisine gardée# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun for scrubs in the Lost Woods# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun for scrubs in the Lost Woods# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne für Dekus in den verlorenen Wäldern# #[[1]]# enthülle.", + /*french*/ "Selon moi, #appeler le soleil pour les Pestes dans les Bois Perdus # révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a royal tomb# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a royal tomb# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer königlichen Gruft# #[[1]]# enthülle.", + /*french*/ "Selon moi, #appeler le soleil dans une tombe royale# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_KF_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a forest# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_KF_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a forest# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Wald# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un fôret# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_LW_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the woods# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_LW_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the woods# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in den Wäldern# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans des bois# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_MARKET_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the market# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_MARKET_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the market# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf dem Markt# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un marché# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_HC_GRASS] = HintText(CustomMessage("They say that #cutting some grass near the castle# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_HC_GRASS] = HintText(CustomMessage("They say that #cutting some grass near the castle# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe vom Schloß# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe près du château# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_KAK_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a village# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_KAK_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a village# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Dorf# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un village# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_GY_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a graveyard# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_GY_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a graveyard# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Friedhof# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un cimetère# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_LH_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a lake# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_LH_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a lake# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser nahe eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe près d'un lac# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_GRASS] = HintText(CustomMessage("They say that #cutting some grass on a field# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_HF_GRASS] = HintText(CustomMessage("They say that #cutting some grass on a field# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Feld# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans une plaine# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_ZR_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a river# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_ZR_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a river# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe von einem Fluß# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe près d'une rivière# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_GROTTO_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a grotto# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_GROTTO_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a grotto# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einer Grotte# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDO_VALLEY] = HintText(CustomMessage("They say that a #crate in Gerudo Valley# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans une vallée# a #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Gerudotal# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans une vallée# a #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDOS_FORTRESS] = HintText(CustomMessage("They say that a #crate in Gerudo Fortress# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans la Forteresse Gerudo# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste in der Gerudofestung# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans la Forteresse Gerudo# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_WASTELAND] = HintText(CustomMessage("They say that a #crate in Haunted Wasteland# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans le Désert Hanté# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Gerudotal# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Désert Hanté# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_MARKET] = HintText(CustomMessage("They say that a #crate in the Market# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans la Place du Marché# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste in der Gespensterwüste# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_KAKARIKO_VILLAGE] = HintText(CustomMessage("They say that a #crate in Kakariko Village# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans le Village de Cocorico# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Dorf von Kakariko# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Village de Cocorico# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GRAVEYARD] = HintText(CustomMessage("They say that a #crate in the Graveyard# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans le Cimetière# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste auf dem Friedhof# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Cimetière# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GORON_CITY] = HintText(CustomMessage("They say that a #crate in Goron City# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans le Village Goron# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste in Goronia# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Village Goron# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_DEATH_MOUNTAIN_CRATER] = HintText(CustomMessage("They say that a #crate in Death Mountain Crater# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse sur le Mont du Péril# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Todeskrater# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse sur le Mont du Péril# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_LON_LON_RANCH] = HintText(CustomMessage("They say that a #crate in Lon Lon Ranch# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans le Ranch Lon Lon# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste auf der Lon Lon-Farm# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Ranch Lon Lon# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_LAKESIDE_LABORATORY] = HintText(CustomMessage("They say that a #crate in the Laboratory# contains #[[1]]#.", - /*german*/ "", - /*french*/ "Selon moi, une #caisse dans un laboratoire# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Laboratorium# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans un laboratoire# contient #[[1]]#.", {QM_RED, QM_GREEN})); // clang-format on } diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp index ba046884964..fa5155fef52 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -1962,69 +1962,69 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a gold fragment", /*german*/"ein goldenes Fragment", /*french*/"un fragment d'or")}); // /*spanish*/un fragmento dorado - hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"die Seele Gohmas", /*french*/""), + hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"die Seele Gohmas", /*french*/"l'Âme de Gohma"), { - CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/"") + CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/"un truc entoilé") }, { - CustomMessage("an invasive soul", /*german*/"eine invasive Seele", /*french*/""), - CustomMessage("some spider essence", /*german*/"etwas spinnenartige Essenz", /*french*/"")}); + CustomMessage("an invasive soul", /*german*/"eine invasive Seele", /*french*/"une âme invasive"), + CustomMessage("some spider essence", /*german*/"etwas spinnenartige Essenz", /*french*/"une essence d'araignée")}); - hintTextTable[RHT_KING_DODONGO_SOUL] = HintText(CustomMessage("the soul of King Dodongo", /*german*/"die Seele König Dodongos", /*french*/""), + hintTextTable[RHT_KING_DODONGO_SOUL] = HintText(CustomMessage("the soul of King Dodongo", /*german*/"die Seele König Dodongos", /*french*/"l'Âme du Roi Dodongo"), { - CustomMessage("something explosive", /*german*/"etwas Explosives", /*french*/"") + CustomMessage("something explosive", /*german*/"etwas Explosives", /*french*/"un truc détonnant") }, { - CustomMessage("a royal soul", /*german*/"eine royale Seele", /*french*/""), - CustomMessage("some reptile essence", /*german*/"etwas reptilienartige Essenz", /*french*/"")}); + CustomMessage("a royal soul", /*german*/"eine royale Seele", /*french*/"une âme royale"), + CustomMessage("some reptile essence", /*german*/"etwas reptilienartige Essenz", /*french*/"une essence de reptile")}); - hintTextTable[RHT_BARINADE_SOUL] = HintText(CustomMessage("the soul of Barinade", /*german*/"die Seele Barinades", /*french*/""), + hintTextTable[RHT_BARINADE_SOUL] = HintText(CustomMessage("the soul of Barinade", /*german*/"die Seele Barinades", /*french*/"l'Âme de Barinade"), { - CustomMessage("something fishy", /*german*/"etwas Fischiges", /*french*/"") + CustomMessage("something fishy", /*german*/"etwas Fischiges", /*french*/"un truc gluant") }, { - CustomMessage("an infectuous soul", /*german*/"eine infektiöse Seele", /*french*/""), - CustomMessage("some parasitic essence", /*german*/"etwas parasitäre Essenz", /*french*/"")}); + CustomMessage("an infectuous soul", /*german*/"eine infektiöse Seele", /*french*/"une âme infectieuse"), + CustomMessage("some parasitic essence", /*german*/"etwas parasitäre Essenz", /*french*/"une essence parasitique")}); - hintTextTable[RHT_PHANTOM_GANON_SOUL] = HintText(CustomMessage("the soul of Phantom Ganon", /*german*/"die Seele Phantom-Ganons", /*french*/""), + hintTextTable[RHT_PHANTOM_GANON_SOUL] = HintText(CustomMessage("the soul of Phantom Ganon", /*german*/"die Seele Phantom-Ganons", /*french*/"l'Âme de Ganon Spectral"), { - CustomMessage("something spectral", /*german*/"etwas Spektrales", /*french*/"") + CustomMessage("something spectral", /*german*/"etwas Spektrales", /*french*/"un truc spectral") }, { - CustomMessage("a duplicate soul", /*german*/"eine duplizierte Seele", /*french*/""), + CustomMessage("a duplicate soul", /*german*/"eine duplizierte Seele", /*french*/"une âme clonée"), - CustomMessage("some illusionary essence", /*german*/"etwas illusionäre Essenz", /*french*/"")}); + CustomMessage("some illusionary essence", /*german*/"etwas illusionäre Essenz", /*french*/"une essence illusoire")}); - hintTextTable[RHT_VOLVAGIA_SOUL] = HintText(CustomMessage("the soul of Volvagia", /*german*/"die Seele Volvagias", /*french*/""), + hintTextTable[RHT_VOLVAGIA_SOUL] = HintText(CustomMessage("the soul of Volvagia", /*german*/"die Seele Volvagias", /*french*/"l'Âme de Volcania"), { - CustomMessage("something hot", /*german*/"etwas Heißes", /*french*/"") + CustomMessage("something hot", /*german*/"etwas Heißes", /*french*/"quelque chose de très chaud") }, { - CustomMessage("a draconic soul", /*german*/"eine drakonische Seele", /*french*/""), - CustomMessage("some magmatic essence", /*german*/"etwas magmatische Essenz", /*french*/"")}); + CustomMessage("a draconic soul", /*german*/"eine drakonische Seele", /*french*/"une essence draconique"), + CustomMessage("some magmatic essence", /*german*/"etwas magmatische Essenz", /*french*/"une essence magmatique")}); - hintTextTable[RHT_MORPHA_SOUL] = HintText(CustomMessage("the soul of Morpha", /*german*/"die Seele Morphas", /*french*/""), + hintTextTable[RHT_MORPHA_SOUL] = HintText(CustomMessage("the soul of Morpha", /*german*/"die Seele Morphas", /*french*/"l'Âme de Morpha"), { - CustomMessage("something wet", /*german*/"etwas Nasses", /*french*/"") + CustomMessage("something wet", /*german*/"etwas Nasses", /*french*/"un truc mouillé") }, { - CustomMessage("an aquatic soul", /*german*/"eine aquatische Seele", /*french*/""), - CustomMessage("some liquid essence", /*german*/"etwas flüssige Essenz", /*french*/"")}); + CustomMessage("an aquatic soul", /*german*/"eine aquatische Seele", /*french*/"une âme aquatique"), + CustomMessage("some liquid essence", /*german*/"etwas flüssige Essenz", /*french*/"une essence liquide")}); - hintTextTable[RHT_BONGO_BONGO_SOUL] = HintText(CustomMessage("the soul of Bongo Bongo", /*german*/"die Seele Bongo Bongos", /*french*/""), + hintTextTable[RHT_BONGO_BONGO_SOUL] = HintText(CustomMessage("the soul of Bongo Bongo", /*german*/"die Seele Bongo Bongos", /*french*/"l'Âme de Bongo Bongo"), { - CustomMessage("something dark", /*german*/"etwas Dunkles", /*french*/"") + CustomMessage("something dark", /*german*/"etwas Dunkles", /*french*/"quelque chose de sombre") }, { - CustomMessage("a shadowy soul", /*german*/"eine schattige Seele", /*french*/""), - CustomMessage("some handy essence", /*german*/"etwas praktische Essenz", /*french*/"")}); + CustomMessage("a shadowy soul", /*german*/"eine schattige Seele", /*french*/"une âme ténébreuse"), + CustomMessage("some handy essence", /*german*/"etwas praktische Essenz", /*french*/"l'âme du tape m'en cinq")}); - hintTextTable[RHT_TWINROVA_SOUL] = HintText(CustomMessage("the soul of Twinrova", /*german*/"die Seele Twinrovas", /*french*/""), + hintTextTable[RHT_TWINROVA_SOUL] = HintText(CustomMessage("the soul of Twinrova", /*german*/"die Seele Twinrovas", /*french*/"l'Âme du Duo Maléfique"), { - CustomMessage("something spiritual", /*german*/"etwas Spirituelles", /*french*/"") + CustomMessage("something spiritual", /*german*/"etwas Spirituelles", /*french*/"un truc spirituel") }, { - CustomMessage("old souls", /*german*/"alte Seelen", /*french*/""), - CustomMessage("twin essences", /*german*/"Zwillingsessenzen", /*french*/"")}); + CustomMessage("old souls", /*german*/"alte Seelen", /*french*/"deux vieilles âmes"), + CustomMessage("twin essences", /*german*/"Zwillingsessenzen", /*french*/"des essences jumelles")}); - hintTextTable[RHT_GANON_SOUL] = HintText(CustomMessage("the soul of Ganon", /*german*/"die Seele Ganons", /*french*/""), + hintTextTable[RHT_GANON_SOUL] = HintText(CustomMessage("the soul of Ganon", /*german*/"die Seele Ganons", /*french*/"l'Âme de Ganon"), { - CustomMessage("something strong", /*german*/"etwas Starkes", /*french*/"") + CustomMessage("something strong", /*german*/"etwas Starkes", /*french*/"un truc de fort") }, { - CustomMessage("an evil soul", /*german*/"eine böse Seele", /*french*/""), - CustomMessage("some powerful essence", /*german*/"etwas mächtige Essenz", /*french*/"")}); + CustomMessage("an evil soul", /*german*/"eine böse Seele", /*french*/"une âme maléfique"), + CustomMessage("some powerful essence", /*german*/"etwas mächtige Essenz", /*french*/"une essence incroyablement puissante")}); hintTextTable[RHT_OCARINA_A_BUTTON] = HintText(CustomMessage("an Ocarina A Button", /*german*/"eine Okarina A Taste", /*french*/"la Touche A de l'Ocarina"), // /*spanish*/un botón A de Ocarina @@ -2098,53 +2098,22 @@ void StaticData::HintTable_Init_Item() { { CustomMessage("a master unlocker", /*german*/ "ein Meisterentsperrer", /*french*/ "un Kit de Déverrouillage") }); // /*spanish*/un desbloqueador maestro - hintTextTable[RHT_QUIVER_INF] = HintText(CustomMessage("an infinite Quiver", /*german*/"ein unendlicher Köcher", /*french*/"un Carquois Infini"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + //RANDOTODO if these are ever used for anything other than name, they want abscure and ambiguous hints + hintTextTable[RHT_QUIVER_INF] = HintText(CustomMessage("an infinite Quiver", /*german*/"ein unendlicher Köcher", /*french*/"un Carquois Infini")); - hintTextTable[RHT_BOMB_BAG_INF] = HintText(CustomMessage("an infinite Bomb Bag", /*german*/"eine unendliche Bombentasche", /*french*/"un Sac de Bombe sans fond"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_BOMB_BAG_INF] = HintText(CustomMessage("an infinite Bomb Bag", /*german*/"eine unendliche Bombentasche", /*french*/"un Sac de Bombe sans fond")); - hintTextTable[RHT_BULLET_BAG_INF] = HintText(CustomMessage("an infinite Bullet Bag", /*german*/"eine unendliche Samentasche", /*french*/"un Sac de Graine sans fond"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_BULLET_BAG_INF] = HintText(CustomMessage("an infinite Bullet Bag", /*german*/"eine unendliche Samentasche", /*french*/"un Sac de Graine sans fond")); - hintTextTable[RHT_STICK_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Sticks", /*german*/"unendliche Deku-Stäbe", /*french*/"des Bâtons Mojo illimités"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_STICK_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Sticks", /*german*/"unendliche Deku-Stäbe", /*french*/"des Bâtons Mojo illimités")); - hintTextTable[RHT_NUT_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Nuts", /*german*/"unendliche Deku-Nüsse", /*french*/"des Noix Mojo illimitées"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_NUT_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Nuts", /*german*/"unendliche Deku-Nüsse", /*french*/"des Noix Mojo illimitées")); - hintTextTable[RHT_MAGIC_INF] = HintText(CustomMessage("unlimited Magic", /*german*/"unendliche Magie", /*french*/"de la Magie infinie"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_MAGIC_INF] = HintText(CustomMessage("unlimited Magic", /*german*/"unendliche Magie", /*french*/"de la Magie infinie")); - hintTextTable[RHT_BOMBCHU_INF] = HintText(CustomMessage("infinite Bombchus", /*german*/"unendliche Krabbelminen", /*french*/"des Missiles Teigneux illimités"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_BOMBCHU_INF] = HintText(CustomMessage("infinite Bombchus", /*german*/"unendliche Krabbelminen", /*french*/"des Missiles Teigneux illimités")); - hintTextTable[RHT_WALLET_INF] = HintText(CustomMessage("an infinite Wallet", /*german*/"eine unendliche Geldbörse", /*french*/"une Bourse sans fond"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_WALLET_INF] = HintText(CustomMessage("an infinite Wallet", /*german*/"eine unendliche Geldbörse", /*french*/"une Bourse sans fond")); hintTextTable[RHT_EPONA] = HintText(CustomMessage("Epona", /*german*/"Epona", /*french*/"Epona"), // /*spanish*/a Epona diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index c55145fcd7d..bf71ef023cb 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -648,6 +648,9 @@ void CreateStoneHints() { if (ctx->GetOption(RSK_SKIP_CHILD_ZELDA)) { ctx->GetItemLocation(RC_SONG_FROM_IMPA)->SetHintAccesible(); } + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)) { + ctx->GetItemLocation(RC_TOT_MASTER_SWORD)->SetHintAccesible(); + } // Add 'always' location hints std::vector alwaysHintLocations = {}; @@ -728,20 +731,22 @@ std::vector FindItemsAndMarkHinted(std::vector i void CreateChildAltarHint() { auto ctx = Rando::Context::GetInstance(); - if (!ctx->GetHint(RH_ALTAR_CHILD)->IsEnabled() && ctx->GetOption(RSK_TOT_ALTAR_HINT)) { + if (!ctx->GetHint(RH_ALTAR_CHILD)->IsEnabled()) { std::vector stoneLocs = {}; - // force marking the rewards as hinted if they are at the end of dungeons as they can be inferred - if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) || - ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA)) { - stoneLocs = FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, {}); - } else { - stoneLocs = - FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, { RC_ALTAR_HINT_CHILD }); - } std::vector stoneAreas = {}; - for (auto loc : stoneLocs) { - if (loc != RC_UNKNOWN_CHECK) { - stoneAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { + // force marking the rewards as hinted if they are at the end of dungeons as they can be inferred + if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) || + ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA)) { + stoneLocs = FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, {}); + } else { + stoneLocs = FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, + { RC_ALTAR_HINT_CHILD }); + } + for (auto loc : stoneLocs) { + if (loc != RC_UNKNOWN_CHECK) { + stoneAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + } } } ctx->AddHint(RH_ALTAR_CHILD, Hint(RH_ALTAR_CHILD, HINT_TYPE_ALTAR_CHILD, {}, stoneLocs, stoneAreas)); @@ -752,6 +757,7 @@ void CreateAdultAltarHint() { auto ctx = Rando::Context::GetInstance(); if (!ctx->GetHint(RH_ALTAR_ADULT)->IsEnabled()) { std::vector medallionLocs = {}; + std::vector medallionAreas = {}; if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { // force marking the rewards as hinted if they are at the end of dungeons as they can be inferred if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) || @@ -764,11 +770,10 @@ void CreateAdultAltarHint() { RG_WATER_MEDALLION, RG_SPIRIT_MEDALLION, RG_SHADOW_MEDALLION }, { RC_ALTAR_HINT_ADULT }); } - } - std::vector medallionAreas = {}; - for (auto loc : medallionLocs) { - if (loc != RC_UNKNOWN_CHECK) { - medallionAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + for (auto loc : medallionLocs) { + if (loc != RC_UNKNOWN_CHECK) { + medallionAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + } } } ctx->AddHint(RH_ALTAR_ADULT, Hint(RH_ALTAR_ADULT, HINT_TYPE_ALTAR_ADULT, {}, medallionLocs, medallionAreas)); @@ -835,7 +840,7 @@ void CreateGanondorfJoke() { void CreateGanondorfHint() { auto ctx = Rando::Context::GetInstance(); if (ctx->GetOption(RSK_GANONDORF_HINT) && !ctx->GetHint(RH_GANONDORF_HINT)->IsEnabled()) { - if (ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD)) { + if (ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD) && ctx->GetOption(RSK_STARTING_MASTER_SWORD).Is(RO_GENERIC_OFF)) { CreateStaticItemHint( RH_GANONDORF_HINT, { RHT_GANONDORF_HINT_LA_ONLY, RHT_GANONDORF_HINT_MS_ONLY, RHT_GANONDORF_HINT_LA_AND_MS }, diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index a04a7d65c68..2a2c294a5b1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -354,7 +354,7 @@ void PlaceJunkInExcludedLocation(const RandomizerCheck il) { // place a non-advancement item in this location auto ctx = Rando::Context::GetInstance(); for (size_t i = 0; i < ItemPool.size(); i++) { - if (!Rando::StaticData::RetrieveItem(ItemPool[i]).IsAdvancement()) { + if (Rando::StaticData::RetrieveItem(ItemPool[i]).GetCategory() == ITEM_CATEGORY_JUNK) { ctx->PlaceItemInLocation(il, ItemPool[i]); ItemPool.erase(ItemPool.begin() + i); return; @@ -861,7 +861,7 @@ void GenerateItemPool() { AddItemToMainPool(RG_GERUDO_MEMBERSHIP_CARD); ctx->possibleIceTrapModels.push_back(RG_GERUDO_MEMBERSHIP_CARD); } else if (ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD)) { - AddItemToPool(PendingJunkPool, RG_GERUDO_MEMBERSHIP_CARD); + AddItemToPool(ItemPool, RG_GERUDO_MEMBERSHIP_CARD); ctx->PlaceItemInLocation(RC_GF_GERUDO_MEMBERSHIP_CARD, RG_ICE_TRAP, false, true); } else { ctx->PlaceItemInLocation(RC_GF_GERUDO_MEMBERSHIP_CARD, RG_GERUDO_MEMBERSHIP_CARD, false, true); diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 8224865ac6e..110ab8a7673 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -759,7 +759,7 @@ void InitTrickNames() { trickNameTable[RG_RECOVERY_HEART] = { Text{ "Love", "Bisou", "Te amo" }, Text{ "Life", "Vie", "vida" }, - Text{ "HP", "VP", "VP" }, + Text{ "HP", "PV", "VP" }, }; trickNameTable[RG_GREEN_RUPEE] = { Text{ "False Greg", "Faux Greg", "Falso Greg" }, Text{ "One Ruby", "Un rubis", "Un rubí" }, @@ -779,15 +779,15 @@ void InitTrickNames() { Text{ "Rupee (20)", "Rubis (20)", "Peso hyliano" }, }; trickNameTable[RG_PURPLE_RUPEE] = { - Text{ "Purpee", "pourbi", "morupiua" }, - Text{ "Fifty Rubies", "cinquante rubis", "Cincuenta rubíes" }, + Text{ "Purpee", "Pourbi", "morupiua" }, + Text{ "Fifty Rubies", "Cinquante rubis", "Cincuenta rubíes" }, Text{ "Rupoor (50)", "Roupir (50)", "Rupobre (50)" }, Text{ "Fifty Rupees", "Cinquante rubis", "Balboa hyliano" }, Text{ "Rupee (50)", "Rubis (50)", "Peso hyliano" }, }; trickNameTable[RG_HUGE_RUPEE] = { Text{ "Hugo", "Or Rubi", "Oro Rubi" }, - Text{ "Two Hundred Rubies", "deux cents rubis", "Doscientos rubíes" }, + Text{ "Two Hundred Rubies", "Deux cents rubis", "Doscientos rubíes" }, Text{ "Diamond", "Diamant", "Diamante" }, Text{ "Huge Ruby", "Énorme rubis", "Rubi gigante" }, Text{ "Two Hundred Rupees", "Deux cent rubis", "Euro hyliano" }, @@ -806,53 +806,54 @@ void InitTrickNames() { trickNameTable[RG_TRIFORCE_PIECE] = { Text{ "Piece of Cheese", "Morceau de Fromage", "Piece of Cheese" }, Text{ "Triforce Shard", "Éclat de Triforce", "Triforce Shard" }, - Text{ "Shiny Rock", "Caiiloux Brillant", "Shiny Rock" }, + Text{ "Shiny Rock", "Caillou Brillant", "Shiny Rock" }, }; + // TODO_TRANSLATE trickNameTable[RG_GOHMA_SOUL] = { - Text{ "Spider Sense", "", "" }, - Text{ "Deku Spirit", "", "" }, - Text{ "Ghost of Ghoma", "", "" }, + Text{ "Spider Sense", "Sens de l'Araignée", "" }, + Text{ "Deku Spirit", "Parasite Mojo", "" }, + Text{ "Ghost of Ghoma", "Fantôme de Gohma", "" }, }; trickNameTable[RG_KING_DODONGO_SOUL] = { - Text{ "Lizard Soul", "", "" }, - Text{ "Regal Remains", "", "" }, - Text{ "Dodongo's Core", "", "" }, + Text{ "Lizard Soul", "Âme d'un Lézard", "" }, + Text{ "Regal Remains", "Restes Délicieux", "" }, + Text{ "Dodongo's Core", "Coeur de Dodongo", "" }, }; trickNameTable[RG_BARINADE_SOUL] = { - Text{ "Parasitic Poltergeist", "", "" }, - Text{ "Jabu Insides", "", "" }, - Text{ "Barinade Bacteria", "", "" }, + Text{ "Parasitic Poltergeist", "Poltergeist Parasite", "" }, + Text{ "Jabu Insides", "Entrailles de Jabu-Jabu", "" }, + Text{ "Barinade Bacteria", "Bactérie de Barinade", "" }, }; trickNameTable[RG_PHANTOM_GANON_SOUL] = { - Text{ "Bigger Poe", "", "" }, - Text{ "Sacred Forest Pine Tree", "", "" }, - Text{ "Ganon's Phantom", "", "" }, + Text{ "Bigger Poe", "Âme Gigantesque", "" }, + Text{ "Sacred Forest Pine Tree", "Grande Perche du Bosquet Sacré", "" }, + Text{ "Ganon's Phantom", "Fantôme de Ganon", "" }, }; trickNameTable[RG_VOLVAGIA_SOUL] = { - Text{ "Dragon Roast", "", "" }, - Text{ "Hot n' Ready", "", "" }, - Text{ "Volvagia's Vitality", "", "" }, + Text{ "Dragon Roast", "Friture du Dragon", "" }, + Text{ "Hot n' Ready", "Sauce Barbecue", "" }, + Text{ "Volvagia's Vitality", "Vitalité de Volcania", "" }, }; trickNameTable[RG_MORPHA_SOUL] = { - Text{ "Dihydrogen Monoxide", "", "" }, - Text{ "Morpha Molecules", "", "" }, - Text{ "Wet Stuff", "", "" }, + Text{ "Dihydrogen Monoxide", "Monoxyde de Dihydrogène", "" }, + Text{ "Morpha Molecules", "Molécule de Morpha", "" }, + Text{ "Wet Stuff", "Truc Mouillé", "" }, }; trickNameTable[RG_BONGO_BONGO_SOUL] = { - Text{ "Shadow Soul", "", "" }, - Text{ "Dark Essence", "", "" }, - Text{ "Bongo Bongo's Bongo", "", "" }, + Text{ "Shadow Soul", "Âme de l'Ombre", "" }, + Text{ "Dark Essence", "Essence Sombre", "" }, + Text{ "Bongo Bongo's Bongo", "Bongo de Bongo Bongo", "" }, }; trickNameTable[RG_TWINROVA_SOUL] = { - Text{ "Sandy Ashes", "", "" }, - Text{ "Spiritual Spirit", "", "" }, - Text{ "Twin Rovers", "", "" }, + Text{ "Sandy Ashes", "Cendres des Vieilles", "" }, + Text{ "Spiritual Spirit", "Esprit Spirituel", "" }, + Text{ "Twin Rovers", "Duo Angélique", "" }, }; trickNameTable[RG_GANON_SOUL] = { - Text{ "Pure Evil", "", "" }, - Text{ "Ganon's Ghost", "", "" }, - Text{ "Pork", "", "" }, + Text{ "Pure Evil", "Mal Incarné", "" }, + Text{ "Ganon's Ghost", "Le Malin", "" }, + Text{ "Pork", "Porc", "" }, }; trickNameTable[RG_FISHING_POLE] = { @@ -862,31 +863,31 @@ void InitTrickNames() { }; trickNameTable[RG_OCARINA_A_BUTTON] = { - Text{ "Ocarina J Button", "", "" }, - Text{ "Ocarina Ayy Button", "", "" }, - Text{ "Ocarina A Trigger", "", "" }, + Text{ "Ocarina J Button", "Touche Ha de l'Ocarina", "" }, + Text{ "Ocarina Ayy Button", "Touche Ah de l'Ocarina", "" }, + Text{ "Ocarina A Trigger", "Bumper A de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_UP_BUTTON] = { - Text{ "Ocarina C North Button", "", "" }, - Text{ "Ocarina C App Button", "", "" }, - Text{ "Ocarina Sup Button", "", "" }, + Text{ "Ocarina C North Button", "Touche C Nord de l'Ocarina", "" }, + Text{ "Ocarina C App Button", "Touche C'est Haut de l'Ocarina", "" }, + Text{ "Ocarina Sup Button", "Touche O de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_DOWN_BUTTON] = { - Text{ "Ocarina C South Button", "", "" }, - Text{ "Ocarina Z Down Button", "", "" }, - Text{ "Ocarina See Down Button", "", "" }, - Text{ "Ocarina C Dawn Button", "", "" }, + Text{ "Ocarina C South Button", "Touche C Sud de l'Ocarina", "" }, + Text{ "Ocarina Z Down Button", "Touche Z Bas de l'Ocarina", "" }, + Text{ "Ocarina See Down Button", "Touche C'est Bas de l'Ocarina", "" }, + Text{ "Ocarina C Dawn Button", "Touche Séba de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_LEFT_BUTTON] = { - Text{ "Ocarina C West Button", "", "" }, - Text{ "Ocarina Sea Left Button", "", "" }, - Text{ "Ocarina C Lift Button", "", "" }, - Text{ "Ocarina Rewind Button", "", "" }, + Text{ "Ocarina C West Button", "Touche C Ouest de l'Ocarina", "" }, + Text{ "Ocarina Sea Left Button", "Touche Cégoche de L'Ocarina", "" }, + Text{ "Ocarina C Lift Button", "Touche C'est Gauche de l'Ocarina", "" }, + Text{ "Ocarina Rewind Button", "Touche Rembobiner de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_RIGHT_BUTTON] = { - Text{ "Ocarina C East Button", "", "" }, - Text{ "Ocarina C Wright Button", "", "" }, - Text{ "Overworld C Right Button", "", "" }, + Text{ "Ocarina C East Button", "Touche C Est de l'Ocarina", "" }, + Text{ "Ocarina C Wright Button", "Touche C'est Droite de l'Ocarina", "" }, + Text{ "Overworld C Right Button", "Trou Droit de l'Ocarina", "" }, }; /* diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index 7938d86654f..a1c91277397 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -267,10 +267,10 @@ static void WriteAllLocations() { switch (gSaveContext.language) { case 0: default: - placedItemName = location->GetPlacedItemName().english; + placedItemName = location->GetPlacedItemName().GetEnglish(); break; case 2: - placedItemName = location->GetPlacedItemName().french; + placedItemName = location->GetPlacedItemName().GetFrench(); break; } @@ -303,18 +303,18 @@ static void WriteAllLocations() { ["model"] = Rando::StaticData::RetrieveItem( ctx->overrides[location->GetRandomizerCheck()].LooksLike()) .GetName() - .english; + .GetEnglish(); jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()] - ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().english; + ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().GetEnglish(); break; case 2: jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()] ["model"] = Rando::StaticData::RetrieveItem( ctx->overrides[location->GetRandomizerCheck()].LooksLike()) .GetName() - .french; + .GetFrench(); jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()] - ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().french; + ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().GetFrench(); break; } } diff --git a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp index 3cb3419806c..fa88d455b1a 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp @@ -362,7 +362,7 @@ void Rando::StaticData::RegisterCrateLocations() { locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1518), "Near Impas House Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1)); locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1470), "Near Impas House Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2)); locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-433, -401), "Near Bazaar Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_1)); - locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2`", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2)); + locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2)); locationTable[RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE] = Location::Crate(RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-724, 871), "Behind GS House Adult Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEHIND_GS_HOUSE_ADULT_CRATE)); locationTable[RC_KAK_NEAR_GY_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_GY_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1732, 1366), "Near Graveyard Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_GY_CHILD_CRATE)); locationTable[RC_KAK_NEAR_WINDMILL_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_WINDMILL_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1170, 601), "Near Windmill Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_WINDMILL_CHILD_CRATE)); diff --git a/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp b/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp index f318ad94884..b8d31144a93 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp @@ -35,8 +35,10 @@ extern "C" void EnKusa_RandomizerDraw(Actor* thisx, PlayState* play) { if (grassActor->grassIdentity.randomizerCheck != RC_MAX && Flags_GetRandomizerInf(grassActor->grassIdentity.randomizerInf) == 0) { int csmc = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), CSMC_DISABLED); + int requiresStoneAgony = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); - if (csmc == CSMC_BOTH || csmc == CSMC_TEXTURE) { + if ((csmc == CSMC_BOTH || csmc == CSMC_TEXTURE) && + (!requiresStoneAgony || (requiresStoneAgony && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) { auto itemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(grassActor->grassIdentity.randomizerCheck, true, GI_NONE); GetItemCategory getItemCategory = itemEntry.getItemCategory; diff --git a/soh/soh/Enhancements/randomizer/ShufflePots.cpp b/soh/soh/Enhancements/randomizer/ShufflePots.cpp index 9e6f50f4274..5afd9859c3b 100644 --- a/soh/soh/Enhancements/randomizer/ShufflePots.cpp +++ b/soh/soh/Enhancements/randomizer/ShufflePots.cpp @@ -103,11 +103,11 @@ void Rando::StaticData::RegisterPotLocations() { // clang-format off // Overworld Pots // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Hint Text Key Vanilla Spoiler Collection Check - locationTable[RC_KF_LINKS_HOUSE_POT] = Location::Pot(RC_KF_LINKS_HOUSE_POT, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_LINKS_HOUSE, TWO_ACTOR_PARAMS(-118, 51), "Links House Pot", RHT_POT_KOKIRI_FOREST, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LINKS_HOUSE_POT)); - locationTable[RC_KF_TWINS_HOUSE_POT_2] = Location::Pot(RC_KF_TWINS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(35, 57), "Twins House Pot 2", RHT_POT_KOKIRI_FOREST, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_2)); - locationTable[RC_KF_TWINS_HOUSE_POT_1] = Location::Pot(RC_KF_TWINS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(33, -55), "Twins House Pot 1", RHT_POT_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_1)); - locationTable[RC_KF_BROTHERS_HOUSE_POT_1] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-134, -29), "Brothers House Pot 1", RHT_POT_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_1)); - locationTable[RC_KF_BROTHERS_HOUSE_POT_2] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-68, 114), "Brothers House Pot 2", RHT_POT_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_2)); + locationTable[RC_KF_LINKS_HOUSE_POT] = Location::Pot(RC_KF_LINKS_HOUSE_POT, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_LINKS_HOUSE, TWO_ACTOR_PARAMS(-118, 51), "Links House Pot", RHT_POT_LINKS_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LINKS_HOUSE_POT)); + locationTable[RC_KF_TWINS_HOUSE_POT_2] = Location::Pot(RC_KF_TWINS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(35, 57), "Twins House Pot 2", RHT_POT_TWINS_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_2)); + locationTable[RC_KF_TWINS_HOUSE_POT_1] = Location::Pot(RC_KF_TWINS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(33, -55), "Twins House Pot 1", RHT_POT_TWINS_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_1)); + locationTable[RC_KF_BROTHERS_HOUSE_POT_1] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-134, -29), "Brothers House Pot 1", RHT_POT_KNOW_IT_ALL, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_1)); + locationTable[RC_KF_BROTHERS_HOUSE_POT_2] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-68, 114), "Brothers House Pot 2", RHT_POT_KNOW_IT_ALL, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_2)); locationTable[RC_GF_BREAK_ROOM_POT_1] = Location::Pot(RC_GF_BREAK_ROOM_POT_1, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1438, -3629), "Break Room Pot 1", RHT_POT_GERUDO_FORTRESS, RG_ARROWS_10, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_BREAK_ROOM_POT_1)); locationTable[RC_GF_BREAK_ROOM_POT_2] = Location::Pot(RC_GF_BREAK_ROOM_POT_2, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1403, -3679), "Break Room Pot 2", RHT_POT_GERUDO_FORTRESS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_BREAK_ROOM_POT_2)); locationTable[RC_GF_KITCHEN_POT_1] = Location::Pot(RC_GF_KITCHEN_POT_1, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1908, -789), "Kitchen Pot 1", RHT_POT_GERUDO_FORTRESS, RG_ARROWS_10, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_KITCHEN_POT_1)); @@ -128,64 +128,64 @@ void Rando::StaticData::RegisterPotLocations() { locationTable[RC_WASTELAND_NEAR_GS_POT_2] = Location::Pot(RC_WASTELAND_NEAR_GS_POT_2, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(485, -2463), "Near GS Pot 2", RHT_POT_GERUDO_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WASTELAND_NEAR_GS_POT_2)); locationTable[RC_WASTELAND_NEAR_GS_POT_3] = Location::Pot(RC_WASTELAND_NEAR_GS_POT_3, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(806, -2426), "Near GS Pot 3", RHT_POT_GERUDO_FORTRESS, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WASTELAND_NEAR_GS_POT_3)); locationTable[RC_WASTELAND_NEAR_GS_POT_4] = Location::Pot(RC_WASTELAND_NEAR_GS_POT_4, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(801, -2460), "Near GS Pot 4", RHT_POT_GERUDO_FORTRESS, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WASTELAND_NEAR_GS_POT_4)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-60, 27), "Guard House Child Pot 1", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_1)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-89, 28), "Guard House Child Pot 2", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_2)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-110, 6), "Guard House Child Pot 3", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_3)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-58, -7), "Guard House Child Pot 4", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_4)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-80, -7), "Guard House Child Pot 5", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_5)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-65, -45), "Guard House Child Pot 6", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_6)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-85, -41), "Guard House Child Pot 7", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_7)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-107, -45), "Guard House Child Pot 8", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_8)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-66, -79), "Guard House Child Pot 9", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_9)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-88, -84), "Guard House Child Pot 10", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_10)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, 215), "Guard House Child Pot 11", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_11)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_12] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_12, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 148), "Guard House Child Pot 12", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_12)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_13] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_13, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(73, 117), "Guard House Child Pot 13", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_13)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_14] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_14, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 123), "Guard House Child Pot 14", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_14)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_15] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_15, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(43, 89), "Guard House Child Pot 15", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_15)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_16] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_16, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 81), "Guard House Child Pot 16", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_16)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_17] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_17, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(21, 73), "Guard House Child Pot 17", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_17)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_18] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_18, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(64, 45), "Guard House Child Pot 18", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_18)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_19] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_19, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(84, 31), "Guard House Child Pot 19", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_19)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_20] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_20, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, 26), "Guard House Child Pot 20", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_20)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_21] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_21, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(20, 34), "Guard House Child Pot 21", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_21)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_22] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_22, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(89, -2), "Guard House Child Pot 22", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_22)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_23] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_23, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -12), "Guard House Child Pot 23", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_23)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_24] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_24, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, -5), "Guard House Child Pot 24", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_24)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_25] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_25, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(16, -6), "Guard House Child Pot 25", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_25)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_26] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_26, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, -44), "Guard House Child Pot 26", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_26)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_27] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_27, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(37, -40), "Guard House Child Pot 27", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_27)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_28] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_28, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -80), "Guard House Child Pot 28", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_28)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_29] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_29, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(50, -74), "Guard House Child Pot 29", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_29)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_30] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_30, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(28, -79), "Guard House Child Pot 30", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_30)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_31] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_31, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(39, -111), "Guard House Child Pot 31", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_31)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_32] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_32, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(169, 216), "Guard House Child Pot 32", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_32)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_33] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_33, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 166), "Guard House Child Pot 33", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_33)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_34] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_34, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, 120), "Guard House Child Pot 34", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_34)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_35] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_35, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(177, 85), "Guard House Child Pot 35", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_35)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_36] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_36, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(155, 39), "Guard House Child Pot 36", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_36)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_37] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_37, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(184, 13), "Guard House Child Pot 37", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_37)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_38] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_38, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -1), "Guard House Child Pot 38", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_38)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_39] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_39, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(181, -33), "Guard House Child Pot 39", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_39)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_40] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_40, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -45), "Guard House Child Pot 40", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_40)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_41] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_41, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(172, -82), "Guard House Child Pot 41", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_41)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_42] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_42, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -120), "Guard House Child Pot 42", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_42)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_43] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_43, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -166), "Guard House Child Pot 43", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_43)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_44] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_44, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -216), "Guard House Child Pot 44", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_44)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(61, 204), "Guard House Adult Pot 1", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_1)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, 132), "Guard House Adult Pot 2", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_2)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(74, 23), "Guard House Adult Pot 3", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_3)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 4), "Guard House Adult Pot 4", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_4)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, -22), "Guard House Adult Pot 5", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_5)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -151), "Guard House Adult Pot 6", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_6)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(79, -182), "Guard House Adult Pot 7", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_7)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -198), "Guard House Adult Pot 8", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_8)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 210), "Guard House Adult Pot 9", RHT_POT_MARKET, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_9)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -122), "Guard House Adult Pot 10", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_10)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -210), "Guard House Adult Pot 11", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_11)); - locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_1] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(100, 45), "Back Alley House Pot 1", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_1)); - locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_2] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(12, -180), "Back Alley House Pot 2", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_2)); - locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_3] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(-54, -180), "Back Alley House Pot 3", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_3)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-60, 27), "Guard House Child Pot 1", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_1)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-89, 28), "Guard House Child Pot 2", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_2)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-110, 6), "Guard House Child Pot 3", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_3)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-58, -7), "Guard House Child Pot 4", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_4)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-80, -7), "Guard House Child Pot 5", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_5)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-65, -45), "Guard House Child Pot 6", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_6)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-85, -41), "Guard House Child Pot 7", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_7)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-107, -45), "Guard House Child Pot 8", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_8)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-66, -79), "Guard House Child Pot 9", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_9)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-88, -84), "Guard House Child Pot 10", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_10)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, 215), "Guard House Child Pot 11", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_11)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_12] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_12, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 148), "Guard House Child Pot 12", RHT_POT_GUARD_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_12)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_13] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_13, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(73, 117), "Guard House Child Pot 13", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_13)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_14] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_14, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 123), "Guard House Child Pot 14", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_14)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_15] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_15, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(43, 89), "Guard House Child Pot 15", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_15)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_16] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_16, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 81), "Guard House Child Pot 16", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_16)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_17] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_17, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(21, 73), "Guard House Child Pot 17", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_17)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_18] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_18, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(64, 45), "Guard House Child Pot 18", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_18)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_19] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_19, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(84, 31), "Guard House Child Pot 19", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_19)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_20] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_20, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, 26), "Guard House Child Pot 20", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_20)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_21] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_21, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(20, 34), "Guard House Child Pot 21", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_21)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_22] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_22, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(89, -2), "Guard House Child Pot 22", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_22)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_23] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_23, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -12), "Guard House Child Pot 23", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_23)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_24] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_24, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, -5), "Guard House Child Pot 24", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_24)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_25] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_25, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(16, -6), "Guard House Child Pot 25", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_25)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_26] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_26, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, -44), "Guard House Child Pot 26", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_26)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_27] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_27, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(37, -40), "Guard House Child Pot 27", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_27)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_28] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_28, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -80), "Guard House Child Pot 28", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_28)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_29] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_29, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(50, -74), "Guard House Child Pot 29", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_29)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_30] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_30, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(28, -79), "Guard House Child Pot 30", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_30)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_31] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_31, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(39, -111), "Guard House Child Pot 31", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_31)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_32] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_32, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(169, 216), "Guard House Child Pot 32", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_32)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_33] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_33, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 166), "Guard House Child Pot 33", RHT_POT_GUARD_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_33)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_34] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_34, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, 120), "Guard House Child Pot 34", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_34)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_35] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_35, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(177, 85), "Guard House Child Pot 35", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_35)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_36] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_36, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(155, 39), "Guard House Child Pot 36", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_36)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_37] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_37, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(184, 13), "Guard House Child Pot 37", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_37)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_38] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_38, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -1), "Guard House Child Pot 38", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_38)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_39] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_39, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(181, -33), "Guard House Child Pot 39", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_39)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_40] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_40, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -45), "Guard House Child Pot 40", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_40)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_41] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_41, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(172, -82), "Guard House Child Pot 41", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_41)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_42] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_42, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -120), "Guard House Child Pot 42", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_42)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_43] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_43, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -166), "Guard House Child Pot 43", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_43)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_44] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_44, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -216), "Guard House Child Pot 44", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_44)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(61, 204), "Guard House Adult Pot 1", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_1)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, 132), "Guard House Adult Pot 2", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_2)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(74, 23), "Guard House Adult Pot 3", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_3)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 4), "Guard House Adult Pot 4", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_4)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, -22), "Guard House Adult Pot 5", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_5)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -151), "Guard House Adult Pot 6", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_6)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(79, -182), "Guard House Adult Pot 7", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_7)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -198), "Guard House Adult Pot 8", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_8)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 210), "Guard House Adult Pot 9", RHT_POT_POE_SHOP, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_9)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -122), "Guard House Adult Pot 10", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_10)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -210), "Guard House Adult Pot 11", RHT_POT_POE_SHOP, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_11)); + locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_1] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(100, 45), "Back Alley House Pot 1", RHT_POT_ALLY_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_1)); + locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_2] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(12, -180), "Back Alley House Pot 2", RHT_POT_ALLY_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_2)); + locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_3] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(-54, -180), "Back Alley House Pot 3", RHT_POT_ALLY_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_3)); locationTable[RC_KAK_NEAR_POTION_SHOP_POT_1] = Location::Pot(RC_KAK_NEAR_POTION_SHOP_POT_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(222, -377), "Near Potion Shop Pot 1", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_POTION_SHOP_POT_1)); locationTable[RC_KAK_NEAR_POTION_SHOP_POT_2] = Location::Pot(RC_KAK_NEAR_POTION_SHOP_POT_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(255, -366), "Near Potion Shop Pot 2", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_POTION_SHOP_POT_2)); locationTable[RC_KAK_NEAR_POTION_SHOP_POT_3] = Location::Pot(RC_KAK_NEAR_POTION_SHOP_POT_3, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(284, -356), "Near Potion Shop Pot 3", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_POTION_SHOP_POT_3)); @@ -197,12 +197,12 @@ void Rando::StaticData::RegisterPotLocations() { locationTable[RC_KAK_NEAR_GUARDS_HOUSE_POT_3] = Location::Pot(RC_KAK_NEAR_GUARDS_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-450, -895), "Near Guards House Pot 3", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_GUARDS_HOUSE_POT_3)); locationTable[RC_KAK_NEAR_MEDICINE_SHOP_POT_1] = Location::Pot(RC_KAK_NEAR_MEDICINE_SHOP_POT_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(781, 89), "Near Medicine Shop Pot 1", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_MEDICINE_SHOP_POT_1)); locationTable[RC_KAK_NEAR_MEDICINE_SHOP_POT_2] = Location::Pot(RC_KAK_NEAR_MEDICINE_SHOP_POT_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(815, 89), "Near Medicine Shop Pot 2", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_MEDICINE_SHOP_POT_2)); - locationTable[RC_GY_DAMPES_GRAVE_POT_1] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_1, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1542), "Dampes Grave Pot 1", RHT_POT_GRAVEYARD, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_1)); - locationTable[RC_GY_DAMPES_GRAVE_POT_2] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_2, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1600), "Dampes Grave Pot 2", RHT_POT_GRAVEYARD, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_2)); - locationTable[RC_GY_DAMPES_GRAVE_POT_3] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_3, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-364, -1571), "Dampes Grave Pot 3", RHT_POT_GRAVEYARD, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_3)); - locationTable[RC_GY_DAMPES_GRAVE_POT_4] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_4, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1540), "Dampes Grave Pot 4", RHT_POT_GRAVEYARD, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_4)); - locationTable[RC_GY_DAMPES_GRAVE_POT_5] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_5, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1608), "Dampes Grave Pot 5", RHT_POT_GRAVEYARD, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_5)); - locationTable[RC_GY_DAMPES_GRAVE_POT_6] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_6, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(239, -1577), "Dampes Grave Pot 6", RHT_POT_GRAVEYARD, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_6)); + locationTable[RC_GY_DAMPES_GRAVE_POT_1] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_1, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1542), "Dampes Grave Pot 1", RHT_POT_DAMPE, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_1)); + locationTable[RC_GY_DAMPES_GRAVE_POT_2] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_2, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1600), "Dampes Grave Pot 2", RHT_POT_DAMPE, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_2)); + locationTable[RC_GY_DAMPES_GRAVE_POT_3] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_3, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-364, -1571), "Dampes Grave Pot 3", RHT_POT_DAMPE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_3)); + locationTable[RC_GY_DAMPES_GRAVE_POT_4] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_4, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1540), "Dampes Grave Pot 4", RHT_POT_DAMPE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_4)); + locationTable[RC_GY_DAMPES_GRAVE_POT_5] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_5, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1608), "Dampes Grave Pot 5", RHT_POT_DAMPE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_5)); + locationTable[RC_GY_DAMPES_GRAVE_POT_6] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_6, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(239, -1577), "Dampes Grave Pot 6", RHT_POT_DAMPE, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_6)); locationTable[RC_GC_LOWER_STAIRCASE_POT_1] = Location::Pot(RC_GC_LOWER_STAIRCASE_POT_1, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, TWO_ACTOR_PARAMS(-189, 866), "Lower Staircase Pot 1", RHT_POT_GORON_CITY, RG_DEKU_STICK_1, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GC_LOWER_STAIRCASE_POT_1)); locationTable[RC_GC_LOWER_STAIRCASE_POT_2] = Location::Pot(RC_GC_LOWER_STAIRCASE_POT_2, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, TWO_ACTOR_PARAMS(-271, 825), "Lower Staircase Pot 2", RHT_POT_GORON_CITY, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GC_LOWER_STAIRCASE_POT_2)); locationTable[RC_GC_UPPER_STAIRCASE_POT_1] = Location::Pot(RC_GC_UPPER_STAIRCASE_POT_1, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, TWO_ACTOR_PARAMS(-1170, 60), "Upper Staircase Pot 1", RHT_POT_GORON_CITY, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GC_UPPER_STAIRCASE_POT_1)); @@ -235,15 +235,15 @@ void Rando::StaticData::RegisterPotLocations() { locationTable[RC_LLR_RAIN_SHED_POT_1] = Location::Pot(RC_LLR_RAIN_SHED_POT_1, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(852, 172), "Rain Shed Pot 1", RHT_POT_LON_LON_RANCH, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_RAIN_SHED_POT_1)); locationTable[RC_LLR_RAIN_SHED_POT_2] = Location::Pot(RC_LLR_RAIN_SHED_POT_2, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(840, 212), "Rain Shed Pot 2", RHT_POT_LON_LON_RANCH, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_RAIN_SHED_POT_2)); locationTable[RC_LLR_RAIN_SHED_POT_3] = Location::Pot(RC_LLR_RAIN_SHED_POT_3, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(872, 219), "Rain Shed Pot 3", RHT_POT_LON_LON_RANCH, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_RAIN_SHED_POT_3)); - locationTable[RC_LLR_TALONS_HOUSE_POT_1] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1255, 47), "Talons House Pot 1", RHT_POT_LON_LON_RANCH, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_1)); - locationTable[RC_LLR_TALONS_HOUSE_POT_2] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -51), "Talons House Pot 2", RHT_POT_LON_LON_RANCH, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_2)); - locationTable[RC_LLR_TALONS_HOUSE_POT_3] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -78), "Talons House Pot 3", RHT_POT_LON_LON_RANCH, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_3)); - locationTable[RC_HF_COW_GROTTO_POT_1] = Location::Pot(RC_HF_COW_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3410, -223), "Cow Grotto Pot 1", RHT_POT_HYRULE_FIELD, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_1)); - locationTable[RC_HF_COW_GROTTO_POT_2] = Location::Pot(RC_HF_COW_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3390, -258), "Cow Grotto Pot 2", RHT_POT_HYRULE_FIELD, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_2)); - locationTable[RC_HC_STORMS_GROTTO_POT_1] = Location::Pot(RC_HC_STORMS_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1843, 1014), "Storms Grotto Pot 1", RHT_POT_HYRULE_CASTLE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_1)); - locationTable[RC_HC_STORMS_GROTTO_POT_2] = Location::Pot(RC_HC_STORMS_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1769, 954), "Storms Grotto Pot 2", RHT_POT_HYRULE_CASTLE, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_2)); - locationTable[RC_HC_STORMS_GROTTO_POT_3] = Location::Pot(RC_HC_STORMS_GROTTO_POT_3, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1857, 897), "Storms Grotto Pot 3", RHT_POT_HYRULE_CASTLE, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_3)); - locationTable[RC_HC_STORMS_GROTTO_POT_4] = Location::Pot(RC_HC_STORMS_GROTTO_POT_4, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1764, 847), "Storms Grotto Pot 4", RHT_POT_HYRULE_CASTLE, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_4)); + locationTable[RC_LLR_TALONS_HOUSE_POT_1] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1255, 47), "Talons House Pot 1", RHT_POT_TALONS_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_1)); + locationTable[RC_LLR_TALONS_HOUSE_POT_2] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -51), "Talons House Pot 2", RHT_POT_TALONS_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_2)); + locationTable[RC_LLR_TALONS_HOUSE_POT_3] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -78), "Talons House Pot 3", RHT_POT_TALONS_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_3)); + locationTable[RC_HF_COW_GROTTO_POT_1] = Location::Pot(RC_HF_COW_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3410, -223), "Cow Grotto Pot 1", RHT_POT_WEB_GROTTO, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_1)); + locationTable[RC_HF_COW_GROTTO_POT_2] = Location::Pot(RC_HF_COW_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3390, -258), "Cow Grotto Pot 2", RHT_POT_WEB_GROTTO, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_2)); + locationTable[RC_HC_STORMS_GROTTO_POT_1] = Location::Pot(RC_HC_STORMS_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1843, 1014), "Storms Grotto Pot 1", RHT_POT_MUD_WALL_GROTTO, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_1)); + locationTable[RC_HC_STORMS_GROTTO_POT_2] = Location::Pot(RC_HC_STORMS_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1769, 954), "Storms Grotto Pot 2", RHT_POT_MUD_WALL_GROTTO, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_2)); + locationTable[RC_HC_STORMS_GROTTO_POT_3] = Location::Pot(RC_HC_STORMS_GROTTO_POT_3, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1857, 897), "Storms Grotto Pot 3", RHT_POT_MUD_WALL_GROTTO, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_3)); + locationTable[RC_HC_STORMS_GROTTO_POT_4] = Location::Pot(RC_HC_STORMS_GROTTO_POT_4, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1764, 847), "Storms Grotto Pot 4", RHT_POT_MUD_WALL_GROTTO, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_4)); // Dungeon Pots // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Spoiler Name Hint Text Key Vanilla Spoiler Collection Check diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index fa755325eda..d291655a011 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -11,6 +11,7 @@ #include "fishsanity.h" #include "macros.h" #include "3drando/hints.hpp" +#include "soh/util.h" #include "../kaleido.h" #include @@ -160,12 +161,16 @@ bool Context::IsQuestOfLocationActive(RandomizerCheck rc) { void Context::GenerateLocationPool() { allLocations.clear(); + overworldLocations.clear(); + for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { + dungeon->locations.clear(); + } for (Location& location : StaticData::GetLocationTable()) { // skip RCs that shouldn't be in the pool for any reason (i.e. settings, unsupported check type, etc.) // TODO: Exclude checks for some of the older shuffles from the pool too i.e. Frog Songs, Scrubs, etc.) if (location.GetRandomizerCheck() == RC_UNKNOWN_CHECK || location.GetRandomizerCheck() == RC_TRIFORCE_COMPLETED || // already in pool - (location.GetRandomizerCheck() == RC_MASTER_SWORD_PEDESTAL && + (location.GetRandomizerCheck() == RC_TOT_MASTER_SWORD && mOptions[RSK_SHUFFLE_MASTER_SWORD].Is(RO_GENERIC_OFF)) || (location.GetRandomizerCheck() == RC_KAK_100_GOLD_SKULLTULA_REWARD && mOptions[RSK_SHUFFLE_100_GS_REWARD].Is(RO_GENERIC_OFF)) || @@ -370,25 +375,8 @@ GetItemEntry Context::GetFinalGIEntry(const RandomizerCheck rc, const bool check return giEntry; } -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. - std::erase_if(stringValue, [](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }); - - return stringValue; -} - void Context::ParseSpoiler(const char* spoilerFileName) { - std::ifstream spoilerFileStream(sanitize(spoilerFileName)); + std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName)); if (!spoilerFileStream) { return; } @@ -397,11 +385,13 @@ void Context::ParseSpoiler(const char* spoilerFileName) { try { nlohmann::json spoilerFileJson; spoilerFileStream >> spoilerFileJson; + spoilerFileStream.close(); ParseHashIconIndexesJson(spoilerFileJson); Rando::Settings::GetInstance()->ParseJson(spoilerFileJson); ParseItemLocationsJson(spoilerFileJson); - ParseHintJson(spoilerFileJson); + ParseTricksJson(spoilerFileJson); mEntranceShuffler->ParseJson(spoilerFileJson); + ParseHintJson(spoilerFileJson); mDungeons->ParseJson(spoilerFileJson); mTrials->ParseJson(spoilerFileJson); mSpoilerLoaded = true; @@ -469,6 +459,17 @@ void Context::ParseHintJson(nlohmann::json spoilerFileJson) { CreateStaticHints(); } +void Context::ParseTricksJson(nlohmann::json spoilerFileJson) { + nlohmann::json enabledTricksJson = spoilerFileJson["enabledTricks"]; + const auto& settings = Rando::Settings::GetInstance(); + for (auto it : enabledTricksJson) { + int rt = settings->GetRandomizerTrickByName(it); + if (rt != -1) { + mTrickOptions[rt].Set(RO_GENERIC_ON); + } + } +} + std::shared_ptr Context::GetEntranceShuffler() { return mEntranceShuffler; } @@ -524,6 +525,10 @@ RandoOptionLACSCondition Context::LACSCondition() const { return mLACSCondition; } +void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) { + mLACSCondition = lacsCondition; +} + std::shared_ptr Context::GetKaleido() { if (mKaleido == nullptr) { mKaleido = std::make_shared(); diff --git a/soh/soh/Enhancements/randomizer/context.h b/soh/soh/Enhancements/randomizer/context.h index 7f18f7a9d5e..c7e3b3c1919 100644 --- a/soh/soh/Enhancements/randomizer/context.h +++ b/soh/soh/Enhancements/randomizer/context.h @@ -104,12 +104,22 @@ class Context { * @return RandoOptionLACSCondition */ RandoOptionLACSCondition LACSCondition() const; + + /** + * @brief Sets the resolved Light Arrow CutScene check condition. + * There is no direct option for this, it is inferred based on the value of a few other options. + * + * @param lacsCondition + */ + void LACSCondition(RandoOptionLACSCondition lacsCondition); + GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE); void ParseSpoiler(const char* spoilerFileName); void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson); void ParseItemLocationsJson(nlohmann::json spoilerFileJson); void WriteHintJson(nlohmann::ordered_json& spoilerFileJson); void ParseHintJson(nlohmann::json spoilerFileJson); + void ParseTricksJson(nlohmann::json spoilerFileJson); std::map overrides = {}; std::vector> playthroughLocations = {}; std::vector everyPossibleLocation = {}; diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 05638a2b3fe..ea9b80ee30c 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -250,7 +250,319 @@ std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegio return RegionTable(parentRegion)->regionName + " -> " + RegionTable(connectedRegion)->regionName; } -void SetAllEntrancesData(std::vector& entranceShuffleTable) { +std::unordered_map entranceMap; + +void SetAllEntrancesData() { + std::vector entranceShuffleTable = { + // clang-format off + // Type Parent Region Connected Region Index + { { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE }, + { EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } }, + { { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE }, + { EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } }, + { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE }, + { EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } }, + { { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE }, + { EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } }, + { { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE }, + { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } }, + { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE }, + { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } }, + { { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE }, + { EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } }, + + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 }, + { EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } }, + { { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 }, + { EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } }, + { { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 }, + { EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } }, + { { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 }, + { EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } }, + { { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 }, + { EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 }, + { EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 }, + { EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 }, + { EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 }, + { EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } }, + { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 }, + { EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } }, + { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE }, + { EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 }, + { EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 }, + { EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT }, + { EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } }, + { { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK }, + { EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } }, + { { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 }, + { EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } }, + { { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 }, + { EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } }, + { { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 }, + { EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } }, + { { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 }, + { EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } }, + { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE }, + { EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } }, + { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 }, + { EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } }, + { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER }, + { EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 }, + { EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 }, + { EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 }, + { EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 }, + { EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } }, + { { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS }, + { EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC }, + { EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD }, + // 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy + // fountain exits (normally they both use 0x340) + { EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } }, + { { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC }, + { EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT }, + { EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF }, + { EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } }, + + { { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 }, + { EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } }, + { { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE }, + { EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } }, + { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL }, + { EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } }, + { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT }, + { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } }, + { { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK }, + { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } }, + + // Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the + // grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c + // Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the + // grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c + { { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) }, + { EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) }, + { EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) }, + { EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) }, + { EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) }, + { EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) }, + { EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) }, + { EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) }, + { EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) }, + { EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) }, + { EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) }, + { EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) }, + { EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } }, + + // Graves have their own specified entrance indices + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } }, + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } }, + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } }, + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } }, + + { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT }, + { EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } }, + { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT }, + { EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } }, + { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT }, + { EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } }, + { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT }, + { EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } }, + { { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT }, + { EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } }, + { { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT }, + { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT }, + { EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT }, + { EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT }, + { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE }, + { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT }, + { EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE }, + { EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } }, + { { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT }, + { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } }, + { { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT }, + { EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } }, + { { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT }, + { EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } }, + { { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT }, + { EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } }, + { { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT }, + { EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } }, + { { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT }, + { EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } }, + { { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT }, + { EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } }, + { { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE }, + { EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } }, + { { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT }, + { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } }, + { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT }, + { EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } }, + { { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT }, + { EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } }, + { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT }, + { EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } }, + { { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE }, + { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } }, + { { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT }, + { EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } }, + + { { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT }, + NO_RETURN_ENTRANCE }, + + { { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP }, + NO_RETURN_ENTRANCE }, + + { { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN }, + NO_RETURN_ENTRANCE }, + { { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 }, + NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between + // Adult Spawn and prelude of light (normally they both use 0x5F4) + { { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD }, + NO_RETURN_ENTRANCE }, + + { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT, ENTR_DEKU_TREE_BOSS_DOOR } }, + { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT, ENTR_DODONGOS_CAVERN_BOSS_DOOR } }, + { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT, ENTR_JABU_JABU_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } }, + + { { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + // clang-format on + }; + auto ctx = Rando::Context::GetInstance(); for (auto& entrancePair : entranceShuffleTable) { @@ -262,6 +574,7 @@ void SetAllEntrancesData(std::vector& entranceShuffleTable) { forwardEntrance->SetIndex(forwardEntry.index); forwardEntrance->SetType(forwardEntry.type); forwardEntrance->SetAsPrimary(); + entranceMap[forwardEntry.index] = forwardEntrance; // When decouple entrances is on, mark the forward entrance if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { @@ -273,6 +586,7 @@ void SetAllEntrancesData(std::vector& entranceShuffleTable) { returnEntrance->SetIndex(returnEntry.index); returnEntrance->SetType(returnEntry.type); forwardEntrance->BindTwoWay(returnEntrance); + entranceMap[returnEntry.index] = returnEntrance; // Mark reverse entrance as decoupled if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { @@ -782,390 +1096,80 @@ bool EntranceShuffler::ShuffleEntrances(std::vector& entrances, std:: if (ReplaceEntrance(entrance, target, rollbacks)) { break; - } - } - - if (entrance->GetConnectedRegionKey() == RR_NONE) { - return false; - } - } - - // all entrances were validly connected - return true; -} - -void EntranceShuffler::ShuffleEntrancePool(std::vector& entrancePool, - std::vector& targetEntrances, int retryCount) { - mNoRandomEntrances = false; - - auto splitEntrances = SplitEntrancesByRequirements(entrancePool, targetEntrances); - - auto& restrictiveEntrances = splitEntrances[0]; - auto& softEntrances = splitEntrances[1]; - - int retries = retryCount; - while (retries > 0) { - if (retries != retryCount) { -#ifdef ENABLE_DEBUG - std::string ticks = std::to_string(svcGetSystemTick()); - auto message = "Failed to connect entrances. Retrying " + std::to_string(retries) + - " more times.\nDumping World Graph at " + ticks + "\n"; - SPDLOG_DEBUG(message); - // Regions::DumpWorldGraph(ticks); -#endif - } - retries--; - - std::vector rollbacks = {}; - - // Shuffle Restrictive Entrances first while more regions are available in - // order to heavily reduce the chances of the placement failing - bool success = ShuffleEntrances(restrictiveEntrances, targetEntrances, rollbacks); - if (success) { - success = ShuffleEntrances(softEntrances, targetEntrances, rollbacks); - if (!success) { - for (auto& pair : rollbacks) { - RestoreConnections(pair.first, pair.second); - mCurNumRandomizedEntrances--; - } - continue; - } - } else { - for (auto& pair : rollbacks) { - RestoreConnections(pair.first, pair.second); - mCurNumRandomizedEntrances--; - } - continue; - } - - // If there are no issues, log the connections and continue - for (auto& pair : rollbacks) { - ConfirmReplacement(pair.first, pair.second); - } - break; - } - - if (retries <= 0) { - SPDLOG_DEBUG("Entrance placement attempt count exceeded. Restarting randomization completely"); - mEntranceShuffleFailure = true; - } -} - -int EntranceShuffler::ShuffleAllEntrances() { - auto ctx = Rando::Context::GetInstance(); - - mTotalRandomizableEntrances = 0; - mCurNumRandomizedEntrances = 0; - - std::vector entranceShuffleTable = { - // clang-format off - // Type Parent Region Connected Region Index - { { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE }, - { EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } }, - { { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE }, - { EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } }, - { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE }, - { EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } }, - { { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE }, - { EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } }, - { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE }, - { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } }, - { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE }, - { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } }, - { { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE }, - { EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } }, - - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 }, - { EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } }, - { { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 }, - { EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } }, - { { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 }, - { EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } }, - { { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 }, - { EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } }, - { { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 }, - { EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 }, - { EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 }, - { EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 }, - { EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 }, - { EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } }, - { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 }, - { EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } }, - { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE }, - { EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 }, - { EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 }, - { EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT }, - { EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } }, - { { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK }, - { EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } }, - { { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 }, - { EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } }, - { { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 }, - { EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } }, - { { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 }, - { EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } }, - { { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 }, - { EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } }, - { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE }, - { EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } }, - { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 }, - { EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } }, - { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER }, - { EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 }, - { EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 }, - { EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 }, - { EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 }, - { EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } }, - { { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS }, - { EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC }, - { EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD }, - // 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy - // fountain exits (normally they both use 0x340) - { EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } }, - { { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC }, - { EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT }, - { EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF }, - { EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } }, + } + } - { { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 }, - { EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } }, - { { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE }, - { EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } }, - { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL }, - { EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } }, - { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT }, - { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } }, - { { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK }, - { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } }, + if (entrance->GetConnectedRegionKey() == RR_NONE) { + return false; + } + } - // Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the - // grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c - // Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the - // grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c - { { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) }, - { EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) }, - { EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) }, - { EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) }, - { EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) }, - { EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) }, - { EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) }, - { EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) }, - { EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) }, - { EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) }, - { EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) }, - { EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) }, - { EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } }, + // all entrances were validly connected + return true; +} - // Graves have their own specified entrance indices - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } }, - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } }, - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } }, - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } }, +void EntranceShuffler::ShuffleEntrancePool(std::vector& entrancePool, + std::vector& targetEntrances, int retryCount) { + mNoRandomEntrances = false; - { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT }, - { EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } }, - { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT }, - { EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } }, - { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT }, - { EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } }, - { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT }, - { EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } }, - { { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT }, - { EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } }, - { { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT }, - { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT }, - { EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT }, - { EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT }, - { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE }, - { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT }, - { EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE }, - { EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } }, - { { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT }, - { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } }, - { { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT }, - { EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } }, - { { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT }, - { EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } }, - { { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT }, - { EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } }, - { { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT }, - { EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } }, - { { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT }, - { EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } }, - { { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT }, - { EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } }, - { { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE }, - { EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } }, - { { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT }, - { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } }, - { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT }, - { EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } }, - { { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT }, - { EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } }, - { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT }, - { EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } }, - { { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE }, - { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } }, - { { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT }, - { EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } }, + auto splitEntrances = SplitEntrancesByRequirements(entrancePool, targetEntrances); - { { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT }, - NO_RETURN_ENTRANCE }, + auto& restrictiveEntrances = splitEntrances[0]; + auto& softEntrances = splitEntrances[1]; - { { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP }, - NO_RETURN_ENTRANCE }, + int retries = retryCount; + while (retries > 0) { + if (retries != retryCount) { +#ifdef ENABLE_DEBUG + std::string ticks = std::to_string(svcGetSystemTick()); + auto message = "Failed to connect entrances. Retrying " + std::to_string(retries) + + " more times.\nDumping World Graph at " + ticks + "\n"; + SPDLOG_DEBUG(message); + // Regions::DumpWorldGraph(ticks); +#endif + } + retries--; - { { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN }, - NO_RETURN_ENTRANCE }, - { { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 }, - NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between - // Adult Spawn and prelude of light (normally they both use 0x5F4) - { { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD }, - NO_RETURN_ENTRANCE }, + std::vector rollbacks = {}; - { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, ENTR_DEKU_TREE_BOSS_DOOR } }, - { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, ENTR_DODONGOS_CAVERN_BOSS_DOOR } }, - { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, ENTR_JABU_JABU_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } }, + // Shuffle Restrictive Entrances first while more regions are available in + // order to heavily reduce the chances of the placement failing + bool success = ShuffleEntrances(restrictiveEntrances, targetEntrances, rollbacks); + if (success) { + success = ShuffleEntrances(softEntrances, targetEntrances, rollbacks); + if (!success) { + for (auto& pair : rollbacks) { + RestoreConnections(pair.first, pair.second); + mCurNumRandomizedEntrances--; + } + continue; + } + } else { + for (auto& pair : rollbacks) { + RestoreConnections(pair.first, pair.second); + mCurNumRandomizedEntrances--; + } + continue; + } - { { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - // clang-format on - }; + // If there are no issues, log the connections and continue + for (auto& pair : rollbacks) { + ConfirmReplacement(pair.first, pair.second); + } + break; + } + + if (retries <= 0) { + SPDLOG_DEBUG("Entrance placement attempt count exceeded. Restarting randomization completely"); + mEntranceShuffleFailure = true; + } +} + +int EntranceShuffler::ShuffleAllEntrances() { + auto ctx = Rando::Context::GetInstance(); + + mTotalRandomizableEntrances = 0; + mCurNumRandomizedEntrances = 0; std::map priorityEntranceTable = { { "Bolero", { { RR_DMC_CENTRAL_LOCAL }, { EntranceType::OwlDrop, EntranceType::WarpSong } } }, @@ -1178,7 +1182,7 @@ int EntranceShuffler::ShuffleAllEntrances() { }; mEntranceShuffleFailure = false; - SetAllEntrancesData(entranceShuffleTable); + SetAllEntrancesData(); EntrancePools oneWayEntrancePools = {}; EntrancePools entrancePools = {}; @@ -1488,11 +1492,11 @@ int EntranceShuffler::ShuffleAllEntrances() { if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) { // If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance std::map bossExits = { - { EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY), + { EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT), GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE)) }, - { EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY), + { EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT), GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL)) }, - { EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY), + { EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT), GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN)) }, { EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY), GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) }, @@ -1530,11 +1534,11 @@ int EntranceShuffler::ShuffleAllEntrances() { // Pair std::vector bossRoomExitPairs = { { GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)), - GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY)) }, + GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT)) }, { GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)), - GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY)) }, + GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT)) }, { GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)), - GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY)) }, + GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT)) }, { GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)), GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY)) }, { GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)), @@ -1662,6 +1666,30 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) { } } } catch (const std::exception& e) { throw e; } + // We may need to reset more things here or elsewhere in spoiler loading + RegionTable_Init(); + ApplyEntranceOverrides(); + SetAreas(); +} + +void EntranceShuffler::ApplyEntranceOverrides() { + SetAllEntrancesData(); + + for (size_t i = 0; i < entranceOverrides.size(); i++) { + EntranceOverride entranceOverride = entranceOverrides[i]; + + if (entranceOverride.index == 0 && entranceOverride.destination == 0 && entranceOverride.override == 0 && + entranceOverride.overrideDestination == 0) { + continue; + } + + Entrance* entrance = entranceMap[entranceOverride.index]; + Entrance* overrideEntrance = entranceMap[entranceOverride.override]; + + entrance->Disconnect(); + entrance->Connect(overrideEntrance->GetOriginalConnectedRegionKey()); + entrance->SetAsShuffled(); + } } } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/entrance.h b/soh/soh/Enhancements/randomizer/entrance.h index 026da742644..6cb631db30f 100644 --- a/soh/soh/Enhancements/randomizer/entrance.h +++ b/soh/soh/Enhancements/randomizer/entrance.h @@ -128,6 +128,7 @@ class EntranceShuffler { void CreateEntranceOverrides(); void UnshuffleAllEntrances(); void ParseJson(nlohmann::json spoilerFileJson); + void ApplyEntranceOverrides(); private: std::vector AssumeEntrancePool(std::vector& entrancePool); diff --git a/soh/soh/Enhancements/randomizer/fishsanity.cpp b/soh/soh/Enhancements/randomizer/fishsanity.cpp index fe9b4c0f171..99636d1a427 100644 --- a/soh/soh/Enhancements/randomizer/fishsanity.cpp +++ b/soh/soh/Enhancements/randomizer/fishsanity.cpp @@ -74,7 +74,7 @@ Fishsanity::~Fishsanity() { bool Fishsanity::GetFishLocationIncluded(Rando::Location* loc, FishsanityOptionsSource optionsSource) { auto [mode, numFish, ageSplit] = GetOptions(optionsSource); - if (loc->GetRCType() != RCTYPE_FISH || mode == RO_FISHSANITY_OFF) { + if (loc->GetRCType() != RCTYPE_FISH || mode == RO_FISHSANITY_OFF || mode == RO_FISHSANITY_HYRULE_LOACH) { return false; } RandomizerCheck rc = loc->GetRandomizerCheck(); diff --git a/soh/soh/Enhancements/randomizer/hint.cpp b/soh/soh/Enhancements/randomizer/hint.cpp index d81e94f2f88..c2011c10165 100644 --- a/soh/soh/Enhancements/randomizer/hint.cpp +++ b/soh/soh/Enhancements/randomizer/hint.cpp @@ -299,6 +299,8 @@ const CustomMessage Hint::GetHintMessage(MessageFormat format, uint8_t id) const } else if (hintType == HINT_TYPE_ALTAR_CHILD) { if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { hintText = StaticData::hintTextTable[RHT_CHILD_ALTAR_STONES].GetHintMessage(); + } else { + hintText.SetTextBoxType(TEXTBOX_TYPE_BLUE); } if (ctx->GetOption(RSK_DOOR_OF_TIME).Is(RO_DOOROFTIME_OPEN)) { hintText += CustomMessage(StaticData::hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTOPEN].GetHintMessage()); @@ -310,6 +312,8 @@ const CustomMessage Hint::GetHintMessage(MessageFormat format, uint8_t id) const } else if (hintType == HINT_TYPE_ALTAR_ADULT) { if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { hintText = StaticData::hintTextTable[RHT_ADULT_ALTAR_MEDALLIONS].GetHintMessage(); + } else { + hintText.SetTextBoxType(TEXTBOX_TYPE_BLUE); } hintText += GetBridgeReqsText() + GetGanonBossKeyText() + StaticData::hintTextTable[RHT_ADULT_ALTAR_TEXT_END].GetHintMessage(); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 5a803f39bd4..0b864c52ce8 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1,4 +1,4 @@ -#include +#include #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" #include "soh/Enhancements/enhancementTypes.h" @@ -245,10 +245,6 @@ void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) { Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER); } - if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_POCKET_EGG) { - Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG); - } - RandomizerCheck rc = GetRandomizerCheckFromFlag(flagType, flag); if (rc == RC_UNKNOWN_CHECK) return; @@ -331,7 +327,10 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() { getItemEntry.modIndex == MOD_RANDOMIZER) && (getItemEntry.getItemCategory == ITEM_CATEGORY_JUNK || getItemEntry.getItemCategory == ITEM_CATEGORY_SKULLTULA_TOKEN || - getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER))))) { + getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER || + // Treat small keys as junk if Skeleton Key is obtained. + (getItemEntry.getItemCategory == ITEM_CATEGORY_SMALL_KEY && + Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY))))))) { Item_DropCollectible(gPlayState, &spawnPos, ITEM00_SOH_GIVE_ITEM_ENTRY | 0x8000); } } @@ -378,14 +377,17 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) { randomizerQueuedItemEntry = GET_ITEM_NONE; } + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (receivedItemEntry.modIndex == MOD_NONE && (receivedItemEntry.itemId == ITEM_HEART_PIECE || receivedItemEntry.itemId == ITEM_HEART_PIECE_2 || receivedItemEntry.itemId == ITEM_HEART_CONTAINER)) { - gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts + gSaveContext.healthAccumulator = heartUnits * 20; // Refill 20 hearts if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health += heartUnits; } } @@ -869,7 +871,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l *should = !Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL) && LINK_IS_ADULT && gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_KAKARIKO_VILLAGE && CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && - CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER); + CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER) && gSaveContext.cutsceneIndex < 0xFFF0; break; case VB_BE_ELIGIBLE_FOR_CHILD_ROLLING_GORON_REWARD: { // Don't require a bomb bag to get prize in rando @@ -885,7 +887,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l break; } case VB_GIVE_ITEM_MASTER_SWORD: - if (RAND_GET_OPTION(RSK_SHUFFLE_MASTER_SWORD)) { + if (RAND_GET_OPTION(RSK_SHUFFLE_MASTER_SWORD) || RAND_GET_OPTION(RSK_STARTING_MASTER_SWORD)) { *should = false; } else { *should = true; @@ -1027,17 +1029,55 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l } if (item00->itemEntry.modIndex == MOD_NONE) { + std::string message; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + message = "Vous obtenez: "; + break; + case LANGUAGE_GER: + message = "Du erhältst: "; + break; + case LANGUAGE_ENG: + default: + message = "You found "; + break; + } + Notification::Emit({ .itemIcon = GetTextureForItemId(item00->itemEntry.itemId), - .message = "You found ", + .message = message, .suffix = SohUtils::GetItemName(item00->itemEntry.itemId), }); } else if (item00->itemEntry.modIndex == MOD_RANDOMIZER) { + std::string message; + std::string itemName; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + message = "Vous obtenez: "; + itemName = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) + .GetName() + .french; + break; + case LANGUAGE_GER: + message = "Du erhältst: "; + itemName = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) + .GetName() + .german; + break; + case LANGUAGE_ENG: + default: + message = "You found "; + itemName = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) + .GetName() + .english; + break; + } + Notification::Emit({ - .message = "You found ", - .suffix = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) - .GetName() - .english, + .message = message, + .suffix = itemName, }); } @@ -1591,6 +1631,17 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l } break; } + case VB_GIVE_RANDO_GLITCH_FISHING_PRIZE: { + if (IS_RANDO) { + Fishing* fishing = va_arg(args, Fishing*); + if (!Flags_GetRandomizerInf(RAND_INF_ADULT_FISHING)) { + Flags_SetRandomizerInf(RAND_INF_ADULT_FISHING); + } + *should = true; + fishing->stateAndTimer = 0; + } + break; + } case VB_TRADE_TIMER_EYEDROPS: { EnMk* enMk = va_arg(args, EnMk*); Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG); @@ -1632,7 +1683,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l gPlayState->actorCtx.flags.tempCollect = 0; // If the respawnFlag is set for a grotto return, we don't want the void out to happen. - // Set the data flag to one to prevent the respawn point from being overriden by dungeon doors. + // Set the data flag to one to prevent the respawn point from being overridden by dungeon doors. if (gSaveContext.respawnFlag == 2) { gSaveContext.respawn[RESPAWN_MODE_DOWN].data = 1; *should = false; @@ -1754,10 +1805,6 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) { // Handle updated link spawn positions Entrance_OverrideSpawnScene(sceneNum, gPlayState->curSpawn); - - Entrance_OverrideWeatherState(); - // Need to reinitialize the environment after replacing the weather mode - Play_InitEnvironment(gPlayState, gPlayState->skyboxId); } // LACs & Prelude checks @@ -1796,6 +1843,13 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) { }); } +void RandomizerAfterSceneCommandsHandler(int16_t sceneNum) { + // ENTRTODO: Move all entrance rando handling to a dedicated file + if (RAND_GET_OPTION(RSK_SHUFFLE_ENTRANCES)) { + Entrance_OverrideWeatherState(); + } +} + void EnSi_DrawRandomizedItem(EnSi* enSi, PlayState* play) { GetItemEntry randoItem = enSi->sohGetItemEntry; if (CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0)) { @@ -2351,6 +2405,7 @@ void RandomizerRegisterHooks() { static uint32_t onDialogMessageHook = 0; static uint32_t onVanillaBehaviorHook = 0; static uint32_t onSceneInitHook = 0; + static uint32_t afterSceneCommandsHook = 0; static uint32_t onActorInitHook = 0; static uint32_t onActorUpdateHook = 0; static uint32_t onPlayerUpdateHook = 0; @@ -2387,6 +2442,7 @@ void RandomizerRegisterHooks() { GameInteractor::Instance->UnregisterGameHook(onDialogMessageHook); GameInteractor::Instance->UnregisterGameHook(onVanillaBehaviorHook); GameInteractor::Instance->UnregisterGameHook(onSceneInitHook); + GameInteractor::Instance->UnregisterGameHook(afterSceneCommandsHook); GameInteractor::Instance->UnregisterGameHook(onActorInitHook); GameInteractor::Instance->UnregisterGameHook(onActorUpdateHook); GameInteractor::Instance->UnregisterGameHook(onPlayerUpdateHook); @@ -2419,6 +2475,7 @@ void RandomizerRegisterHooks() { onDialogMessageHook = 0; onVanillaBehaviorHook = 0; onSceneInitHook = 0; + afterSceneCommandsHook = 0; onActorInitHook = 0; onActorUpdateHook = 0; onPlayerUpdateHook = 0; @@ -2470,6 +2527,8 @@ void RandomizerRegisterHooks() { RandomizerOnVanillaBehaviorHandler); onSceneInitHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnSceneInitHandler); + afterSceneCommandsHook = GameInteractor::Instance->RegisterGameHook( + RandomizerAfterSceneCommandsHandler); onActorInitHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnActorInitHandler); onActorUpdateHook = diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp index ee0dc940385..71f9fff38e6 100644 --- a/soh/soh/Enhancements/randomizer/item.cpp +++ b/soh/soh/Enhancements/randomizer/item.cpp @@ -21,7 +21,8 @@ Item::Item(const RandomizerGet randomizerGet_, Text name_, const ItemType type_, const int16_t chestAnimation_, const GetItemCategory category_, const uint16_t modIndex_, const bool progressive_, const uint16_t price_) : randomizerGet(randomizerGet_), name(std::move(name_)), type(type_), getItemId(getItemId_), - advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), progressive(progressive_), price(price_) { + advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), category(category_), progressive(progressive_), + price(price_) { if (modIndex_ == MOD_RANDOMIZER || getItemId > 0x7D) { giEntry = std::make_shared(GetItemEntry{ itemId_, field_, static_cast((chestAnimation_ != CHEST_ANIM_SHORT ? 1 : -1) * (gid_ + 1)), textId_, @@ -36,24 +37,31 @@ Item::Item(const RandomizerGet randomizerGet_, Text name_, const ItemType type_, } Item::Item(const RandomizerGet randomizerGet_, Text name_, const ItemType type_, const int16_t getItemId_, - const bool advancement_, LogicVal logicVal_, const RandomizerHintTextKey hintKey_, const bool progressive_, - const uint16_t price_) + const bool advancement_, LogicVal logicVal_, const RandomizerHintTextKey hintKey_, + const GetItemCategory category_, const bool progressive_, const uint16_t price_) : randomizerGet(randomizerGet_), name(std::move(name_)), type(type_), getItemId(getItemId_), - advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), progressive(progressive_), price(price_) { + advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), category(category_), progressive(progressive_), + price(price_) { } Item::~Item() = default; void Item::ApplyEffect() const { auto ctx = Rando::Context::GetInstance(); - ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true); - ctx->GetLogic()->SetInLogic(logicVal, true); + auto logic = ctx->GetLogic(); + if (!logic->CalculatingAvailableChecks) { + logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true); + } + logic->SetInLogic(logicVal, true); } void Item::UndoEffect() const { auto ctx = Rando::Context::GetInstance(); - ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false); - ctx->GetLogic()->SetInLogic(logicVal, false); + auto logic = ctx->GetLogic(); + if (!logic->CalculatingAvailableChecks) { + logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false); + } + logic->SetInLogic(logicVal, false); } const Text& Item::GetName() const { @@ -450,6 +458,10 @@ const HintText& Item::GetHint() const { return StaticData::hintTextTable[hintKey]; } +GetItemCategory Item::GetCategory() { + return category; +} + bool Item::operator==(const Item& right) const { return type == right.GetItemType() && getItemId == right.GetItemID(); } diff --git a/soh/soh/Enhancements/randomizer/item.h b/soh/soh/Enhancements/randomizer/item.h index da86321518c..13a654d9c22 100644 --- a/soh/soh/Enhancements/randomizer/item.h +++ b/soh/soh/Enhancements/randomizer/item.h @@ -35,7 +35,8 @@ class Item { uint16_t textId_, uint16_t field_, int16_t chestAnimation_, GetItemCategory category_, uint16_t modIndex_, bool progressive_ = false, uint16_t price_ = 0); Item(RandomizerGet randomizerGet_, Text name_, ItemType type_, int16_t getItemId_, bool advancement_, - LogicVal logicVal_, RandomizerHintTextKey hintKey_, bool progressive_ = false, uint16_t price_ = 0); + LogicVal logicVal_, RandomizerHintTextKey hintKey_, GetItemCategory category_, bool progressive_ = false, + uint16_t price_ = 0); ~Item(); void ApplyEffect() const; @@ -58,6 +59,7 @@ class Item { bool IsMajorItem() const; RandomizerHintTextKey GetHintKey() const; const HintText& GetHint() const; + GetItemCategory GetCategory(); bool operator==(const Item& right) const; bool operator!=(const Item& right) const; @@ -69,6 +71,7 @@ class Item { bool advancement; LogicVal logicVal; RandomizerHintTextKey hintKey; + GetItemCategory category; bool progressive; uint16_t price; bool playthrough = false; diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index d8113edd3ad..f00796f317f 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -59,19 +59,19 @@ void Rando::StaticData::InitItemTable() { // Skulltula Token itemTable[RG_GOLD_SKULLTULA_TOKEN] = Item(RG_GOLD_SKULLTULA_TOKEN, Text{ "Gold Skulltula Token", "Symbole de Skulltula d'Or", "Goldenes Skulltula-Symbol" }, ITEMTYPE_TOKEN, GI_SKULL_TOKEN, true, LOGIC_GOLD_SKULLTULA_TOKENS, RHT_GOLD_SKULLTULA_TOKEN, ITEM_SKULL_TOKEN, OBJECT_GI_SUTARU, GID_SKULL_TOKEN, 0xB4, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SKULLTULA_TOKEN, MOD_NONE); // Progressive Items - itemTable[RG_PROGRESSIVE_HOOKSHOT] = Item(RG_PROGRESSIVE_HOOKSHOT, Text{ "Progressive Hookshot", "Grappin (prog.)", "Progressiver Fanghaken" }, ITEMTYPE_ITEM, 0x80, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_PROGRESSIVE_HOOKSHOT, true); - itemTable[RG_PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{ "Strength Upgrade", "Amélioration de Force (prog.)", "Progressives Kraft-Upgrade" }, ITEMTYPE_ITEM, 0x81, true, LOGIC_PROGRESSIVE_STRENGTH, RHT_PROGRESSIVE_STRENGTH, true); - itemTable[RG_PROGRESSIVE_BOMB_BAG] = Item(RG_PROGRESSIVE_BOMB_BAG, Text{ "Progressive Bomb Bag", "Sac de Bombes (prog.)", "Progressive Bombentasche" }, ITEMTYPE_ITEM, 0x82, true, LOGIC_PROGRESSIVE_BOMB_BAG, RHT_PROGRESSIVE_BOMB_BAG, true); - itemTable[RG_PROGRESSIVE_BOW] = Item(RG_PROGRESSIVE_BOW, Text{ "Progressive Bow", "Arc (prog.)", "Progressiver Bogen" }, ITEMTYPE_ITEM, 0x83, true, LOGIC_PROGRESSIVE_BOW, RHT_PROGRESSIVE_BOW, true); - itemTable[RG_PROGRESSIVE_SLINGSHOT] = Item(RG_PROGRESSIVE_SLINGSHOT, Text{ "Progressive Slingshot", "Lance-Pierre (prog.)", "Progressive Steinschleuder" }, ITEMTYPE_ITEM, 0x84, true, LOGIC_PROGRESSIVE_BULLET_BAG, RHT_PROGRESSIVE_SLINGSHOT, true); - itemTable[RG_PROGRESSIVE_WALLET] = Item(RG_PROGRESSIVE_WALLET, Text{ "Progressive Wallet", "Bourse (prog.)", "Progressive Geldbörse" }, ITEMTYPE_ITEM, 0x85, true, LOGIC_PROGRESSIVE_WALLET, RHT_PROGRESSIVE_WALLET, true); - itemTable[RG_PROGRESSIVE_SCALE] = Item(RG_PROGRESSIVE_SCALE, Text{ "Progressive Scale", "Écaille (prog.)", "Progressive Schuppe" }, ITEMTYPE_ITEM, 0x86, true, LOGIC_PROGRESSIVE_SCALE, RHT_PROGRESSIVE_SCALE, true); - itemTable[RG_PROGRESSIVE_NUT_UPGRADE] = Item(RG_PROGRESSIVE_NUT_UPGRADE, Text{ "Progressive Nut Capacity", "Capacité de Noix (prog.)", "Progressive Nuß-Kapazität" }, ITEMTYPE_ITEM, 0x87, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_PROGRESSIVE_NUT_UPGRADE, true); - itemTable[RG_PROGRESSIVE_STICK_UPGRADE] = Item(RG_PROGRESSIVE_STICK_UPGRADE, Text{ "Progressive Stick Capacity", "Capacité de Bâtons (prog.)", "Progressive Stab-Kapazität" }, ITEMTYPE_ITEM, 0x88, true, LOGIC_PROGRESSIVE_STICK_BAG, RHT_PROGRESSIVE_STICK_UPGRADE, true); - itemTable[RG_PROGRESSIVE_BOMBCHUS] = Item(RG_PROGRESSIVE_BOMBCHUS, Text{ "Progressive Bombchu", "Missiles (prog.)", "Progressive Krabbelminen" }, ITEMTYPE_ITEM, 0x89, true, LOGIC_BOMBCHUS, RHT_PROGRESSIVE_BOMBCHUS, true); - itemTable[RG_PROGRESSIVE_MAGIC_METER] = Item(RG_PROGRESSIVE_MAGIC_METER, Text{ "Progressive Magic Meter", "Jauge de Magie (prog.)", "Progressives Magisches Maß" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_PROGRESSIVE_MAGIC_METER, true); - itemTable[RG_PROGRESSIVE_OCARINA] = Item(RG_PROGRESSIVE_OCARINA, Text{ "Progressive Ocarina", "Ocarina (prog.)", "Progressive Okarina" }, ITEMTYPE_ITEM, 0x8B, true, LOGIC_PROGRESSIVE_OCARINA, RHT_PROGRESSIVE_OCARINA, true); - itemTable[RG_PROGRESSIVE_GORONSWORD] = Item(RG_PROGRESSIVE_GORONSWORD, Text{ "Progressive Goron Sword", "Épée Goron (prog.)", "Progressives Goronen-Schwert" }, ITEMTYPE_ITEM, 0xD4, true, LOGIC_PROGRESSIVE_GIANT_KNIFE, RHT_PROGRESSIVE_GORONSWORD, true); + itemTable[RG_PROGRESSIVE_HOOKSHOT] = Item(RG_PROGRESSIVE_HOOKSHOT, Text{ "Progressive Hookshot", "Grappin (prog.)", "Progressiver Fanghaken" }, ITEMTYPE_ITEM, 0x80, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_PROGRESSIVE_HOOKSHOT, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{ "Strength Upgrade", "Amélioration de Force (prog.)", "Progressives Kraft-Upgrade" }, ITEMTYPE_ITEM, 0x81, true, LOGIC_PROGRESSIVE_STRENGTH, RHT_PROGRESSIVE_STRENGTH, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_BOMB_BAG] = Item(RG_PROGRESSIVE_BOMB_BAG, Text{ "Progressive Bomb Bag", "Sac de Bombes (prog.)", "Progressive Bombentasche" }, ITEMTYPE_ITEM, 0x82, true, LOGIC_PROGRESSIVE_BOMB_BAG, RHT_PROGRESSIVE_BOMB_BAG, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_BOW] = Item(RG_PROGRESSIVE_BOW, Text{ "Progressive Bow", "Arc (prog.)", "Progressiver Bogen" }, ITEMTYPE_ITEM, 0x83, true, LOGIC_PROGRESSIVE_BOW, RHT_PROGRESSIVE_BOW, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_SLINGSHOT] = Item(RG_PROGRESSIVE_SLINGSHOT, Text{ "Progressive Slingshot", "Lance-Pierre (prog.)", "Progressive Steinschleuder" }, ITEMTYPE_ITEM, 0x84, true, LOGIC_PROGRESSIVE_BULLET_BAG, RHT_PROGRESSIVE_SLINGSHOT, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_WALLET] = Item(RG_PROGRESSIVE_WALLET, Text{ "Progressive Wallet", "Bourse (prog.)", "Progressive Geldbörse" }, ITEMTYPE_ITEM, 0x85, true, LOGIC_PROGRESSIVE_WALLET, RHT_PROGRESSIVE_WALLET, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_SCALE] = Item(RG_PROGRESSIVE_SCALE, Text{ "Progressive Scale", "Écaille (prog.)", "Progressive Schuppe" }, ITEMTYPE_ITEM, 0x86, true, LOGIC_PROGRESSIVE_SCALE, RHT_PROGRESSIVE_SCALE, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_NUT_UPGRADE] = Item(RG_PROGRESSIVE_NUT_UPGRADE, Text{ "Progressive Nut Capacity", "Capacité de Noix (prog.)", "Progressive Nuß-Kapazität" }, ITEMTYPE_ITEM, 0x87, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_PROGRESSIVE_NUT_UPGRADE, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_STICK_UPGRADE] = Item(RG_PROGRESSIVE_STICK_UPGRADE, Text{ "Progressive Stick Capacity", "Capacité de Bâtons (prog.)", "Progressive Stab-Kapazität" }, ITEMTYPE_ITEM, 0x88, true, LOGIC_PROGRESSIVE_STICK_BAG, RHT_PROGRESSIVE_STICK_UPGRADE, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_BOMBCHUS] = Item(RG_PROGRESSIVE_BOMBCHUS, Text{ "Progressive Bombchu", "Missiles (prog.)", "Progressive Krabbelminen" }, ITEMTYPE_ITEM, 0x89, true, LOGIC_BOMBCHUS, RHT_PROGRESSIVE_BOMBCHUS, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_MAGIC_METER] = Item(RG_PROGRESSIVE_MAGIC_METER, Text{ "Progressive Magic Meter", "Jauge de Magie (prog.)", "Progressives Magisches Maß" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_PROGRESSIVE_MAGIC_METER, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_OCARINA] = Item(RG_PROGRESSIVE_OCARINA, Text{ "Progressive Ocarina", "Ocarina (prog.)", "Progressive Okarina" }, ITEMTYPE_ITEM, 0x8B, true, LOGIC_PROGRESSIVE_OCARINA, RHT_PROGRESSIVE_OCARINA, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_GORONSWORD] = Item(RG_PROGRESSIVE_GORONSWORD, Text{ "Progressive Goron Sword", "Épée Goron (prog.)", "Progressives Goronen-Schwert" }, ITEMTYPE_ITEM, 0xD4, true, LOGIC_PROGRESSIVE_GIANT_KNIFE, RHT_PROGRESSIVE_GORONSWORD, ITEM_CATEGORY_MAJOR, true); // Bottles itemTable[RG_EMPTY_BOTTLE] = Item(RG_EMPTY_BOTTLE, Text{ "Empty Bottle", "Bouteille Vide", "Leere Flasche" }, ITEMTYPE_ITEM, GI_BOTTLE, true, LOGIC_BOTTLES, RHT_BOTTLE_WITH_MILK, ITEM_BOTTLE, OBJECT_GI_BOTTLE, GID_BOTTLE, 0x42, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); itemTable[RG_BOTTLE_WITH_MILK] = Item(RG_BOTTLE_WITH_MILK, Text{ "Bottle with Milk", "Bouteille avec du Lait", "Flasche mit Milch" }, ITEMTYPE_ITEM, GI_MILK_BOTTLE, true, LOGIC_BOTTLES, RHT_BOTTLE_WITH_MILK, ITEM_MILK_BOTTLE, OBJECT_GI_MILK, GID_MILK, 0x98, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); @@ -172,53 +172,53 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_GANONS_CASTLE_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); itemTable[RG_TREASURE_GAME_SMALL_KEY] = Item(RG_TREASURE_GAME_SMALL_KEY, Text{ "Chest Game Small Key", "Petite Clé du jeu la Chasse-aux-Trésors", "Kleiner Schlüssel für das Truhenspiel" }, ITEMTYPE_SMALLKEY, GI_DOOR_KEY, true, LOGIC_TREASURE_GAME_KEYS, RHT_TREASURE_GAME_SMALL_KEY, ITEM_KEY_SMALL, OBJECT_GI_KEY, GID_KEY_SMALL, 0xF3, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_NONE); itemTable[RG_TREASURE_GAME_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); - itemTable[RG_GUARD_HOUSE_KEY] = Item(RG_GUARD_HOUSE_KEY, Text{ "Guard House Key", "", "Schlüssel für das Haus der Wachen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GUARD_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_GUARD_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_GUARD_HOUSE_KEY] = Item(RG_GUARD_HOUSE_KEY, Text{ "Guard House Key", "Clé de la Maison des Gardes", "Schlüssel für das Haus der Wachen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GUARD_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_GUARD_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_GUARD_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_BAZAAR_KEY] = Item(RG_MARKET_BAZAAR_KEY, Text{ "Market Bazaar Key", "", "Schlüssel für den Basar des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_MARKET_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_BAZAAR_KEY] = Item(RG_MARKET_BAZAAR_KEY, Text{ "Market Bazaar Key", "Clé du Bazar de la Place du Marché", "Schlüssel für den Basar des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_MARKET_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_BAZAAR_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_POTION_SHOP_KEY] = Item(RG_MARKET_POTION_SHOP_KEY, Text{ "Market Potion Shop Key", "", "Schlüssel für den Magie-Laden des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MARKET_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_POTION_SHOP_KEY] = Item(RG_MARKET_POTION_SHOP_KEY, Text{ "Market Potion Shop Key", "Clé du Magasin de Potions de la Place du Marché", "Schlüssel für den Magie-Laden des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MARKET_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MASK_SHOP_KEY] = Item(RG_MASK_SHOP_KEY, Text{ "Mask Shop Key", "", "Schlüssel für den Maskenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MASK_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MASK_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MASK_SHOP_KEY] = Item(RG_MASK_SHOP_KEY, Text{ "Mask Shop Key", "Clé de la Foire aux Masques", "Schlüssel für den Maskenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MASK_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MASK_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MASK_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_SHOOTING_GALLERY_KEY] = Item(RG_MARKET_SHOOTING_GALLERY_KEY, Text{ "Market Shooting Gallery Key", "", "Schlüssel für die Schießbude des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_MARKET_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_SHOOTING_GALLERY_KEY] = Item(RG_MARKET_SHOOTING_GALLERY_KEY, Text{ "Market Shooting Gallery Key", "Clé du Stand de Tir de la Place du Marché", "Schlüssel für die Schießbude des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_MARKET_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_SHOOTING_GALLERY_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOMBCHU_BOWLING_KEY] = Item(RG_BOMBCHU_BOWLING_KEY, Text{ "Bombchu Bowling Alley Key", "", "Schlüssel für die Minenbowlingbahn" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_BOWLING_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_BOWLING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOMBCHU_BOWLING_KEY] = Item(RG_BOMBCHU_BOWLING_KEY, Text{ "Bombchu Bowling Alley Key", "Clé du Bowling Teigneux", "Schlüssel für die Minenbowlingbahn" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_BOWLING_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_BOWLING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOMBCHU_BOWLING_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY] = Item(RG_TREASURE_CHEST_GAME_BUILDING_KEY, Text{ "Treasure Chest Game Building Key", "", "Schlüssel für das Haus des Schatzkisten-Pokers" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TREASURE_CHEST_GAME_BUILDING_KEY,RHT_OVERWORLD_KEY, RG_TREASURE_CHEST_GAME_BUILDING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY] = Item(RG_TREASURE_CHEST_GAME_BUILDING_KEY, Text{ "Treasure Chest Game Building Key", "Clé de la Chasse au Trésor", "Schlüssel für das Haus des Schatzkisten-Pokers" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TREASURE_CHEST_GAME_BUILDING_KEY,RHT_OVERWORLD_KEY, RG_TREASURE_CHEST_GAME_BUILDING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOMBCHU_SHOP_KEY] = Item(RG_BOMBCHU_SHOP_KEY, Text{ "Bombchu Shop Key", "", "Schlüssel für den Krabbelminenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_SHOP_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOMBCHU_SHOP_KEY] = Item(RG_BOMBCHU_SHOP_KEY, Text{ "Bombchu Shop Key", "Clé du Magasin de Missiles", "Schlüssel für den Krabbelminenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_SHOP_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOMBCHU_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_RICHARDS_HOUSE_KEY] = Item(RG_RICHARDS_HOUSE_KEY, Text{ "Richard's House Key", "", "Schlüssel für das Haus von Richard" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_RICHARDS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_RICHARDS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_RICHARDS_HOUSE_KEY] = Item(RG_RICHARDS_HOUSE_KEY, Text{ "Richard's House Key", "Clé de la Maison de Kiki", "Schlüssel für das Haus von Richard" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_RICHARDS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_RICHARDS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_RICHARDS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_ALLEY_HOUSE_KEY] = Item(RG_ALLEY_HOUSE_KEY, Text{ "Alley House Key", "", "Schlüssel für das Gassenhaus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_ALLEY_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_ALLEY_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_ALLEY_HOUSE_KEY] = Item(RG_ALLEY_HOUSE_KEY, Text{ "Alley House Key", "Clé de la Maison de la Ruelle", "Schlüssel für das Gassenhaus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_ALLEY_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_ALLEY_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_ALLEY_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_BAZAAR_KEY] = Item(RG_KAK_BAZAAR_KEY, Text{ "Kakariko Bazaar Key", "", "Schlüssel für den Basar von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_KAK_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_BAZAAR_KEY] = Item(RG_KAK_BAZAAR_KEY, Text{ "Kakariko Bazaar Key", "Clé du Bazar de Cocorico", "Schlüssel für den Basar von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_KAK_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_BAZAAR_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_POTION_SHOP_KEY] = Item(RG_KAK_POTION_SHOP_KEY, Text{ "Kakariko Potion Shop Key", "", "Schlüssel für den Magie-Laden von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_KAK_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_POTION_SHOP_KEY] = Item(RG_KAK_POTION_SHOP_KEY, Text{ "Kakariko Potion Shop Key", "Clé du Magasin de Potions de Cocorico", "Schlüssel für den Magie-Laden von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_KAK_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOSS_HOUSE_KEY] = Item(RG_BOSS_HOUSE_KEY, Text{ "Boss's House Key", "", "Schlüssel für das Haus des Chefs" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOSS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_BOSS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOSS_HOUSE_KEY] = Item(RG_BOSS_HOUSE_KEY, Text{ "Boss's House Key", "Clé de la Maison du Chef des Ouvriers", "Schlüssel für das Haus des Chefs" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOSS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_BOSS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOSS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_GRANNYS_POTION_SHOP_KEY] = Item(RG_GRANNYS_POTION_SHOP_KEY, Text{ "Granny's Potion Shop Key", "", "Schlüssel für Asas Hexenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GRANNYS_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_GRANNYS_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_GRANNYS_POTION_SHOP_KEY] = Item(RG_GRANNYS_POTION_SHOP_KEY, Text{ "Granny's Potion Shop Key", "Clé de l'Apothicaire", "Schlüssel für Asas Hexenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GRANNYS_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_GRANNYS_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_GRANNYS_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_SKULLTULA_HOUSE_KEY] = Item(RG_SKULLTULA_HOUSE_KEY, Text{ "Skulltula House Key", "", "Schlüssel für das Skulltula-Haus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_SKULLTULA_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_SKULLTULA_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_SKULLTULA_HOUSE_KEY] = Item(RG_SKULLTULA_HOUSE_KEY, Text{ "Skulltula House Key", "Clé de la Maison des Araignées", "Schlüssel für das Skulltula-Haus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_SKULLTULA_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_SKULLTULA_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_SKULLTULA_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_IMPAS_HOUSE_KEY] = Item(RG_IMPAS_HOUSE_KEY, Text{ "Impa's House Key", "", "Schlüssel für das Haus von Impa" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_IMPAS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_IMPAS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_IMPAS_HOUSE_KEY] = Item(RG_IMPAS_HOUSE_KEY, Text{ "Impa's House Key", "Clé de la Maison d'Impa", "Schlüssel für das Haus von Impa" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_IMPAS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_IMPAS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_IMPAS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_WINDMILL_KEY] = Item(RG_WINDMILL_KEY, Text{ "Windmill Key", "", "Schlüssel für die Windmühle" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_WINDMILL_KEY, RHT_OVERWORLD_KEY, RG_WINDMILL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_WINDMILL_KEY] = Item(RG_WINDMILL_KEY, Text{ "Windmill Key", "Clé du Moulin", "Schlüssel für die Windmühle" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_WINDMILL_KEY, RHT_OVERWORLD_KEY, RG_WINDMILL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_WINDMILL_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_SHOOTING_GALLERY_KEY] = Item(RG_KAK_SHOOTING_GALLERY_KEY, Text{ "Kakariko Shooting Gallery Key", "", "Schlüssel für die Schießbude von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_KAK_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_SHOOTING_GALLERY_KEY] = Item(RG_KAK_SHOOTING_GALLERY_KEY, Text{ "Kakariko Shooting Gallery Key", "Clé du Stand de Tir de Cocorico", "Schlüssel für die Schießbude von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_KAK_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_SHOOTING_GALLERY_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_DAMPES_HUT_KEY] = Item(RG_DAMPES_HUT_KEY, Text{ "Dampe's Hut Key", "", "Schlüssel für die Hütte von Boris" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_DAMPES_HUT_KEY, RHT_OVERWORLD_KEY, RG_DAMPES_HUT_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_DAMPES_HUT_KEY] = Item(RG_DAMPES_HUT_KEY, Text{ "Dampe's Hut Key", "Clé de la Cabane d'Igor", "Schlüssel für die Hütte von Boris" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_DAMPES_HUT_KEY, RHT_OVERWORLD_KEY, RG_DAMPES_HUT_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_DAMPES_HUT_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_TALONS_HOUSE_KEY] = Item(RG_TALONS_HOUSE_KEY, Text{ "Talon's House Key", "", "Schlüssel für das Haus von Talon" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TALONS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_TALONS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_TALONS_HOUSE_KEY] = Item(RG_TALONS_HOUSE_KEY, Text{ "Talon's House Key", "Clé de la Maison de Talon", "Schlüssel für das Haus von Talon" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TALONS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_TALONS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_TALONS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_STABLES_KEY] = Item(RG_STABLES_KEY, Text{ "Stables Key", "", "Schlüssel für die Ställe" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_STABLES_KEY, RHT_OVERWORLD_KEY, RG_STABLES_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_STABLES_KEY] = Item(RG_STABLES_KEY, Text{ "Stables Key", "Clé des Écuries", "Schlüssel für die Ställe" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_STABLES_KEY, RHT_OVERWORLD_KEY, RG_STABLES_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_STABLES_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BACK_TOWER_KEY] = Item(RG_BACK_TOWER_KEY, Text{ "Back Tower Key", "", "Schlüssel für den hinteren Turm" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BACK_TOWER_KEY, RHT_OVERWORLD_KEY, RG_BACK_TOWER_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BACK_TOWER_KEY] = Item(RG_BACK_TOWER_KEY, Text{ "Back Tower Key", "Clé du Silo", "Schlüssel für den hinteren Turm" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BACK_TOWER_KEY, RHT_OVERWORLD_KEY, RG_BACK_TOWER_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BACK_TOWER_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_HYLIA_LAB_KEY] = Item(RG_HYLIA_LAB_KEY, Text{ "Hylia Laboratory Key", "", "Schlüssel für das Hylia-Labor" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_HYLIA_LAB_KEY, RHT_OVERWORLD_KEY, RG_HYLIA_LAB_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_HYLIA_LAB_KEY] = Item(RG_HYLIA_LAB_KEY, Text{ "Hylia Laboratory Key", "Clé du Laboratoire du Lac Hylia", "Schlüssel für das Hylia-Labor" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_HYLIA_LAB_KEY, RHT_OVERWORLD_KEY, RG_HYLIA_LAB_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_HYLIA_LAB_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_FISHING_HOLE_KEY] = Item(RG_FISHING_HOLE_KEY, Text{ "Fishing Hole Key", "", "Schlüssel für den Fischweiher" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_FISHING_HOLE_KEY, RHT_OVERWORLD_KEY, RG_FISHING_HOLE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_FISHING_HOLE_KEY] = Item(RG_FISHING_HOLE_KEY, Text{ "Fishing Hole Key", "Clé de l'Étang", "Schlüssel für den Fischweiher" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_FISHING_HOLE_KEY, RHT_OVERWORLD_KEY, RG_FISHING_HOLE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_FISHING_HOLE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); // Key Rings itemTable[RG_FOREST_TEMPLE_KEY_RING] = Item(RG_FOREST_TEMPLE_KEY_RING, Text{ "Forest Temple Key Ring", "Trousseau du Temple de la Forêt", "Schlüsselbund für den Waldtempel" }, ITEMTYPE_SMALLKEY, 0xD5, true, LOGIC_FOREST_TEMPLE_KEYS, RHT_FOREST_TEMPLE_KEY_RING, RG_FOREST_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); @@ -305,7 +305,7 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_BUY_BOMBCHUS_10] = Item(RG_BUY_BOMBCHUS_10, Text{ "Buy Bombchu (10)", "Acheter: Missiles (10)", "Krabbelminen kaufen (10)" }, ITEMTYPE_SHOP, GI_BOMBCHUS_10, true, LOGIC_BUY_BOMBCHUS, RHT_BOMBCHUS_10, ITEM_BOMBCHU, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 99); itemTable[RG_BUY_BOMBCHUS_20] = Item(RG_BUY_BOMBCHUS_20, Text{ "Buy Bombchu (20)", "Acheter: Missiles (20)", "Krabbelminen kaufen (20)" }, ITEMTYPE_SHOP, GI_BOMBCHUS_20, true, LOGIC_BUY_BOMBCHUS, RHT_BOMBCHUS_20, ITEM_BOMBCHUS_20, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 180); itemTable[RG_BUY_DEKU_SEEDS_30] = Item(RG_BUY_DEKU_SEEDS_30, Text{ "Buy Deku Seeds (30)", "Acheter: Graines Mojo (30)", "Deku-Samen kaufen (30)" }, ITEMTYPE_SHOP, GI_SEEDS_30, true, LOGIC_BUY_SEED, RHT_DEKU_SEEDS_30, ITEM_SEEDS_30, OBJECT_GI_SEED, GID_SEEDS, 0xDC, 0x50, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 30); - itemTable[RG_SOLD_OUT] = Item(RG_SOLD_OUT, Text{ "Sold Out", "Rupture de stock", "Ausverkauft" }, ITEMTYPE_SHOP, RG_SOLD_OUT, false, LOGIC_NONE, RHT_NONE, false, 0); + itemTable[RG_SOLD_OUT] = Item(RG_SOLD_OUT, Text{ "Sold Out", "Rupture de stock", "Ausverkauft" }, ITEMTYPE_SHOP, RG_SOLD_OUT, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_JUNK, false, 0); itemTable[RG_BUY_BLUE_FIRE] = Item(RG_BUY_BLUE_FIRE, Text{ "Buy Blue Fire", "Acheter: Flamme Bleue", "Blaues Feuer kaufen" }, ITEMTYPE_SHOP, GI_BLUE_FIRE, true, LOGIC_BLUE_FIRE_ACCESS, RHT_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, 0x5D, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 300); itemTable[RG_BUY_BOTTLE_BUG] = Item(RG_BUY_BOTTLE_BUG, Text{ "Buy Bottle Bug", "Acheter: Insecte en bouteille", "Flaschenkäfer kaufen" }, ITEMTYPE_SHOP, GI_BUGS, true, LOGIC_BUGS_ACCESS, RHT_BOTTLE_WITH_BUGS, ITEM_BUG, OBJECT_GI_INSECT, GID_BUG, 0x7A, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 50); itemTable[RG_BUY_POE] = Item(RG_BUY_POE, Text{ "Buy Poe", "Acheter: Esprit", "Geist kaufen" }, ITEMTYPE_SHOP, RG_BUY_POE, false, LOGIC_NONE, RHT_BOTTLE_WITH_BIG_POE, ITEM_POE, OBJECT_GI_GHOST, GID_POE, 0x97, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 30); @@ -372,8 +372,8 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_DEKU_NUT_BAG] = Item(RG_DEKU_NUT_BAG, Text{ "Deku Nut Bag", "Sac de Noix Mojo", "Deku-Nuß-Tasche" }, ITEMTYPE_ITEM, GI_NUT_UPGRADE_30, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_NONE, RG_DEKU_NUT_BAG, OBJECT_GI_NUTS, GID_NUTS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); - itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_EVENT, RG_TRIFORCE, false, LOGIC_NONE, RHT_NONE); - itemTable[RG_HINT] = Item(RG_HINT, Text{ "Hint", "Indice", "Hinweis" }, ITEMTYPE_EVENT, RG_HINT, false, LOGIC_NONE, RHT_NONE); + itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_EVENT, RG_TRIFORCE, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_MAJOR); + itemTable[RG_HINT] = Item(RG_HINT, Text{ "Hint", "Indice", "Hinweis" }, ITEMTYPE_EVENT, RG_HINT, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_LESSER); // Individual stages of progressive items (only here for GetItemEntry purposes, not for use in seed gen) itemTable[RG_HOOKSHOT] = Item(RG_HOOKSHOT, Text{ "Hookshot", "Grappin", "Fanghaken" }, ITEMTYPE_ITEM, GI_HOOKSHOT, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_HOOKSHOT, ITEM_HOOKSHOT, OBJECT_GI_HOOKSHOT, GID_HOOKSHOT, 0x36, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); itemTable[RG_LONGSHOT] = Item(RG_LONGSHOT, Text{ "Longshot", "Super-Grappin", "Enterhaken" }, ITEMTYPE_ITEM, GI_LONGSHOT, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_LONGSHOT, ITEM_LONGSHOT, OBJECT_GI_HOOKSHOT, GID_LONGSHOT, 0x4F, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); diff --git a/soh/soh/Enhancements/randomizer/location.cpp b/soh/soh/Enhancements/randomizer/location.cpp index f474397aa4a..6c27e3aef98 100644 --- a/soh/soh/Enhancements/randomizer/location.cpp +++ b/soh/soh/Enhancements/randomizer/location.cpp @@ -54,8 +54,9 @@ const std::string& Rando::Location::GetShortName() const { bool Rando::Location::IsDungeon() const { return (checkType != RCTYPE_SKULL_TOKEN && - (scene <= SCENE_GERUDO_TRAINING_GROUND || scene == SCENE_INSIDE_GANONS_CASTLE || - (scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_GANONDORF_BOSS))) || + (scene <= SCENE_GERUDO_TRAINING_GROUND || scene == SCENE_INSIDE_GANONS_CASTLE || + (scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_GANONDORF_BOSS)) || + (rc == RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST || rc == RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST)) || (checkType == RCTYPE_SKULL_TOKEN && scene <= SCENE_ICE_CAVERN); } diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index c06ca5d70b2..26ec51f271e 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -10,9 +10,10 @@ #include "soh/Enhancements/debugger/performanceTimer.h" #include +#include +#include "3drando/shops.hpp" extern "C" { -extern SaveContext gSaveContext; extern PlayState* gPlayState; } @@ -45,17 +46,71 @@ bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailab conditionsMet = true; } - return conditionsMet && - (calculatingAvailableChecks || CanBuy()); // TODO: run CanBuy when price is known due to settings + return conditionsMet && CanBuy(calculatingAvailableChecks); } -bool LocationAccess::CanBuy() const { - return CanBuyAnother(location); +static uint16_t GetMinimumPrice(const Rando::Location* loc) { + extern PriceSettingsStruct shopsanityPrices; + extern PriceSettingsStruct scrubPrices; + extern PriceSettingsStruct merchantPrices; + PriceSettingsStruct priceSettings = loc->GetRCType() == RCTYPE_SHOP ? shopsanityPrices + : loc->GetRCType() == RCTYPE_SCRUB ? scrubPrices + : merchantPrices; + + auto ctx = Rando::Context::GetInstance(); + switch (ctx->GetOption(priceSettings.main).Get()) { + case RO_PRICE_VANILLA: + return loc->GetVanillaPrice(); + case RO_PRICE_CHEAP_BALANCED: + return 0; + case RO_PRICE_BALANCED: + return 0; + case RO_PRICE_FIXED: + return ctx->GetOption(priceSettings.fixedPrice).Get() * 5; + case RO_PRICE_RANGE: { + uint16_t range1 = ctx->GetOption(priceSettings.range1).Get() * 5; + uint16_t range2 = ctx->GetOption(priceSettings.range1).Get() * 5; + return range1 < range2 ? range1 : range2; + } + case RO_PRICE_SET_BY_WALLET: { + if (ctx->GetOption(priceSettings.noWallet).Get()) { + return 0; + } else if (ctx->GetOption(priceSettings.childWallet).Get()) { + return 1; + } else if (ctx->GetOption(priceSettings.adultWallet).Get()) { + return 100; + } else if (ctx->GetOption(priceSettings.giantWallet).Get()) { + return 201; + } else { + return 501; + } + } + default: + return 0; + } +} + +bool LocationAccess::CanBuy(bool calculatingAvailableChecks) const { + const auto& loc = Rando::StaticData::GetLocation(location); + const auto& itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(location); + + if (loc->GetRCType() == RCTYPE_SHOP || loc->GetRCType() == RCTYPE_SCRUB || loc->GetRCType() == RCTYPE_MERCHANT) { + // Checks should only be identified while playing + if (calculatingAvailableChecks && itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) { + return CanBuyAnother(GetMinimumPrice(loc)); + } else { + return CanBuyAnother(itemLoc->GetPrice()); + } + } + + return true; } bool CanBuyAnother(RandomizerCheck rc) { - uint16_t price = ctx->GetItemLocation(rc)->GetPrice(); + return CanBuyAnother(ctx->GetItemLocation(rc)->GetPrice()); +} +bool CanBuyAnother(uint16_t price) { if (price > 500) { return logic->HasItem(RG_TYCOON_WALLET); } else if (price > 200) { @@ -275,7 +330,7 @@ bool BeanPlanted(const RandomizerRegion region) { if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) { swch = gPlayState->actorCtx.flags.swch; } else if (sceneID != SCENE_ID_MAX) { - swch = gSaveContext.sceneFlags[sceneID].swch; + swch = Rando::Context::GetInstance()->GetLogic()->GetSaveContext()->sceneFlags[sceneID].swch; } else { swch = 0; } @@ -326,12 +381,15 @@ void RegionTable_Init() { EventAccess(&logic->KakarikoVillageGateOpen, []{return ctx->GetOption(RSK_KAK_GATE).Is(RO_KAK_GATE_OPEN);}), //The big poes bottle softlock safety check does not account for the guard house lock if the guard house is not shuffled, so the key is needed before we can safely allow bottle use in logic //RANDOTODO a setting that lets you drink/dump big poes so we don't need this logic - EventAccess(&logic->CouldEmptyBigPoes, []{return ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL) || logic->CanOpenOverworldDoor(RG_GUARD_HOUSE_KEY);}), + EventAccess(&logic->CouldEmptyBigPoes, []{return !ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF) || logic->CanOpenOverworldDoor(RG_GUARD_HOUSE_KEY);}), + EventAccess(&logic->FreedEpona, []{return (bool)ctx->GetOption(RSK_SKIP_EPONA_RACE);}), }, { //Locations LOCATION(RC_LINKS_POCKET, true), LOCATION(RC_TRIFORCE_COMPLETED, logic->GetSaveContext()->ship.quest.data.randomizer.triforcePiecesCollected >= ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1;), LOCATION(RC_SARIA_SONG_HINT, logic->CanUse(RG_SARIAS_SONG)), + LOCATION(RC_SONG_FROM_IMPA, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), + LOCATION(RC_TOT_MASTER_SWORD, (bool)ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)), }, { //Exits Entrance(RR_ROOT_EXITS, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 828c31a0834..8f7bb82d3cc 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -100,9 +100,10 @@ class LocationAccess { std::string condition_str; // Makes sure shop locations are buyable - bool CanBuy() const; + bool CanBuy(bool calculatingAvailableChecks) const; }; +bool CanBuyAnother(uint16_t price); bool CanBuyAnother(RandomizerCheck rc); namespace Rando { @@ -136,7 +137,6 @@ class Region { bool adultDay = false; bool adultNight = false; bool addedToPool = false; - ; void ApplyTimePass(); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp index 4ec6b677306..8a8393ba18a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp @@ -25,7 +25,6 @@ void RegionTable_Init_BottomOfTheWell() { }, { //Locations LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_CENTER_BOMBABLE_CHEST, logic->HasExplosives()), - LOCATION(RC_BOTTOM_OF_THE_WELL_FREESTANDING_KEY, (logic->HasItem(RG_BRONZE_SCALE) || logic->LoweredWaterInsideBotw) && logic->CanUse(RG_STICKS) || logic->CanUse(RG_DINS_FIRE)), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_FRONT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_LEFT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_NEAR_ENTRANCE_POT_1, logic->CanBreakPots()), @@ -50,7 +49,7 @@ void RegionTable_Init_BottomOfTheWell() { LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_LEFT_FAKE_WALL_CHEST, true), LOCATION(RC_BOTTOM_OF_THE_WELL_RIGHT_BOTTOM_FAKE_WALL_CHEST, true), LOCATION(RC_BOTTOM_OF_THE_WELL_COMPASS_CHEST, true), - //N64 has no extra check here, but I can't get past without dealing with the spider or taking a hit, they probably assume sticks + //You can just barely pass the spider on the right side without damage or items, but it's probably tight enough to count as as a trick LOCATION(RC_BOTTOM_OF_THE_WELL_CENTER_SKULLTULA_CHEST, logic->CanPassEnemy(RE_BIG_SKULLTULA) || logic->TakeDamage()), //Not technically behind a wall, but still logically needs lens due to pits LOCATION(RC_BOTTOM_OF_THE_WELL_BACK_LEFT_BOMBABLE_CHEST, logic->HasExplosives()), @@ -160,7 +159,7 @@ void RegionTable_Init_BottomOfTheWell() { }, { //Exits Entrance(RR_BOTTOM_OF_THE_WELL_SOUTHWEST_ROOM, []{return logic->IsChild && logic->CanPassEnemy(RE_BIG_SKULLTULA);}), - //It's possible top abuse boulder's limited range of collision detection to detonate the flowers through the boulder with bow, but this is a glitch + //It's possible to abuse boulder's limited range of collision detection to detonate the flowers through the boulder with bow, but this is a glitch //the exact range is just past the furthest away plank in the green goo section Entrance(RR_BOTTOM_OF_THE_WELL_BASEMENT_USEFUL_BOMB_FLOWERS, []{return Here(RR_BOTTOM_OF_THE_WELL_BASEMENT, []{return logic->BlastOrSmash() || logic->CanUse(RG_DINS_FIRE) || (logic->CanUse(RG_STICKS) && ctx->GetTrickOption(RT_BOTW_BASEMENT));});}), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp index f327c9992b0..c829d1bc97f 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp @@ -436,11 +436,16 @@ void RegionTable_Init_DekuTree() { #pragma endregion // Boss Room + // RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance areaTable[RR_DEKU_TREE_BOSS_ENTRYWAY] = Region("Deku Tree Boss Entryway", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + // Exits + Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}), + }); + + areaTable[RR_DEKU_TREE_BOSS_EXIT] = Region("Deku Tree Boss Exit", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}), Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}), - Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}), }); areaTable[RR_DEKU_TREE_BOSS_ROOM] = Region("Deku Tree Boss Room", "Deku Tree", {}, NO_DAY_NIGHT_CYCLE, { @@ -460,8 +465,8 @@ void RegionTable_Init_DekuTree() { LOCATION(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8, logic->CanCutShrubs()), }, { // Exits - Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, []{return true;}), - Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false), + Entrance(RR_DEKU_TREE_BOSS_EXIT, []{return true;}), + Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index 8cd60f268e7..bffc6c290ad 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -268,6 +268,7 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()), }, { //Exits + Entrance(RR_DODONGOS_CAVERN_MQ_BEGINNING, []{return true;}), Entrance(RR_DODONGOS_CAVERN_MQ_GOSSIP_STONE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}), Entrance(RR_DODONGOS_CAVERN_MQ_MOUTH_SIDE_BRIDGE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}), Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}), @@ -386,7 +387,7 @@ void RegionTable_Init_DodongosCavern() { Entrance(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanUse(RG_STICKS) && logic->HasItem(RG_GORONS_BRACELET);}), //Implies access to RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM from here }); - areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Torch Puzzle Lower", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Big Block Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_2, logic->CanBreakPots()), @@ -426,7 +427,7 @@ void RegionTable_Init_DodongosCavern() { Entrance(RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM, []{return Here(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanKillEnemy(RE_LIZALFOS);});}), }); - areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Before Upper Lizalfos", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Two Fires Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_2, logic->CanBreakPots()), @@ -558,11 +559,16 @@ void RegionTable_Init_DodongosCavern() { #pragma endregion // Boss Room + // RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance areaTable[RR_DODONGOS_CAVERN_BOSS_ENTRYWAY] = Region("Dodongos Cavern Boss Entryway", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, { + // Exits + Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}), + }); + + areaTable[RR_DODONGOS_CAVERN_BOSS_EXIT] = Region("Dodongos Cavern Boss Exit", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_DODONGOS_CAVERN_BOSS_AREA, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsVanilla();}), Entrance(RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsMQ();}), - Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}), }); areaTable[RR_DODONGOS_CAVERN_BOSS_ROOM] = Region("Dodongos Cavern Boss Room", "Dodongos Cavern", {}, NO_DAY_NIGHT_CYCLE, { @@ -575,8 +581,8 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_KING_DODONGO, logic->DodongosCavernClear), }, { // Exits - Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, []{return true;}), - Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false), + Entrance(RR_DODONGOS_CAVERN_BOSS_EXIT, []{return true;}), + Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 8888cdac9be..978f5eabf06 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -20,7 +20,7 @@ void RegionTable_Init_FireTemple() { //Exits Entrance(RR_FIRE_TEMPLE_ENTRYWAY, []{return true;}), Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return logic->FireTimer() >= 24;}), - Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity);}), + Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked);}), Entrance(RR_FIRE_TEMPLE_LOOP_EXIT, []{return true;}), Entrance(RR_FIRE_TEMPLE_BIG_LAVA_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 2) && logic->FireTimer() >= 24;}), }); @@ -38,12 +38,12 @@ void RegionTable_Init_FireTemple() { }, { //Exits Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return true;}), - Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}))) || logic->CanUse(RG_HOVER_BOOTS));}), + Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}) || logic->CanUse(RG_HOVER_BOOTS));}), }); areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity;}), + Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked;}), Entrance(RR_FIRE_TEMPLE_LOOP_TILES, []{return Here(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return logic->CanKillEnemy(RE_TORCH_SLUG) && logic->CanKillEnemy(RE_FIRE_KEESE);});}), }); @@ -445,7 +445,7 @@ void RegionTable_Init_FireTemple() { Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return true;}), //Child cannot make it to the north side torches without a hook without specifically bunny hood speed + hover boots Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM_NORTH, []{return logic->FireTimer() > 32 && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)));}), - Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}), + Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}), }); //This room assumes tunic logic is handled on entry. @@ -701,7 +701,7 @@ void RegionTable_Init_FireTemple() { Entrance(RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER, []{return true;}), }); - areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ North Fire Maze", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ Upper Flare Dancer", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_MQ_FREESTANDING_KEY, logic->CanKillEnemy(RE_FLARE_DANCER)), }, { @@ -740,7 +740,7 @@ void RegionTable_Init_FireTemple() { // Exits Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsVanilla() && false;}), Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsMQ() && false;}), - Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY);}), }); areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", "Fire Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp index 2f5ba96bf73..3deacb0426e 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp @@ -298,7 +298,7 @@ void RegionTable_Init_ForestTemple() { }, { //Exits Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), + Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -481,7 +481,7 @@ void RegionTable_Init_ForestTemple() { Entrance(RR_FOREST_TEMPLE_MQ_FALLING_ROOM, []{return logic->CanUse(RG_SONG_OF_TIME);}), }); - areaTable[RR_FOREST_TEMPLE_MQ_JOELLE_ROOM] = Region("Forest Temple MQ Joelle room", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_FOREST_TEMPLE_MQ_JOELLE_ROOM] = Region("Forest Temple MQ Joelle Room", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->ForestTempleJoelle, []{return logic->CanUse(RG_FAIRY_BOW);}), }, { @@ -593,7 +593,7 @@ void RegionTable_Init_ForestTemple() { areaTable[RR_FOREST_TEMPLE_MQ_BOSS_REGION] = Region("Forest Temple MQ Boss Region", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_FOREST_TEMPLE_MQ_BASEMENT, []{return logic->ForestOpenBossCorridor;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), + Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -603,7 +603,7 @@ void RegionTable_Init_ForestTemple() { // Exits Entrance(RR_FOREST_TEMPLE_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsVanilla() && false;}), Entrance(RR_FOREST_TEMPLE_MQ_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsMQ() && false;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), }); areaTable[RR_FOREST_TEMPLE_BOSS_ROOM] = Region("Forest Temple Boss Room", "Forest Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index ded6c827950..e4c33b49a41 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -29,12 +29,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}), - Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && - (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && - (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && - (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && - (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && - (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}), Entrance(RR_GANONS_CASTLE_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}), }); @@ -63,8 +58,8 @@ void RegionTable_Init_GanonsCastle() { }, { //Locations LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_CHEST, logic->CanKillEnemy(RE_WOLFOS)), - LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_1, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || logic->CanUse(RG_DINS_FIRE))), - LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_2, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || logic->CanUse(RG_DINS_FIRE))), + LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_1, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || (logic->CanUse(RG_DINS_FIRE) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))), + LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_2, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || (logic->CanUse(RG_DINS_FIRE) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))), }, {}); areaTable[RR_GANONS_CASTLE_FIRE_TRIAL] = Region("Ganon's Castle Fire Trial", "Ganon's Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, { @@ -162,13 +157,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_STARTING_LEDGE, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_DINOLFOS_ROOM, []{return Here(RR_GANONS_CASTLE_MQ_MAIN, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);});}), - //RANDOTODO could we just set these events automatically based on the setting? - Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && - (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && - (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && - (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && - (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && - (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}), }); @@ -436,7 +425,13 @@ void RegionTable_Init_GanonsCastle() { //Exits Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}), Entrance(RR_GANONS_CASTLE_MQ_MAIN, []{return ctx->GetDungeon(GANONS_CASTLE)->IsMQ();}), - Entrance(RR_GANONS_TOWER_FLOOR_1, []{return true;}), + //RANDOTODO could we just set these events automatically based on the setting? + Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && + (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && + (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && + (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && + (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && + (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), }); areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp index 5466c598c9e..f1a0ebee7c5 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp @@ -149,7 +149,7 @@ void RegionTable_Init_GerudoTrainingGround() { Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER, []{return logic->SmallKeys(RR_GERUDO_TRAINING_GROUND, 3);}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Maze Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->MQGTGMazeSwitch, []{return logic->CanUse(RG_MEGATON_HAMMER);}), }, diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp index ff2bf959437..20ff9b1e1a0 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp @@ -11,7 +11,7 @@ void RegionTable_Init_IceCavern() { //Exits Entrance(RR_ICE_CAVERN_BEGINNING, []{return ctx->GetDungeon(ICE_CAVERN)->IsVanilla();}), Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return ctx->GetDungeon(ICE_CAVERN)->IsMQ() && logic->CanUseProjectile();}), - Entrance(RR_ZORAS_FOUNTAIN, []{return true;}), + Entrance(RR_ZF_LEDGE, []{return true;}), }); #pragma region Vanilla @@ -84,7 +84,7 @@ void RegionTable_Init_IceCavern() { //Exits //the switch for the glass blocking the entrance is linked to the switch that controls the glass around the skulltulla in RR_ICE_CAVERN_MQ_SCARECROW_ROOM //if you clear the ice, you can hit it with a pot from here. - Entrance(RR_ICE_CAVERN_BEGINNING, []{return logic->BlueFire();}), + Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->BlueFire();}), Entrance(RR_ICE_CAVERN_MQ_MAP_ROOM, []{return Here(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanKillEnemy(RE_WHITE_WOLFOS) && logic->CanKillEnemy(RE_FREEZARD);});}), Entrance(RR_ICE_CAVERN_MQ_COMPASS_ROOM, []{return logic->IsAdult && logic->BlueFire();}), Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp index 3c45682ae6c..305ec3e0d69 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp @@ -64,7 +64,7 @@ void RegionTable_Init_JabuJabusBelly() { //Exits Entrance(RR_JABU_JABUS_BELLY_MAIN, []{return true;}), //there's tricks for getting here with bunny-jumps or just side-hops - Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}), + Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_SOUTH, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE);}), }); @@ -90,7 +90,7 @@ void RegionTable_Init_JabuJabusBelly() { }, { //Exits Entrance(RR_JABU_JABUS_BELLY_B1_NORTH, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE);}), - Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}), + Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_JABU_JABUS_BELLY_MAIN, []{return logic->CanUseProjectile();}), }); @@ -351,10 +351,14 @@ void RegionTable_Init_JabuJabusBelly() { // Boss Room areaTable[RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY] = Region("Jabu Jabus Belly Boss Entryway", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, { + // Exits + Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}), + }); + + areaTable[RR_JABU_JABUS_BELLY_BOSS_EXIT] = Region("Jabu Jabus Belly Boss Exit", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsVanilla();}), Entrance(RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsMQ();}), - Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}), }); areaTable[RR_JABU_JABUS_BELLY_BOSS_ROOM] = Region("Jabu Jabus Belly Boss Room", "Jabu Jabus Belly", {}, NO_DAY_NIGHT_CYCLE, { @@ -372,8 +376,8 @@ void RegionTable_Init_JabuJabusBelly() { LOCATION(RC_BARINADE, logic->JabuJabusBellyClear), }, { // Exits - Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, []{return false;}), - Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false), + Entrance(RR_JABU_JABUS_BELLY_BOSS_EXIT, []{return false;}), + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp index 3be1acac42f..eef68c78baf 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp @@ -112,7 +112,7 @@ void RegionTable_Init_ShadowTemple() { LOCATION(RC_SHADOW_TEMPLE_AFTER_SHIP_LOWER_HEART, (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->CanUse(RG_SONG_OF_TIME) || (logic->CanUse(RG_DISTANT_SCARECROW) && logic->CanUse(RG_HOVER_BOOTS))), }, { //Exits - Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}) + Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS);}) }); #pragma endregion @@ -370,7 +370,7 @@ void RegionTable_Init_ShadowTemple() { }, { //Exits Entrance(RR_SHADOW_TEMPLE_MQ_ACROSS_CHASM, []{return logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH));}), - Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}), + Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); //Assumes lens is checked on entry @@ -403,7 +403,7 @@ void RegionTable_Init_ShadowTemple() { // Exits Entrance(RR_SHADOW_TEMPLE_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsVanilla() && false;}), Entrance(RR_SHADOW_TEMPLE_MQ_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}), }); areaTable[RR_SHADOW_TEMPLE_BOSS_ROOM] = Region("Shadow Temple Boss Room", "Shadow Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index e7f9c0ee63f..7589c7b746b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -145,7 +145,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -194,13 +194,13 @@ void RegionTable_Init_SpiritTemple() { }); // Room to store the 2 pots in to handle glitch logic going backwards around the loop later - areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ 1F Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, logic->CanBreakPots()), }, {}); - areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple MQ Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events //For non-fairy pot items, you can also get them with rang without killing the stalfos EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), @@ -315,7 +315,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ West Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), @@ -541,7 +541,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -551,7 +551,7 @@ void RegionTable_Init_SpiritTemple() { // Exits Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index 0ae5ac28983..61fb0962db8 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -277,7 +277,7 @@ void RegionTable_Init_WaterTemple() { }, { //Exits Entrance(RR_WATER_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), + Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -362,10 +362,10 @@ void RegionTable_Init_WaterTemple() { Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_ICE_ARROWS) || logic->CanUse(RG_NAYRUS_LOVE);}), }); - areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Main", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Boss Door", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_WATER_TEMPLE_MQ_3F_NORTH_LEDGE, []{return logic->CanUse(RG_ICE_ARROWS) || logic->TakeDamage();}), - Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), + Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); areaTable[RR_WATER_TEMPLE_MQ_EAST_TOWER] = Region("Water Temple MQ East Tower", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -507,7 +507,7 @@ void RegionTable_Init_WaterTemple() { Entrance(RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F, []{return logic->CanUse(RG_LONGSHOT);}), }); - areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 2F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 3F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_WATER_TEMPLE_MQ_GS_BEFORE_UPPER_WATER_SWITCH, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), }, { @@ -552,7 +552,7 @@ void RegionTable_Init_WaterTemple() { }, {}); //This room exists to hold the wonderitems that drop from the emblems here. Specifically this assumes you are standing on the final ledge - areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple MQ Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_WATER_TEMPLE_MQ_3F_CENTRAL, []{return logic->SmallKeys(RR_WATER_TEMPLE, 1) && logic->CanUse(RG_LONGSHOT);}), Entrance(RR_WATER_TEMPLE_MQ_STALFOS_PIT, []{return true;}), @@ -658,7 +658,7 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_DRAGON_ROOM_ALCOVE] = Region("Water Temple MQ Dragon Room Alcove", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQWaterDragonTorches, []{return true;}), + EventAccess(&logic->MQWaterDragonTorches, []{return logic->HasFireSource();}), }, { //Locations @@ -843,7 +843,7 @@ void RegionTable_Init_WaterTemple() { // Exits Entrance(RR_WATER_TEMPLE_PRE_BOSS_ROOM, []{return ctx->GetDungeon(WATER_TEMPLE)->IsVanilla() && false;}), Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return ctx->GetDungeon(WATER_TEMPLE)->IsMQ() && false;}), - Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), }); areaTable[RR_WATER_TEMPLE_BOSS_ROOM] = Region("Water Temple Boss Room", "Water Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp b/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp index 4b4579210a0..dd95b11fb01 100644 --- a/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp @@ -27,8 +27,8 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_GERUDO_MEMBERSHIP_CARD, logic->CanFinishGerudoFortress()), LOCATION(RC_GF_GS_ARCHERY_RANGE, logic->IsAdult && logic->HookshotOrBoomerang() && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) && logic->CanGetNightTimeGS()), LOCATION(RC_GF_GS_TOP_FLOOR, logic->IsAdult && (logic->CanJumpslashExceptHammer() || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->HasExplosives() || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_DINS_FIRE)) && (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_GF_KITCHEN) || ctx->GetTrickOption(RT_GF_JUMP)) && logic->CanGetNightTimeGS()), - LOCATION(RC_GF_BREAK_ROOM_POT_1, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), - LOCATION(RC_GF_BREAK_ROOM_POT_2, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), + LOCATION(RC_GF_BREAK_ROOM_POT_1, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakPots()), + LOCATION(RC_GF_BREAK_ROOM_POT_2, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakPots()), LOCATION(RC_GF_KITCHEN_POT_1, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), LOCATION(RC_GF_KITCHEN_POT_2, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), LOCATION(RC_GF_NORTH_F1_CARPENTER_POT_1, logic->CanBreakPots()), @@ -43,7 +43,7 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_SOUTH_F1_CARPENTER_CELL_POT_2, logic->CanBreakPots()), LOCATION(RC_GF_SOUTH_F1_CARPENTER_CELL_POT_3, logic->CanBreakPots()), LOCATION(RC_GF_SOUTH_F1_CARPENTER_CELL_POT_4, logic->CanBreakPots()), - LOCATION(RC_GF_ABOVE_JAIL_CRATE, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), + LOCATION(RC_GF_ABOVE_JAIL_CRATE, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOOKSHOT) && ctx->GetTrickOption(RT_GF_JUMP))) && logic->CanBreakCrates()), LOCATION(RC_GF_OUTSIDE_CENTER_CRATE_1, logic->CanBreakCrates()), LOCATION(RC_GF_OUTSIDE_CENTER_CRATE_2, logic->CanBreakCrates()), LOCATION(RC_GF_OUTSIDE_CENTER_CRATE_3, logic->CanBreakCrates()), @@ -60,7 +60,7 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_ARCHERY_START_CRATE_1, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_START_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_LEFT_END_CRATE_1, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), - LOCATION(RC_GF_ARCHERY_LEFT_END_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), + LOCATION(RC_GF_ARCHERY_LEFT_END_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && (logic->IsAdult || (logic->BlastOrSmash() || logic->HookshotOrBoomerang() || logic->CanUse(RG_HOVER_BOOTS)))), LOCATION(RC_GF_ARCHERY_LEFT_END_CHILD_CRATE, logic->IsChild && logic->HasExplosives() && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_RIGHT_END_CRATE_1, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_RIGHT_END_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), @@ -69,10 +69,10 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_KITCHEN_CRATE_3, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_KITCHEN_CRATE_4, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_KITCHEN_CRATE_5, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_1, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_2, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_3, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_4, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_1, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_2, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_3, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || ((logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_GF_JUMP)) && (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW)))) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_4, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || ((logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_GF_JUMP)) && (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW)))) && logic->CanBreakCrates()), LOCATION(RC_GF_NORTH_F1_CARPENTER_CRATE, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_NORTH_F3_CARPENTER_CRATE, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_SOUTH_F2_CARPENTER_CRATE_1, logic->CanBreakCrates()), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp index bdcdf9a4ccc..dbf467b0f48 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp @@ -34,7 +34,7 @@ void RegionTable_Init_CastleGrounds() { }, { //Exits Entrance(RR_CASTLE_GROUNDS, []{return true;}), - Entrance(RR_HC_GARDEN, []{return logic->CanUse(RG_WEIRD_EGG) || !ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->HasExplosives() && logic->CanJumpslash());}), + Entrance(RR_HC_GARDEN, []{return logic->CanUse(RG_WEIRD_EGG) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->HasExplosives() && logic->CanJumpslash());}), Entrance(RR_HC_GREAT_FAIRY_FOUNTAIN, []{return logic->BlastOrSmash();}), Entrance(RR_HC_STORMS_GROTTO, []{return logic->CanOpenStormsGrotto();}), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp index 012503d9a21..d04606018db 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp @@ -7,9 +7,10 @@ void RegionTable_Init_Graveyard() { // clang-format off areaTable[RR_THE_GRAVEYARD] = Region("The Graveyard", "The Graveyard", {RA_THE_GRAVEYARD}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->ButterflyFairy, []{return logic->ButterflyFairy || (logic->CanUse(RG_STICKS) && logic->AtDay);}), - EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), - EventAccess(&logic->BugRock, []{return true;}), + EventAccess(&logic->ButterflyFairy, []{return logic->ButterflyFairy || (logic->CanUse(RG_STICKS) && logic->AtDay);}), + EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), + EventAccess(&logic->BugRock, []{return true;}), + EventAccess(&logic->BorrowBunnyHood, []{return logic->IsChild && logic->AtDay && logic->BorrowSpookyMask && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_GRAVEYARD_FREESTANDING_POH, (((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()) || (ctx->GetTrickOption(RT_GY_POH) && logic->CanUse(RG_BOOMERANG))), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp index add3a2f8647..3359089e0db 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp @@ -7,7 +7,8 @@ void RegionTable_Init_HyruleField() { // clang-format off areaTable[RR_HYRULE_FIELD] = Region("Hyrule Field", "Hyrule Field", {RA_HYRULE_FIELD}, DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->BigPoeKill, []{return logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_EPONA) && logic->HasBottle();}), + EventAccess(&logic->BigPoeKill, []{return logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_EPONA) && logic->HasBottle();}), + EventAccess(&logic->BorrowRightMasks, []{return logic->IsChild && logic->BorrowBunnyHood && logic->HasItem(RG_KOKIRI_EMERALD) && logic->HasItem(RG_GORON_RUBY) && logic->HasItem(RG_ZORA_SAPPHIRE) && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_HF_OCARINA_OF_TIME_ITEM, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp index 777e2905061..3247ef5f4c1 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp @@ -10,6 +10,8 @@ void RegionTable_Init_Kakariko() { EventAccess(&logic->BugRock, []{return true;}), //Open Gate setting is applied in RR_ROOT EventAccess(&logic->KakarikoVillageGateOpen, []{return logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER);}), + //Needs wallet to be able to get another mask after selling Keaton + EventAccess(&logic->BorrowSkullMask, []{return logic->IsChild && logic->CanBorrowMasks && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_SHEIK_IN_KAKARIKO, logic->IsAdult && logic->HasItem(RG_FOREST_MEDALLION) && logic->HasItem(RG_FIRE_MEDALLION) && logic->HasItem(RG_WATER_MEDALLION)), @@ -265,7 +267,7 @@ void RegionTable_Init_Kakariko() { Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return true;}), }); - areaTable[RR_KAK_WELL] = Region("Kak Behind Gate", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_KAK_WELL] = Region("Kak Well", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_KAKARIKO_VILLAGE, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->DrainWell;}), Entrance(RR_BOTTOM_OF_THE_WELL_ENTRYWAY, []{return logic->IsChild || (logic->DrainWell && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF));}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp index 36d3ee1a39d..19e0497f094 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp @@ -105,7 +105,7 @@ void RegionTable_Init_LakeHylia() { areaTable[RR_LH_LAB] = Region("LH Lab", "LH Lab", {}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE))), LOCATION(RC_LH_TRADE_FROG, logic->IsAdult && logic->CanUse(RG_EYEBALL_FROG)), LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()), LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp index fae41664e8e..6fe85afc7bd 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp @@ -7,8 +7,8 @@ void RegionTable_Init_LonLonRanch() { // clang-format off areaTable[RR_LON_LON_RANCH] = Region("Lon Lon Ranch", "Lon Lon Ranch", {RA_LON_LON_RANCH}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->FreedEpona, []{return logic->FreedEpona || ((logic->HasItem(RG_CHILD_WALLET) || ctx->GetOption(RSK_SKIP_EPONA_RACE)) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay);}), - EventAccess(&logic->LinksCow, []{return logic->LinksCow || (logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay);}), + EventAccess(&logic->FreedEpona, []{return logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay;}), + EventAccess(&logic->LinksCow, []{return logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONA) && logic->IsAdult && logic->AtDay;}), }, { //Locations LOCATION(RC_SONG_FROM_MALON, logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_FAIRY_OCARINA) && logic->AtDay), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp index 812b33a557a..a83664fd620 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp @@ -12,9 +12,10 @@ void RegionTable_Init_LostWoods() { areaTable[RR_THE_LOST_WOODS] = Region("Lost Woods", "Lost Woods", {RA_THE_LOST_WOODS}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->GossipStoneFairy, []{return logic->CallGossipFairyExceptSuns();}), - EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), - EventAccess(&logic->BugShrub, []{return logic->IsChild && logic->CanCutShrubs();}), + EventAccess(&logic->GossipStoneFairy, []{return logic->CallGossipFairyExceptSuns();}), + EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), + EventAccess(&logic->BugShrub, []{return logic->IsChild && logic->CanCutShrubs();}), + EventAccess(&logic->BorrowSpookyMask, []{return logic->IsChild && logic->BorrowSkullMask && logic->CanUse(RG_SARIAS_SONG) && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_LW_SKULL_KID, logic->IsChild && logic->CanUse(RG_SARIAS_SONG)), @@ -110,8 +111,8 @@ void RegionTable_Init_LostWoods() { areaTable[RR_DEKU_THEATER] = Region("Deku Theater", "Deku Theater", {}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_DEKU_THEATER_SKULL_MASK, logic->IsChild && logic->SkullMask), - LOCATION(RC_DEKU_THEATER_MASK_OF_TRUTH, logic->IsChild && logic->MaskOfTruth), + LOCATION(RC_DEKU_THEATER_SKULL_MASK, logic->IsChild && logic->BorrowSkullMask), + LOCATION(RC_DEKU_THEATER_MASK_OF_TRUTH, logic->IsChild && logic->BorrowRightMasks), }, { //Exits Entrance(RR_LW_BEYOND_MIDO, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index 2016b24cd2f..b1090d3614a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -14,18 +14,19 @@ void RegionTable_Init_Market() { areaTable[RR_THE_MARKET] = Region("Market", "Market", {RA_THE_MARKET}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_MARKET_GRASS_1, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_2, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_3, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_4, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_5, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_6, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_7, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_8, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MK_NEAR_BAZAAR_CRATE_1, logic->IsChild && logic->CanBreakCrates()), - LOCATION(RC_MK_NEAR_BAZAAR_CRATE_2, logic->IsChild && logic->CanBreakCrates()), - LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_1, logic->IsChild && logic->CanBreakCrates()), - LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_2, logic->IsChild && logic->CanBreakCrates()), + //RANDOTODO add item avalibility to regions to remove need to hardcode logic in limited item use situations + LOCATION(RC_MARKET_GRASS_1, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_2, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_3, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_4, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_5, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_6, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_7, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_8, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MK_NEAR_BAZAAR_CRATE_1, logic->IsChild /*&& logic->CanRoll()*/), + LOCATION(RC_MK_NEAR_BAZAAR_CRATE_2, logic->IsChild /*&& logic->CanRoll()*/), + LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_1, logic->IsChild /*&& logic->CanRoll()*/), + LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_2, logic->IsChild /*&& logic->CanRoll()*/), }, { //Exits Entrance(RR_MARKET_ENTRANCE, []{return true;}), @@ -137,8 +138,14 @@ void RegionTable_Init_Market() { areaTable[RR_MARKET_MASK_SHOP] = Region("Market Mask Shop", "Market Mask Shop", {}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SkullMask, []{return logic->SkullMask || (logic->HasItem(RG_ZELDAS_LETTER) && (ctx->GetOption(RSK_COMPLETE_MASK_QUEST) || ChildCanAccess(RR_KAKARIKO_VILLAGE)));}), //RANDOTODO Complete mask quest does not need this location, so should be tied to link's pocket - EventAccess(&logic->MaskOfTruth, []{return logic->MaskOfTruth || (logic->SkullMask && (ctx->GetOption(RSK_COMPLETE_MASK_QUEST) || (ChildCanAccess(RR_THE_LOST_WOODS) && logic->CanUse(RG_SARIAS_SONG) && RegionTable(RR_THE_GRAVEYARD)->childDay && ChildCanAccess(RR_HYRULE_FIELD) && logic->StoneCount() == 3)));}), + //Currently, mask swap in menu doesn't need access to the mask shop + //If it is forced on/a setting, a copy of these events should be added to root + //it also doesn't need you to open kak gate, but that might be best treated as a bug + EventAccess(&logic->CanBorrowMasks, []{return logic->HasItem(RG_ZELDAS_LETTER) && logic->KakarikoVillageGateOpen;}), + EventAccess(&logic->BorrowSkullMask, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), + EventAccess(&logic->BorrowSpookyMask, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), + EventAccess(&logic->BorrowBunnyHood, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), + EventAccess(&logic->BorrowRightMasks, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), }, { //Locations LOCATION(RC_MASK_SHOP_HINT, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp index 07922bc8f00..3bee82b26b8 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp @@ -17,8 +17,8 @@ void RegionTable_Init_ZorasDomain() { //Locations LOCATION(RC_ZD_DIVING_MINIGAME, logic->HasItem(RG_BRONZE_SCALE) && logic->HasItem(RG_CHILD_WALLET) && logic->IsChild), LOCATION(RC_ZD_CHEST, logic->IsChild && logic->CanUse(RG_STICKS)), - LOCATION(RC_ZD_KING_ZORA_THAWED, logic->KingZoraThawed), - LOCATION(RC_ZD_TRADE_PRESCRIPTION, logic->KingZoraThawed && logic->CanUse(RG_PRESCRIPTION)), + LOCATION(RC_ZD_KING_ZORA_THAWED, logic->IsAdult && logic->KingZoraThawed), + LOCATION(RC_ZD_TRADE_PRESCRIPTION, logic->IsAdult && logic->KingZoraThawed && logic->CanUse(RG_PRESCRIPTION)), LOCATION(RC_ZD_GS_FROZEN_WATERFALL, logic->IsAdult && (logic->HookshotOrBoomerang() || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_MAGIC_SINGLE) && (logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_BIGGORON_SWORD))) || (ctx->GetTrickOption(RT_ZD_GS) && logic->CanJumpslashExceptHammer())) && logic->CanGetNightTimeGS()), LOCATION(RC_ZD_FISH_1, logic->IsChild && logic->HasBottle()), LOCATION(RC_ZD_FISH_2, logic->IsChild && logic->HasBottle()), @@ -28,8 +28,8 @@ void RegionTable_Init_ZorasDomain() { LOCATION(RC_ZD_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), LOCATION(RC_ZD_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_ZD_GOSSIP_STONE, true), - LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_LEFT, logic->CanBreakUpperBeehives()), - LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_RIGHT, logic->CanBreakUpperBeehives()), + LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_LEFT, logic->IsChild && logic->CanBreakUpperBeehives()), + LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_RIGHT, logic->IsChild && logic->CanBreakUpperBeehives()), LOCATION(RC_ZD_NEAR_SHOP_POT_1, logic->CanBreakPots()), LOCATION(RC_ZD_NEAR_SHOP_POT_2, logic->CanBreakPots()), LOCATION(RC_ZD_NEAR_SHOP_POT_3, logic->CanBreakPots()), @@ -50,9 +50,12 @@ void RegionTable_Init_ZorasDomain() { Entrance(RR_ZD_STORMS_GROTTO, []{return logic->CanOpenStormsGrotto();}), }); - areaTable[RR_ZD_BEHIND_KING_ZORA] = Region("ZD Behind King Zora", "Zoras Domain", {RA_ZORAS_DOMAIN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_ZD_BEHIND_KING_ZORA] = Region("ZD Behind King Zora", "Zoras Domain", {RA_ZORAS_DOMAIN}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->KingZoraThawed, []{return logic->IsAdult && logic->BlueFire();}), + }, { //Locations - LOCATION(RC_ZD_BEHIND_KING_ZORA_BEEHIVE, logic->CanBreakUpperBeehives()), + LOCATION(RC_ZD_BEHIND_KING_ZORA_BEEHIVE, logic->IsChild && logic->CanBreakUpperBeehives()), }, { //Exits Entrance(RR_ZORAS_DOMAIN, []{return logic->DeliverLetter || ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_OPEN) || (ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_CLOSED_CHILD) && logic->IsAdult);}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp index a0307045406..2a5390e99fc 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp @@ -11,18 +11,44 @@ void RegionTable_Init_ZorasFountain() { EventAccess(&logic->ButterflyFairy, []{return logic->ButterflyFairy || (logic->CanUse(RG_STICKS) && logic->AtDay);}), }, { //Locations - LOCATION(RC_ZF_ICEBERG_FREESTANDING_POH, logic->IsAdult), + LOCATION(RC_ZF_GS_TREE, logic->IsChild), + LOCATION(RC_ZF_GS_ABOVE_THE_LOG, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), + LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE, true), + LOCATION(RC_ZF_JABU_GOSSIP_STONE, true), + LOCATION(RC_ZF_NEAR_JABU_POT_1, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_2, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_3, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_4, logic->IsChild && logic->CanBreakPots()), + }, { + //Exits + Entrance(RR_ZD_BEHIND_KING_ZORA, []{return true;}), + Entrance(RR_ZF_ICEBERGS, []{return logic->IsAdult;}), + Entrance(RR_ZF_LAKEBED, []{return logic->CanUse(RG_IRON_BOOTS);}), + //child can break the brown rock without lifting the silver rock and it stays gone for adult, but it's not intuitive and there's no reasonable case where it matters. + Entrance(RR_ZF_HIDDEN_CAVE, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->BlastOrSmash();}), + Entrance(RR_ZF_ROCK, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}), + Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, []{return logic->IsChild && (ctx->GetOption(RSK_JABU_OPEN).Is(RO_JABU_OPEN) || logic->CanUse(RG_BOTTLE_WITH_FISH));}), + Entrance(RR_ZF_GREAT_FAIRY_FOUNTAIN, []{return logic->HasExplosives() || (ctx->GetTrickOption(RT_ZF_GREAT_FAIRY_WITHOUT_EXPLOSIVES) && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SILVER_GAUNTLETS));}), + }); + + areaTable[RR_ZF_ICEBERGS] = Region("ZF Icebergs", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_ZF_ICEBERG_FREESTANDING_POH, logic->IsAdult), + }, { + //Exits + //This hover is pretty tight, come at it with momentum and aim for the small corner polygon of the big iceburg while spamming roll + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_ZF_LAKEBED, []{return logic->CanUse(RG_IRON_BOOTS);}), + Entrance(RR_ZF_LEDGE, []{return true;}), + }); + + areaTable[RR_ZF_LAKEBED] = Region("ZF Lakebed", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations LOCATION(RC_ZF_BOTTOM_FREESTANDING_POH, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), - LOCATION(RC_ZF_GS_TREE, logic->IsChild), - LOCATION(RC_ZF_GS_ABOVE_THE_LOG, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), - LOCATION(RC_ZF_GS_HIDDEN_CAVE, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->BlastOrSmash() && logic->HookshotOrBoomerang() && logic->IsAdult && logic->CanGetNightTimeGS()), - LOCATION(RC_ZF_HIDDEN_CAVE_POT_1, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && logic->BlastOrSmash() && logic->CanBreakPots()), - LOCATION(RC_ZF_HIDDEN_CAVE_POT_2, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && logic->BlastOrSmash() && logic->CanBreakPots()), - LOCATION(RC_ZF_HIDDEN_CAVE_POT_3, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && logic->BlastOrSmash() && logic->CanBreakPots()), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), - LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_ZF_BOTTOM_NORTH_INNER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_NORTHEAST_INNER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_SOUTHEAST_INNER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), @@ -41,18 +67,47 @@ void RegionTable_Init_ZorasFountain() { LOCATION(RC_ZF_BOTTOM_SOUTH_OUTER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_SOUTHWEST_OUTER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_NORTHWEST_OUTER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE, true), - LOCATION(RC_ZF_JABU_GOSSIP_STONE, true), - LOCATION(RC_ZF_NEAR_JABU_POT_1, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_2, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_3, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_4, logic->IsChild && logic->CanBreakPots()), }, { //Exits - Entrance(RR_ZD_BEHIND_KING_ZORA, []{return true;}), - Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, []{return (logic->IsChild && logic->CanUse(RG_BOTTLE_WITH_FISH));}), - Entrance(RR_ICE_CAVERN_ENTRYWAY, []{return logic->IsAdult;}), - Entrance(RR_ZF_GREAT_FAIRY_FOUNTAIN, []{return logic->HasExplosives() || (ctx->GetTrickOption(RT_ZF_GREAT_FAIRY_WITHOUT_EXPLOSIVES) && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SILVER_GAUNTLETS));}), + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE);}), + }); + + areaTable[RR_ZF_LEDGE] = Region("ZF Ledge", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE);}), + Entrance(RR_ZF_ICEBERGS, []{return logic->IsAdult;}), + Entrance(RR_ZF_LAKEBED, []{return logic->CanUse(RG_IRON_BOOTS);}), + Entrance(RR_ICE_CAVERN_ENTRYWAY, []{return true;}), + }); + + areaTable[RR_ZF_HIDDEN_CAVE] = Region("ZF Hidden Cave", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_ZF_HIDDEN_CAVE_POT_1, logic->IsAdult && logic->CanBreakPots()), + LOCATION(RC_ZF_HIDDEN_CAVE_POT_2, logic->IsAdult && logic->CanBreakPots()), + LOCATION(RC_ZF_HIDDEN_CAVE_POT_3, logic->IsAdult && logic->CanBreakPots()), + }, { + //Exits + //There are invisible big skultullas here as adult but they do not block the path and can be "seen" with Z-target + //Lens is not currently needed for this either, implying they are not considered blocking, but it's open for discussion long-term + Entrance(RR_ZF_HIDDEN_LEDGE, []{return true;}), + }); + + areaTable[RR_ZF_HIDDEN_LEDGE] = Region("ZF Hidden Ledge", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_ZF_GS_HIDDEN_CAVE, logic->IsAdult && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOMB_THROW) && logic->CanGetNightTimeGS()), + }, { + //Exits + //It is possible to avoid fall damage by jumping towards the right and landing in deeper water, but this is basically never relevent + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->TakeDamage();}), + Entrance(RR_ZF_HIDDEN_CAVE, []{return true;}), + }); + + areaTable[RR_ZF_ROCK] = Region("ZF Rock", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + //Has a wonder item + }, { + //Exits + Entrance(RR_ZORAS_FOUNTAIN, []{return true;}), }); areaTable[RR_ZF_GREAT_FAIRY_FOUNTAIN] = Region("ZF Great Fairy Fountain", "ZF Great Fairy Fountain", {}, NO_DAY_NIGHT_CYCLE, {}, { diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 4a1ede8e72a..93d4093d428 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -71,7 +71,7 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_DISTANT_SCARECROW: return ScarecrowsSong() && CanUse(RG_LONGSHOT); case RG_MAGIC_BEAN: - return GetAmmo(ITEM_BEAN) > 0; + return GetAmmo(ITEM_BEAN) > 0 || CheckInventory(ITEM_BEAN, true); case RG_KOKIRI_SWORD: case RG_DEKU_SHIELD: case RG_GORON_TUNIC: @@ -121,8 +121,6 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_STONE_OF_AGONY: case RG_GERUDO_MEMBERSHIP_CARD: return CheckQuestItem(RandoGetToQuestItem.at(itemName)); - case RG_RUTOS_LETTER: - return CheckEventChkInf(EVENTCHKINF_OBTAINED_RUTOS_LETTER); case RG_DOUBLE_DEFENSE: return GetSaveContext()->isDoubleDefenseAcquired; case RG_FISHING_POLE: @@ -171,6 +169,7 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_BACK_TOWER_KEY: case RG_HYLIA_LAB_KEY: case RG_FISHING_HOLE_KEY: + case RG_RUTOS_LETTER: return CheckRandoInf(RandoGetToRandInf.at(itemName)); // Boss Keys case RG_EPONA: @@ -558,9 +557,8 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal } return killed; case RE_DODONGO: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER) || (quantity <= 5 && CanUse(RG_STICKS)) || HasExplosives() || - CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); + return CanUseSword() || CanUse(RG_MEGATON_HAMMER) || (quantity <= 5 && CanUse(RG_STICKS)) || + HasExplosives() || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); case RE_LIZALFOS: return CanJumpslash() || HasExplosives() || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); case RE_KEESE: @@ -604,11 +602,9 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal (CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield())); case RE_DEAD_HAND: // RANDOTODO change Dead Hand trick to be sticks Dead Hand - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - (CanUse(RG_STICKS) && ctx->GetTrickOption(RT_BOTW_CHILD_DEADHAND)); + return CanUseSword() || (CanUse(RG_STICKS) && ctx->GetTrickOption(RT_BOTW_CHILD_DEADHAND)); case RE_WITHERED_DEKU_BABA: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_BOOMERANG); + return CanUseSword() || CanUse(RG_BOOMERANG); case RE_LIKE_LIKE: case RE_FLOORMASTER: return CanDamage(); @@ -645,8 +641,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal // bow and sling can wake them and damage after they shed their armour, so could reduce ammo requirements for // explosives to 10. requires 8 sticks to kill so would be a trick unless we apply higher stick bag logic case RE_IRON_KNUCKLE: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER) || HasExplosives(); + return CanUseSword() || CanUse(RG_MEGATON_HAMMER) || HasExplosives(); // To stun flare dancer with chus, you have to hit the flame under it while it is spinning. It should eventually // return to spinning after dashing for a while if you miss the window it is possible to damage the core with // explosives, but difficult to get all 4 hits in even with chus, and if it reconstructs the core heals, so it @@ -695,9 +690,8 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal CanUse(RG_STICKS) || HasExplosives() || CanUse(RG_HOOKSHOT) || CanUse(RG_DINS_FIRE) || CanUse(RG_FIRE_ARROWS); case RE_SHELL_BLADE: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER) || CanUse(RG_STICKS) || HasExplosives() || CanUse(RG_HOOKSHOT) || - CanUse(RG_FAIRY_BOW) || CanUse(RG_DINS_FIRE); + return CanJumpslash() || HasExplosives() || CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || + CanUse(RG_DINS_FIRE); case RE_SPIKE: return CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_MEGATON_HAMMER) || CanUse(RG_STICKS) || HasExplosives() || CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || @@ -744,25 +738,20 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal case RE_BARINADE: return HasBossSoul(RG_BARINADE_SOUL) && CanUse(RG_BOOMERANG) && CanJumpslashExceptHammer(); case RE_PHANTOM_GANON: - return HasBossSoul(RG_PHANTOM_GANON_SOUL) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD)) && + return HasBossSoul(RG_PHANTOM_GANON_SOUL) && CanUseSword() && (CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT)); case RE_VOLVAGIA: return HasBossSoul(RG_VOLVAGIA_SOUL) && CanUse(RG_MEGATON_HAMMER); case RE_MORPHA: - return HasBossSoul(RG_MORPHA_SOUL) && CanUse(RG_HOOKSHOT) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER)); + return HasBossSoul(RG_MORPHA_SOUL) && CanUse(RG_HOOKSHOT) && (CanUseSword() || CanUse(RG_MEGATON_HAMMER)); case RE_BONGO_BONGO: return HasBossSoul(RG_BONGO_BONGO_SOUL) && - (CanUse(RG_LENS_OF_TRUTH) || ctx->GetTrickOption(RT_LENS_BONGO)) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD)) && + (CanUse(RG_LENS_OF_TRUTH) || ctx->GetTrickOption(RT_LENS_BONGO)) && CanUseSword() && (CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT) || ctx->GetTrickOption(RT_SHADOW_BONGO)); case RE_TWINROVA: return HasBossSoul(RG_TWINROVA_SOUL) && CanUse(RG_MIRROR_SHIELD) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER)); + (CanUseSword() || CanUse(RG_MEGATON_HAMMER)); case RE_GANONDORF: // RANDOTODO: Trick to use hammer (no jumpslash) or stick (only jumpslash) instead of a sword to reflect the // energy ball and either of them regardless of jumpslashing to damage and kill ganondorf @@ -771,8 +760,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal // for killing ganondorf and all of those can reflect the energy ball // This will not be the case once ammo logic in taken into account as // sticks are limited and using a bottle might become a requirement in that case - return HasBossSoul(RG_GANON_SOUL) && CanUse(RG_LIGHT_ARROWS) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD)); + return HasBossSoul(RG_GANON_SOUL) && CanUse(RG_LIGHT_ARROWS) && CanUseSword(); case RE_GANON: return HasBossSoul(RG_GANON_SOUL) && CanUse(RG_MASTER_SWORD); case RE_DARK_LINK: @@ -792,8 +780,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal return CanUse(RG_BOOMERANG); case RE_BARI: return HookshotOrBoomerang() || CanUse(RG_FAIRY_BOW) || HasExplosives() || CanUse(RG_MEGATON_HAMMER) || - CanUse(RG_STICKS) || CanUse(RG_DINS_FIRE) || - (TakeDamage() && (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD))); + CanUse(RG_STICKS) || CanUse(RG_DINS_FIRE) || (TakeDamage() && CanUseSword()); case RE_SHABOM: // RANDOTODO when you add better damage logic, you can kill this by taking hits return CanUse(RG_BOOMERANG) || CanUse(RG_NUTS) || CanJumpslash() || CanUse(RG_DINS_FIRE) || @@ -962,7 +949,7 @@ bool Logic::CanBreakMudWalls() { } bool Logic::CanGetDekuBabaSticks() { - return (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_BOOMERANG)); + return CanUseSword() || CanUse(RG_BOOMERANG); } bool Logic::CanGetDekuBabaNuts() { @@ -1060,9 +1047,13 @@ bool Logic::HasBottle() { return BottleCount() >= 1; } +bool Logic::CanUseSword() { + return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD); +} + bool Logic::CanJumpslashExceptHammer() { // Not including hammer as hammer jump attacks can be weird; - return CanUse(RG_STICKS) || CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD); + return CanUse(RG_STICKS) || CanUseSword(); } bool Logic::CanJumpslash() { @@ -1398,6 +1389,7 @@ bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless static_cast(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHover) >= static_cast(GlitchDifficulty::INTERMEDIATE))) { return FireTempleKeys >= requiredAmountGlitched; }*/ + // If the Fire Temple loop lock is removed, Small key Count is set to 1 before starting return GetSmallKeyCount(SCENE_FIRE_TEMPLE) >= requiredAmountGlitchless; case RR_WATER_TEMPLE: @@ -1463,6 +1455,7 @@ std::map Logic::RandoGetToEquipFlag = { std::map Logic::RandoGetToRandInf = { { RG_ZELDAS_LETTER, RAND_INF_ZELDAS_LETTER }, { RG_WEIRD_EGG, RAND_INF_WEIRD_EGG }, + { RG_RUTOS_LETTER, RAND_INF_OBTAINED_RUTOS_LETTER }, { RG_GOHMA_SOUL, RAND_INF_GOHMA_SOUL }, { RG_KING_DODONGO_SOUL, RAND_INF_KING_DODONGO_SOUL }, { RG_BARINADE_SOUL, RAND_INF_BARINADE_SOUL }, @@ -1778,9 +1771,13 @@ void Logic::ApplyItemEffect(Item& item, bool state) { } break; case RG_HEART_CONTAINER: mSaveContext->healthCapacity += (!state ? -16 : 16); + if (gPlayState != nullptr) + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_PIECE_OF_HEART: mSaveContext->healthCapacity += (!state ? -4 : 4); + if (gPlayState != nullptr) + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_BOOMERANG: case RG_LENS_OF_TRUTH: @@ -1827,7 +1824,7 @@ void Logic::ApplyItemEffect(Item& item, bool state) { mSaveContext->inventory.items[slot] = itemId; } break; case RG_RUTOS_LETTER: - SetEventChkInf(EVENTCHKINF_OBTAINED_RUTOS_LETTER, state); + SetRandoInf(RAND_INF_OBTAINED_RUTOS_LETTER, state); break; case RG_GOHMA_SOUL: case RG_KING_DODONGO_SOUL: @@ -2235,7 +2232,7 @@ const std::vector& GetDungeonSmallKeyDoors(SceneID sceneId) { } } else if (transitionActor.id == ACTOR_DOOR_SHUTTER) { uint8_t doorType = (transitionActor.params >> 7) & 15; - if (doorType == SHUTTER_BACK_LOCKED || doorType == SHUTTER_BOSS || doorType == SHUTTER_KEY_LOCKED) { + if (doorType == SHUTTER_KEY_LOCKED) { dungeonSmallKeyDoors[key].emplace_back(transitionActor.params & 0x3F); } } @@ -2244,7 +2241,7 @@ const std::vector& GetDungeonSmallKeyDoors(SceneID sceneId) { return dungeonSmallKeyDoors[key]; } -int8_t GetUsedSmallKeyCount(SceneID sceneId) { +int8_t Logic::GetUsedSmallKeyCount(SceneID sceneId) { const auto& smallKeyDoors = GetDungeonSmallKeyDoors(sceneId); // Get the swch value for the scene @@ -2252,7 +2249,7 @@ int8_t GetUsedSmallKeyCount(SceneID sceneId) { if (gPlayState != nullptr && gPlayState->sceneNum == sceneId) { swch = gPlayState->actorCtx.flags.swch; } else { - swch = gSaveContext.sceneFlags[sceneId].swch; + swch = mSaveContext->sceneFlags[sceneId].swch; } // Count the number of small keys doors unlocked @@ -2339,20 +2336,25 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } -void Logic::Reset() { - NewSaveContext(); +void Logic::Reset(bool resetSaveContext /*= true*/) { + if (resetSaveContext) { + NewSaveContext(); + } StartPerformanceTimer(PT_LOGIC_RESET); memset(inLogic, false, sizeof(inLogic)); // Settings-dependent variables - IsKeysanity = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE); + IsFireLoopLocked = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || + ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD) || + ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON); // AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting - // Child item logic - SkullMask = false; - MaskOfTruth = false; + // Mask quest + CanBorrowMasks = false; + BorrowSkullMask = false; + BorrowSpookyMask = false; + BorrowBunnyHood = false; + BorrowRightMasks = false; // Adult logic FreedEpona = false; @@ -2379,37 +2381,39 @@ void Logic::Reset() { ShadowTrialClear = false; LightTrialClear = false; - // Ocarina C Buttons - bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true); - SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle); - - // Progressive Items - SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1); - SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1); - - // If we're not shuffling swim, we start with it - if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) { - SetRandoInf(RAND_INF_CAN_SWIM, true); - } + if (resetSaveContext) { + // Ocarina C Buttons + bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true); + SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle); + + // Progressive Items + SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1); + SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1); + + // If we're not shuffling swim, we start with it + if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) { + SetRandoInf(RAND_INF_CAN_SWIM, true); + } - // If we're not shuffling child's wallet, we start with it - if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) { - SetRandoInf(RAND_INF_HAS_WALLET, true); - } + // If we're not shuffling child's wallet, we start with it + if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) { + SetRandoInf(RAND_INF_HAS_WALLET, true); + } - // If we're not shuffling fishing pole, we start with it - if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) { - SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true); - } + // If we're not shuffling fishing pole, we start with it + if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) { + SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true); + } - // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in vanilla - // FiT - if (!IsKeysanity && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { - SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); + // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in + // vanilla FiT + if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { + SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); + } } // Bottle Count @@ -2462,7 +2466,9 @@ void Logic::Reset() { // Other AtDay = false; AtNight = false; - GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get(); + if (resetSaveContext) { + GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get(); + } // Events ShowedMidoSwordAndShield = false; @@ -2506,6 +2512,7 @@ void Logic::Reset() { ForestOpenBossCorridor = false; ShadowTrialFirstChest = false; MQGTGMazeSwitch = false; + MQGTGRightSideSwitch = false; GTGPlatformSilverRupees = false; MQJabuHolesRoomDoor = false; JabuWestTentacle = false; @@ -2526,6 +2533,8 @@ void Logic::Reset() { Spirit1FSilverRupees = false; JabuRutoIn1F = false; + CalculatingAvailableChecks = false; + StopPerformanceTimer(PT_LOGIC_RESET); } } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 4549c995258..05e44b402c0 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -30,9 +30,12 @@ class Logic { public: bool noVariable = false; - // Child item logic - bool SkullMask = false; - bool MaskOfTruth = false; + // Mask Quest + bool CanBorrowMasks = false; + bool BorrowSkullMask = false; + bool BorrowSpookyMask = false; + bool BorrowBunnyHood = false; + bool BorrowRightMasks = false; // Adult logic bool FreedEpona = false; @@ -59,7 +62,7 @@ class Logic { bool LightTrialClear = false; // Logical keysanity - bool IsKeysanity = false; + bool IsFireLoopLocked = false; // Bottle Count uint8_t Bottles = 0; @@ -183,6 +186,9 @@ class Logic { /* --- END OF HELPERS AND LOCATION ACCESS --- */ + bool CalculatingAvailableChecks = false; + bool ACProcessUndiscoveredExits = false; + SaveContext* mSaveContext = nullptr; Logic(); bool CanUse(RandomizerGet itemName); @@ -209,6 +215,7 @@ class Logic { uint8_t BottleCount(); uint8_t OcarinaButtons(); bool HasBottle(); + bool CanUseSword(); bool CanJumpslashExceptHammer(); bool CanJumpslash(); bool CanHitSwitch(EnemyDistance distance = ED_CLOSE, bool inWater = false); @@ -253,7 +260,7 @@ class Logic { bool CanUseProjectile(); bool CanBuildRainbowBridge(); bool CanTriggerLACS(); - void Reset(); + void Reset(bool resetSaveContext = true); void SetContext(std::shared_ptr _ctx); bool GetInLogic(LogicVal logicVal); void SetInLogic(LogicVal logicVal, bool remove); @@ -267,6 +274,7 @@ class Logic { bool CheckEquipment(uint32_t item); bool CheckQuestItem(uint32_t item); void SetQuestItem(uint32_t item, bool state); + int8_t GetUsedSmallKeyCount(SceneID sceneId); uint8_t GetSmallKeyCount(uint32_t dungeonIndex); void SetSmallKeyCount(uint32_t dungeonIndex, uint8_t count); bool CheckDungeonItem(uint32_t item, uint32_t dungeonIndex); diff --git a/soh/soh/Enhancements/randomizer/option.cpp b/soh/soh/Enhancements/randomizer/option.cpp index 43fcc2f6589..bec719d4b20 100644 --- a/soh/soh/Enhancements/randomizer/option.cpp +++ b/soh/soh/Enhancements/randomizer/option.cpp @@ -368,7 +368,9 @@ void OptionGroup::Disable() { bool OptionGroup::RenderImGui() const { // NOLINT(*-no-recursion) ImGuiWindow* window = ImGui::GetCurrentWindow(); bool changed = false; - ImGui::BeginDisabled(mDisabled); + ImGui::BeginDisabled(mDisabled || CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || + CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0) || + CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0)); if (mContainerType == WidgetContainerType::TABLE) { if (ImGui::BeginTable(mName.c_str(), static_cast(mSubGroups.size()), ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { diff --git a/soh/soh/Enhancements/randomizer/option.h b/soh/soh/Enhancements/randomizer/option.h index d92fa447375..507729498e4 100644 --- a/soh/soh/Enhancements/randomizer/option.h +++ b/soh/soh/Enhancements/randomizer/option.h @@ -302,7 +302,7 @@ class Option { /** * @brief Automatically renders a widget for this option in ImGui, based on the various * properties of this Option. Typically, Bool options are rendered as Checkboxes and - * U8 options are rendered as Comboboxes, but this can be overriden during construction with + * U8 options are rendered as Comboboxes, but this can be overridden during construction with * the `widgetType` property. */ bool RenderImGui(); diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 8dc07ce7a2d..cc5c5fec118 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -42,6 +42,8 @@ void Settings::CreateOptionDescriptions() { "\n" "Open - Sleeping Waterfall is always open. " "Link may always enter Zora's Domain."; + mOptionDescriptions[RSK_JABU_OPEN] = "Closed - A fish is required to open Jabu-Jabu's mouth.\n\n" + "Open - Jabu-Jabu's mouth opens without the need for a fish."; mOptionDescriptions[RSK_LOCK_OVERWORLD_DOORS] = "Add locks to all wooden overworld doors, requiring specific small keys to open them"; mOptionDescriptions[RSK_STARTING_AGE] = diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index d567a3d8e24..41e2b3fa228 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -65,9 +65,10 @@ const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi"; const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap"; const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints"; -static const char* englishRupeeNames[175] = { +static const char* englishRupeeNames[188] = { "[P]", "Bad RNG Rolls", + "Baht", "Bananas", "Beanbean Coins", "Beans", @@ -86,6 +87,7 @@ static const char* englishRupeeNames[175] = { "BugFrags", "Canadian Dollars", "Cards", + "Cents", "Chaos Orbs", "Clams", "Coal", @@ -106,15 +108,17 @@ static const char* englishRupeeNames[175] = { "Darseks", "Dead Memes", "Diamonds", + "Dimes", + "Dinars", "DNA", - "Doge", - "Dogecoin", "Doll Hairs", "Dollars", "Dollarydoos", "Dosh", "Doubloons", + "Drakes", "Dwarfbucks", + "ECU", "Elexit", "Emeralds", "Energon", @@ -150,7 +154,7 @@ static const char* englishRupeeNames[175] = { "KF7 Ammo", "Kinstones", "Kremcoins", - "Kroner", + "Kronor", "Leaves", "Lemmings", "Lien", @@ -173,11 +177,14 @@ static const char* englishRupeeNames[175] = { "Munny", "Mushrooms", "Mysteries", + "Naira", "Neopoints", + "Nickels", "Notes", "Nuyen", "Orbs", "Ore", + "Pence", "Pix", "Pixels", "Plastyks", @@ -190,19 +197,23 @@ static const char* englishRupeeNames[175] = { "Pounds", "Power Pellets", "Primogems", - "Réals", + "Rand", + "Reais", "Refined Metal", "Remote Mines", "Retweets", "Rhinu", + "Rials", "Rings", "Riot Points", + "Riyals", "Robux", "Rubies", "Rubles", "Runite Ore", "Rupees", "Saint Quartz", + "Sapphires", "Septims", "Shekels", "Shillings", @@ -235,32 +246,35 @@ static const char* englishRupeeNames[175] = { "Vespene Gas", "Watts", "Widgets", + "Won", "Woolongs", "World Dollars", "Wumpa Fruit", "Yen", + "Yuan", "Zenny", "Zorkmids", }; -static const char* germanRupeeNames[65] = { - "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", "Diamanten", - "Diridari", "Dogecoin", "ECU", "Elexit", "Erz", "Erzbrocken", "Euro", "EXP", - "Forint", "Franken", "Freunde", "Gil", "Gold", "Groschen", "Gulden", "Gummibären", - "Heller", "Juwelen", "Karolin", "Kartoffeln", "Kies", "Knete", "Knochen", "Kohle", - "Kraniche", "Kreuzer", "Kronen", "Kronkorken", "Kröten", "Mark", "Mäuse", "Monde", - "Moorhühner", "Moos", "Münzen", "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", - "Pilze", "Plastiks", "Pokédollar", "Radieschen", "Rappen", "Rubine", "Saphire", "Schilling", - "Seelen", "Smaragde", "Steine", "Sterne", "Sternis", "Tael", "Taler", "Wagenchips", - "Zenny" +static const char* germanRupeeNames[79] = { + "Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", + "Diamanten", "Dinar", "Diridari", "Dollar", "Draken", "ECU", "Elexit", "Erz", + "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", "Gold", + "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", "Kies", + "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", "Kröten", + "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", "Naira", + "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", "Pokédollar", + "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", "Rupien", + "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", "Sternis", + "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny", }; -static const char* frenchRupeeNames[40] = { - "Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules", - "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dogecoin", "Dollars", - "Émeraudes", "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", - "Grouses", "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", - "Pépètes", "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies" +static const char* frenchRupeeNames[39] = { + "Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules", + "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dollars", "Émeraudes", + "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", "Grouses", + "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", "Pépètes", + "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", }; Randomizer::Randomizer() { @@ -345,16 +359,74 @@ std::unordered_map getItemIdToItemId = { #pragma GCC push_options #pragma GCC optimize("O0") bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { - if (strcmp(spoilerFileName, "") != 0) { - std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName)); - if (!spoilerFileStream) { + static std::unordered_map existsCache; + static std::unordered_map lastModifiedCache; + + if (strcmp(spoilerFileName, "") == 0) { + return false; + } + + std::string sanitizedFileName = SohUtils::Sanitize(spoilerFileName); + + try { + // Check if file exists and get last modified time + std::filesystem::path filePath(sanitizedFileName); + if (!std::filesystem::exists(filePath)) { + // Cache and return false if file doesn't exist + existsCache[sanitizedFileName] = false; + lastModifiedCache.erase(sanitizedFileName); return false; - } else { - return true; } - } - return false; + auto currentLastModified = std::filesystem::last_write_time(filePath); + + // Check cache first + auto existsCacheIt = existsCache.find(sanitizedFileName); + auto lastModifiedCacheIt = lastModifiedCache.find(sanitizedFileName); + + // If we have a valid cache entry and the file hasn't been modified + if (existsCacheIt != existsCache.end() && lastModifiedCacheIt != lastModifiedCache.end() && + lastModifiedCacheIt->second == currentLastModified) { + return existsCacheIt->second; + } + + // Cache miss or file modified - need to check contents + std::ifstream spoilerFileStream(sanitizedFileName); + if (spoilerFileStream) { + nlohmann::json contents; + spoilerFileStream >> contents; + spoilerFileStream.close(); + + bool isValid = contents.contains("version") && + strcmp(std::string(contents["version"]).c_str(), (char*)gBuildVersion) == 0; + + if (!isValid) { + SohGui::RegisterPopup( + "Old Spoiler Version", + "The spoiler file located at\n" + std::string(spoilerFileName) + + "\nwas made by a version that doesn't match the currently running version.\n" + + "Loading for this file has been cancelled."); + CVarClear(CVAR_GENERAL("SpoilerLog")); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + + // Update cache + existsCache[sanitizedFileName] = isValid; + lastModifiedCache[sanitizedFileName] = currentLastModified; + return isValid; + } + + // File couldn't be opened + existsCache[sanitizedFileName] = false; + lastModifiedCache.erase(sanitizedFileName); + return false; + + } catch (const std::filesystem::filesystem_error&) { + // Handle filesystem errors by invalidating cache + existsCache[sanitizedFileName] = false; + lastModifiedCache.erase(sanitizedFileName); + return false; + } } #pragma GCC pop_options #pragma optimize("", on) @@ -472,11 +544,10 @@ void Randomizer::LoadMerchantMessages() { // prompted buy/don't buy CustomMessageManager::Instance->CreateMessage( Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM, - CustomMessage( - "\x08#[[1]]# #[[2]]_Rupees#&Special deal! #ONE LEFT#!&Get it while it lasts!\x0A\x02", - "\x08#[[1]]# #[[2]]_Rubine#&Sonderangebot! #NUR NOCH EINES VERFÜGBAR#!&Beeilen Sie sich!\x0A\x02", - "\x08#[[1]]# #[[2]]_Rubis#&Offre spéciale! #DERNIER EN STOCK#!&Faites vite!\x0A\x02", - { QM_GREEN, QM_YELLOW, QM_RED })); + CustomMessage("\x08#[[1]]# #[[2]]_Rupees#&Special deal! #ONE LEFT#!&Get it while it lasts!\x0A\x02", + "\x08#[[1]]# #[[2]]_Rubine#&#NUR NOCH EINES VERFÜGBAR#!&Beeilen Sie sich!\x0A\x02", + "\x08#[[1]]# #[[2]]_Rubis#&#DERNIER EN STOCK#!&Faites vite!\x0A\x02", + { QM_GREEN, QM_YELLOW, QM_RED })); CustomMessageManager::Instance->CreateMessage( Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM, @@ -3563,7 +3634,7 @@ std::thread randoThread; void GenerateRandomizerImgui(std::string seed = "") { CVarSetInteger(CVAR_GENERAL("RandoGenerating"), 1); - CVarSave(); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); auto ctx = Rando::Context::GetInstance(); // RANDOTODO proper UI for selecting if a spoiler loaded should be used for settings Rando::Settings::GetInstance()->SetAllToContext(); @@ -3675,6 +3746,7 @@ void RandomizerSettingsWindow::DrawElement() { locationsTabOpen = false; tricksTabOpen = false; } + ImGui::EndDisabled(); UIWidgets::Spacer(0); UIWidgets::CVarCheckbox("Manual seed entry", CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), @@ -3710,15 +3782,15 @@ void RandomizerSettingsWindow::DrawElement() { } UIWidgets::Spacer(0); - ImGui::BeginDisabled((CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0) && - gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || - GameInteractor::IsSaveLoaded()); - if (UIWidgets::Button("Generate Randomizer", - UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR))) { + UIWidgets::ButtonOptions options = UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR); + options.Disabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); + if (options.disabled) { + options.DisabledTooltip("Must be on File Select to generate a randomizer seed."); + } + if (UIWidgets::Button("Generate Randomizer", options)) { ctx->SetSpoilerLoaded(false); GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : ""); } - ImGui::EndDisabled(); ImGui::SameLine(); if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { @@ -3731,7 +3803,6 @@ void RandomizerSettingsWindow::DrawElement() { // ImGui::Text("Settings File: %s", presetfilepath.c_str()); UIWidgets::Separator(true, true, 0.f, 0.f); - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGuiWindow* window = ImGui::GetCurrentWindow(); static ImVec2 cellPadding(8.0f, 8.0f); @@ -3747,8 +3818,6 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::EndTabItem(); } - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); if (ImGui::BeginTabItem("Items")) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == @@ -3760,23 +3829,23 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::PopStyleVar(1); ImGui::EndTabItem(); } - ImGui::EndDisabled(); - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); if (ImGui::BeginTabItem("Gameplay")) { + ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == + RO_LOGIC_VANILLA); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); if (mSettings->GetOptionGroup(RSG_GAMEPLAY_IMGUI_TABLE).RenderImGui()) { mNeedsUpdate = true; } + ImGui::EndDisabled(); ImGui::PopStyleVar(1); ImGui::EndTabItem(); } - ImGui::EndDisabled(); - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); if (ImGui::BeginTabItem("Locations")) { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == + RO_LOGIC_VANILLA); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); if (!locationsTabOpen) { locationsTabOpen = true; @@ -3909,10 +3978,10 @@ void RandomizerSettingsWindow::DrawElement() { } ImGui::PopStyleVar(1); ImGui::EndTabItem(); + ImGui::EndDisabled(); } else { locationsTabOpen = false; } - ImGui::EndDisabled(); if (ImGui::BeginTabItem("Tricks/Glitches")) { if (!tricksTabOpen) { @@ -3960,8 +4029,9 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::EndTable(); } - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == + RO_LOGIC_VANILLA); // Tricks static std::unordered_map areaTreeDisabled{ @@ -4339,9 +4409,6 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::EndTabBar(); } UIWidgets::PopStyleTabs(); - - ImGui::EndDisabled(); - ImGui::EndDisabled(); } void RandomizerSettingsWindow::UpdateElement() { @@ -4506,6 +4573,9 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { messageEntry.Replace("[[typeHint]]", Rando::StaticData::hintTextTable[RHT_DUNGEON_ORDINARY].GetHintMessage()); } + // BUG: the icon is not in the message yet so are not accounted for, so overflows are possible + messageEntry.AutoFormat(); + return messageEntry; } @@ -5239,7 +5309,7 @@ void Randomizer::CreateCustomMessages() { "Vous obtenez la %rClé %wdu %gBazar&de la Place du Marché%w!"), GIMESSAGE(RG_MARKET_POTION_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMarket Potion Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gMagie-Laden des Marktes%w!", - "Vous obtenez la %rClé %wde la&%gPlace du Marché%w!"), + "Vous obtenez la %rClé %wdu&%gMagasin de Potions de la&Place du Marché%w!"), GIMESSAGE(RG_MASK_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMask Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gMaskenladen%w!", "Vous obtenez la %rClé %wde la&%gFoire aux Masques%w!"), @@ -5271,7 +5341,7 @@ void Randomizer::CreateCustomMessages() { "Vous obtenez la %rClé %wdu %gMagasin de&Potions de Cocorico%w!"), GIMESSAGE(RG_BOSS_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to the&%gBoss's House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus des Chefs%w!", - "Vous obtenez la %rClé %wde la %gMaison&du chef des ouvriers%w!"), + "Vous obtenez la %rClé %wde la %gMaison&du Chef des Ouvriers%w!"), GIMESSAGE(RG_GRANNYS_POTION_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to&%gGranny's Potion Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für %gAsas Hexenladen%w!", "Vous obtenez la %rClé %wde&l'%gApothicaire%w!"), @@ -5813,7 +5883,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { switch (item) { case RG_MAGIC_SINGLE: gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Magic_Fill(play); break; case RG_MAGIC_DOUBLE: @@ -5821,7 +5891,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Magic_Fill(play); break; @@ -5834,7 +5904,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { case RG_DOUBLE_DEFENSE: gSaveContext.isDoubleDefenseAcquired = true; gSaveContext.inventory.defenseHearts = 20; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; break; case RG_TYCOON_WALLET: Inventory_ChangeUpgrade(UPG_WALLET, 3); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 34bddd0a5ad..63dbeb3896c 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -509,6 +509,12 @@ typedef enum { RR_ZD_SHOP, RR_ZD_STORMS_GROTTO, RR_ZORAS_FOUNTAIN, + RR_ZF_ICEBERGS, + RR_ZF_LAKEBED, + RR_ZF_LEDGE, + RR_ZF_HIDDEN_CAVE, + RR_ZF_HIDDEN_LEDGE, + RR_ZF_ROCK, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, @@ -560,6 +566,7 @@ typedef enum { RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, + RR_DEKU_TREE_BOSS_EXIT, RR_DEKU_TREE_BOSS_ROOM, RR_DODONGOS_CAVERN_BEGINNING, @@ -607,7 +614,9 @@ typedef enum { RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, RR_DODONGOS_CAVERN_MQ_BACK_BEHIND_FIRE, RR_DODONGOS_CAVERN_MQ_BACK_SWITCH_GRAVE, + RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, + RR_DODONGOS_CAVERN_BOSS_EXIT, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_JABU_JABUS_BELLY_BEGINNING, @@ -636,6 +645,7 @@ typedef enum { RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, + RR_JABU_JABUS_BELLY_BOSS_EXIT, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_FOREST_TEMPLE_FIRST_ROOM, @@ -2599,7 +2609,6 @@ typedef enum { RC_PIERRE, RC_DELIVER_RUTOS_LETTER, - RC_MASTER_SWORD_PEDESTAL, RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE, RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE, RC_KF_GOSSIP_STONE, @@ -4892,7 +4901,6 @@ typedef enum { RHT_GANONS_CASTLE_MQ_DEKU_SCRUB_RIGHT, RHT_GANONS_TOWER_BOSS_KEY_CHEST, RHT_DELIVER_RUTOS_LETTER, - RHT_MASTER_SWORD_PEDESTAL, // Beehives RHT_BEEHIVE_CHEST_GROTTO, RHT_BEEHIVE_COW_GROTTO, @@ -5360,19 +5368,24 @@ typedef enum { RHT_SKULLS_HINT, RHT_MASK_SHOP_HINT, // Shuffle Pots - RHT_POT_KOKIRI_FOREST, + RHT_POT_LINKS_HOUSE, + RHT_POT_TWINS_HOUSE, + RHT_POT_KNOW_IT_ALL, RHT_POT_GERUDO_FORTRESS, RHT_POT_WASTELAND, - RHT_POT_MARKET, + RHT_POT_GUARD_HOUSE, + RHT_POT_POE_SHOP, + RHT_POT_ALLY_HOUSE, RHT_POT_KAKARIKO, - RHT_POT_GRAVEYARD, + RHT_POT_DAMPE, RHT_POT_GORON_CITY, RHT_POT_DEATH_MOUNTAIN_CRATER, RHT_POT_ZORAS_DOMAIN, RHT_POT_ZORAS_FOUNTAIN, RHT_POT_LON_LON_RANCH, - RHT_POT_HYRULE_FIELD, - RHT_POT_HYRULE_CASTLE, + RHT_POT_TALONS_HOUSE, + RHT_POT_WEB_GROTTO, + RHT_POT_MUD_WALL_GROTTO, RHT_POT_DODONGOS_CAVERN, RHT_POT_JABU_JABUS_BELLY, RHT_POT_FOREST_TEMPLE, @@ -5678,6 +5691,7 @@ typedef enum { RSK_DOOR_OF_TIME, RSK_ZORAS_FOUNTAIN, RSK_SLEEPING_WATERFALL, + RSK_JABU_OPEN, RSK_STARTING_AGE, RSK_SELECTED_STARTING_AGE, RSK_GERUDO_FORTRESS, @@ -5928,6 +5942,12 @@ typedef enum { RO_WATERFALL_OPEN, } RandoOptionSleepingWaterfall; +// Jabu-Jabu settings (closed, open) +typedef enum { + RO_JABU_CLOSED, + RO_JABU_OPEN, +} RandoOptionJabu; + // Starting Age settings (child, adult, random) typedef enum { RO_AGE_CHILD, diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 5eeabb99c63..81e1c09f022 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -137,9 +137,9 @@ std::map> checksByArea; bool areasFullyChecked[RCAREA_INVALID]; u32 areasSpoiled = 0; bool showVOrMQ; -s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)" -s8 areaChecksAvailable[RCAREA_INVALID]; -s8 areaCheckTotals[RCAREA_INVALID]; +s16 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)" +s16 areaChecksAvailable[RCAREA_INVALID]; +s16 areaCheckTotals[RCAREA_INVALID]; uint16_t totalChecks = 0; uint16_t totalChecksAvailable = 0; uint16_t totalChecksGotten = 0; @@ -245,6 +245,7 @@ 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 }; static ImGuiTextFilter checkSearch; +static bool recalculateAvailable = false; std::array filterAreasHidden = { 0 }; std::array filterChecksHidden = { 0 }; @@ -494,6 +495,9 @@ void SetShopSeen(uint32_t sceneNum, bool prices) { } void CheckTrackerLoadGame(int32_t fileNum) { + if (IS_BOSS_RUSH) { + return; + } LoadSettings(); TrySetAreas(); for (auto& entry : Rando::StaticData::GetLocationTable()) { @@ -579,7 +583,14 @@ void CheckTrackerLoadGame(int32_t fileNum) { UpdateAllOrdering(); UpdateInventoryChecks(); UpdateFilters(); - RecalculateAvailableChecks(); + + RegionTable_Init(); + + if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_ENTRANCES).Get()) { + Rando::Context::GetInstance()->GetEntranceShuffler()->ApplyEntranceOverrides(); + } + + recalculateAvailable = true; } void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) { @@ -877,7 +888,7 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave) { void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { SaveTrackerData(saveContext, sectionID, fullSave); if (fullSave) { - RecalculateAvailableChecks(); + recalculateAvailable = true; } } @@ -897,7 +908,6 @@ void LoadFile() { SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0); UpdateAllOrdering(); UpdateAllAreas(); - RegionTable_Init(); } void Teardown() { @@ -987,6 +997,11 @@ void CheckTrackerWindow::DrawElement() { return; } + if (recalculateAvailable) { + recalculateAvailable = false; + RecalculateAvailableChecks(); + } + SceneID sceneId = SCENE_ID_MAX; if (gPlayState != nullptr) { sceneId = (SceneID)gPlayState->sceneNum; @@ -1086,14 +1101,6 @@ void CheckTrackerWindow::DrawElement() { bool doingCollapseOrExpand = optExpandAll || optCollapseAll; bool isThisAreaSpoiled; RandomizerCheckArea lastArea = RCAREA_INVALID; - Color_RGBA8 areaCompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.MainColor.Value"), Color_Main_Default); - Color_RGBA8 areaIncompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.MainColor.Value"), Color_Main_Default); - Color_RGBA8 extraCompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.ExtraColor.Value"), Color_Area_Complete_Extra_Default); - Color_RGBA8 extraIncompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor.Value"), Color_Area_Incomplete_Extra_Default); Color_RGBA8 mainColor; Color_RGBA8 extraColor; std::string stemp; @@ -1121,11 +1128,11 @@ void CheckTrackerWindow::DrawElement() { } else { // Get the colour for the area if (thisAreaFullyChecked) { - mainColor = areaCompleteColor; - extraColor = extraCompleteColor; + mainColor = Color_Area_Complete_Main; + extraColor = Color_Area_Complete_Extra; } else { - mainColor = areaIncompleteColor; - extraColor = extraIncompleteColor; + mainColor = Color_Area_Incomplete_Main; + extraColor = Color_Area_Incomplete_Extra; } // Draw the area @@ -1265,8 +1272,13 @@ void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flag windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; } } - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Color_Background.r / 255.0f, Color_Background.g / 255.0f, - Color_Background.b / 255.0f, Color_Background.a / 255.0f)); + auto maybeParent = ImGui::GetCurrentWindow(); + ImGuiWindow* window = ImGui::FindWindowByName(UniqueName.c_str()); + if (window != NULL && window->DockTabIsVisible && window->ParentWindow != NULL && + std::string(window->ParentWindow->Name).compare(0, strlen("Main - Deck"), "Main - Deck") == 0) { + Color_Background.a = 255; + } + ImGui::PushStyleColor(ImGuiCol_WindowBg, VecFromRGBA8(Color_Background)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::Begin(UniqueName.c_str(), &open, windowFlags); @@ -1483,6 +1495,27 @@ void LoadSettings() { showOverworldFreestanding = false; showDungeonFreestanding = true; } + + switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY)) { + case RO_GANON_BOSS_KEY_LACS_STONES: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_STONES); + break; + case RO_GANON_BOSS_KEY_LACS_MEDALLIONS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_MEDALLIONS); + break; + case RO_GANON_BOSS_KEY_LACS_REWARDS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_REWARDS); + break; + case RO_GANON_BOSS_KEY_LACS_DUNGEONS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_DUNGEONS); + break; + case RO_GANON_BOSS_KEY_LACS_TOKENS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_TOKENS); + break; + default: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_VANILLA); + break; + } } bool IsCheckShuffled(RandomizerCheck rc) { @@ -1563,10 +1596,11 @@ bool IsCheckShuffled(RandomizerCheck rc) { bool IsVisibleInCheckTracker(RandomizerCheck rc) { auto loc = Rando::StaticData::GetLocation(rc); if (IS_RANDO) { - return IsCheckShuffled(rc) || - (alwaysShowGS && loc->GetRCType() == RCTYPE_SKULL_TOKEN && - OTRGlobals::Instance->gRandoContext->IsQuestOfLocationActive(rc)) || - (loc->GetRCType() == RCTYPE_SHOP && showShops && !hideShopUnshuffledChecks); + return !Rando::Context::GetInstance()->GetItemLocation(rc)->IsExcluded() && + (IsCheckShuffled(rc) || + (alwaysShowGS && loc->GetRCType() == RCTYPE_SKULL_TOKEN && + OTRGlobals::Instance->gRandoContext->IsQuestOfLocationActive(rc)) || + (loc->GetRCType() == RCTYPE_SHOP && showShops && !hideShopUnshuffledChecks)); } else { return loc->IsVanillaCompletion() && (!loc->IsDungeon() || (loc->IsDungeon() && loc->GetQuest() == gSaveContext.ship.quest.id)); @@ -1822,7 +1856,7 @@ void DrawLocation(RandomizerCheck rc) { case RCSHOW_IDENTIFIED: case RCSHOW_SEEN: if (IS_RANDO) { - if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery && !itemLoc->IsAddedToPool()) { + if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery) { if (status == RCSHOW_IDENTIFIED) { txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage( gSaveContext.language); @@ -1832,11 +1866,10 @@ void DrawLocation(RandomizerCheck rc) { .GetName() .GetForLanguage(gSaveContext.language); } - } else if (!mystery && !itemLoc->IsAddedToPool()) { + } else if (!mystery) { txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language); } - if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery && - !itemLoc->IsAddedToPool()) { + if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery) { auto price = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice(); if (price) { txt += fmt::format(" - {}", price); @@ -1932,7 +1965,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, "Hidden", cvarHideName, UIWidgets::CheckboxOptions( { { .tooltip = "When active, checks will hide by default when updated to this state. Can " - "be overriden with the \"Show Hidden Items\" option." } }) + "be overridden with the \"Show Hidden Items\" option." } }) .Color(theme)); ImGui::PopID(); } @@ -1960,43 +1993,38 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, UIWidgets::PopStyleCombobox(); } -void RecalculateAvailableChecks() { - if (!enableAvailableChecks) { +void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) { + if (!enableAvailableChecks || !GameInteractor::IsSaveLoaded()) { return; } ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); + const auto& ctx = Rando::Context::GetInstance(); + std::vector targetLocations; - targetLocations.reserve(RR_MAX); + targetLocations.reserve(RC_MAX); for (auto& location : Rando::StaticData::GetLocationTable()) { RandomizerCheck rc = location.GetRandomizerCheck(); - Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc); itemLocation->SetAvailable(false); if (!itemLocation->HasObtained()) { targetLocations.emplace_back(rc); } } - std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true); + std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true, startingRegion); for (auto& rc : availableChecks) { - const auto& location = Rando::StaticData::GetLocation(rc); - const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); - if (location->GetRCType() == RCTYPE_SHOP && itemLocation->GetCheckStatus() == RCSHOW_IDENTIFIED) { - if (CanBuyAnother(rc)) { - itemLocation->SetAvailable(true); - } - } else { - itemLocation->SetAvailable(true); - } + const auto& itemLocation = ctx->GetItemLocation(rc); + itemLocation->SetAvailable(true); } totalChecksAvailable = 0; for (auto& [rcArea, vec] : checksByArea) { areaChecksAvailable[rcArea] = 0; for (auto& rc : vec) { - Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc); if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) { areaChecksAvailable[rcArea]++; } @@ -2078,11 +2106,13 @@ void CheckTrackerSettingsWindow::DrawElement() { .DefaultIndex(TRACKER_COMBO_BUTTON_L)); } } + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Vanilla/MQ Dungeon Spoilers", CVAR_TRACKER_CHECK("MQSpoilers"), UIWidgets::CheckboxOptions() .Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. " "Otherwise, Vanilla/MQ dungeon locations must be unlocked.") .Color(THEME_COLOR)); + ImGui::EndDisabled(); if (UIWidgets::CVarCheckbox( "Hide unshuffled shop item checks", CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), UIWidgets::CheckboxOptions() @@ -2103,6 +2133,7 @@ void CheckTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("If enabled, will show a check's logic when hovering over it.") .Color(THEME_COLOR)); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (UIWidgets::CVarCheckbox("Enable Available Checks", CVAR_TRACKER_CHECK("EnableAvailableChecks"), UIWidgets::CheckboxOptions() .Tooltip("If enabled, will show the checks that are available to be collected " @@ -2111,6 +2142,7 @@ void CheckTrackerSettingsWindow::DrawElement() { enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); RecalculateAvailableChecks(); } + ImGui::EndDisabled(); // Filtering settings UIWidgets::PaddedSeparator(); @@ -2167,8 +2199,8 @@ void CheckTrackerSettingsWindow::DrawElement() { "Checks that you saved the game while having collected.", THEME_COLOR); ImGui::PopStyleVar(1); + ImGui::EndTable(); } - ImGui::EndTable(); } void CheckTrackerWindow::InitElement() { diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h index d6fd503b361..7966c4b370a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -61,5 +61,5 @@ void UpdateAllOrdering(); void UpdateAllAreas(); void RecalculateAllAreaTotals(); void SpoilAreaFromCheck(RandomizerCheck rc); -void RecalculateAvailableChecks(); +void RecalculateAvailableChecks(RandomizerRegion startingRegion = RR_ROOT); } // namespace CheckTracker diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index 3c7b433afaa..e32223124ee 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -253,7 +253,9 @@ void Entrance_Init(void) { for (s16 i = 0; i < 4; i++) { // Zero out the bit in the field which tells the game to keep playing // background music for all four scene setups at each index - gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG; + if (override + i < ENTRANCE_TABLE_SIZE) { + gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG; + } } } } @@ -404,9 +406,9 @@ void Entrance_SetSavewarpEntrance(void) { gSaveContext.entranceIndex = ENTR_ICE_CAVERN_ENTRANCE; } else if (scene == SCENE_INSIDE_GANONS_CASTLE) { gSaveContext.entranceIndex = ENTR_INSIDE_GANONS_CASTLE_ENTRANCE; - } else if (scene == SCENE_GANONS_TOWER || scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || - scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || scene == SCENE_GANON_BOSS || - scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) { + } else if (scene == SCENE_GANONS_TOWER || scene == SCENE_GANONDORF_BOSS || + scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || + scene == SCENE_GANON_BOSS || scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) { gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb } else if (scene == SCENE_THIEVES_HIDEOUT) { // Theives hideout gSaveContext.entranceIndex = ENTR_THIEVES_HIDEOUT_0; // Gerudo Fortress -> Thieve's Hideout spawn 0 @@ -809,6 +811,7 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) { if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) { u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex)); gSaveContext.ship.stats.entrancesDiscovered[idx] |= entranceBit; + CheckTracker_RecalculateAvailableChecks(); // Set reverse entrance when not decoupled if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) { diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp index c8e12f44980..b71bae5b315 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp @@ -423,7 +423,7 @@ bool IsEntranceDiscovered(s16 index) { bool isDiscovered = Entrance_GetIsEntranceDiscovered(index); if (!isDiscovered) { // If the pair included one of the hyrule field <-> zora's river entrances, - // the randomizer will have also overriden the water-based entrances, so check those too + // the randomizer will have also overridden the water-based entrances, so check those too if ((index == ENTR_ZORAS_RIVER_WEST_EXIT && Entrance_GetIsEntranceDiscovered(ENTR_ZORAS_RIVER_3)) || (index == ENTR_ZORAS_RIVER_3 && Entrance_GetIsEntranceDiscovered(ENTR_ZORAS_RIVER_WEST_EXIT))) { isDiscovered = true; @@ -690,6 +690,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("Automatically scroll to the first available entrance in the current scene") .Color(THEME_COLOR)); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Highlight previous", CVAR_TRACKER_ENTRANCE("HighlightPrevious"), UIWidgets::CheckboxOptions() .Tooltip("Highlight the previous entrance that Link came from") @@ -698,6 +699,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("Highlight available entrances in the current scene") .Color(THEME_COLOR)); + ImGui::EndDisabled(); UIWidgets::CVarCheckbox("Hide undiscovered", CVAR_TRACKER_ENTRANCE("CollapseUndiscovered"), UIWidgets::CheckboxOptions() .Tooltip("Collapse undiscovered entrances towards the bottom of each group") @@ -724,6 +726,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::RadioButtonsOptions().Color(THEME_COLOR).Tooltip("Group entrances by their entrance type")); ImGui::Text("Spoiler Reveal"); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox( "Show Source", CVAR_TRACKER_ENTRANCE("ShowFrom"), UIWidgets::CheckboxOptions().Tooltip("Reveal the sourcefor undiscovered entrances").Color(THEME_COLOR)); @@ -731,7 +734,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("Reveal the destination for undiscovered entrances") .Color(THEME_COLOR)); - + ImGui::EndDisabled(); ImGui::EndTable(); } diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h index 048fcc86ca4..b6cb4d1ddd8 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_inf.h +++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h @@ -1952,6 +1952,7 @@ typedef enum { RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_7, RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_8, // End Grass + RAND_INF_OBTAINED_RUTOS_LETTER, // If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be // ceil(RAND_INF_MAX / 16) diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index 8d9d2559a89..aea0a75d11a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1161,8 +1161,14 @@ void BeginFloatingWindows(std::string UniqueName, ImGuiWindowFlags flags = 0) { windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; } } - ImGui::PushStyleColor(ImGuiCol_WindowBg, - VecFromRGBA8(CVarGetColor(CVAR_TRACKER_ITEM("BgColor.Value"), { 0, 0, 0, 0 }))); + auto color = VecFromRGBA8(CVarGetColor(CVAR_TRACKER_ITEM("BgColor.Value"), { 0, 0, 0, 0 })); + auto maybeParent = ImGui::GetCurrentWindow(); + ImGuiWindow* window = ImGui::FindWindowByName(UniqueName.c_str()); + if (window != NULL && window->DockTabIsVisible && window->ParentWindow != NULL && + std::string(window->ParentWindow->Name).compare(0, strlen("Main - Deck"), "Main - Deck") == 0) { + color.w = 1.0f; + } + ImGui::PushStyleColor(ImGuiCol_WindowBg, color); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::Begin(UniqueName.c_str(), nullptr, windowFlags); @@ -1526,8 +1532,9 @@ void ItemTrackerWindow::DrawElement() { if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Notes"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW && - CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == - TRACKER_DISPLAY_ALWAYS) { + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == + TRACKER_DISPLAY_ALWAYS)) { DrawNotes(); } EndFloatingWindows(); @@ -1636,7 +1643,10 @@ void ItemTrackerWindow::DrawElement() { if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Notes"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE && - CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != + TRACKER_DISPLAY_COMBO_BUTTON))) { ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver); BeginFloatingWindows("Personal Notes", ImGuiWindowFlags_NoFocusOnAppearing); DrawNotes(true); @@ -1703,256 +1713,261 @@ static std::unordered_map minimalDisplayTypes = { void ItemTrackerSettingsWindow::DrawElement() { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); - ImGui::BeginTable("itemTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV); - ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableHeadersRow(); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - CVarColorPicker("Background Color##gItemTrackerBgColor", CVAR_TRACKER_ITEM("BgColor"), { 0, 0, 0, 0 }, true, - ColorPickerRandomButton | ColorPickerResetButton, THEME_COLOR); - - ImGui::PopItemWidth(); - if (CVarCombobox("Window Type", CVAR_TRACKER_ITEM("WindowType"), windowTypes, - ComboboxOptions() - .DefaultIndex(TRACKER_WINDOW_FLOATING) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - - if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING) { - if (CVarCheckbox("Enable Dragging", CVAR_TRACKER_ITEM("Draggable"), CheckboxOptions().Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCheckbox("Only enable while paused", CVAR_TRACKER_ITEM("ShowOnlyPaused"), - CheckboxOptions().Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Display Mode", CVAR_TRACKER_ITEM("DisplayType.Main"), displayModes, + if (ImGui::BeginTable("itemTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { + ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + CVarColorPicker("Background Color##gItemTrackerBgColor", CVAR_TRACKER_ITEM("BgColor"), { 0, 0, 0, 0 }, true, + ColorPickerRandomButton | ColorPickerResetButton, THEME_COLOR); + + ImGui::PopItemWidth(); + if (CVarCombobox("Window Type", CVAR_TRACKER_ITEM("WindowType"), windowTypes, ComboboxOptions() - .DefaultIndex(TRACKER_DISPLAY_ALWAYS) + .DefaultIndex(TRACKER_WINDOW_FLOATING) .ComponentAlignment(ComponentAlignments::Right) .LabelPosition(LabelPositions::Far) .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == - TRACKER_DISPLAY_COMBO_BUTTON) { - if (CVarCombobox("Combo Button 1", CVAR_TRACKER_ITEM("ComboButton1"), buttons, - ComboboxOptions() - .DefaultIndex(TRACKER_COMBO_BUTTON_L) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { + + if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING) { + if (CVarCheckbox("Enable Dragging", CVAR_TRACKER_ITEM("Draggable"), CheckboxOptions().Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCheckbox("Only enable while paused", CVAR_TRACKER_ITEM("ShowOnlyPaused"), + CheckboxOptions().Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (CVarCombobox("Combo Button 2", CVAR_TRACKER_ITEM("ComboButton2"), buttons, + if (CVarCombobox("Display Mode", CVAR_TRACKER_ITEM("DisplayType.Main"), displayModes, ComboboxOptions() - .DefaultIndex(TRACKER_COMBO_BUTTON_R) + .DefaultIndex(TRACKER_DISPLAY_ALWAYS) .ComponentAlignment(ComponentAlignments::Right) .LabelPosition(LabelPositions::Far) .Color(THEME_COLOR))) { shouldUpdateVectors = true; } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == + TRACKER_DISPLAY_COMBO_BUTTON) { + if (CVarCombobox("Combo Button 1", CVAR_TRACKER_ITEM("ComboButton1"), buttons, + ComboboxOptions() + .DefaultIndex(TRACKER_COMBO_BUTTON_L) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Combo Button 2", CVAR_TRACKER_ITEM("ComboButton2"), buttons, + ComboboxOptions() + .DefaultIndex(TRACKER_COMBO_BUTTON_R) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } } - } - ImGui::Separator(); - CVarSliderInt("Icon size : %dpx", CVAR_TRACKER_ITEM("IconSize"), - IntSliderOptions().Min(25).Max(128).DefaultValue(36).Color(THEME_COLOR)); - CVarSliderInt("Icon margins : %dpx", CVAR_TRACKER_ITEM("IconSpacing"), - IntSliderOptions().Min(-5).Max(50).DefaultValue(12).Color(THEME_COLOR)); - CVarSliderInt("Text size : %dpx", CVAR_TRACKER_ITEM("TextSize"), - IntSliderOptions().Min(1).Max(30).DefaultValue(13).Color(THEME_COLOR)); - - ImGui::NewLine(); - CVarCombobox("Ammo/Capacity Tracking", CVAR_TRACKER_ITEM("ItemCountType"), itemTrackerCapacityTrackOptions, - ComboboxOptions() - .DefaultIndex(ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) - .ComponentAlignment(ComponentAlignments::Left) - .LabelPosition(LabelPositions::Above) - .Color(THEME_COLOR) - .Tooltip("Customize what the numbers under each item are tracking." - "\n\nNote: items without capacity upgrades will track ammo even in capacity mode")); - if (CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == - ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY || - CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == - ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY) { - if (CVarCheckbox("Align count to left side", CVAR_TRACKER_ITEM("ItemCountAlignLeft"), - CheckboxOptions().Color(THEME_COLOR))) { - shouldUpdateVectors = true; + ImGui::Separator(); + CVarSliderInt("Icon size : %dpx", CVAR_TRACKER_ITEM("IconSize"), + IntSliderOptions().Min(25).Max(128).DefaultValue(36).Color(THEME_COLOR)); + CVarSliderInt("Icon margins : %dpx", CVAR_TRACKER_ITEM("IconSpacing"), + IntSliderOptions().Min(-5).Max(50).DefaultValue(12).Color(THEME_COLOR)); + CVarSliderInt("Text size : %dpx", CVAR_TRACKER_ITEM("TextSize"), + IntSliderOptions().Min(1).Max(30).DefaultValue(13).Color(THEME_COLOR)); + + ImGui::NewLine(); + CVarCombobox("Ammo/Capacity Tracking", CVAR_TRACKER_ITEM("ItemCountType"), itemTrackerCapacityTrackOptions, + ComboboxOptions() + .DefaultIndex(ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) + .ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above) + .Color(THEME_COLOR) + .Tooltip("Customize what the numbers under each item are tracking." + "\n\nNote: items without capacity upgrades will track ammo even in capacity mode")); + if (CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == + ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY || + CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == + ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY) { + if (CVarCheckbox("Align count to left side", CVAR_TRACKER_ITEM("ItemCountAlignLeft"), + CheckboxOptions().Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } } - } - CVarCombobox("Key Count Tracking", CVAR_TRACKER_ITEM("KeyCounts"), itemTrackerKeyTrackOptions, - ComboboxOptions() - .DefaultIndex(KEYS_COLLECTED_MAX) - .ComponentAlignment(ComponentAlignments::Left) - .LabelPosition(LabelPositions::Above) - .Color(THEME_COLOR) - .Tooltip("Customize what numbers are shown for key tracking.")); - - CVarCombobox("Triforce Piece Count Tracking", CVAR_TRACKER_ITEM("TriforcePieceCounts"), - itemTrackerTriforcePieceTrackOptions, - ComboboxOptions() - .DefaultIndex(TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX) - .ComponentAlignment(ComponentAlignments::Left) - .LabelPosition(LabelPositions::Above) - .Color(THEME_COLOR) - .Tooltip("Customize what numbers are shown for triforce piece tracking.")); - - ImGui::TableNextColumn(); - - if (CVarCombobox("Inventory", CVAR_TRACKER_ITEM("DisplayType.Inventory"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Equipment", CVAR_TRACKER_ITEM("DisplayType.Equipment"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Misc", CVAR_TRACKER_ITEM("DisplayType.Misc"), displayTypes, + CVarCombobox("Key Count Tracking", CVAR_TRACKER_ITEM("KeyCounts"), itemTrackerKeyTrackOptions, ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Dungeon Rewards", CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), displayTypes, + .DefaultIndex(KEYS_COLLECTED_MAX) + .ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above) + .Color(THEME_COLOR) + .Tooltip("Customize what numbers are shown for key tracking.")); + + CVarCombobox("Triforce Piece Count Tracking", CVAR_TRACKER_ITEM("TriforcePieceCounts"), + itemTrackerTriforcePieceTrackOptions, ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), SECTION_DISPLAY_MAIN_WINDOW) == - SECTION_DISPLAY_SEPARATE) { - if (CVarCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"))) { + .DefaultIndex(TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX) + .ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above) + .Color(THEME_COLOR) + .Tooltip("Customize what numbers are shown for triforce piece tracking.")); + + ImGui::TableNextColumn(); + + if (CVarCombobox("Inventory", CVAR_TRACKER_ITEM("DisplayType.Inventory"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - } - if (CVarCombobox("Songs", CVAR_TRACKER_ITEM("DisplayType.Songs"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Dungeon Items", CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) != - SECTION_DISPLAY_HIDDEN) { - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) == + if (CVarCombobox("Equipment", CVAR_TRACKER_ITEM("DisplayType.Equipment"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Misc", CVAR_TRACKER_ITEM("DisplayType.Misc"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Dungeon Rewards", CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), SECTION_DISPLAY_MAIN_WINDOW) == SECTION_DISPLAY_SEPARATE) { - if (CVarCheckbox("Horizontal display", CVAR_TRACKER_ITEM("DungeonItems.Layout"), + if (CVarCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"), + CheckboxOptions().DefaultValue(false).Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } + if (CVarCombobox("Songs", CVAR_TRACKER_ITEM("DisplayType.Songs"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Dungeon Items", CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) != + SECTION_DISPLAY_HIDDEN) { + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) == + SECTION_DISPLAY_SEPARATE) { + if (CVarCheckbox("Horizontal display", CVAR_TRACKER_ITEM("DungeonItems.Layout"), + CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } + if (CVarCheckbox("Maps and compasses", CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { shouldUpdateVectors = true; } } - if (CVarCheckbox("Maps and compasses", CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), - CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { + if (CVarCombobox("Greg", CVAR_TRACKER_ITEM("DisplayType.Greg"), extendedDisplayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - } - if (CVarCombobox("Greg", CVAR_TRACKER_ITEM("DisplayType.Greg"), extendedDisplayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Triforce Pieces", CVAR_TRACKER_ITEM("DisplayType.TriforcePieces"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - - if (CVarCombobox("Boss Souls", CVAR_TRACKER_ITEM("DisplayType.BossSouls"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Triforce Pieces", CVAR_TRACKER_ITEM("DisplayType.TriforcePieces"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Ocarina Buttons", CVAR_TRACKER_ITEM("DisplayType.OcarinaButtons"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Boss Souls", CVAR_TRACKER_ITEM("DisplayType.BossSouls"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Overworld Keys", CVAR_TRACKER_ITEM("DisplayType.OverworldKeys"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Ocarina Buttons", CVAR_TRACKER_ITEM("DisplayType.OcarinaButtons"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Fishing Pole", CVAR_TRACKER_ITEM("DisplayType.FishingPole"), extendedDisplayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Overworld Keys", CVAR_TRACKER_ITEM("DisplayType.OverworldKeys"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Total Checks", "gTrackers.ItemTracker.TotalChecks.DisplayType", minimalDisplayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MINIMAL_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Fishing Pole", CVAR_TRACKER_ITEM("DisplayType.FishingPole"), extendedDisplayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { - if (CVarCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, + if (CVarCombobox("Total Checks", "gTrackers.ItemTracker.TotalChecks.DisplayType", minimalDisplayTypes, ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .DefaultIndex(SECTION_DISPLAY_MINIMAL_HIDDEN) .ComponentAlignment(ComponentAlignments::Right) .LabelPosition(LabelPositions::Far) .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - } - CVarCheckbox("Show Hookshot Identifiers", CVAR_TRACKER_ITEM("HookshotIdentifier"), - CheckboxOptions() - .Tooltip("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot.") - .Color(THEME_COLOR)); - ImGui::PopStyleVar(1); - ImGui::EndTable(); + if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != + TRACKER_DISPLAY_COMBO_BUTTON)) { + if (CVarCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } + CVarCheckbox("Show Hookshot Identifiers", CVAR_TRACKER_ITEM("HookshotIdentifier"), + CheckboxOptions() + .Tooltip("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot.") + .Color(THEME_COLOR)); + + ImGui::PopStyleVar(1); + ImGui::EndTable(); + } } void ItemTrackerWindow::InitElement() { diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 4753bb4b4e5..91b5a50fc24 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -107,6 +107,7 @@ void GiveLinksPocketItem() { } void SetStartingItems() { + int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get(); if (Randomizer_GetSettingValue(RSK_STARTING_KOKIRI_SWORD)) Item_Give(NULL, ITEM_SWORD_KOKIRI); if (Randomizer_GetSettingValue(RSK_STARTING_DEKU_SHIELD)) @@ -158,6 +159,13 @@ void SetStartingItems() { if (Randomizer_GetSettingValue(RSK_STARTING_NUTS) && !Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_NUT_BAG)) { GiveLinkDekuNuts(20); } + if (Randomizer_GetSettingValue(RSK_STARTING_MASTER_SWORD)) { + if (startingAge == RO_AGE_ADULT) { + Item_Give(NULL, ITEM_SWORD_MASTER); + } else { + gSaveContext.inventory.equipment |= 1 << 1; + } + } if (Randomizer_GetSettingValue(RSK_FULL_WALLETS)) { GiveLinkRupees(9001); @@ -224,6 +232,8 @@ extern "C" void Randomizer_InitSaveFile() { // Reset triforce pieces collected. gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0; + SetStartingItems(); + // Set Cutscene flags and texts to skip them. Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO); Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA); @@ -261,9 +271,9 @@ extern "C" void Randomizer_InitSaveFile() { // Remove One Time Scrubs with Scrubsanity off if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_OFF) { - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE); - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT); - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO); + Flags_SetItemGetInf(ITEMGETINF_DEKU_SCRUB_HEART_PIECE); + Flags_SetInfTable(INFTABLE_BOUGHT_STICK_UPGRADE); + Flags_SetInfTable(INFTABLE_BOUGHT_NUT_UPGRADE); } int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get(); @@ -422,6 +432,4 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.itemGetInf[3] |= 0x800; // Bunny Hood related gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth } - - SetStartingItems(); } diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index c40f9916884..d2f93fe98c4 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -120,6 +120,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_DOOR_OF_TIME, "Door of Time", {"Closed", "Song only", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DoorOfTime"), mOptionDescriptions[RSK_DOOR_OF_TIME], WidgetType::Combobox); OPT_U8(RSK_ZORAS_FOUNTAIN, "Zora's Fountain", {"Closed", "Closed as child", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ZorasFountain"), mOptionDescriptions[RSK_ZORAS_FOUNTAIN]); OPT_U8(RSK_SLEEPING_WATERFALL, "Sleeping Waterfall", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SleepingWaterfall"), mOptionDescriptions[RSK_SLEEPING_WATERFALL]); + OPT_U8(RSK_JABU_OPEN, "Jabu-Jabu", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("JabuJabu"), mOptionDescriptions[RSK_JABU_OPEN]); OPT_BOOL(RSK_LOCK_OVERWORLD_DOORS, "Lock Overworld Doors", CVAR_RANDOMIZER_SETTING("LockOverworldDoors"), mOptionDescriptions[RSK_LOCK_OVERWORLD_DOORS]); OPT_U8(RSK_GERUDO_FORTRESS, "Fortress Carpenters", {"Normal", "Fast", "Free"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FortressCarpenters"), mOptionDescriptions[RSK_GERUDO_FORTRESS]); OPT_U8(RSK_RAINBOW_BRIDGE, "Rainbow Bridge", {"Vanilla", "Always open", "Stones", "Medallions", "Dungeon rewards", "Dungeons", "Tokens", "Greg"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RainbowBridge"), mOptionDescriptions[RSK_RAINBOW_BRIDGE], WidgetType::Combobox, RO_BRIDGE_VANILLA, false, IMFLAG_NONE); @@ -250,7 +251,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WidgetType::Slider, 9, true); OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WidgetType::Slider, 8, true); OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WidgetType::Slider, 100, true); - OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), "", WidgetType::Combobox, RO_LACS_STANDARD_REWARD); + OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), mOptionDescriptions[RSK_LACS_OPTIONS], WidgetType::Combobox, RO_LACS_STANDARD_REWARD); OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WidgetType::Combobox, RO_KEYRINGS_OFF); OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WidgetType::Slider, 8); OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::Combobox, 0); @@ -1151,6 +1152,7 @@ void Settings::CreateOptions() { &mOptions[RSK_DOOR_OF_TIME], &mOptions[RSK_ZORAS_FOUNTAIN], &mOptions[RSK_SLEEPING_WATERFALL], + &mOptions[RSK_JABU_OPEN], &mOptions[RSK_LOCK_OVERWORLD_DOORS], }, WidgetContainerType::COLUMN); @@ -1405,6 +1407,7 @@ void Settings::CreateOptions() { &mOptions[RSK_DOOR_OF_TIME], &mOptions[RSK_ZORAS_FOUNTAIN], &mOptions[RSK_SLEEPING_WATERFALL], + &mOptions[RSK_JABU_OPEN], &mOptions[RSK_LOCK_OVERWORLD_DOORS], &mOptions[RSK_GERUDO_FORTRESS], &mOptions[RSK_RAINBOW_BRIDGE], @@ -1739,6 +1742,14 @@ TrickOption& Settings::GetTrickOption(const RandomizerTrick key) { return mTrickOptions[key]; } +int Settings::GetRandomizerTrickByName(const std::string& name) { + const auto& it = mTrickNameToEnum.find(name); + if (it == mTrickNameToEnum.end()) { + return -1; + } + return it->second; +} + void Context::ResetTrickOptions() { for (int count = 0; count < RT_MAX; count++) { mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better @@ -2014,16 +2025,16 @@ void Settings::UpdateOptionProperties() { } } - int dungeonShuffle = + bool dungeonShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), RO_DUNGEON_ENTRANCE_SHUFFLE_OFF); - int bossShuffle = + bool bossShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF); - int overworldShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF); - int interiorShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF); - int grottoShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF); + bool overworldShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF); + bool interiorShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF); + bool grottoShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF); - // Hide Mixed Entrances option if no applicable entrance shuffles are visible - if (!dungeonShuffle && !bossShuffle && !overworldShuffle && !interiorShuffle && !grottoShuffle) { + // Hide Mixed Entrances option if 1 or no applicable entrance shuffles are visible + if (dungeonShuffle + bossShuffle + overworldShuffle + interiorShuffle + grottoShuffle <= 1) { mOptions[RSK_MIXED_ENTRANCE_POOLS].Hide(); } else { mOptions[RSK_MIXED_ENTRANCE_POOLS].Unhide(); @@ -2560,7 +2571,7 @@ void Context::FinalizeSettings(const std::set& excludedLocation } if (!mOptions[RSK_SHUFFLE_MASTER_SWORD]) { if (mOptions[RSK_STARTING_MASTER_SWORD]) { - this->GetItemLocation(RC_MASTER_SWORD_PEDESTAL)->SetExcludedOption(1); + this->GetItemLocation(RC_TOT_MASTER_SWORD)->SetExcludedOption(1); } } if (!mOptions[RSK_SHUFFLE_OCARINA]) { @@ -2782,6 +2793,36 @@ void Context::FinalizeSettings(const std::set& excludedLocation trials[i]->SetAsRequired(); } + bool dungeonShuffle = !mOptions[RSK_SHUFFLE_DUNGEON_ENTRANCES].Is(RO_GENERIC_OFF); + bool bossShuffle = !mOptions[RSK_SHUFFLE_BOSS_ENTRANCES].Is(RO_GENERIC_OFF); + bool overworldShuffle = !mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES].Is(RO_GENERIC_OFF); + bool interiorShuffle = !mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES].Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF); + bool grottoShuffle = !mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES].Is(RO_GENERIC_OFF); + + if (dungeonShuffle + bossShuffle + overworldShuffle + interiorShuffle + grottoShuffle <= 1) { + mOptions[RSK_MIXED_ENTRANCE_POOLS].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !dungeonShuffle) { + mOptions[RSK_MIX_DUNGEON_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !bossShuffle) { + mOptions[RSK_MIX_BOSS_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !overworldShuffle) { + mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !interiorShuffle) { + mOptions[RSK_MIX_INTERIOR_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !grottoShuffle) { + mOptions[RSK_MIX_GROTTO_ENTRANCES].Set(RO_GENERIC_OFF); + } + if (mOptions[RSK_FOREST].Is(RO_CLOSED_FOREST_ON) && (mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES].Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL) || mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES] || mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS] || diff --git a/soh/soh/Enhancements/randomizer/settings.h b/soh/soh/Enhancements/randomizer/settings.h index c820a9df9b4..ed214471a51 100644 --- a/soh/soh/Enhancements/randomizer/settings.h +++ b/soh/soh/Enhancements/randomizer/settings.h @@ -50,6 +50,14 @@ class Settings { */ TrickOption& GetTrickOption(RandomizerTrick key); + /** + * @brief Get the RandomizerTrick corresponding to the provided name. + * + * @param name + * @return int RandomizerTrick index or -1 if not found + */ + int GetRandomizerTrickByName(const std::string& name); + /** * @brief Returns a reference to the entire array of options. * diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index b738e2fc16c..ce8ee63d854 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -1,6 +1,5 @@ #include #include "soh/OTRGlobals.h" -#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/enhancementTypes.h" @@ -13,16 +12,19 @@ extern "C" { #include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h" #include "src/overlays/actors/ovl_En_Owl/z_en_owl.h" #include "src/overlays/actors/ovl_En_Go2/z_en_go2.h" +#include "src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.h" #include "src/overlays/actors/ovl_En_Ko/z_en_ko.h" #include "src/overlays/actors/ovl_En_Ma1/z_en_ma1.h" #include "src/overlays/actors/ovl_En_Ru2/z_en_ru2.h" #include "src/overlays/actors/ovl_En_Zl4/z_en_zl4.h" #include "src/overlays/actors/ovl_En_Box/z_en_box.h" #include "src/overlays/actors/ovl_Demo_Im/z_demo_im.h" +#include "src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h" #include "src/overlays/actors/ovl_En_Sa/z_en_sa.h" #include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h" #include "src/overlays/actors/ovl_En_Tk/z_en_tk.h" #include "src/overlays/actors/ovl_En_Fu/z_en_fu.h" +#include "src/overlays/actors/ovl_En_Jj/z_en_jj.h" #include "src/overlays/actors/ovl_En_Daiku/z_en_daiku.h" #include "src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h" #include "src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h" @@ -45,6 +47,10 @@ extern void EnGo2_CurledUp(EnGo2* enGo2, PlayState* play); extern void EnRu2_SetEncounterSwitchFlag(EnRu2* enRu2, PlayState* play); extern void EnDaiku_EscapeSuccess(EnDaiku* enDaiku, PlayState* play); + +extern void EnJj_WaitToOpenMouth(EnJj* enJj, PlayState* play); +extern void EnJj_WaitForFish(EnJj* enJj, PlayState* play); +extern void EnJj_SetupAction(EnJj* enJj, EnJjActionFunc actionFunc); } #define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get() @@ -158,7 +164,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; } - u8 meetsBurningKakRequirements = LINK_IS_ADULT && + u8 meetsBurningKakRequirements = LINK_IS_ADULT && gSaveContext.cutsceneIndex < 0xFFF0 && gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_FRONT_GATE && Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) && Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) && @@ -217,6 +223,25 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && (entranceFlag != EVENTCHKINF_EPONA_OBTAINED) && entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE) { *should = false; + + // Check for dispulsion of Ganon's Tower barrier + switch (entranceIndex) { + case ENTR_INSIDE_GANONS_CASTLE_2: + case ENTR_INSIDE_GANONS_CASTLE_3: + case ENTR_INSIDE_GANONS_CASTLE_4: + case ENTR_INSIDE_GANONS_CASTLE_5: + case ENTR_INSIDE_GANONS_CASTLE_6: + case ENTR_INSIDE_GANONS_CASTLE_7: + if (Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FOREST_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_WATER_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SHADOW_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FIRE_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_LIGHT_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SPIRIT_TRIAL)) { + Flags_SetEventChkInf(EVENTCHKINF_DISPELLED_GANONS_TOWER_BARRIER); + } + break; + } } break; } @@ -275,6 +300,12 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } switch (actor->id) { case ACTOR_OBJ_SWITCH: { + if (((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) || + (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) || + (actor->params == 8961 && gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE)) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { + break; + } ObjSwitch* switchActor = (ObjSwitch*)actor; switchActor->cooldownTimer = 0; *should = false; @@ -310,6 +341,16 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; break; } + case ACTOR_EN_BOX: { + if (actor->params == -30457 && gPlayState->sceneNum == SCENE_JABU_JABU && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { + break; + } + EnBox* boxActor = (EnBox*)actor; + *should = false; + RateLimitedSuccessChime(); + break; + } case ACTOR_BG_HIDAN_FWBIG: case ACTOR_EN_EX_ITEM: case ACTOR_EN_DNT_NOMAL: @@ -322,7 +363,6 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li case ACTOR_DOOR_SHUTTER: case ACTOR_BG_ICE_SHUTTER: case ACTOR_OBJ_LIGHTSWITCH: - case ACTOR_EN_BOX: case ACTOR_OBJ_SYOKUDAI: case ACTOR_OBJ_TIMEBLOCK: case ACTOR_EN_PO_SISTERS: @@ -349,6 +389,11 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } break; } + case VB_FREEZE_LINK_FOR_FOREST_PILLARS: + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) { + *should = false; + } + break; case VB_SHOW_TITLE_CARD: if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO)) { *should = false; @@ -441,6 +486,26 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; } break; + case VB_PLAY_DISPEL_BARRIER_CS: { + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) { + static s16 trialEntrances[] = { + 0, + ENTR_INSIDE_GANONS_CASTLE_3, + ENTR_INSIDE_GANONS_CASTLE_6, + ENTR_INSIDE_GANONS_CASTLE_5, + ENTR_INSIDE_GANONS_CASTLE_4, + ENTR_INSIDE_GANONS_CASTLE_7, + ENTR_INSIDE_GANONS_CASTLE_2, + }; + RateLimitedSuccessChime(); + DemoKekkai* kekkai = va_arg(args, DemoKekkai*); + gPlayState->nextEntranceIndex = trialEntrances[kekkai->actor.params]; + gPlayState->transitionTrigger = TRANS_TRIGGER_START; + gPlayState->transitionType = TRANS_TYPE_FADE_BLACK; + *should = false; + } + break; + } case VB_OWL_INTERACTION: { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && *should) { EnOwl* enOwl = va_arg(args, EnOwl*); @@ -720,6 +785,18 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li break; } + case VB_PLAY_GATE_OPENING_OR_CLOSING_CS: { + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) { + EnHeishi2* enHeishi2 = va_arg(args, EnHeishi2*); + enHeishi2->unk_2F2[0] = 0; + + // The second argument determines whether the vanilla code should be run anyway. It + // should be set to `true` ONLY IF said code calls `Play_ClearCamera`, false otherwise. + bool clearCamera = (bool)va_arg(args, int); + *should = clearCamera && enHeishi2->cameraId != MAIN_CAM; + } + break; + } case VB_PLAY_RAINBOW_BRIDGE_CS: { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { *should = false; @@ -760,7 +837,8 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li EnIk* ik = va_arg(args, EnIk*); if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO)) { // Because no CS in rando, we hide the death of the knuckle by spawning flames and kill the actor - if ((ik->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, ik->actor.level); + if ((ik->actor.colChkInfo.health <= healthCheck)) { s32 i; Vec3f pos; Vec3f sp7C = { 0.0f, 0.5f, 0.0f }; @@ -773,6 +851,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li EffectSsDeadDb_Spawn(gPlayState, &pos, &sp7C, &sp7C, 100, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); } + Player_GainExperience(gPlayState, ik->actor.exp); Actor_Kill(&ik->actor); } *should = false; @@ -806,6 +885,8 @@ static uint32_t enMa1UpdateHook = 0; static uint32_t enMa1KillHook = 0; static uint32_t enFuUpdateHook = 0; static uint32_t enFuKillHook = 0; +static uint32_t enJjUpdateHook = 0; +static uint32_t enJjKillHook = 0; static uint32_t bgSpot02UpdateHook = 0; static uint32_t bgSpot02KillHook = 0; static uint32_t bgSpot03UpdateHook = 0; @@ -871,6 +952,39 @@ void TimeSaverOnActorInitHandler(void* actorRef) { }); } + if (actor->id == ACTOR_EN_JJ) { + enJjUpdateHook = + GameInteractor::Instance->RegisterGameHook([](void* innerActorRef) mutable { + Actor* innerActor = static_cast(innerActorRef); + + if (innerActor->id != ACTOR_EN_JJ || Flags_GetEventChkInf(EVENTCHKINF_OFFERED_FISH_TO_JABU_JABU)) { + return; + } + + bool shouldOpen = IS_RANDO ? RAND_GET_OPTION(RSK_JABU_OPEN) + : CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish"), 0); + if (!shouldOpen) { + return; + } + + EnJj* enJj = static_cast(innerActorRef); + if (enJj->actionFunc == EnJj_WaitForFish) { + EnJj_SetupAction(enJj, EnJj_WaitToOpenMouth); + GameInteractor::Instance->UnregisterGameHook(enJjUpdateHook); + GameInteractor::Instance->UnregisterGameHook(enJjKillHook); + enJjUpdateHook = 0; + enJjKillHook = 0; + } + }); + enJjKillHook = + GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) mutable { + GameInteractor::Instance->UnregisterGameHook(enJjUpdateHook); + GameInteractor::Instance->UnregisterGameHook(enJjKillHook); + enJjUpdateHook = 0; + enJjKillHook = 0; + }); + } + if (actor->id == ACTOR_EN_OWL && gPlayState->sceneNum == SCENE_ZORAS_RIVER && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 0) == 2) { Actor_Kill(actor); @@ -1096,6 +1210,10 @@ void TimeSaverOnSceneInitHandler(int16_t sceneNum) { static GetItemEntry vanillaQueuedItemEntry = GET_ITEM_NONE; +void TimeSaverQueueItem(RandomizerGet randoGet) { + vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(randoGet).GetGIEntry_Copy(); +} + void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) { // Do nothing when in a boss rush if (IS_BOSS_RUSH) { diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.h b/soh/soh/Enhancements/timesaver_hook_handlers.h index 41bccd8d99d..373c9eaf8b2 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.h +++ b/soh/soh/Enhancements/timesaver_hook_handlers.h @@ -1,6 +1,9 @@ #ifndef TIMESAVER_HOOK_HANDLERS_H #define TIMESAVER_HOOK_HANDLERS_H +#include "soh/Enhancements/randomizer/randomizerTypes.h" + void TimeSaverRegisterHooks(); +void TimeSaverQueueItem(RandomizerGet randoGet); #endif // TIMESAVER_HOOK_HANDLERS_H \ No newline at end of file diff --git a/soh/soh/Enhancements/timesplits/TimeSplits.cpp b/soh/soh/Enhancements/timesplits/TimeSplits.cpp index f08235970e0..d590bbd0742 100644 --- a/soh/soh/Enhancements/timesplits/TimeSplits.cpp +++ b/soh/soh/Enhancements/timesplits/TimeSplits.cpp @@ -4,6 +4,7 @@ #include "soh/util.h" #include #include "include/z64item.h" +#include "Context.h" #include #include @@ -363,7 +364,7 @@ void TimeSplitsSkipSplit(uint32_t index) { } void TimeSplitsFileManagement(uint32_t action, const char* listEntry, std::vector listData) { - std::string filename = "timesplitdata.json"; + std::string filename = Ship::Context::GetPathRelativeToAppDirectory("timesplitdata.json"); json saveFile; json listArray = nlohmann::json::array(); @@ -948,9 +949,10 @@ void TimeSplitsDrawManageList() { } void InitializeSplitDataFile() { - if (!std::filesystem::exists("timesplitdata.json")) { + std::string filename = Ship::Context::GetPathRelativeToAppDirectory("timesplitdata.json"); + if (!std::filesystem::exists(filename)) { json j; - std::ofstream file("timesplitdata.json"); + std::ofstream file(filename); file << j.dump(4); file.close(); } diff --git a/soh/soh/Notification/Notification.cpp b/soh/soh/Notification/Notification.cpp index 4ba779e5487..14873b0684e 100644 --- a/soh/soh/Notification/Notification.cpp +++ b/soh/soh/Notification/Notification.cpp @@ -61,7 +61,7 @@ void Window::Draw() { ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoScrollbar); + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings); ImGui::SetWindowFontScale(CVarGetFloat(CVAR_SETTING("Notifications.Size"), 1.8f)); // Make this adjustable diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 0151773a902..a88a0102c23 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -310,7 +310,7 @@ OTRGlobals::OTRGlobals() { // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) context->InitResourceManager(OTRFiles, {}, 3); - prevAltAssets = CVarGetInteger(CVAR_ENHANCEMENT("AltAssets"), 0); + prevAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 0); context->GetResourceManager()->SetAltAssetsEnabled(prevAltAssets); auto controlDeck = std::make_shared(std::vector({ @@ -1145,6 +1145,13 @@ extern "C" void InitOTR() { "Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again."); exit(1); } + if (ownPath.string().find("OneDrive") != std::string::npos) { + Extractor::ShowErrorBox( + "Error", + "SoH appears to be in a OneDrive folder, which will cause issues. " + "Please move it to a folder outside of OneDrive, like the root of a drive (e.g. \"C:\\Games\\SoH\")."); + exit(1); + } #endif #if not defined(__SWITCH__) && not defined(__WIIU__) @@ -1428,7 +1435,9 @@ extern "C" void Graph_StartFrame() { } #endif case KbScancode::LUS_KB_TAB: { - CVarSetInteger(CVAR_ENHANCEMENT("AltAssets"), !CVarGetInteger(CVAR_ENHANCEMENT("AltAssets"), 0)); + if (CVarGetInteger(CVAR_SETTING("Mods.AlternateAssetsHotkey"), 1)) { + CVarSetInteger(CVAR_SETTING("AltAssets"), !CVarGetInteger(CVAR_SETTING("AltAssets"), 0)); + } break; } } @@ -1524,7 +1533,7 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { } } - bool curAltAssets = CVarGetInteger(CVAR_ENHANCEMENT("AltAssets"), 0); + bool curAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 0); if (prevAltAssets != curAltAssets) { prevAltAssets = curAltAssets; Ship::Context::GetInstance()->GetResourceManager()->SetAltAssetsEnabled(curAltAssets); @@ -2034,6 +2043,50 @@ extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSi return 0; } +extern "C" int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor) { + std::string postfix; + + if (!actor) + return 0; + + if (gSaveContext.language == LANGUAGE_FRA) { + postfix = ""; + } else if (gSaveContext.language == LANGUAGE_GER) { + postfix = ""; + } else { + postfix = ""; + if (CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1)) { + postfix += " \x05" + "F" + "Lv" + + std::to_string(actor->level); + } + if (CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1) && actor->maximumHealth > 0) { + postfix += " \x05" + "A" + "MaxHP " + + std::to_string(actor->maximumHealth); + } + } + std::string str; + std::string FixedBaseStr(src); + int FoundControlChar = FixedBaseStr.find_first_of("\x01"); + + if (FoundControlChar != std::string::npos) { + FixedBaseStr = FixedBaseStr.insert(FoundControlChar, postfix); + } + + str = FixedBaseStr; + + if (!str.empty()) { + memset(buffer, 0, maxBufferSize); + const int copiedCharLen = std::min(maxBufferSize - 1, str.length()); + memcpy(buffer, str.c_str(), copiedCharLen); + return copiedCharLen; + } + return 0; +} + extern "C" void Randomizer_ParseSpoiler(const char* fileLoc) { OTRGlobals::Instance->gRandoContext->ParseSpoiler(fileLoc); } @@ -2251,7 +2304,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { } } else if ((textId == TEXT_ALTAR_CHILD || textId == TEXT_ALTAR_ADULT)) { // rando hints at altar - messageEntry = (LINK_IS_ADULT) ? ctx->GetHint(RH_ALTAR_ADULT)->GetHintMessage() + messageEntry = (LINK_IS_ADULT) ? ctx->GetHint(RH_ALTAR_ADULT)->GetHintMessage(MF_AUTO_FORMAT) : ctx->GetHint(RH_ALTAR_CHILD)->GetHintMessage(MF_AUTO_FORMAT); } else if (textId == TEXT_GANONDORF) { if (ctx->GetOption(RSK_GANONDORF_HINT)) { @@ -2437,15 +2490,15 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { // animation until the text box auto-dismisses. // RANDOTODO: Implement a way to determine if an item came from a skulltula and // inject the auto-dismiss control code if it did. - if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && - !(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF)) { + bool gsTokensShuffled = Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF; + if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && !(IS_RANDO && gsTokensShuffled)) { textId = TEXT_GS_NO_FREEZE; } else { textId = TEXT_GS_FREEZE; } // In vanilla, GS token count is incremented prior to the text box displaying // In rando we need to bump the token count by one to show the correct count - s16 gsCount = gSaveContext.inventory.gsTokens + (IS_RANDO ? 1 : 0); + s16 gsCount = gSaveContext.inventory.gsTokens + ((IS_RANDO && gsTokensShuffled) ? 1 : 0); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId, MF_FORMATTED); messageEntry.Replace("[[gsCount]]", std::to_string(gsCount)); } else if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && @@ -2608,4 +2661,7 @@ void SoH_ProcessDroppedFiles(std::string filePath) { return; } } -// #endregion + +extern "C" void CheckTracker_RecalculateAvailableChecks() { + CheckTracker::RecalculateAvailableChecks(); +} diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index b14d322d281..f233f139805 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -163,6 +163,8 @@ void Gfx_UnregisterBlendedTexture(const char* name); void Gfx_TextureCacheDelete(const uint8_t* addr); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); +void CheckTracker_RecalculateAvailableChecks(); +int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor); GetItemID RetrieveGetItemIDFromItemID(ItemID itemID); RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 36640c72896..69eb18da97e 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -15,6 +15,7 @@ #include "functions.h" #include "macros.h" #include +#include "leveled_stat_math.h" #include #include "soh/SohGui/SohGui.hpp" @@ -119,10 +120,8 @@ SaveManager::SaveManager() { AddLoadFunction("base", 4, LoadBaseVersion4); AddSaveFunction("base", 4, SaveBase, true, SECTION_PARENT_NONE); - AddLoadFunction("randomizer", 1, LoadRandomizerVersion1); - AddLoadFunction("randomizer", 2, LoadRandomizerVersion2); - AddLoadFunction("randomizer", 3, LoadRandomizerVersion3); - AddSaveFunction("randomizer", 3, SaveRandomizer, true, SECTION_PARENT_NONE); + AddLoadFunction("randomizer", 1, LoadRandomizer); + AddSaveFunction("randomizer", 1, SaveRandomizer, true, SECTION_PARENT_NONE); AddInitFunction(InitFileImpl); @@ -157,234 +156,7 @@ SaveManager::SaveManager() { } } -// RANDOTODO should we just have dummy functions that raise warnings instead if these aren't supported? -void SaveManager::LoadRandomizerVersion1() { - auto randoContext = Rando::Context::GetInstance(); - RandomizerCheck location = RC_UNKNOWN_CHECK; - for (int i = 0; i < RC_MAX; i++) { - SaveManager::Instance->LoadData("check" + std::to_string(i), location); - SaveManager::Instance->LoadStruct("get" + std::to_string(i), [&]() { - SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(location)->RefPlacedItem()); - if (randoContext->GetItemLocation(location)->GetPlacedRandomizerGet() == RG_ICE_TRAP) { - randoContext->overrides[location].SetLocation(location); - SaveManager::Instance->LoadData("fakeRgID", randoContext->overrides[location].RefLooksLike()); - SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().english); - SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().french); - } - }); - } - - for (uint32_t i = 0; i < randoContext->hashIconIndexes.size(); i++) { - SaveManager::Instance->LoadData("seed" + std::to_string(i), randoContext->hashIconIndexes[i]); - } - - for (int i = 0; i < RSK_MAX; i++) { - int key, value; - SaveManager::Instance->LoadData("sk" + std::to_string(i), key); - SaveManager::Instance->LoadData("sv" + std::to_string(i), value); - randoContext->GetOption(RandomizerSettingKey(key)).Set(value); - } - - for (int i = 0; i < 50; i++) { - RandomizerCheck check; - char hintText[200]; - SaveManager::Instance->LoadData("hc" + std::to_string(i), check); - for (int j = 0; j < ARRAY_COUNT(hintText); j++) { - SaveManager::Instance->LoadData("ht" + std::to_string(i) + "-" + std::to_string(j), hintText[j]); - } - RandomizerHint stoneHint = Rando::StaticData::oldVerHintOrder[i - Rando::StaticData::oldVerGossipStoneStart]; - randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) })); - } - - char childAltarText[250]; - for (int i = 0; i < ARRAY_COUNT(childAltarText); i++) { - SaveManager::Instance->LoadData("cat" + std::to_string(i), childAltarText[i]); - } - randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) })); - - char adultAltarText[750]; - for (int i = 0; i < ARRAY_COUNT(adultAltarText); i++) { - SaveManager::Instance->LoadData("aat" + std::to_string(i), adultAltarText[i]); - } - randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) })); - - char ganonHintText[150]; - for (int i = 0; i < ARRAY_COUNT(ganonHintText); i++) { - SaveManager::Instance->LoadData("ght" + std::to_string(i), ganonHintText[i]); - } - randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) })); - - char ganonText[250]; - for (int i = 0; i < ARRAY_COUNT(ganonText); i++) { - SaveManager::Instance->LoadData("gt" + std::to_string(i), ganonText[i]); - } - randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) })); - - SaveManager::Instance->LoadData("triforcePiecesCollected", - gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); - - SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount); - - size_t merchantPricesSize = 0; - if (randoContext->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_OFF)) { - merchantPricesSize += NUM_SCRUBS; - } - if (randoContext->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_OFF)) { - merchantPricesSize += NUM_SHOP_ITEMS; - } - - SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - RandomizerCheck rc; - SaveManager::Instance->LoadData("check", rc); - uint32_t price; - SaveManager::Instance->LoadData("price", price); - randoContext->GetItemLocation(rc)->SetCustomPrice(price); - }); - }); -} - -// RANDOTODO if we actually support this, be less lazy -void SaveManager::LoadRandomizerVersion2() { - auto randoContext = Rando::Context::GetInstance(); - SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(i)->RefPlacedItem()); - RandomizerGet rg = RG_NONE; - SaveManager::Instance->LoadData("fakeRgID", rg, RG_NONE); - if (rg != RG_NONE) { - randoContext->overrides[static_cast(i)] = - Rando::ItemOverride(static_cast(i), rg); - SaveManager::Instance->LoadData( - "trickName", randoContext->overrides[static_cast(i)].GetTrickName().english); - SaveManager::Instance->LoadData( - "trickName", randoContext->overrides[static_cast(i)].GetTrickName().french); - } - }); - }); - - auto entranceCtx = randoContext->GetEntranceShuffler(); - SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type); - SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index); - SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination); - SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override); - SaveManager::Instance->LoadData("overrideDestination", - entranceCtx->entranceOverrides[i].overrideDestination); - }); - }); - - SaveManager::Instance->LoadArray("seed", randoContext->hashIconIndexes.size(), [&](size_t i) { - SaveManager::Instance->LoadData("", randoContext->hashIconIndexes[i]); - }); - - std::string inputSeed; - SaveManager::Instance->LoadData("inputSeed", inputSeed); - randoContext->SetSeedString(inputSeed); - - uint32_t finalSeed; - SaveManager::Instance->LoadData("finalSeed", finalSeed); - randoContext->SetSeed(finalSeed); - - SaveManager::Instance->LoadArray("randoSettings", RSK_MAX, [&](size_t i) { - int value = 0; - SaveManager::Instance->LoadData("", value); - randoContext->GetOption(RandomizerSettingKey(i)).Set(value); - }); - - SaveManager::Instance->LoadArray("hintLocations", RH_ZR_OPEN_GROTTO_GOSSIP_STONE + 1, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - RandomizerCheck rc = RC_UNKNOWN_CHECK; - SaveManager::Instance->LoadData("check", rc); - if (rc != RC_UNKNOWN_CHECK) { - std::string hintText; - SaveManager::Instance->LoadData("hintText", hintText); - RandomizerHint stoneHint = - Rando::StaticData::oldVerHintOrder[rc - Rando::StaticData::oldVerGossipStoneStart]; - randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) })); - } - }); - }); - - std::string childAltarText; - SaveManager::Instance->LoadData("childAltarText", childAltarText); - randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) })); - std::string adultAltarText; - SaveManager::Instance->LoadData("adultAltarText", adultAltarText); - randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) })); - std::string ganonHintText; - SaveManager::Instance->LoadData("ganonHintText", ganonHintText); - randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) })); - std::string ganonText; - SaveManager::Instance->LoadData("ganonText", ganonText); - randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) })); - std::string dampeText; - SaveManager::Instance->LoadData("dampeText", dampeText); - randoContext->AddHint(RH_DAMPES_DIARY, Rando::Hint(RH_DAMPES_DIARY, { CustomMessage(dampeText) })); - std::string gregHintText; - SaveManager::Instance->LoadData("gregHintText", gregHintText); - randoContext->AddHint(RH_GREG_RUPEE, Rando::Hint(RH_GREG_RUPEE, { CustomMessage(gregHintText) })); - std::string sheikText; - SaveManager::Instance->LoadData("sheikText", sheikText); - randoContext->AddHint(RH_SHEIK_HINT, Rando::Hint(RH_SHEIK_HINT, { CustomMessage(sheikText) })); - std::string sariaText; - SaveManager::Instance->LoadData("sariaText", sariaText); - randoContext->AddHint(RH_SARIA_HINT, Rando::Hint(RH_SARIA_HINT, { CustomMessage(sariaText) })); - std::string fishingPoleText; - SaveManager::Instance->LoadData("fishingPoleText", fishingPoleText); - randoContext->AddHint(RH_FISHING_POLE, Rando::Hint(RH_FISHING_POLE, { CustomMessage(fishingPoleText) })); - std::string warpMinuetText; - SaveManager::Instance->LoadData("warpMinuetText", warpMinuetText); - randoContext->AddHint(RH_MINUET_WARP_LOC, Rando::Hint(RH_MINUET_WARP_LOC, { CustomMessage(warpMinuetText) })); - std::string warpBoleroText; - SaveManager::Instance->LoadData("warpBoleroText", warpBoleroText); - randoContext->AddHint(RH_BOLERO_WARP_LOC, Rando::Hint(RH_BOLERO_WARP_LOC, { CustomMessage(warpBoleroText) })); - std::string warpSerenadeText; - SaveManager::Instance->LoadData("warpSerenadeText", warpSerenadeText); - randoContext->AddHint(RH_SERENADE_WARP_LOC, Rando::Hint(RH_SERENADE_WARP_LOC, { CustomMessage(warpSerenadeText) })); - std::string warpRequiemText; - SaveManager::Instance->LoadData("warpRequiemText", warpRequiemText); - randoContext->AddHint(RH_REQUIEM_WARP_LOC, Rando::Hint(RH_REQUIEM_WARP_LOC, { CustomMessage(warpRequiemText) })); - std::string warpNocturneText; - SaveManager::Instance->LoadData("warpNocturneText", warpNocturneText); - randoContext->AddHint(RH_NOCTURNE_WARP_LOC, Rando::Hint(RH_NOCTURNE_WARP_LOC, { CustomMessage(warpNocturneText) })); - std::string warpPreludeText; - SaveManager::Instance->LoadData("warpPreludeText", warpPreludeText); - randoContext->AddHint(RH_PRELUDE_WARP_LOC, Rando::Hint(RH_PRELUDE_WARP_LOC, { CustomMessage(warpPreludeText) })); - - SaveManager::Instance->LoadData("triforcePiecesCollected", - gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); - - SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount); - - std::shared_ptr randomizer = OTRGlobals::Instance->gRandomizer; - - size_t merchantPricesSize = 0; - SaveManager::Instance->LoadData("merchantPricesSize", merchantPricesSize); - - SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - RandomizerCheck rc; - SaveManager::Instance->LoadData("check", rc); - uint32_t price; - SaveManager::Instance->LoadData("price", price); - randoContext->GetItemLocation(rc)->SetCustomPrice(price); - }); - }); - - size_t mqDungeonCount; - SaveManager::Instance->LoadData("masterQuestDungeonCount", mqDungeonCount, (size_t)0); - - randoContext->GetDungeons()->ClearAllMQ(); - SaveManager::Instance->LoadArray("masterQuestDungeons", mqDungeonCount, [&](size_t i) { - uint16_t scene; - SaveManager::Instance->LoadData("", scene); - randoContext->GetDungeons()->GetDungeonFromScene(SceneID(scene))->SetMQ(); - }); -} - -void SaveManager::LoadRandomizerVersion3() { +void SaveManager::LoadRandomizer() { auto randoContext = Rando::Context::GetInstance(); SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) { SaveManager::Instance->LoadStruct("", [&]() { @@ -408,6 +180,9 @@ void SaveManager::LoadRandomizerVersion3() { // all ItemLocations is 0 anyway. randoContext->GetItemLocation(i)->SetCustomPrice(price); } + bool excluded = false; + SaveManager::Instance->LoadData("excluded", excluded, false); + randoContext->GetItemLocation(i)->SetExcludedOption(excluded); }); }); @@ -498,6 +273,9 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f // TODO: German (trick names don't have german translations yet) }); } + if (randoContext->GetItemLocation(i)->IsExcluded()) { + SaveManager::Instance->SaveData("excluded", true); + } if (randoContext->GetItemLocation(i)->HasCustomPrice()) { SaveManager::Instance->SaveData("price", randoContext->GetItemLocation(i)->GetPrice()); } @@ -706,6 +484,12 @@ void SaveManager::InitMeta(int fileNum) { fileMetaInfo[fileNum].hasWallet = Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) || !IS_RANDO; fileMetaInfo[fileNum].defense = gSaveContext.inventory.defenseHearts; fileMetaInfo[fileNum].health = gSaveContext.health; + fileMetaInfo[fileNum].level = 0; + + while (GetActorStat_NextLevelExp(fileMetaInfo[fileNum].level, gSaveContext.experience) <= 0 && + fileMetaInfo[fileNum].level < 99) { + fileMetaInfo[fileNum].level += 1; + } auto randoContext = Rando::Context::GetInstance(); for (int i = 0; i < ARRAY_COUNT(fileMetaInfo[fileNum].seedHash); i++) { @@ -760,9 +544,13 @@ void SaveManager::InitFileNormal() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } + gSaveContext.healthCapacity2 = 9999; + gSaveContext.magicUnits = 9999; + gSaveContext.experience = 0; + gSaveContext.showNeededExpTimer = 0; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0x30; - gSaveContext.health = 0x30; + gSaveContext.health = 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 0; @@ -930,9 +718,11 @@ void SaveManager::InitFileDebug() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } + gSaveContext.experience = 80000; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0xE0; - gSaveContext.health = 0xE0; + gSaveContext.healthCapacity2 = 0x270F; + gSaveContext.health = 0x270F; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 150; @@ -1334,6 +1124,7 @@ void SaveManager::LoadFile(int fileNum) { std::ifstream input(fileName); try { + bool deleteRando = false; saveBlock = nlohmann::json::object(); input >> saveBlock; if (!saveBlock.contains("version")) { @@ -1343,8 +1134,59 @@ void SaveManager::LoadFile(int fileNum) { switch (saveBlock["version"].get()) { case 1: for (auto& block : saveBlock["sections"].items()) { - int sectionVersion = block.value()["version"]; + bool oldVanilla = + block.value()["data"].empty() || block.value()["data"].contains("aat0") || + block.value()["data"]["entrances"].empty() || + SohUtils::IsStringEmpty(saveBlock["sections"]["sohStats"]["data"]["buildVersion"]); std::string sectionName = block.key(); + if (sectionName == "randomizer") { + bool hasStats = saveBlock["sections"].contains("sohStats"); + if (oldVanilla || !hasStats) { // Vanilla "rando" data + SohGui::RegisterPopup( + "Loading old file", + "The file in slot " + std::to_string(fileNum + 1) + + " appears to contain randomizer data, but is a very old format or is empty.\n" + + "The randomizer data has been removed, and this file will be treated as a vanilla " + "file.\nIf this was a vanilla file, it still is, and you shouldn't see this " + "message again.\n" + + "If this was a randomizer file, the file will not work, and should be deleted."); + deleteRando = true; + continue; + } + s16 major = saveBlock["sections"]["sohStats"]["data"]["buildVersionMajor"]; + s16 minor = saveBlock["sections"]["sohStats"]["data"]["buildVersionMinor"]; + s16 patch = saveBlock["sections"]["sohStats"]["data"]["buildVersionPatch"]; + // block loading outdated rando save + if (!(major == gBuildVersionMajor && minor == gBuildVersionMinor && + patch == gBuildVersionPatch)) { + input.close(); + std::string newFileName = Ship::Context::GetPathRelativeToAppDirectory("Save") + + ("/file" + std::to_string(fileNum + 1) + "-" + + std::to_string(GetUnixTimestamp()) + ".bak"); + std::filesystem::path newFile(newFileName); + +#if defined(__SWITCH__) || defined(__WIIU__) + copy_file(fileName.c_str(), newFile.c_str()); +#else + std::filesystem::copy_file(fileName, newFile); +#endif + + std::filesystem::remove(fileName); + SohGui::RegisterPopup( + "Outdated Randomizer Save", + "The SoH version in the file in slot " + std::to_string(fileNum + 1) + + " does not match the currently running version.\n" + + "Non-matching rando saves are unsupported, and the file has been renamed to\n" + + " " + newFileName + "\n" + + "If this was not in error, the file should be deleted."); + saveMtx.unlock(); + return; + } + } + int sectionVersion = block.value()["version"]; + if (sectionName == "randomizer" && sectionVersion != 1) { + sectionVersion = 1; + } if (!sectionLoadHandlers.contains(sectionName)) { // Unloadable sections aren't necessarily errors, they are probably mods that were unloaded // TODO report in a more noticeable manner @@ -1376,6 +1218,12 @@ void SaveManager::LoadFile(int fileNum) { assert(false); break; } + input.close(); + if (deleteRando) { + saveBlock["sections"].erase(saveBlock["sections"].find("randomizer")); + SaveFile(fileNum); + deleteRando = false; + } InitMeta(fileNum); GameInteractor::Instance->ExecuteHooks(fileNum); } catch (const std::exception& e) { @@ -1482,6 +1330,7 @@ void SaveManager::CreateDefaultGlobal() { } void SaveManager::LoadBaseVersion1() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1626,6 +1475,7 @@ void SaveManager::LoadBaseVersion1() { } void SaveManager::LoadBaseVersion2() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1842,6 +1692,7 @@ void SaveManager::LoadBaseVersion2() { } void SaveManager::LoadBaseVersion3() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -2062,6 +1913,7 @@ void SaveManager::LoadBaseVersion3() { } void SaveManager::LoadBaseVersion4() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -2244,6 +2096,7 @@ void SaveManager::LoadBaseVersion4() { } void SaveManager::SaveBase(SaveContext* saveContext, int sectionID, bool fullSave) { + SaveManager::Instance->SaveData("experience", saveContext->experience); SaveManager::Instance->SaveData("entranceIndex", saveContext->entranceIndex); SaveManager::Instance->SaveData("linkAge", saveContext->linkAge); SaveManager::Instance->SaveData("cutsceneIndex", saveContext->cutsceneIndex); @@ -2908,6 +2761,7 @@ extern "C" void Save_LoadFile(void) { OTRGlobals::Instance->gRandoContext = Rando::Context::CreateInstance(); OTRGlobals::Instance->gRandoContext->GetLogic()->SetSaveContext(&gSaveContext); Rando::Settings::GetInstance()->AssignContext(OTRGlobals::Instance->gRandoContext); + OTRGlobals::Instance->gRandoContext->AddExcludedOptions(); SaveManager::Instance->LoadFile(gSaveContext.fileNum); } diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 3c4a3124323..2a30deb4247 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -32,6 +32,7 @@ typedef struct { s32 filenameLanguage; s32 gregFound; s32 hasWallet; + u8 level; } SaveFileMetaInfo; typedef enum { @@ -166,11 +167,7 @@ class SaveManager { static void InitFileDebug(); static void InitFileMaxed(); - static void LoadRandomizerVersion1(); - static void LoadRandomizerVersion2(); - static void LoadRandomizerVersion3(); - static void LoadTrackerData(); - static void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave); + static void LoadRandomizer(); static void SaveRandomizer(SaveContext* saveContext, int sectionID, bool fullSave); static void LoadBaseVersion1(); diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp index a1321a6f661..98db5d23907 100644 --- a/soh/soh/SohGui/Menu.cpp +++ b/soh/soh/SohGui/Menu.cpp @@ -65,6 +65,8 @@ uint32_t GetVectorIndexOf(std::vector& vector, std::string value) { return std::distance(vector.begin(), std::find(vector.begin(), vector.end(), value)); } +static bool raceDisableActive = false; + void Menu::InsertSidebarSearch() { menuEntries["Settings"].sidebars.emplace("Search", searchSidebarEntry); uint32_t curIndex = 0; @@ -183,33 +185,35 @@ bool ModernMenuHeaderEntry(std::string label) { } uint32_t Menu::DrawSearchResults(std::string& menuSearchText) { - ImGui::BeginChild("Search Results"); int searchCount = 0; - for (auto& menuLabel : menuOrder) { - auto& menuEntry = menuEntries.at(menuLabel); - for (auto& sidebarLabel : menuEntry.sidebarOrder) { - auto& sidebar = menuEntry.sidebars[sidebarLabel]; - for (int i = 0; i < sidebar.columnWidgets.size(); i++) { - auto& column = sidebar.columnWidgets.at(i); - for (auto& info : column) { - if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR || - info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) { - continue; - } - const char* tooltip = info.options->tooltip; - std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : ""); - std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower); - menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), - menuSearchText.end()); - std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); - widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); - if (widgetStr.find(menuSearchText) != std::string::npos) { - MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex); - ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); - std::string origin = fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1); - ImGui::Text("%s", origin.c_str()); - ImGui::PopStyleColor(); - searchCount++; + if (ImGui::BeginChild("Search Results")) { + for (auto& menuLabel : menuOrder) { + auto& menuEntry = menuEntries.at(menuLabel); + for (auto& sidebarLabel : menuEntry.sidebarOrder) { + auto& sidebar = menuEntry.sidebars[sidebarLabel]; + for (int i = 0; i < sidebar.columnWidgets.size(); i++) { + auto& column = sidebar.columnWidgets.at(i); + for (auto& info : column) { + if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR || + info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) { + continue; + } + const char* tooltip = info.options->tooltip; + std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : ""); + std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower); + menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), + menuSearchText.end()); + std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); + widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); + if (widgetStr.find(menuSearchText) != std::string::npos) { + MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex); + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); + std::string origin = + fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1); + ImGui::Text("%s", origin.c_str()); + ImGui::PopStyleColor(); + searchCount++; + } } } } @@ -228,7 +232,7 @@ std::unordered_map& Menu::GetDisabledMap() { } void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex) { - disabledTempTooltip = "This setting is disabled because: \n\n"; + disabledTempTooltip = "This setting is disabled because: \n"; disabledValue = false; disabledTooltip = " "; @@ -241,11 +245,16 @@ void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors me if (!widget.activeDisables.empty()) { widget.options->disabled = true; for (auto option : widget.activeDisables) { - disabledTempTooltip += std::string("- ") + disabledMap.at(option).reason + std::string("\n"); + disabledTempTooltip += std::string("\n- ") + disabledMap.at(option).reason; } widget.options->disabledTooltip = disabledTempTooltip.c_str(); } } + if (widget.raceDisable && raceDisableActive) { + widget.options->disabled = true; + disabledTempTooltip += std::string("\n- Race Lockout Active"); + widget.options->disabledTooltip = disabledTempTooltip.c_str(); + } if (widget.sameLine) { ImGui::SameLine(); @@ -487,11 +496,14 @@ void Menu::Draw() { SyncVisibilityConsoleVariable(); } +static bool freshOpen = true; void Menu::DrawElement() { for (auto& [reason, info] : disabledMap) { info.active = info.evaluation(info); } + raceDisableActive = CVarGetInteger(CVAR_SETTING("DisableChanges"), 0); + windowHeight = ImGui::GetMainViewport()->WorkSize.y; windowWidth = ImGui::GetMainViewport()->WorkSize.x; auto windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; @@ -527,6 +539,7 @@ void Menu::DrawElement() { if (!popout) { ImGui::PopStyleVar(); } + freshOpen = true; ImGui::PopStyleColor(); ImGui::End(); return; @@ -643,13 +656,13 @@ void Menu::DrawElement() { std::string menuSearchText = ""; if (headerSearch) { ImGui::SameLine(); - if (autoFocus && ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() && - !ImGui::IsMouseClicked(0)) { - ImGui::SetKeyboardFocusHere(0); + if (autoFocus && freshOpen) { + ImGui::SetKeyboardFocusHere(); } auto color = UIWidgets::ColorValues.at(menuThemeIndex); - color.w = 0.2f; + color.w = 0.6f; ImGui::PushStyleColor(ImGuiCol_FrameBg, color); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); menuSearch.Draw("##search", 200.0f); menuSearchText = menuSearch.InputBuf; menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end()); @@ -657,6 +670,7 @@ void Menu::DrawElement() { ImGui::SameLine(headerWidth - 200.0f + style.ItemSpacing.x); ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search..."); } + ImGui::PopStyleVar(); ImGui::PopStyleColor(); } ImGui::EndChild(); @@ -842,6 +856,9 @@ void Menu::DrawElement() { poppedSize = ImGui::GetWindowSize(); poppedPos = ImGui::GetWindowPos(); } + if (freshOpen) { + freshOpen = false; + } ImGui::End(); } } // namespace Ship diff --git a/soh/soh/SohGui/MenuTypes.h b/soh/soh/SohGui/MenuTypes.h index 162f80ac5f3..de78e89612c 100644 --- a/soh/soh/SohGui/MenuTypes.h +++ b/soh/soh/SohGui/MenuTypes.h @@ -109,6 +109,7 @@ struct WidgetInfo { const char* windowName = ""; bool isHidden = false; bool sameLine = false; + bool raceDisable = true; WidgetInfo& CVar(const char* cVar_) { cVar = cVar_; @@ -191,6 +192,10 @@ struct WidgetInfo { customFunction = customFunction_; return *this; } + WidgetInfo& RaceDisable(bool disable) { + raceDisable = disable; + return *this; + } }; struct WidgetPath { diff --git a/soh/soh/SohGui/ResolutionEditor.cpp b/soh/soh/SohGui/ResolutionEditor.cpp index 298e2c470d3..d3810f6a4ba 100644 --- a/soh/soh/SohGui/ResolutionEditor.cpp +++ b/soh/soh/SohGui/ResolutionEditor.cpp @@ -371,29 +371,37 @@ void RegisterResolutionWidgets() { WidgetPath path = { "Settings", "Graphics", SECTION_COLUMN_2 }; // Resolution visualiser - mSohMenu->AddWidget(path, "Viewport dimensions: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { - info.name = fmt::format("Viewport dimensions: {} x {}", gfx_current_game_window_viewport.width, - gfx_current_game_window_viewport.height); - }); - mSohMenu->AddWidget(path, "Internal resolution: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { - info.name = - fmt::format("Internal resolution: {} x {}", gfx_current_dimensions.width, gfx_current_dimensions.height); - }); + mSohMenu->AddWidget(path, "Viewport dimensions: {} x {}", WIDGET_TEXT) + .RaceDisable(false) + .PreFunc([](WidgetInfo& info) { + info.name = fmt::format("Viewport dimensions: {} x {}", gfx_current_game_window_viewport.width, + gfx_current_game_window_viewport.height); + }); + mSohMenu->AddWidget(path, "Internal resolution: {} x {}", WIDGET_TEXT) + .RaceDisable(false) + .PreFunc([](WidgetInfo& info) { + info.name = fmt::format("Internal resolution: {} x {}", gfx_current_dimensions.width, + gfx_current_dimensions.height); + }); // Activator mSohMenu->AddWidget(path, "Enable advanced settings.", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled"); + .CVar(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled") + .RaceDisable(false); // Error/Warning display mSohMenu ->AddWidget(path, ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring.", WIDGET_TEXT) + .RaceDisable(false) .PreFunc( [](WidgetInfo& info) { info.isHidden = !(!CVarGetInteger(CVAR_LOW_RES_MODE, 0) && IsDroppingFrames()); }) .Options(TextOptions().Color(Colors::Orange)); mSohMenu->AddWidget(path, ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings.", WIDGET_TEXT) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); }) .Options(TextOptions().Color(Colors::LightBlue)); mSohMenu->AddWidget(path, "Click to disable N64 mode", WIDGET_BUTTON) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); }) .Callback([](WidgetInfo& info) { CVarSetInteger(CVAR_LOW_RES_MODE, 0); @@ -401,17 +409,18 @@ void RegisterResolutionWidgets() { }); // Aspect Ratio - mSohMenu->AddWidget(path, "AspectSep", WIDGET_SEPARATOR).PreFunc([](WidgetInfo& info) { + mSohMenu->AddWidget(path, "AspectSep", WIDGET_SEPARATOR).RaceDisable(false).PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); } }); - mSohMenu->AddWidget(path, "Force aspect ratio:", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { + mSohMenu->AddWidget(path, "Force aspect ratio:", WIDGET_TEXT).RaceDisable(false).PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); } }); mSohMenu->AddWidget(path, "(Select \"Off\" to disable.)", WIDGET_TEXT) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); @@ -422,6 +431,7 @@ void RegisterResolutionWidgets() { // Presets mSohMenu->AddWidget(path, "Aspect Ratio", WIDGET_COMBOBOX) .ValuePointer(&item_aspectRatio) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); @@ -443,44 +453,49 @@ void RegisterResolutionWidgets() { Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); }) .Options(ComboboxOptions().ComboMap(aspectRatioPresetLabels)); - mSohMenu->AddWidget(path, "AspectRatioCustom", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { - // Hide aspect ratio input fields if using one of the presets. - if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) { - // Declare input interaction bools outside of IF statement to prevent Y field from disappearing. - const bool input_X = - UIWidgets::SliderFloat("X", &aspectRatioX, - UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) - .Min(0.1f) - .Max(32.0f) - .Step(0.001f) - .Format("%3f") - .Color(THEME_COLOR) - .LabelPosition(UIWidgets::LabelPositions::Near) - .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); - const bool input_Y = - UIWidgets::SliderFloat("Y", &aspectRatioY, - UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) - .Min(0.1f) - .Max(24.0f) - .Step(0.001f) - .Format("%3f") - .Color(THEME_COLOR) - .LabelPosition(UIWidgets::LabelPositions::Near) - .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); - if (input_X || input_Y) { - item_aspectRatio = default_aspectRatio; - update[UPDATE_aspectRatioX] = true; - update[UPDATE_aspectRatioY] = true; - } - } else if (showHorizontalResField) { // Show calculated aspect ratio - if (item_aspectRatio) { - ImGui::Dummy({ 0, 2 }); - const float resolvedAspectRatio = (float)gfx_current_dimensions.width / gfx_current_dimensions.height; - ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio); + mSohMenu->AddWidget(path, "AspectRatioCustom", WIDGET_CUSTOM) + .RaceDisable(false) + .CustomFunction([](WidgetInfo& info) { + // Hide aspect ratio input fields if using one of the presets. + if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) { + // Declare input interaction bools outside of IF statement to prevent Y field from disappearing. + const bool input_X = + UIWidgets::SliderFloat("X", &aspectRatioX, + UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) + .Min(0.1f) + .Max(32.0f) + .Step(0.001f) + .Format("%3f") + .Color(THEME_COLOR) + .LabelPosition(UIWidgets::LabelPositions::Near) + .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); + const bool input_Y = + UIWidgets::SliderFloat("Y", &aspectRatioY, + UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) + .Min(0.1f) + .Max(24.0f) + .Step(0.001f) + .Format("%3f") + .Color(THEME_COLOR) + .LabelPosition(UIWidgets::LabelPositions::Near) + .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); + if (input_X || input_Y) { + item_aspectRatio = default_aspectRatio; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + } + } else if (showHorizontalResField) { // Show calculated aspect ratio + if (item_aspectRatio) { + ImGui::Dummy({ 0, 2 }); + const float resolvedAspectRatio = + (float)gfx_current_dimensions.width / gfx_current_dimensions.height; + ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio); + } } - } - }); - mSohMenu->AddWidget(path, "MoreResolutionSettings", WIDGET_CUSTOM).CustomFunction(ResolutionCustomWidget); + }); + mSohMenu->AddWidget(path, "MoreResolutionSettings", WIDGET_CUSTOM) + .CustomFunction(ResolutionCustomWidget) + .RaceDisable(false); } void UpdateResolutionVars() { @@ -548,7 +563,6 @@ void UpdateResolutionVars() { verticalPixelCount = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", pixelCountPresets[item_pixelCount]); // Additional settings - showHorizontalResField = false; horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; // Disabling flags disabled_everything = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 7eb3fc6c711..67fb99f0a6a 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -71,7 +71,6 @@ std::shared_ptr mSohMenuBar; std::shared_ptr mConsoleWindow; std::shared_ptr mStatsWindow; std::shared_ptr mGfxDebuggerWindow; -std::shared_ptr mInputEditorWindow; std::shared_ptr mSohMenu; std::shared_ptr mAudioEditorWindow; @@ -131,10 +130,10 @@ void SetupGuiElements() { mStatsWindow = std::make_shared(CVAR_WINDOW("SohStats"), "Stats##Soh", ImVec2(400, 100)); gui->AddGuiWindow(mStatsWindow); - mInputEditorWindow = gui->GetGuiWindow("Controller Configuration"); + /*mInputEditorWindow = gui->GetGuiWindow("Controller Configuration"); if (mInputEditorWindow == nullptr) { SPDLOG_ERROR("Could not find input editor window"); - } + }*/ mAudioEditorWindow = std::make_shared(CVAR_WINDOW("AudioEditor"), "Audio Editor", ImVec2(820, 630)); gui->AddGuiWindow(mAudioEditorWindow); @@ -228,7 +227,6 @@ void Destroy() { mActorViewerWindow = nullptr; mCosmeticsEditorWindow = nullptr; mAudioEditorWindow = nullptr; - mInputEditorWindow = nullptr; mStatsWindow = nullptr; mConsoleWindow = nullptr; mGfxDebuggerWindow = nullptr; diff --git a/soh/soh/SohGui/SohMenu.cpp b/soh/soh/SohGui/SohMenu.cpp index 61967087bfb..a7d6a82f5ba 100644 --- a/soh/soh/SohGui/SohMenu.cpp +++ b/soh/soh/SohGui/SohMenu.cpp @@ -85,6 +85,7 @@ void SohMenu::InitElement() { AddMenuSettings(); AddMenuEnhancements(); AddMenuRandomizer(); + AddMenuLeveled(); #ifdef ENABLE_REMOTE_CONTROL AddMenuNetwork(); #endif diff --git a/soh/soh/SohGui/SohMenu.h b/soh/soh/SohGui/SohMenu.h index 4a72938bb31..4a530b153fd 100644 --- a/soh/soh/SohGui/SohMenu.h +++ b/soh/soh/SohGui/SohMenu.h @@ -28,6 +28,13 @@ void disableBetaQuest(); namespace SohGui { +static std::unordered_map languages = { + { LANGUAGE_ENG, "English" }, + { LANGUAGE_GER, "German" }, + { LANGUAGE_FRA, "French" }, + { LANGUAGE_JPN, "Japanese" }, +}; + static const std::unordered_map menuThemeOptions = { { UIWidgets::Colors::Red, "Red" }, { UIWidgets::Colors::DarkRed, "Dark Red" }, @@ -198,6 +205,7 @@ class SohMenu : public Ship::Menu { void AddMenuEnhancements(); void AddMenuDevTools(); void AddMenuRandomizer(); + void AddMenuLeveled(); void AddMenuNetwork(); static void UpdateLanguageMap(std::unordered_map& languageMap); diff --git a/soh/soh/SohGui/SohMenuDevTools.cpp b/soh/soh/SohGui/SohMenuDevTools.cpp index 0588ae508fa..91d456c95ca 100644 --- a/soh/soh/SohGui/SohMenuDevTools.cpp +++ b/soh/soh/SohGui/SohMenuDevTools.cpp @@ -93,6 +93,7 @@ void SohMenu::AddMenuDevTools() { AddSidebarEntry("Dev Tools", path.sidebarName, 1); AddWidget(path, "Popout Stats Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("SohStats")) + .RaceDisable(false) .WindowName("Stats##Soh") .Options(WindowButtonOptions().Tooltip("Enables the separate Stats Window.")); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 10975589f7c..4499782403d 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -7,6 +7,10 @@ #include #include +#define CVAR_INT_SHIP_INIT(cvar, val) \ + CVarSetInteger(cvar, val); \ + ShipInit::Init(cvar); + static std::string comboboxTooltip = ""; static int32_t enhancementPresetSelected = ENHANCEMENT_PRESET_DEFAULT; bool isBetaQuestEnabled = false; @@ -85,6 +89,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Saving", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Autosave", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("Autosave")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Save the game automatically on a 3 minute interval and when soft-resetting the game. The interval " "autosave will wait if the game is paused in any way (dialogue, pause screen up, cutscenes, " @@ -168,9 +173,11 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Controls", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Answer Navi Prompt with L Button", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NaviOnL")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Speak to Navi with L but enter First-Person Camera with C-Up.")); AddWidget(path, "Don't Require Input for Credits Sequence", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NoInputForCredits")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Removes the Input Requirement on Text boxes after defeating Ganon, allowing the Credits " "Sequence to continue to progress.")); @@ -202,14 +209,20 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Item Count Messages", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Gold Skulltula Tokens", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("InjectItemCounts.GoldSkulltula")); - AddWidget(path, "Pieces of Heart", WIDGET_CVAR_CHECKBOX).CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartPiece")); - AddWidget(path, "Heart Containers", WIDGET_CVAR_CHECKBOX).CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartContainer")); + .CVar(CVAR_ENHANCEMENT("InjectItemCounts.GoldSkulltula")) + .RaceDisable(false); + AddWidget(path, "Pieces of Heart", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartPiece")) + .RaceDisable(false); + AddWidget(path, "Heart Containers", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartContainer")) + .RaceDisable(false); path.column = SECTION_COLUMN_3; AddWidget(path, "Misc", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Disable Crit Wiggle", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableCritWiggle")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Disable Random Camera Wiggle at Low Health.")); AddWidget(path, "Better Owl", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("BetterOwl")) @@ -253,6 +266,15 @@ void SohMenu::AddMenuEnhancements() { "open permanently.\n" "Never: Link never needs to play Zelda's Lullaby to open the waterfall. He only needs to have " "learned it and have an Ocarina.")); + AddWidget(path, "Skip Feeding Jabu-Jabu", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_JABU_OPEN).Is(RO_JABU_OPEN); + info.options->disabledTooltip = + "This setting is disabled because a randomizer savefile with \"Jabu-Jabu: Open\" is loaded."; + }) + .Options(CheckboxOptions().Tooltip("Allow Link to enter Jabu-Jabu without feeding him a fish.")); // Skips & Speed-ups path.sidebarName = "Skips & Speed-ups"; @@ -263,16 +285,16 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "All##Skips", WIDGET_BUTTON) .Options(ButtonOptions().Size(Sizes::Inline)) .Callback([](WidgetInfo& info) { - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), true); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); }); @@ -280,16 +302,16 @@ void SohMenu::AddMenuEnhancements() { .SameLine(true) .Options(ButtonOptions().Size(Sizes::Inline)) .Callback([](WidgetInfo& info) { - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), false); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); }); @@ -325,15 +347,19 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().DefaultValue(IS_RANDO)); AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding")) - .Options(CheckboxOptions().Tooltip( - "Don't skip cutscenes that are associated with useful glitches. Currently, it is " - "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, and the Box Skip One " - "Point in Jabu.")); + .Options( + CheckboxOptions().Tooltip("Don't skip cutscenes that are associated with useful glitches. Currently, it is " + "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " + "Door Switch CS, Water Temple Dragon Switch CS, the Box Skip One Point in Jabu, " + "Early Hammer Switch CS in MQ Spirit, and Cow Switch Chest CS in MQ Jabu.")); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); - AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Skip Bottle Pickup Messages", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastBottles")) + .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for Bottle Swipes.")); + AddWidget(path, "Skip Consumable Item Pickup Messages", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FastDrops")) - .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for new Consumable Items and Bottle Swipes.")); + .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for new Consumable Items.")); AddWidget(path, "Skip Forced Dialog", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipForcedDialog")) .Options(ComboboxOptions() @@ -429,28 +455,33 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Mods", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Use Alternate Assets", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("AltAssets")) + .CVar(CVAR_SETTING("AltAssets")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Toggle between standard assets and alternate assets. Usually mods will indicate if " "this setting has to be used or not.")); AddWidget(path, "Disable Bomb Billboarding", WIDGET_CVAR_CHECKBOX) - .CVar("DisableBombBillboarding") + .CVar(CVAR_ENHANCEMENT("DisableBombBillboarding")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to " "replace bombs with 3D objects.")); AddWidget(path, "Disable Grotto Fixed Rotation", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableGrottoRotation")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Disables Grottos rotating with the Camera. To be used in conjuction with mods that want to " "replace grottos with 3D objects.")); AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_ENHANCEMENT("TextSpacing")) + .RaceDisable(false) .Options(IntSliderOptions().Min(4).Max(6).DefaultValue(6).Tooltip( "Space between text characters (useful for HD font textures).")); AddWidget(path, "Models & Textures", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Disable LOD", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableLOD")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Turns off the Level of Detail setting, making models use their Higher-Poly variants at any distance.")); AddWidget(path, "Enemy Health Bars", WIDGET_CVAR_CHECKBOX) @@ -458,31 +489,38 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip("Renders a health bar for Enemies when Z-Targeted.")); AddWidget(path, "Enable 3D Dropped Items/Projectiles", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NewDrops")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Replaces most 2D items and projectiles on the overworld with their equivalent 3D models.")); AddWidget(path, "Animated Link in Pause Menu", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("PauseMenuAnimatedLink")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Turns the Static Image of Link in the Pause Menu's Equipment Subscreen " "into a model cycling through his idle animations.")); AddWidget(path, "Show Age-Dependent Equipment", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("EquipmentAlwaysVisible")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { UpdatePatchHand(); }) .Options(CheckboxOptions().Tooltip("Makes all equipment visible, regardless of age.")); AddWidget(path, "Scale Adult Equipment as Child", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ScaleAdultEquipmentAsChild")) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0) == 0; }) .Options(CheckboxOptions().Tooltip( "Scales all of the Adult Equipment, as well as moving some a bit, to fit on Child Link better. May " "not work properly with some mods.")); - AddWidget(path, "Show Gauntlets in First Person", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Show Gauntlets in First-Person", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FirstPersonGauntlets")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Renders Gauntlets when using the Bow and Hookshot like in OoT3D.")); AddWidget(path, "Show Chains on Both Sides of Locked Doors", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("ShowDoorLocksOnBothSides")); + .CVar(CVAR_ENHANCEMENT("ShowDoorLocksOnBothSides")) + .RaceDisable(false); AddWidget(path, "Color Temple of Time's Medallions", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ToTMedallionsColors")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { PatchToTMedallions(); }) .Options(CheckboxOptions().Tooltip( "When Medallions are collected, the Medallion imprints around the Master Sword Pedestal in the Temple " @@ -500,15 +538,18 @@ void SohMenu::AddMenuEnhancements() { "in Hot/Underwater conditions.")); AddWidget(path, "Remember Minimap State Between Areas", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("RememberMapToggleState")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( - "Preverse the minimap visibility state when going between areas rather than default it to \"on\" " + "Preserve the minimap visibility state when going between areas rather than default it to \"on\" " "when going through loading zones.")); AddWidget(path, "Visual Stone of Agony", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("VisualAgony")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Displays an icon and plays a sound when Stone of Agony should be activated, for those without rumble.")); AddWidget(path, "Disable HUD Heart Animations", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NoHUDHeartAnimation")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Disables the Beating Animation of the Hearts on the HUD.")); AddWidget(path, "Glitch Line-up Tick", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DrawLineupTick")) @@ -517,26 +558,32 @@ void SohMenu::AddMenuEnhancements() { "UI based line-ups do not work outside of 4:3")); AddWidget(path, "Disable Black Bar Letterboxes", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableBlackBars")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Disables Black Bar Letterboxes during cutscenes and Z-Targeting. NOTE: There may be minor visual " "glitches that were covered up by the black bars. Please disable this setting before reporting a bug.")); AddWidget(path, "Dynamic Wallet Icon", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DynamicWalletIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Changes the Rupee in the Wallet icon to match the wallet size you currently have.")); AddWidget(path, "Always Show Dungeon Entrances", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("AlwaysShowDungeonMinimapIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Always shows dungeon entrance icons on the Minimap.")); AddWidget(path, "More Info in File Select", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FileSelectMoreInfo")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Shows what items you have collected in the File Select screen, like in N64 Randomizer.")); AddWidget(path, "Better Ammo Rendering in Pause Menu", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("BetterAmmoRendering")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Ammo counts in the pause menu will work correctly regardless of the position of items in the Inventory.")); AddWidget(path, "Enable Passage of Time on File Select", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TimeFlowFileSelect")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("The skybox in the background of the File Select screen will go through the " "day and night cycle over time.")); @@ -544,14 +591,17 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Misc.", WIDGET_SEPARATOR_TEXT); AddWidget(path, "N64 Mode", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_LOW_RES_MODE) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Sets the aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution.")); AddWidget(path, "Remove Spin Attack Darkness", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("RemoveSpinAttackDarkness")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Remove the Darkness that appears when charging a Spin Attack.")); - AddWidget(path, "Draw Distance", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Draw Distance", WIDGET_SEPARATOR_TEXT).RaceDisable(false); AddWidget(path, "Increase Actor Draw Distance: %dx", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_ENHANCEMENT("DisableDrawDistance")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { if (CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1) { CVarSetInteger(CVAR_ENHANCEMENT("DisableKokiriDrawDistance"), 0); @@ -561,8 +611,9 @@ void SohMenu::AddMenuEnhancements() { "Increases the range in which Actors/Objects are drawn.")); AddWidget(path, "Kokiri Draw Distance", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableKokiriDrawDistance")) + .RaceDisable(false) .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) > 1; }) + [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1; }) .Options(CheckboxOptions().Tooltip( "The Kokiri are mystical beings that fade into view when approached. Enabling this will remove their " "draw distance.")); @@ -745,7 +796,7 @@ void SohMenu::AddMenuEnhancements() { }) .Options(CheckboxOptions().Tooltip( "Allows Light Arrows to activate Sun Switches. May require a room reload if toggled during gameplay.")); - AddWidget(path, "Bow and Child/Slingshot as Adult", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Bow as Child/Slingshot as Adult", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("BowSlingshotAmmoFix")) .Options(CheckboxOptions().Tooltip("Allows Child Link to use a Bow with Arrows.\n" "Allows Adult Link to use a Slingshot with Seeds.\n\n" @@ -777,9 +828,8 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip( "Change aiming for the Boomerang from Third-Person to First-Person to see past Link's head.")); AddWidget(path, "Aiming Reticle for Boomerang", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("BoomerangFirstPerson")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0) != 0; }) + .CVar(CVAR_ENHANCEMENT("BoomerangReticle")) + .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0); }) .Options(CheckboxOptions().Tooltip("Aiming with the Boomerang will display a reticle as with the Hookshot.")); AddWidget(path, "Magic Spells", WIDGET_SEPARATOR_TEXT); @@ -861,7 +911,7 @@ void SohMenu::AddMenuEnhancements() { [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("CrouchStabHammerFix"), 0) == 0; }) .Options(CheckboxOptions().Tooltip("Make crouch stabbing always do the same damage as a regular slash.")); AddWidget(path, "Fix Broken Giant's Knife Bug", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("FixGrokenGiantsKnife")) + .CVar(CVAR_ENHANCEMENT("FixBrokenGiantsKnife")) .PreFunc([](WidgetInfo& info) { info.options->disabled = IS_RANDO && GameInteractor::IsSaveLoaded(true); info.options->disabledTooltip = "This setting is forcefully enabled when you are playing a Randomizer."; @@ -897,22 +947,27 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Graphical Fixes", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Fix L&R Pause Menu", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixMenuLR")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Makes the L and R buttons in the pause menu the same color.")); AddWidget(path, "Fix Dungeon Entrances", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixDungeonMinimapIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Removes the Dungeon Entrance icon on the top-left corner of the screen when no dungeon is present on the " "current map.")); AddWidget(path, "Fix Two-Handed Idle Animations", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TwoHandedIdle")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Re-Enables the two-handed idle animation, a seemingly finished animation that was disabled on accident " "in the original game.")); AddWidget(path, "Fix Navi Text HUD Position", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NaviTextFix")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Correctly centers the Navi text prompt on the HUD's C-Up button.")); AddWidget(path, "Fix Gerudo Warrior's Clothing Colors", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("GerudoWarriorClothingFix")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Prevent the Gerudo Warrior's clothes changing color when changing Link's tunic or " "using bombs in front of her.")); @@ -923,15 +978,18 @@ void SohMenu::AddMenuEnhancements() { "Fixes authentic out of bounds texture reads, instead loading textures with the correct size.")); AddWidget(path, "Fix Link's Eyes Open while Sleeping", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixEyesOpenWhileSleeping")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping.")); AddWidget(path, "Fix Hand Holding Hammer", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixHammerHand")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { UpdatePatchHand(); }) .Options(CheckboxOptions().Tooltip( "Fixes Adult Link having a backwards Left hand when holding the Megaton Hammer.")); AddWidget(path, "Fix Vanishing Paths", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { if (gPlayState != NULL) { UpdateDirtPathFixState(gPlayState->sceneNum); @@ -949,6 +1007,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Audio Fixes", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Fix Missing Jingle after 5 Silver Rupees", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("SilverRupeeJingleExtend")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Adds 5 higher pitches for the Silver Rupee Jingle for the rooms with more than 5 Silver Rupees. " "Only relevant for playthroughs involving Master Quest Dungeons.")); @@ -956,10 +1015,12 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Desync Fixes", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Fix Darunia Dancing too Fast", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixDaruniaDanceSpeed")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Fixes Darunia's dancing speed so he dances to the beat of Saria's Song, like in the Original Game.")); AddWidget(path, "Fix Credits Timing (PAL)", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("CreditsFix")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Extend certain credits scenes so the music lines up properly with the visuals. (PAL only)")); @@ -967,6 +1028,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Graphical Restorations", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Red Ganon Blood", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("RedGanonBlood")) + .RaceDisable(false) .Options( CheckboxOptions().Tooltip("Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.")); AddWidget(path, "Restore Old Gold Skulltula Cutscene", WIDGET_CVAR_CHECKBOX) @@ -975,6 +1037,7 @@ void SohMenu::AddMenuEnhancements() { "Restore pre-release behavior where defeating a Gold Skulltula will play a cutscene showing it die.")); AddWidget(path, "Pulsate Boss Icon", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("PulsateBossIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Restores an unfinished feature to pulsate the boss room icon when you are in the boss room.")); @@ -1098,7 +1161,7 @@ void SohMenu::AddMenuEnhancements() { .CVar(CVAR_ENHANCEMENT("DeleteFileOnDeath")) .Options(CheckboxOptions().Tooltip("Dying will delete your file.\n\n" ICON_FA_EXCLAMATION_TRIANGLE " WARNING " ICON_FA_EXCLAMATION_TRIANGLE - "\nTHIS IS NOT REVERSABLE!\nUSE AT YOUR OWN RISK!")); + "\nTHIS IS NOT REVERSIBLE!\nUSE AT YOUR OWN RISK!")); AddWidget(path, "Always Win Goron Pot", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("GoronPot")) .Options(CheckboxOptions().Tooltip("Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.")); @@ -1658,6 +1721,7 @@ void SohMenu::AddMenuEnhancements() { path.column = SECTION_COLUMN_1; AddWidget(path, "Popout Cosmetics Editor Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("CosmeticsEditor")) + .RaceDisable(false) .WindowName("Cosmetics Editor") .Options(WindowButtonOptions().Tooltip("Enables the separate Cosmetics Editor Window.")); @@ -1666,6 +1730,7 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 1); AddWidget(path, "Popout Audio Editor Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("AudioEditor")) + .RaceDisable(false) .WindowName("Audio Editor") .Options(WindowButtonOptions().Tooltip("Enables the separate Audio Editor Window.")); @@ -1674,6 +1739,7 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 2); AddWidget(path, "Popout Gameplay Stats Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("GameplayStats")) + .RaceDisable(false) .WindowName("Gameplay Stats") .Options(WindowButtonOptions().Tooltip("Enables the separate Gameplay Stats Window.")); @@ -1682,6 +1748,7 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 1); AddWidget(path, "Popout Time Splits Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("TimeSplits")) + .RaceDisable(false) .WindowName("Time Splits") .Options(WindowButtonOptions().Tooltip("Enables the separate Time Splits Window.")); @@ -1690,19 +1757,23 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 3); AddWidget(path, "Toggle Timers Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("TimeDisplayEnabled")) + .RaceDisable(false) .WindowName("Additional Timers") .Options(WindowButtonOptions().Tooltip("Enables the separate Additional Timers Window.")); AddWidget(path, "Font Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_TIME_DISPLAY("FontScale")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { TimeDisplayInitSettings(); }) .Options(FloatSliderOptions().Min(1.0f).Max(5.0f).DefaultValue(1.0f).Format("%.2fx")); AddWidget(path, "Hide Background", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_TIME_DISPLAY("ShowWindowBG")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { TimeDisplayInitSettings(); }); for (auto& timer : timeDisplayList) { - AddWidget(path, timer.timeLabel, WIDGET_CVAR_CHECKBOX).CVar(timer.timeEnable).Callback([](WidgetInfo& info) { - TimeDisplayUpdateDisplayOptions(); - }); + AddWidget(path, timer.timeLabel, WIDGET_CVAR_CHECKBOX) + .RaceDisable(false) + .CVar(timer.timeEnable) + .Callback([](WidgetInfo& info) { TimeDisplayUpdateDisplayOptions(); }); } } diff --git a/soh/soh/SohGui/SohMenuLeveled.cpp b/soh/soh/SohGui/SohMenuLeveled.cpp new file mode 100644 index 00000000000..0636f37644a --- /dev/null +++ b/soh/soh/SohGui/SohMenuLeveled.cpp @@ -0,0 +1,190 @@ +#include "SohMenu.h" +#include "SohGui.hpp" +#include + +namespace SohGui { + enum LeveledUIMagicNumberType { + LEVELED_MAGIC_NUMBER_UI_FULL, + LEVELED_MAGIC_NUMBER_UI_CURRENT, + LEVELED_MAGIC_NUMBER_UI_NONE + }; + static const std::unordered_map magicNumberTypeList = { + { LEVELED_MAGIC_NUMBER_UI_FULL, "Full" }, + { LEVELED_MAGIC_NUMBER_UI_CURRENT, "Current" }, + { LEVELED_MAGIC_NUMBER_UI_NONE, "None" } + }; + + extern std::shared_ptr mSohMenu; + using namespace UIWidgets; + + void SohMenu::AddMenuLeveled() { + // Add Leveled Menu + AddMenuEntry("Leveled", CVAR_SETTING("Menu.LeveledSidebarSection")); + + ////////////////////////////////////////////////////////////////////////////////////////// + // UI Settings + WidgetPath path = { "Leveled", "UI Settings", SECTION_COLUMN_1 }; + AddSidebarEntry("Leveled", path.sidebarName, 1); + // HUD + AddWidget(path, "HUD", WIDGET_SEPARATOR_TEXT); + // EXP Next Level + AddWidget(path, "EXP to NEXT Level", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.ExperienceNextLevel") + .Options(CheckboxOptions() + .Tooltip("Show experience required to level up popup in the HUD when gaining EXP.") + .DefaultValue(true)); + // Level Up + AddWidget(path, "Level Up", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.LevelUp") + .Options(CheckboxOptions().Tooltip("Show Level Up popup when leveling up.").DefaultValue(true)); + // Level Up Sound + AddWidget(path, "Level Up Sound", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.LevelUpSound") + .Options(CheckboxOptions().Tooltip("Plays sound when leveling up.").DefaultValue(true)); + + // Health Meter Numbers + AddWidget(path, "Health Numbers", WIDGET_CVAR_COMBOBOX) + .CVar("gLeveled.HUD.HealthNumbersType") + .Options(ComboboxOptions() + .ComboMap(magicNumberTypeList) + .DefaultIndex(LEVELED_MAGIC_NUMBER_UI_FULL) + .Tooltip("Sets the numbers displayed on the health meter.") + .Color(THEME_COLOR)); + + // Magic Meter Numbers + AddWidget(path, "Magic Meter Numbers", WIDGET_CVAR_COMBOBOX) + .CVar("gLeveled.HUD.MagicMeterNumbersType") + .SameLine(true) + .Options(ComboboxOptions() + .ComboMap(magicNumberTypeList) + .DefaultIndex(LEVELED_MAGIC_NUMBER_UI_FULL) + .Tooltip("Sets the numbers displayed on the magic meter.") + .Color(THEME_COLOR)); + //---------------------------------------------------------------------------------------- + // Floating Numbers + AddWidget(path, "Floating Numbers", WIDGET_SEPARATOR_TEXT); + // Enemy Damage + AddWidget(path, "Enemy Damage", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.EnemyDamage") + .Options(CheckboxOptions().Tooltip("Show floating damage numbers on enemies.").DefaultValue(true)); + // Player Damage + AddWidget(path, "Player Damage", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.PlayerDamage") + .Options(CheckboxOptions().Tooltip("Show floating damage numbers on link.").DefaultValue(true)); + // EXP Gain + AddWidget(path, "EXP Gain", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.ExpGain") + .Options(CheckboxOptions().Tooltip("Show floating EXP gain numbers.").DefaultValue(true)); + //---------------------------------------------------------------------------------------- + // Navi Info + AddWidget(path, "Navi Enemy Info", WIDGET_SEPARATOR_TEXT); + // Navi Level + AddWidget(path, "Navi tells enemy level", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Navi.TellEnemyLevel") + .Options(CheckboxOptions().Tooltip("Navi will tell the enemy level.").DefaultValue(true)); + // Navi HP + AddWidget(path, "Navi tells enemy max HP", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Navi.TellEnemyMaxHP") + .Options(CheckboxOptions().Tooltip("Navi will tell the enemy's maximum HP.").DefaultValue(true)); + ////////////////////////////////////////////////////////////////////////////////////////// + // Entity Modifications + path.sidebarName = "Entity Modifications"; + AddSidebarEntry("Leveled", path.sidebarName, 1); + // Enemy / Link HP Mult + AddWidget(path, "Enemy HP and Link's Attack Multiplier: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar("gLeveled.Enhancements.AttackAndHPMultiplier") + .Options(IntSliderOptions().Min(1).Max(32).Format("%d").DefaultValue(9).Tooltip( + "Changes Link's Attack and enemy HP multiplier.\nThis doesn't change balance, but rather the size of " + "numbers.")); + //---------------------------------------------------------------------------------------- + AddWidget(path, "Player Modifications", WIDGET_SEPARATOR_TEXT); + // Level Increases Life + AddWidget(path, "Level Gives Bonus Hearts", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.HeartsWithLevelUp") + .Options( + CheckboxOptions().Tooltip("Levels give health upgrades, up to 10 extra hearts.").DefaultValue(true)); + // Level Modifies Magic + AddWidget(path, "Level Affects Magic Capacity", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.MagicWithLevelUp") + .Options(CheckboxOptions() + .Tooltip("Levels alter magic capacity. Starts at 25% of the vanilla value, but will go up to " + "150% of vanilla. Double magic works the same as the original, doubling capacity.") + .DefaultValue(true)); + // Equipment Stats + AddWidget(path, "Equipment Affects Stats", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.EquipmentStats") + .Options(CheckboxOptions() + .Tooltip("Equipment will alter stats. Such as Goron Tunic providing +3 STR, but gives -3 " + "Defense, Bracelets give +1 STR, Shields, etc.") + .DefaultValue(true)); + //---------------------------------------------------------------------------------------- + AddWidget(path, "Enemy Modifications", WIDGET_SEPARATOR_TEXT); + // Enemy Level Affects Attack + AddWidget(path, "Enemy Level Affects Base Attack", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Enemy.Enhancements.AttackScalesWithLevel") + .Options( + CheckboxOptions() + .Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " + "Strength stat. \nThis will increase difficulty a bit.") + .DefaultValue(true)); + ////////////////////////////////////////////////////////////////////////////////////////// + // Difficulty Options + path.sidebarName = "Difficulty Options"; + AddSidebarEntry("Leveled", path.sidebarName, 1); + + // Player Options + AddWidget(path, "Player Options", WIDGET_SEPARATOR_TEXT); + // Heart Value + AddWidget(path, ("Heart Container Value in Units: "), WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + ImGui::Text("Heart Container Value in Units: %d (%.2fx)", CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2, (float)CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) / 4.0f); + UIWidgets::CVarSliderInt("Heart Container Value in Units", "gLeveled.Difficulty.HeartUnits", + IntSliderOptions().LabelPosition(Within).Min(1).Max(32).Format("").DefaultValue(4).Color(THEME_COLOR) + .Tooltip( + "Sets how many health units each completed heart container is worth.\n" + "One heart on the health meter is equal to 16 health units.\n" + "A lower setting will result in lower total health.\n" + "Change areas to update health capacity.")); + }); + // Damage Multiplier + AddWidget(path, ("Damage Multiplier: "), WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + ImGui::Text("Damage Multiplier: %.2fx", (float)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f); + UIWidgets::CVarSliderInt("Leveled Damage Multiplier", "gLeveled.Difficulty.Player.DamageMultiplier", + IntSliderOptions().LabelPosition(Within).Min(1).Max(32).Format("").DefaultValue(4).Color(THEME_COLOR) + .Tooltip("Sets a multiplier for the damage the player takes. Includes ALL sources, even damage while being frozen or burned.\nDamage cannot be reduced below 1.")); + }); + //---------------------------------------------------------------------------------------- + // EXP Options + AddWidget(path, "EXP Options", WIDGET_SEPARATOR_TEXT); + // EXP Multiplier + AddWidget(path, "EXP Rate: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.EXP.Rate") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the EXP multiplier link gains, with the exception of gold skulltula tokens.")); + // Token EXP + AddWidget(path, "Skulltula Token EXP Rate: %.2f x", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.EXP.TokenRate") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the EXP multiplier link gains from gold skulltula tokens.")); + //---------------------------------------------------------------------------------------- + // Enemy Options + AddWidget(path, "Enemy Options", WIDGET_SEPARATOR_TEXT); + // Max GS Tokens make Ganon's Castle Enemies Lv99 + AddWidget(path, "Collecting all 100 tokens makes all enemies in Ganon's Castle level 99", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle") + .Options(CheckboxOptions() + .Tooltip("If all tokens are collected, all enemies in Ganon's Casle are level 99.") + .DefaultValue(false)); + // Enemy HP + AddWidget(path, "Enemy HP: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.Enemy.HPPercent") + .Options( + FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the HP multiplier for enemies.")); + // Level Scale + AddWidget(path, "Level Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.Enemy.LevelScale") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the multiplier for enemy levels.\nFor example, if set to 150%, a level 20 enemy will be level 30.\nCaps at level 99.")); + } + +} // namespace SohGui diff --git a/soh/soh/SohGui/SohMenuNetwork.cpp b/soh/soh/SohGui/SohMenuNetwork.cpp index 1c67ce28d89..637c782854a 100644 --- a/soh/soh/SohGui/SohMenuNetwork.cpp +++ b/soh/soh/SohGui/SohMenuNetwork.cpp @@ -39,7 +39,7 @@ void SohMenu::AddMenuNetwork() { }) .Options(ButtonOptions().Tooltip("https://github.com/HarbourMasters/sail")); AddWidget(path, "Host & Port", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { - ImGui::BeginDisabled(Sail::Instance->isEnabled); + ImGui::BeginDisabled(Sail::Instance->isEnabled || CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::Text("%s", info.name.c_str()); CVarInputString("##HostSail", CVAR_REMOTE_SAIL("Host"), InputOptions() @@ -111,7 +111,7 @@ void SohMenu::AddMenuNetwork() { }) .Options(ButtonOptions().Tooltip("https://crowdcontrol.live")); AddWidget(path, "Host & Port", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { - ImGui::BeginDisabled(CrowdControl::Instance->isEnabled); + ImGui::BeginDisabled(CrowdControl::Instance->isEnabled || CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::Text("%s", info.name.c_str()); CVarInputString("##HostCrowdControl", CVAR_REMOTE_CROWD_CONTROL("Host"), InputOptions() diff --git a/soh/soh/SohGui/SohMenuRandomizer.cpp b/soh/soh/SohGui/SohMenuRandomizer.cpp index 9c95431e896..8f4e6e43b29 100644 --- a/soh/soh/SohGui/SohMenuRandomizer.cpp +++ b/soh/soh/SohGui/SohMenuRandomizer.cpp @@ -29,6 +29,7 @@ void SohMenu::AddMenuRandomizer() { .DefaultValue(true)); AddWidget(path, "Random Rupee Names", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_RANDOMIZER_ENHANCEMENT("RandomizeRupeeNames")) + .RaceDisable(false) .Options(CheckboxOptions() .Tooltip("When obtaining Rupees, randomize what the Rupee is called in the textbox.") .DefaultValue(true)); @@ -60,6 +61,7 @@ void SohMenu::AddMenuRandomizer() { .DefaultValue(true)); AddWidget(path, "Quest Item Fanfares", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_RANDOMIZER_ENHANCEMENT("QuestItemFanfares")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Play unique fanfares when obtaining quest items (medallions/stones/songs). Note that these " "fanfares can be longer than usual.")); @@ -71,6 +73,7 @@ void SohMenu::AddMenuRandomizer() { "so you never know what you're getting.")); AddWidget(path, "Simpler Boss Soul Models", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_RANDOMIZER_ENHANCEMENT("SimplerBossSoulModels")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "When shuffling boss souls, they'll appear as a simpler model instead of showing the boss' models." "This might make boss souls more distinguishable from a distance, and can help with performance.")); @@ -93,6 +96,7 @@ void SohMenu::AddMenuRandomizer() { AddSidebarEntry("Randomizer", path.sidebarName, 1); AddWidget(path, "Popout Plandomizer Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("PlandomizerEditor")) + .RaceDisable(false) .WindowName("Plandomizer Editor") .Options(WindowButtonOptions().Tooltip("Enables the separate Randomizer Settings Window.")); @@ -103,12 +107,14 @@ void SohMenu::AddMenuRandomizer() { AddWidget(path, "Item Tracker", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Item Tracker", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ItemTracker")) + .RaceDisable(false) .WindowName("Item Tracker") .Options(WindowButtonOptions().Tooltip("Toggles the Item Tracker.").EmbedWindow(false)); AddWidget(path, "Item Tracker Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Item Tracker Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ItemTrackerSettings")) + .RaceDisable(false) .WindowName("Item Tracker Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Item Tracker Settings Window.")); @@ -119,12 +125,14 @@ void SohMenu::AddMenuRandomizer() { AddWidget(path, "Entrance Tracker", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Entrance Tracker", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("EntranceTracker")) + .RaceDisable(false) .WindowName("Entrance Tracker") .Options(WindowButtonOptions().Tooltip("Toggles the Entrance Tracker.").EmbedWindow(false)); AddWidget(path, "Entrance Tracker Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Entrance Tracker Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("EntranceTrackerSettings")) + .RaceDisable(false) .WindowName("Entrance Tracker Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Entrance Tracker Settings Window.")); @@ -135,12 +143,14 @@ void SohMenu::AddMenuRandomizer() { AddWidget(path, "Check Tracker", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Check Tracker", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("CheckTracker")) + .RaceDisable(false) .WindowName("Check Tracker") .Options(WindowButtonOptions().Tooltip("Toggles the Check Tracker.").EmbedWindow(false)); AddWidget(path, "Check Tracker Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Check Tracker Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("CheckTrackerSettings")) + .RaceDisable(false) .WindowName("Check Tracker Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Check Tracker Settings Window.")); } diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp index 94619a43b03..04452f1954c 100644 --- a/soh/soh/SohGui/SohMenuSettings.cpp +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -1,5 +1,7 @@ #include "SohMenu.h" #include "soh/Notification/Notification.h" +#include "soh/Enhancements/controls/SohInputEditorWindow.h" +#include "SohModals.h" #include #include "soh/ResourceManagerHelpers.h" #include "UIWidgets.hpp" @@ -13,13 +15,9 @@ extern "C" { namespace SohGui { extern std::shared_ptr mSohMenu; +extern std::shared_ptr mModalWindow; using namespace UIWidgets; -static const std::unordered_map languages = { - { LANGUAGE_ENG, "English" }, - { LANGUAGE_GER, "German" }, - { LANGUAGE_FRA, "French" }, - { LANGUAGE_JPN, "Japanese" }, -}; + static std::unordered_map imguiScaleOptions = { { 0, "Small" }, { 1, "Normal" }, @@ -100,6 +98,7 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Menu Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Menu Theme", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("Menu.Theme")) + .RaceDisable(false) .Options(ComboboxOptions() .Tooltip("Changes the Theme of the Menu Widgets.") .ComboMap(menuThemeOptions) @@ -107,18 +106,21 @@ void SohMenu::AddMenuSettings() { #if not defined(__SWITCH__) and not defined(__WIIU__) AddWidget(path, "Menu Controller Navigation", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_IMGUI_CONTROLLER_NAV) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Allows controller navigation of the port menu (Settings, Enhancements,...)\nCAUTION: " "This will disable game inputs while the menu is visible.\n\nD-pad to move between " "items, A to select, B to move up in scope.")); AddWidget(path, "Menu Background Opacity", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Menu.BackgroundOpacity")) + .RaceDisable(false) .Options(FloatSliderOptions().DefaultValue(0.85f).IsPercentage().Tooltip( "Sets the opacity of the background of the port menu.")); AddWidget(path, "General Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Cursor Always Visible", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("CursorVisibility")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->SetForceCursorVisibility( CVarGetInteger(CVAR_SETTING("CursorVisibility"), 0)); @@ -127,6 +129,7 @@ void SohMenu::AddMenuSettings() { #endif AddWidget(path, "Search In Sidebar", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("Menu.SidebarSearch")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { if (CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0)) { mSohMenu->InsertSidebarSearch(); @@ -138,13 +141,16 @@ void SohMenu::AddMenuSettings() { "Displays the Search menu as a sidebar entry in Settings instead of in the header.")); AddWidget(path, "Search Input Autofocus", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("Menu.SearchAutofocus")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Search input box gets autofocus when visible. Does not affect using other widgets.")); AddWidget(path, "Alt Assets Tab hotkey", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("Mods.AlternateAssetsHotkey")) + .RaceDisable(false) .Options( CheckboxOptions().Tooltip("Allows pressing the Tab key to toggle alternate assets").DefaultValue(true)); AddWidget(path, "Open App Files Folder", WIDGET_BUTTON) + .RaceDisable(false) .Callback([](WidgetInfo& info) { std::string filesPath = Ship::Context::GetInstance()->GetAppDirectoryPath(); SDL_OpenURL(std::string("file:///" + std::filesystem::absolute(filesPath).string()).c_str()); @@ -154,6 +160,7 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Boot", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Boot Sequence", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("BootSequence")) + .RaceDisable(false) .Options(ComboboxOptions() .DefaultIndex(BOOTSEQUENCE_DEFAULT) .LabelPosition(LabelPositions::Far) @@ -165,9 +172,12 @@ void SohMenu::AddMenuSettings() { "File Select: Skip to file select menu")); AddWidget(path, "Languages", WIDGET_SEPARATOR_TEXT); - AddWidget(path, "Translate Title Screen", WIDGET_CVAR_CHECKBOX).CVar(CVAR_SETTING("TitleScreenTranslation")); + AddWidget(path, "Translate Title Screen", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("TitleScreenTranslation")) + .RaceDisable(false); AddWidget(path, "Language", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("Languages")) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { auto options = std::static_pointer_cast(info.options); SohMenu::UpdateLanguageMap(options->comboMap); @@ -181,14 +191,17 @@ void SohMenu::AddMenuSettings() { #if defined(_WIN32) || defined(__APPLE__) AddWidget(path, "Text to Speech", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("A11yTTS")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Enables text to speech for in game dialog")); #endif AddWidget(path, "Disable Idle Camera Re-Centering", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("A11yDisableIdleCam")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Disables the automatic re-centering of the camera when idle.")); AddWidget(path, "EXPERIMENTAL", WIDGET_SEPARATOR_TEXT).Options(TextOptions().Color(Colors::Orange)); AddWidget(path, "ImGui Menu Scaling", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("ImGuiScale")) + .RaceDisable(false) .Options(ComboboxOptions() .ComboMap(imguiScaleOptions) .Tooltip("Changes the scaling of the ImGui menu elements.") @@ -202,6 +215,9 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "About", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Ship Of Harkinian", WIDGET_TEXT); + + AddWidget(path, "Leveled Mod Version: 1.1.4", WIDGET_TEXT); + AddWidget(path, "A Mod By Gotest, AKA Arrenton", WIDGET_TEXT); if (gGitCommitTag[0] != 0) { AddWidget(path, gBuildVersion, WIDGET_TEXT); } else { @@ -219,9 +235,11 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Master Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.Master")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(40).ShowButtons(true).Format("")); AddWidget(path, "Main Music Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.MainMusic")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_BGM_MAIN, @@ -229,6 +247,7 @@ void SohMenu::AddMenuSettings() { }); AddWidget(path, "Sub Music Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.SubMusic")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_BGM_SUB, @@ -236,6 +255,7 @@ void SohMenu::AddMenuSettings() { }); AddWidget(path, "Fanfare Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.Fanfare")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_FANFARE, @@ -243,11 +263,12 @@ void SohMenu::AddMenuSettings() { }); AddWidget(path, "Sound Effects Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.SFX")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_SFX, ((float)CVarGetInteger(CVAR_SETTING("Volume.SFX"), 100) / 100.0f)); }); - AddWidget(path, "Audio API (Needs reload)", WIDGET_AUDIO_BACKEND); + AddWidget(path, "Audio API (Needs reload)", WIDGET_AUDIO_BACKEND).RaceDisable(false); // Graphics Settings static int32_t maxFps = 360; @@ -258,10 +279,12 @@ void SohMenu::AddMenuSettings() { AddSidebarEntry("Settings", "Graphics", 3); AddWidget(path, "Graphics Options", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Fullscreen", WIDGET_BUTTON) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->ToggleFullscreen(); }) .Options(ButtonOptions().Tooltip("Toggles Fullscreen On/Off.")); AddWidget(path, "Internal Resolution", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_INTERNAL_RESOLUTION) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier( CVarGetFloat(CVAR_INTERNAL_RESOLUTION, 1)); @@ -286,6 +309,7 @@ void SohMenu::AddMenuSettings() { #ifndef __WIIU__ AddWidget(path, "Anti-aliasing (MSAA)", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_MSAA_VALUE) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger(CVAR_MSAA_VALUE, 1)); }) @@ -302,6 +326,7 @@ void SohMenu::AddMenuSettings() { const char* fpsFormat = fps == 20 ? "Original (%d)" : "%d"; AddWidget(path, "Current FPS", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("InterpolationFPS")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { auto options = std::static_pointer_cast(info.options); int32_t defaultValue = options->defaultValue; @@ -318,28 +343,33 @@ void SohMenu::AddMenuSettings() { .Options(IntSliderOptions().Tooltip(tooltip).Min(20).Max(maxFps).DefaultValue(20).Format(fpsFormat)); AddWidget(path, "Match Refresh Rate", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("MatchRefreshRate")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Matches interpolation value to the refresh rate of your display.")); - AddWidget(path, "Renderer API (Needs reload)", WIDGET_VIDEO_BACKEND); + AddWidget(path, "Renderer API (Needs reload)", WIDGET_VIDEO_BACKEND).RaceDisable(false); AddWidget(path, "Enable Vsync", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_VSYNC_ENABLED) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_VSYNC).active; }) .Options(CheckboxOptions() .Tooltip("Removes tearing, but clamps your max FPS to your displays refresh rate.") .DefaultValue(true)); AddWidget(path, "Windowed Fullscreen", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SDL_WINDOWED_FULLSCREEN) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_WINDOWED_FULLSCREEN).active; }) .Options(CheckboxOptions().Tooltip("Enables Windowed Fullscreen Mode.")); AddWidget(path, "Allow multi-windows", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENABLE_MULTI_VIEWPORTS) + .RaceDisable(false) .PreFunc( [](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_MULTI_VIEWPORT).active; }) .Options(CheckboxOptions().Tooltip( "Allows multiple windows to be opened at once. Requires a reload to take effect.")); AddWidget(path, "Texture Filter (Needs reload)", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_TEXTURE_FILTER) + .RaceDisable(false) .Options(ComboboxOptions().Tooltip("Sets the applied Texture Filtering.").ComboMap(textureFilteringMap)); path.column = SECTION_COLUMN_2; @@ -349,9 +379,24 @@ void SohMenu::AddMenuSettings() { path.sidebarName = "Controls"; path.column = SECTION_COLUMN_1; AddSidebarEntry("Settings", "Controls", 2); + AddWidget(path, "Clear Devices", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + SohGui::mModalWindow->RegisterPopup( + "Clear Config", + "This will completely erase the controls config, including registered devices.\nContinue?", "Clear", + "Cancel", + []() { + Ship::Context::GetInstance()->GetConsoleVariables()->ClearBlock(CVAR_PREFIX_SETTING ".Controllers"); + uint8_t bits = 0; + Ship::Context::GetInstance()->GetControlDeck()->Init(&bits); + }, + nullptr); + }) + .Options(ButtonOptions().Size(Sizes::Inline)); AddWidget(path, "Controller Bindings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Bindings Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ControllerConfiguration")) + .RaceDisable(false) .WindowName("Configure Controller") .Options(WindowButtonOptions().Tooltip("Enables the separate Bindings Window.")); @@ -361,12 +406,14 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Input Viewer", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Input Viewer", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("InputViewer")) + .RaceDisable(false) .WindowName("Input Viewer") .Options(WindowButtonOptions().Tooltip("Toggles the Input Viewer.").EmbedWindow(false)); AddWidget(path, "Input Viewer Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Input Viewer Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("InputViewerSettings")) + .RaceDisable(false) .WindowName("Input Viewer Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Input Viewer Settings Window.")); @@ -376,12 +423,14 @@ void SohMenu::AddMenuSettings() { AddSidebarEntry("Settings", path.sidebarName, 3); AddWidget(path, "Position", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("Notifications.Position")) + .RaceDisable(false) .Options(ComboboxOptions() .Tooltip("Which corner of the screen notifications appear in.") .ComboMap(notificationPosition) .DefaultIndex(3)); AddWidget(path, "Duration (seconds):", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Notifications.Duration")) + .RaceDisable(false) .Options(FloatSliderOptions() .Tooltip("How long notifications are displayed for.") .Format("%.1f") @@ -391,12 +440,14 @@ void SohMenu::AddMenuSettings() { .DefaultValue(10.0f)); AddWidget(path, "Background Opacity", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Notifications.BgOpacity")) + .RaceDisable(false) .Options(FloatSliderOptions() .Tooltip("How opaque the background of notifications is.") .DefaultValue(0.5f) .IsPercentage()); AddWidget(path, "Size:", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Notifications.Size")) + .RaceDisable(false) .Options(FloatSliderOptions() .Tooltip("How large notifications are.") .Format("%.1f") @@ -405,6 +456,7 @@ void SohMenu::AddMenuSettings() { .Max(5.0f) .DefaultValue(1.8f)); AddWidget(path, "Test Notification", WIDGET_BUTTON) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Notification::Emit({ .itemIcon = "__OTR__textures/icon_item_24_static/gQuestIconGoldSkulltulaTex", diff --git a/soh/soh/SohGui/UIWidgets.cpp b/soh/soh/SohGui/UIWidgets.cpp index f5e84f302bb..3c4e91147dd 100644 --- a/soh/soh/SohGui/UIWidgets.cpp +++ b/soh/soh/SohGui/UIWidgets.cpp @@ -895,6 +895,12 @@ bool CVarColorPicker(const char* label, const char* cvarName, Color_RGBA8 defaul UIWidgets::ButtonOptions({ { .tooltip = "Resets this color to its default value" } }) .Color(themeColor) .Size(UIWidgets::Sizes::Inline))) { + // TODO: Remove for next minor or major version, temporary fix for already migrated configs to 3 for 9.0.0 + CVarClear((std::string(cvarName) + ".R").c_str()); + CVarClear((std::string(cvarName) + ".G").c_str()); + CVarClear((std::string(cvarName) + ".B").c_str()); + CVarClear((std::string(cvarName) + ".A").c_str()); + CVarClear((std::string(cvarName) + ".Type").c_str()); CVarClearBlock(valueCVar.c_str()); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } diff --git a/soh/soh/config/ConfigMigrators.h b/soh/soh/config/ConfigMigrators.h index a312be604e7..ee7e91792b4 100644 --- a/soh/soh/config/ConfigMigrators.h +++ b/soh/soh/config/ConfigMigrators.h @@ -1263,10 +1263,10 @@ std::vector version3Migrations = { { MigrationAction::Rename, "gUnfixGoronSpin", "gCosmetics.UnfixGoronSpin" }, { MigrationAction::Rename, "gNL_Diamond_Env", "gCosmetics.Magic.NayrusSecondary" }, { MigrationAction::Rename, "gUseSpellsCol", "gCosmetics.UseSpellsColors" }, - { MigrationAction::Rename, "gItemTrackerBgColorA", "gTrackers.ItemTracker.BgColorA" }, - { MigrationAction::Rename, "gItemTrackerBgColorB", "gTrackers.ItemTracker.BgColorB" }, - { MigrationAction::Rename, "gItemTrackerBgColorG", "gTrackers.ItemTracker.BgColorG" }, - { MigrationAction::Rename, "gItemTrackerBgColorR", "gTrackers.ItemTracker.BgColorR" }, + { MigrationAction::Rename, "gItemTrackerBgColorA", "gTrackers.ItemTracker.BgColor.Value.A" }, + { MigrationAction::Rename, "gItemTrackerBgColorB", "gTrackers.ItemTracker.BgColor.Value.B" }, + { MigrationAction::Rename, "gItemTrackerBgColorG", "gTrackers.ItemTracker.BgColor.Value.G" }, + { MigrationAction::Rename, "gItemTrackerBgColorR", "gTrackers.ItemTracker.BgColor.Value.R" }, { MigrationAction::Rename, "gItemTrackerCapacityTrack", "gTrackers.ItemTracker.ItemCountType" }, { MigrationAction::Rename, "gItemTrackerComboButton1", "gTrackers.ItemTracker.ComboButton1" }, { MigrationAction::Rename, "gItemTrackerComboButton2", "gTrackers.ItemTracker.ComboButton2" }, @@ -1310,22 +1310,24 @@ std::vector version3Migrations = { { MigrationAction::Rename, "gEntranceTrackerSortBy", "gTrackers.EntranceTracker.SortBy" }, { MigrationAction::Rename, "gCheckTrackerAreaCompleteHide", "gTrackers.CheckTracker.AreaComplete.Hide" }, { MigrationAction::Rename, "gCheckTrackerAreaExtraCompleteColor", - "gTrackers.CheckTracker.AreaComplete.ExtraColor" }, - { MigrationAction::Rename, "gCheckTrackerAreaExtraIncompleteColor", - "gTrackers.CheckTracker.AreaIncomplete.ExtraColor" }, + "gTrackers.CheckTracker.AreaComplete.ExtraColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerAreaExtraIncompleteColor.Value", + "gTrackers.CheckTracker.AreaIncomplete.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerAreaIncompleteHide", "gTrackers.CheckTracker.AreaIncomplete.Hide" }, - { MigrationAction::Rename, "gCheckTrackerAreaMainCompleteColor", "gTrackers.CheckTracker.AreaComplete.MainColor" }, - { MigrationAction::Rename, "gCheckTrackerAreaMainIncompleteColor", - "gTrackers.CheckTracker.AreaIncomplete.MainColor" }, - { MigrationAction::Rename, "gCheckTrackerBgColor", "gTrackers.CheckTracker.BgColor" }, - { MigrationAction::Rename, "gCheckTrackerCollectedExtraColor", "gTrackers.CheckTracker.Collected.ExtraColor" }, + { MigrationAction::Rename, "gCheckTrackerAreaMainCompleteColor", + "gTrackers.CheckTracker.AreaComplete.MainColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerAreaMainIncompleteColor.Value", + "gTrackers.CheckTracker.AreaIncomplete.MainColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerBgColor", "gTrackers.CheckTracker.BgColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerCollectedExtraColor", + "gTrackers.CheckTracker.Collected.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerCollectedHide", "gTrackers.CheckTracker.Collected.Hide" }, - { MigrationAction::Rename, "gCheckTrackerCollectedMainColor", "gTrackers.CheckTracker.Collected.MainColor" }, + { MigrationAction::Rename, "gCheckTrackerCollectedMainColor", "gTrackers.CheckTracker.Collected.MainColor.Value" }, { MigrationAction::Rename, "gCheckTrackerComboButton1", "gTrackers.CheckTracker.ComboButton1" }, { MigrationAction::Rename, "gCheckTrackerComboButton2", "gTrackers.CheckTracker.ComboButton2" }, { MigrationAction::Rename, "gCheckTrackerDisplayType", "gTrackers.CheckTracker.DisplayType" }, - { MigrationAction::Rename, "gCheckTrackerHintedExtraColor", "gTrackers.CheckTracker.Hinted.ExtraColor" }, - { MigrationAction::Rename, "gCheckTrackerHintedMainColor", "gTrackers.CheckTracker.Hinted.MainColor" }, + { MigrationAction::Rename, "gCheckTrackerHintedExtraColor", "gTrackers.CheckTracker.Hinted.ExtraColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerHintedMainColor", "gTrackers.CheckTracker.Hinted.MainColor.Value" }, { MigrationAction::Rename, "gCheckTrackerHintedHide", "gTrackers.CheckTracker.Hinted.Hide" }, { MigrationAction::Rename, "gCheckTrackerHudEditMode", "gTrackers.CheckTracker.Draggable" }, { MigrationAction::Rename, "gCheckTrackerKnownHide", "gTrackers.CheckTracker.Scummed.Hide" }, @@ -1334,7 +1336,7 @@ std::vector version3Migrations = { "gTrackers.CheckTracker.HideUnshuffledShopChecks" }, { MigrationAction::Rename, "gCheckTrackerOptionMQSpoilers", "gTrackers.CheckTracker.MQSpoilers" }, { MigrationAction::Rename, "gCheckTrackerOptionShowHidden", "gTrackers.CheckTracker.ShowHidden" }, - { MigrationAction::Rename, "gCheckTrackerSavedExtraColor", "gTrackers.CheckTracker.Saved.ExtraColor" }, + { MigrationAction::Rename, "gCheckTrackerSavedExtraColor", "gTrackers.CheckTracker.Saved.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerSavedHide", "gTrackers.CheckTracker.Saved.Hide" }, { MigrationAction::Rename, "gCheckTrackerSavedMainColor", "gTrackers.CheckTracker.Saved.MainColor" }, { MigrationAction::Rename, "gCheckTrackerScummedExtraColor", "gTrackers.CheckTracker.Scummed.ExtraColor" }, @@ -1345,10 +1347,11 @@ std::vector version3Migrations = { { MigrationAction::Rename, "gCheckTrackerShowOnlyPaused", "gTrackers.CheckTracker.ShowOnlyPaused" }, { MigrationAction::Rename, "gCheckTrackerSkippedExtraColor", "gTrackers.CheckTracker.Skipped.ExtraColor" }, { MigrationAction::Rename, "gCheckTrackerSkippedHide", "gTrackers.CheckTracker.Skipped.Hide" }, - { MigrationAction::Rename, "gCheckTrackerSkippedMainColor", "gTrackers.CheckTracker.Skipped.MainColor" }, - { MigrationAction::Rename, "gCheckTrackerUncheckedExtraColor", "gTrackers.CheckTracker.Unchecked.ExtraColor" }, + { MigrationAction::Rename, "gCheckTrackerSkippedMainColor", "gTrackers.CheckTracker.Skipped.MainColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerUncheckedExtraColor", + "gTrackers.CheckTracker.Unchecked.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerUncheckedHide", "gTrackers.CheckTracker.Unchecked.Hide" }, - { MigrationAction::Rename, "gCheckTrackerUncheckedMainColor", "gTrackers.CheckTracker.Unchecked.MainColor" }, + { MigrationAction::Rename, "gCheckTrackerUncheckedMainColor", "gTrackers.CheckTracker.Unchecked.MainColor.Value" }, { MigrationAction::Rename, "gCheckTrackerWindowType", "gTrackers.CheckTracker.WindowType" }, { MigrationAction::Rename, "gRandomize10GSHint", "gRandoSettings.10GSHint" }, { MigrationAction::Rename, "gRandomize20GSHint", "gRandoSettings.20GSHint" }, diff --git a/soh/soh/stubs.c b/soh/soh/stubs.c index 56013e56f3d..118b36e724f 100644 --- a/soh/soh/stubs.c +++ b/soh/soh/stubs.c @@ -145,7 +145,47 @@ void Audio_osWritebackDCache(void* mem, s32 size) { } s32 osAiSetFrequency(u32 freq) { - return 1; + // this is based off the math from the original method + /* + + s32 osAiSetFrequency(u32 frequency) { + u8 bitrate; + f32 dacRateF = ((f32)osViClock / frequency) + 0.5f; + u32 dacRate = dacRateF; + + if (dacRate < 132) { + return -1; + } + + bitrate = (dacRate / 66); + if (bitrate > 16) { + bitrate = 16; + } + + HW_REG(AI_DACRATE_REG, u32) = dacRate - 1; + HW_REG(AI_BITRATE_REG, u32) = bitrate - 1; + return osViClock / (s32)dacRate; + } + + */ + + // bitrate is unused + + // osViClock comes from + // #define VI_NTSC_CLOCK 48681812 /* Hz = 48.681812 MHz */ + // s32 osViClock = VI_NTSC_CLOCK; + + // frequency was originally 32000 + + // given all of that, dacRate is + // (u32)(((f32)48681812 / 32000) + 0.5f) + // which evaluates to 1521 (which is > 132) + + // this leaves us with a final calculation of + // 48681812 / 1521 + // which evaluates to 32006 + + return 32006; } void osInvalDCache(void* vaddr, s32 nbytes) { diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index 1e5bb2ae824..45e59d14ec5 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -7,6 +7,7 @@ #include #include #include "Enhancements/randomizer/randomizerTypes.h" +#include std::vector sceneNames = { "Inside the Deku Tree", @@ -121,7 +122,7 @@ std::vector sceneNames = { "Treasure Chest Room", }; -std::vector itemNames = { +std::vector itemNamesEng = { "Deku Stick", "Deku Nut", "Bomb", @@ -280,7 +281,325 @@ std::vector itemNames = { "Deku Nut Upgrade (40)", }; -std::vector questItemNames = { +std::vector itemNamesFra = { + "Bâton Mojo", + "Noix Mojo", + "Bombe", + "Arc des Fées", + "Flèche de Feu", + "Feu de Din", + "Lance-Pierre des Fées", + "Ocarina des Fées", + "Ocarina du Temps", + "Missile Teigneux", + "Grappin", + "Super Grappin", + "Flèche de Glace", + "Vent de Farore", + "Boomerang", + "Monocle de Vérité", + "Haricot Magique", + "Masse des Titans", + "Flèche de Lumière", + "Amour de Nayru", + "Bouteille Vide", + "Potion Rouge", + "Potion Verte", + "Potion Bleue", + "Fée en Bouteille", + "Poisson", + "Lait Lon Lon et Bouteille", + "Lettre de Ruto", + "Flamme Bleue", + "Insectes", + "Grand Spectre", + "Lait Lon Lon (Demi)", + "Spectre", + "Oeuf Suspect", + "Poule", + "Lettre de Zelda", + "Masque Renard", + "Masque de Mort", + "Masque du Fantôme", + "Capuche de Lapin", + "Masque Goron", + "Masque Zora", + "Masque Gerudo", + "Masque de Vérité", + "ÉPUISÉ", + "Oeuf de Poche", + "Cocotte de Poche", + "Cojiro", + "Champignon Suspect", + "Potion Suspecte", + "Scie du Chasseur", + "Épée Goron (Cassée)", + "Ordonnance", + "Crapaud-qui-louche", + "Super Gouttes", + "Certificat", + "Arc des Fées & Flèche de Feu", + "Arc des Fées & Flèche de Glace", + "Arc des Fées & Flèche de Lumière", + "Épée Kokiri", + "Épée de Légende", + "Lame des Géants & Épée Biggoron", + "Bouclier Mojo", + "Bouclier Hylien", + "Bouclier Miroir", + "Tunique Kokiri", + "Tunique Goron", + "Tunique Zora", + "Bottes Kokiri", + "Bottes de Plomb", + "Bottes des Airs", + "Sac de Graines (30)", + "Sac de Graines (40)", + "Sac de Graines (50)", + "Carquois (30)", + "Grand Carquois (40)", + "Énorme Grand Carquois (50)", + "Sac de Bombes (20)", + "Gros Sac de Bombes (30)", + "Énorme Sac de Bombes (40)", + "Bracelet Goron", + "Gantelets d'Argent", + "Gantelets d'Or", + "Écaille d'Argent", + "Écaille d'Or", + "Lame des Géants (Cassée)", + "Grande Bourse", + "Bourse de Géant", + "Graines Mojo (5)", + "Canne à Pêche", + "Menuet des Bois", + "Boléro du Feu", + "Sérénade de l'Eau", + "Requiem de l'Esprit", + "Nocturne de l'Ombre", + "Prélude de la Lumière", + "Berceuse de Zelda", + "Chant d'Epona", + "Chant de Saria", + "Chant du Soleil", + "Chant du Temps", + "Chant des Tempêtes", + "Médaillon de la Forêt", + "Médaillon du Feu", + "Médaillon de l'Eau", + "Médaillon de l'Esprit", + "Médaillon de l'Ombre", + "Médaillon de la Lumière", + "Émeraude Kokiri", + "Rubis Goron", + "Saphir Zora", + "Pierre de Souffrance", + "Carte Gerudo", + "Symbole de Skulltula d'Or", + "Réceptacle de Coeur", + "Quart de Coeur", + "Clé du Boss", + "Boussole", + "Carte du Donjon", + "Petite Clé", + "Petite Magie", + "Grande Magie", + "Quart de Coeur", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "Lait Lon Lon", + "Coeur", + "Rubis Vert", + "Rubis Bleu", + "Rubis Rouge", + "Rubis Pourpre", + "Énorme Rubis", + "[Retiré]", + "Bâtons Mojo (5)", + "Bâtons Mojo (10)", + "Noix Mojo (5)", + "Noix Mojo (10)", + "Bombes (5)", + "Bombes (10)", + "Bombes (20)", + "Bombes (30)", + "Flèches (Petites)", + "Flèches (Moyennes)", + "Flèches (Grandes)", + "Graines Mojo (30)", + "Missile Teigneux (5)", + "Missile Teigneux (20)", + "Amélioration des Bâtons Mojo (20)", + "Amélioration des Bâtons Mojo (30)", + "Amélioration des Noix Mojo (30)", + "Amélioration des Noix Mojo (40)", +}; + +std::vector itemNamesGer = { + "Deku-Stab", + "Deku-Nuß", + "Bombe", + "Feen-Bogen", + "Feuer-Pfeil", + "Dins Feuerinferno", + "Feen-Schleuder", + "Feen-Okarina", + "Okarina der Zeit", + "Krabbelmine", + "Fanghaken", + "Enterhaken", + "Eis-Pfeil", + "Farores Donnersturm", + "Bumerang", + "Auge der Wahrheit", + "Wundererbse", + "Stahlhammer", + "Licht-Pfeil", + "Nayrus Umarmung", + "Leere Flasche", + "Rotes Elixier", + "Grünes Elixier", + "Blaues Elixier", + "Flasche (Fee)", + "Fisch", + "Flasche (Milch)", + "Rutos Brief", + "Blaues Feuer", + "Käfer", + "Nachtschwärmer", + "Lon Lon-Milch (Halbe Füllung)", + "Irrlicht", + "Seltsames Ei", + "Huhn", + "Zeldas Brief", + "Fuchs-Maske", + "Geister-Maske", + "Schädel-Maske", + "Hasenohren", + "Goronen-Maske", + "Zora-Maske", + "Gerudo-Maske", + "Maske des Wissens", + "AUSVERKAUFT", + "Ei", + "Kiki", + "Henni", + "Schimmelpilz", + "Modertrank", + "Säge", + "Zerbr. Goronen-Schwert", + "Rezept", + "Glotzfrosch", + "Augentropfen", + "Zertifikat", + "Feen-Bogen & Feuer-Pfeil", + "Feen-Bogen & Eis-Pfeil", + "Feen-Bogen & Licht-Pfeil", + "Kokiri-Schwert", + "Master-Schwert", + "Langschwert & Biggoron-Schwert", + "Deku-Schild", + "Hylia-Schild", + "Spiegel-Schild", + "Kokiri-Rüstung", + "Goronen-Rüstung", + "Zora-Rüstung", + "Lederstiefel", + "Eisenstiefel", + "Gleitstiefel", + "Munitionstasche (30)", + "Große Munitionstasche (40)", + "Riesen-Munitionstasche (50)", + "Köcher (30)", + "Großer Köcher (40)", + "Riesenköcher (50)", + "Bombentasche (20)", + "Große Bombentasche (30)", + "Riesen-Bombentasche (40)", + "Goronen-Armband", + "Krafthandschuhe", + "Titanhandschuhe", + "Silberne Schuppe", + "Goldene Schuppe", + "Zerbr. Langschwert", + "Große Börse", + "Riesenbörse", + "Deku-Kerne (5)", + "Angelrute", + "Menuett des Waldes", + "Bolero des Feuers", + "Serenade des Wassers", + "Requiem der Geister", + "Nocturne des Schattens", + "Kantate des Lichts", + "Zeldas Wiegenlied", + "Eponas Lied", + "Salias Lied", + "Hymne der Sonne", + "Hymne der Zeit", + "Hymne des Sturms", + "Amulett des Waldes", + "Amulett des Feuers", + "Amulett des Wassers", + "Amulett der Geister", + "Amulett des Schattens", + "Amulett des Lichts", + "Kokiri-Smaragd", + "Goronen-Rubin", + "Zora-Saphir", + "Stein des Wissens", + "Gerudo-Paß", + "Skulltula-Symbol", + "Herzcontainer", + "Herzteil", + "Master-Schlüssel", + "Kompaß", + "Labyrinth-Karte", + "Kleiner Schlüssel", + "Kleine Magieflasche", + "Große Magieflasche", + "Herzteil", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "Lon Lon-Milch", + "Herz", + "Grüner Rubin", + "Blauer Rubin", + "Roter Rubin", + "Violetter Rubin", + "Silberner Rubin", + "[Entfernt]", + "Deku-Stäbe (5)", + "Deku-Stäbe (10)", + "Deku-Nüsse (5)", + "Deku-Nüsse (10)", + "Bomben (5)", + "Bomben (10)", + "Bomben (20)", + "Bomben (30)", + "Pfeile (5)", + "Pfeile (10)", + "Pfeile (30)", + "Deku-Kerne (30)", + "Krabbelminen (5)", + "Krabbelminen (20)", + "Deku-Stab-Kapazität (20)", + "Deku-Stab-Kapazität (30)", + "Deku-Nuß-Kapazität (30)", + "Deku-Nuß-Kapazität (40)", +}; + +std::vector questItemNamesEng = { "Forest Medallion", "Fire Medallion", "Water Medallion", "Spirit Medallion", "Shadow Medallion", "Light Medallion", "Minuet of Forest", "Bolero of Fire", "Serenade of Water", "Requiem of Spirit", "Nocturne of Shadow", "Prelude of Light", "Zelda's Lullaby", "Epona's Song", "Saria's Song", @@ -288,6 +607,42 @@ std::vector questItemNames = { "Zora's Sapphire", "Stone of Agony", "Gerudo's Card", "Gold Skulltula Token", }; +std::vector questItemNamesFra = { + "Médaillon de la Forêt", "Médaillon du Feu", "Médaillon de l'Eau", "Médaillon de l'Esprit", + "Médaillon de l'Ombre", "Médaillon de la Lumière", "Menuet des Bois", "Boléro du Feu", + "Sérénade de l'Eau", "Requiem de l'Esprit", "Nocturne de l'Ombre", "Prélude de la Lumière", + "Berceuse de Zelda", "Chant d'Epona", "Chant de Saria", "Chant du Soleil", + "Chant du Temps", "Chant des Tempêtes", "Émeraude Kokiri", "Rubis Goron", + "Saphir Zora", "Pierre de Souffrance", "Carte Gerudo", "Symbole de Skulltula d'Or", +}; + +std::vector questItemNamesGer = { + "Amulett des Waldes", + "Amulett des Feuers", + "Amulett des Wassers", + "Amulett der Geister", + "Amulett des Schattens", + "Amulett des Lichts", + "Menuett des Waldes", + "Bolero des Feuers", + "Serenade des Wassers", + "Requiem der Geister", + "Nocturne des Schattens", + "Kantate des Lichts", + "Zeldas Wiegenlied", + "Eponas Lied", + "Salias Lied", + "Hymne der Sonne", + "Hymne der Zeit", + "Hymne des Sturms", + "Kokiri-Smaragd", + "Goronen-Rubin", + "Zora-Saphir", + "Stein des Wissens", + "Gerudo-Paß", + "Goldenes Skulltula-Symbol", +}; + std::array rcareaPrefixes = { "KF", "LW", @@ -334,23 +689,52 @@ const std::string& SohUtils::GetSceneName(int32_t scene) { } const std::string& SohUtils::GetItemName(int32_t item) { - if (item > itemNames.size()) { + const std::vector* currentItemNames = nullptr; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + currentItemNames = &itemNamesFra; + break; + case LANGUAGE_GER: + currentItemNames = &itemNamesGer; + break; + case LANGUAGE_ENG: + default: + currentItemNames = &itemNamesEng; + break; + } + + if (item >= currentItemNames->size()) { SPDLOG_WARN("Passed invalid item id to SohUtils::GetItemName: ({})", item); assert(false); return ""; } - return itemNames[item]; + return (*currentItemNames)[item]; } const std::string& SohUtils::GetQuestItemName(int32_t item) { - if (item > questItemNames.size()) { + const std::vector* currentQuestItemNames = nullptr; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + currentQuestItemNames = &questItemNamesFra; + break; + case LANGUAGE_GER: + currentQuestItemNames = &questItemNamesGer; + break; + case LANGUAGE_ENG: + default: + currentQuestItemNames = &questItemNamesEng; + break; + } + if (item > questItemNamesEng.size()) { SPDLOG_WARN("Passed invalid quest item id to SohUtils::GetQuestItemName: ({})", item); assert(false); return ""; } - return questItemNames[item]; + return (*currentQuestItemNames)[item]; } const std::string& SohUtils::GetRandomizerCheckAreaPrefix(int32_t rcarea) { @@ -369,17 +753,6 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size } 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()); diff --git a/soh/soh/z_play_otr.cpp b/soh/soh/z_play_otr.cpp index 54f4b00169b..23e61261c7b 100644 --- a/soh/soh/z_play_otr.cpp +++ b/soh/soh/z_play_otr.cpp @@ -3,7 +3,7 @@ #include #include "soh/resource/type/Scene.h" #include -#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "global.h" #include "vt.h" #include @@ -59,7 +59,7 @@ extern "C" void OTRPlay_SpawnScene(PlayState* play, s32 sceneId, s32 spawn) { osSyncPrintf("ROOM SIZE=%fK\n", roomSize / 1024.0f); - GameInteractor::Instance->ExecuteHooks(play->sceneNum); + GameInteractor_ExecuteOnSceneInit(play->sceneNum); SPDLOG_INFO("Scene Init - sceneNum: {0:#x}, entranceIndex: {1:#x}", play->sceneNum, gSaveContext.entranceIndex); } @@ -79,6 +79,7 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) { YREG(15) = 0; gSaveContext.worldMapArea = 0; OTRScene_ExecuteCommands(play, (SOH::Scene*)play->sceneSegment); + GameInteractor_ExecuteAfterSceneCommands(play->sceneNum); Play_InitEnvironment(play, play->skyboxId); /* auto data = static_cast(Ship::Context::GetInstance() ->GetResourceManager() diff --git a/soh/src/code/leveled_actor_level_table.c b/soh/src/code/leveled_actor_level_table.c new file mode 100644 index 00000000000..e059bab5371 --- /dev/null +++ b/soh/src/code/leveled_actor_level_table.c @@ -0,0 +1,970 @@ +#include "global.h" + +typedef struct { + s8 levelModifier; + u16 experienceRate; + u8 ignoreEntry; +} SceneLevelEntry; + +///////////////////////////////////////////////// +////////////////// BOSSES ///////////////////// +///////////////////////////////////////////////// +void Leveled_Gohma(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 7; + actor->exp = 120; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 3000; + + if (actor->category == ACTORCAT_ENEMY) { // Larva + actor->level = 5; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 0; + if (actor->params < 10) { + actor->exp = 5; + levelEntry->experienceRate = 100; + } + } +} + +void Leveled_Dodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->category == ACTORCAT_BOSS) { // King Dodongo + actor->level = 15; + actor->exp = 500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 4000; + } else { + actor->level = 14; + actor->exp = 18; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 160; + } +} + +void Leveled_Barinade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 21; + levelEntry->levelModifier = 5; + if (actor->params == -1) { // Body + actor->exp = 850; + levelEntry->experienceRate = 5000; + } +} + +void Leveled_PhantomGanon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 28; + levelEntry->levelModifier = 5; + if (actor->params == 1) + actor->exp = 1400; + levelEntry->experienceRate = 6000; +} + +void Leveled_Volvagia(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 35; + levelEntry->levelModifier = 5; + if (actor->id == ACTOR_BOSS_FD2) + actor->exp = 2475; + levelEntry->experienceRate = 6300; +} + +void Leveled_Morpha(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 40; + actor->exp = 3500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 6600; +} + +void Leveled_BongoBongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 4666; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 7000; +} + +void Leveled_Twinrova(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 52; + levelEntry->levelModifier = 6; + if (actor->params == 2) + actor->exp = 6000; + levelEntry->experienceRate = 7000; +} + +void Leveled_Ganondorf(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 56; + actor->exp = 9999; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 40000; +} + +void Leveled_Ganon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 60; + levelEntry->levelModifier = 8; +} + +///////////////////////////////////////////////// +/////////////// MINI-BOSSES /////////////////// +///////////////////////////////////////////////// +void Leveled_BigOctorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 20; + actor->exp = 320; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 2100; +} + +void Leveled_FlareDancer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 34; + levelEntry->levelModifier = 4; + if (actor->id == ACTOR_EN_FW) { + actor->exp = 1000; + levelEntry->experienceRate = 3000; + } +} + +void Leveled_DarkLink(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = GET_PLAYER(play)->actor.level; + actor->exp = (u16)((f32)GetPlayerStat_NextLevelExpAtLevel(GET_PLAYER(play)->actor.level) * 0.50f); + levelEntry->ignoreEntry = true; +} + +void Leveled_DeadHand(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 44; + levelEntry->levelModifier = 4; + + if (actor->id == ACTOR_EN_DH) { + actor->exp = 2465; + levelEntry->experienceRate = 3666; + } +} + +void Leveled_IronKnuckle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 50; + actor->exp = 2733; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 5000; + + if (actor->params == 0) { + actor->level = 52; + actor->exp = 3100; + levelEntry->levelModifier = 6; + } + if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 55; + actor->exp = 2850; + } +} +///////////////////////////////////////////////// +////////////// NORMAL ENEMIES ///////////////// +///////////////////////////////////////////////// +void Leveled_Dekubaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 1) { // Big baba + actor->level = 24; + actor->exp = 32; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 116; + if (LINK_IS_CHILD) { + actor->level = 5; + actor->exp = 8; + } + } else { + actor->level = 2; + actor->exp = 3; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 60; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 23; + actor->exp = 19; + } + } +} + +void Leveled_StickDekuBaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 1; + actor->exp = 1; + levelEntry->levelModifier = -99; + levelEntry->experienceRate = 1; +} + +void Leveled_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 3; + actor->exp = 3; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 80; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 21; + actor->exp = 19; + } + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 30; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 44; + actor->exp = 34; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 50; + } + if (actor->params == 1) { // Big + actor->level += 3; + actor->exp += 2; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; + } +} + +void Leveled_Wall_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (((play, actor->params & 0xE000) >> 0xD) != 0) { // Gold Skulltula + actor->level = gSaveContext.inventory.gsTokens + 1; + actor->exp = 0; + levelEntry->ignoreEntry = true; + } else { + actor->level = 3; + actor->exp = 2; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 40; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 24; + actor->exp = 18; + } + } +} + +void Leveled_DekuScrub(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 8; + actor->exp = 7; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 75; +} + +void Leveled_Tektite(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -2) { // Blue + actor->level = 12; + actor->exp = 12; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; + if (LINK_IS_ADULT) { + actor->level = 25; + actor->exp = 21; + } + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 36; + actor->exp = 44; + } + } else { + actor->level = 9; + actor->exp = 9; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; + if (LINK_IS_ADULT) { + actor->level = 27; + actor->exp = 24; + } + } +} + +void Leveled_Guay(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 8; + actor->exp = 4; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; + if (LINK_IS_ADULT) { + actor->level = 21; + actor->exp = 13; + } +} + +void Leveled_Octorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 13; + actor->exp = 12; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 70; + if (actor->params != 0) { + actor->level = 0; + actor->exp = 0; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 0; + } +} + +void Leveled_Armos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 12; + actor->exp = 17; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 135; + if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 60; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { // Ganon's Castle + actor->level = 50; + actor->exp = 70; + } +} + +void Leveled_BabyDodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 11; + actor->exp = 7; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 75; +} + +void Leveled_Keese(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 4) { // Ice Keese + actor->level = 34; + actor->exp = 33; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 60; + } else { + actor->level = 9; + actor->exp = 5; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; + + if (play->sceneNum == SCENE_FIRE_TEMPLE) { + actor->level = 28; + actor->exp = 22; + } else if (play->sceneNum == SCENE_ICE_CAVERN) { + actor->level = 31; + actor->exp = 26; + } else if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 34; + actor->exp = 29; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 42; + actor->exp = 36; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 33; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 38; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 80; + } + } +} + +void Leveled_Peahat(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 18; + actor->exp = 47; + levelEntry->levelModifier = 10; + levelEntry->experienceRate = 275; + if (actor->params == 1) { // Larva + actor->level = 13; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 0; + } +} +void Leveled_Poh(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 10; + actor->exp = 14; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; + if (actor->params == 2 || actor->params == 3) { // Composer + actor->level = 15; + actor->exp = 25; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 130; + } +} + +void Leveled_Poh_Field(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 24; + actor->exp = 25; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; +} + +void Leveled_ReDead(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params >= -1) { + actor->level = 25; + actor->exp = 45; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 280; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 171; + } else if (play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 230; + } + } else { // Gibdo + actor->level = 43; + actor->exp = 183; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 305; + } +} + +void Leveled_Shabom(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 15; + levelEntry->levelModifier = -1; +} + +void Leveled_Bari(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 18; + actor->exp = 25; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 85; +} + +void Leveled_Biri(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 16; + actor->exp = 17; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 100; +} + +void Leveled_Stinger(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 17; + actor->exp = 19; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 125; + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 37; + actor->exp = 85; + } +} + +void Leveled_JabuTentacle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 19; + levelEntry->levelModifier = 3; + if (actor->params < 3) { + actor->exp = 61; + levelEntry->experienceRate = 400; + } +} + +void Leveled_Tailpasaran(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 19; + actor->exp = 22; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 125; +} + +void Leveled_Stalchild(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 6 + (u16)(play, actor->params / 2); + actor->exp = 3 + (u16)(play, actor->params / 2.5); + levelEntry->levelModifier = 1 + (u16)(play, actor->params / 2); + levelEntry->experienceRate = 45 + (u16)(play, actor->params * 5); +} + +void Leveled_Beamos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 0) { // Big + actor->level = 48; + actor->exp = 90; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 150; + } else { + actor->level = 11; + actor->exp = 17; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 150; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 80; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 87; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 90; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 300; + } + } +} + +void Leveled_Wolfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 0) { + actor->level = 10; + actor->exp = 24; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; + if (LINK_IS_ADULT) { + actor->level = 20; + actor->exp = 29; + } + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 26; + actor->exp = 43; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 158; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 165; + } + } else { // White + actor->level = 37; + actor->exp = 126; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 180; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 420; + } +} + +void Leveled_Lizalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -2) { // Dinolfos + actor->level = 52; + actor->exp = 250; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 320; + } else if (actor->params == -1) { // Lizalfos + actor->level = 47; + actor->exp = 210; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 290; + } else { // Miniboss Lizalfos + actor->level = 13; + actor->exp = 140; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 1150; + actor->ignoreExpReward = true; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 570; + } +} + +void Leveled_Moblins(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -1) { // Spear + actor->level = 24; + actor->exp = 32; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; + } else if (actor->params == 0) { // Club + actor->level = 25; + actor->exp = 47; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 240; + } else { // Spear patrol + actor->level = 24; + actor->exp = 24; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; + } +} + +void Leveled_Bubble(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -1) { // Blue + actor->level = 25; + actor->exp = 29; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 105; + } else if (actor->params == -2) { // Red + actor->level = 32; + actor->exp = 47; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 110; + if ( play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 40; + actor->exp = 75; + } + } else if (actor->params == -3) { // White + actor->level = 45; + actor->exp = 76; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 107; + } else if (actor->params == -4) { // Big Green + actor->level = 41; + actor->exp = 82; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; + } else { // Green + actor->level = 26; + actor->exp = 31; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 93; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 70; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 77; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 85; + } + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 220; + } +} + +void Leveled_Stalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 27; + actor->exp = 64; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 345; + + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->ignoreExpReward = true; + actor->exp = 250; + levelEntry->experienceRate = 800; + }else if (play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 214; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 236; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER || + play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || + play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 260; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 700; + } +} + +void Leveled_Floormaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 26; + actor->exp = 41; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 170; + actor->ignoreExpReward = true; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 140; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 160; + } +} + +void Leveled_Wallmaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 27; + actor->exp = 44; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 175; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 129; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 138; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 150; + } +} + +void Leveled_Poh_Sisters(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 28; + actor->exp = 127; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 450; +} + +void Leveled_LikeLike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 32; + actor->exp = 99; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 245; + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 38; + actor->exp = 142; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 161; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 182; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 190; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 375; + } +} + +void Leveled_TorchSlug(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 33; + actor->exp = 67; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 135; + if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 163; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 170; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 350; + } +} +void Leveled_Freezer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 35; + actor->exp = 101; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 205; + if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 160; + } +} + +void Leveled_ShellBlade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 38; + actor->exp = 129; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 200; + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 390; + } +} + +void Leveled_RollingSpike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 37; + actor->exp = 100; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 325; + } +} + +void Leveled_GerudoFighter(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 455; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 1250; +} + +void Leveled_Leever(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 15; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 20; +} + +void Leveled_Anubis(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 48; + levelEntry->levelModifier = 3; + if (actor->id == ACTOR_EN_ANUBICE) { + actor->exp = 120; + levelEntry->experienceRate = 155; + } +} + +void Leveled_Arwing(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 20; + actor->exp = 50; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 215; +} + + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride) { + SceneLevelEntry levelEntry; + levelEntry.levelModifier = 0; + levelEntry.experienceRate = 0; + levelEntry.ignoreEntry = 0; + + u16 actorId = actor->id; + if (actorIdOverride != 0) { + actorId = actorIdOverride; + } + + switch (actorId) { + case ACTOR_BOSS_DODONGO: + case ACTOR_EN_DODONGO: + Leveled_Dodongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GOMA: + case ACTOR_EN_GOMA: + Leveled_Gohma(play, actor, &levelEntry); + break; + case ACTOR_BOSS_VA: + Leveled_Barinade(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANONDROF: + Leveled_PhantomGanon(play, actor, &levelEntry); + break; + case ACTOR_BOSS_FD: + case ACTOR_BOSS_FD2: + Leveled_Volvagia(play, actor, &levelEntry); + break; + case ACTOR_BOSS_MO: + Leveled_Morpha(play, actor, &levelEntry); + break; + case ACTOR_BOSS_SST: + Leveled_BongoBongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_TW: + Leveled_Twinrova(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON: + Leveled_Ganondorf(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON2: + Leveled_Ganon(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUBABA: + Leveled_Dekubaba(play, actor, &levelEntry); + break; + case ACTOR_EN_KAREBABA: + Leveled_StickDekuBaba(play, actor, &levelEntry); + break; + case ACTOR_EN_ST: + Leveled_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUNUTS: + Leveled_DekuScrub(play, actor, &levelEntry); + break; + case ACTOR_EN_TITE: + Leveled_Tektite(play, actor, &levelEntry); + break; + case ACTOR_EN_CROW: + Leveled_Guay(play, actor, &levelEntry); + break; + case ACTOR_EN_OKUTA: + Leveled_Octorock(play, actor, &levelEntry); + break; + case ACTOR_EN_AM: + Leveled_Armos(play, actor, &levelEntry); + break; + case ACTOR_EN_DODOJR: + Leveled_BabyDodongo(play, actor, &levelEntry); + break; + case ACTOR_EN_FIREFLY: + Leveled_Keese(play, actor, &levelEntry); + break; + case ACTOR_EN_PEEHAT: + Leveled_Peahat(play, actor, &levelEntry); + break; + case ACTOR_EN_POH: + Leveled_Poh(play, actor, &levelEntry); + case ACTOR_EN_PO_FIELD: + Leveled_Poh_Field(play, actor, &levelEntry); + break; + case ACTOR_EN_RD: + Leveled_ReDead(play, actor, &levelEntry); + break; + case ACTOR_EN_SKB: + Leveled_Stalchild(play, actor, &levelEntry); + break; + case ACTOR_EN_SW: + Leveled_Wall_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_VM: + Leveled_Beamos(play, actor, &levelEntry); + break; + case ACTOR_EN_WF: + Leveled_Wolfos(play, actor, &levelEntry); + break; + case ACTOR_EN_ZF: + Leveled_Lizalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_BUBBLE: + Leveled_Shabom(play, actor, &levelEntry); + break; + case ACTOR_EN_BILI: + Leveled_Biri(play, actor, &levelEntry); + break; + case ACTOR_EN_VALI: + Leveled_Bari(play, actor, &levelEntry); + break; + case ACTOR_EN_TP: + Leveled_Tailpasaran(play, actor, &levelEntry); + break; + case ACTOR_EN_BA: + case ACTOR_EN_BX: + Leveled_JabuTentacle(play, actor, &levelEntry); + break; + case ACTOR_EN_EIYER: + case ACTOR_EN_WEIYER: + Leveled_Stinger(play, actor, &levelEntry); + break; + case ACTOR_EN_BIGOKUTA: + Leveled_BigOctorock(play, actor, &levelEntry); + break; + case ACTOR_EN_MB: + Leveled_Moblins(play, actor, &levelEntry); + break; + case ACTOR_EN_BB: + Leveled_Bubble(play, actor, &levelEntry); + break; + case ACTOR_EN_TEST: + Leveled_Stalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_FLOORMAS: + Leveled_Floormaster(play, actor, &levelEntry); + break; + case ACTOR_EN_WALLMAS: + Leveled_Wallmaster(play, actor, &levelEntry); + break; + case ACTOR_EN_PO_SISTERS: + Leveled_Poh_Sisters(play, actor, &levelEntry); + break; + case ACTOR_EN_RR: + Leveled_LikeLike(play, actor, &levelEntry); + break; + case ACTOR_EN_BW: + Leveled_TorchSlug(play, actor, &levelEntry); + break; + case ACTOR_EN_FZ: + Leveled_Freezer(play, actor, &levelEntry); + break; + case ACTOR_EN_SB: + Leveled_ShellBlade(play, actor, &levelEntry); + break; + case ACTOR_EN_NY: + Leveled_RollingSpike(play, actor, &levelEntry); + break; + case ACTOR_EN_TORCH2: + Leveled_DarkLink(play, actor, &levelEntry); + break; + case ACTOR_EN_DH: + case ACTOR_EN_DHA: + Leveled_DeadHand(play, actor, &levelEntry); + break; + case ACTOR_EN_FD: + case ACTOR_EN_FW: + case ACTOR_EN_FD_FIRE: + Leveled_FlareDancer(play, actor, &levelEntry); + break; + case ACTOR_EN_IK: + Leveled_IronKnuckle(play, actor, &levelEntry); + break; + case ACTOR_EN_GELDB: + Leveled_GerudoFighter(play, actor, &levelEntry); + break; + case ACTOR_EN_REEBA: + Leveled_Leever(play, actor, &levelEntry); + break; + case ACTOR_EN_ANUBICE: + case ACTOR_EN_ANUBICE_FIRE: + Leveled_Anubis(play, actor, &levelEntry); + break; + case ACTOR_EN_CLEAR_TAG: + Leveled_Arwing(play, actor, &levelEntry); + break; + default: + actor->level = GET_PLAYER(play)->actor.level; + break; + } + + s8 sceneLevel = Leveled_GetSceneLevel(play->sceneNum); + if (!levelEntry.ignoreEntry && sceneLevel >= 0) { + actor->level = (u8)CLAMP((f32)(sceneLevel + levelEntry.levelModifier) * (f32)CVarGetFloat("gLeveled.Difficulty.Enemy.LevelScale", 1.0f), 1, 99); + actor->exp = GetEnemyExperienceReward(actor->level, levelEntry.experienceRate); + } +} \ No newline at end of file diff --git a/soh/src/code/leveled_map_levels.c b/soh/src/code/leveled_map_levels.c new file mode 100644 index 00000000000..97191a3e687 --- /dev/null +++ b/soh/src/code/leveled_map_levels.c @@ -0,0 +1,179 @@ +#include "global.h" + +s8 Leveled_GetSceneLevel(s16 sceneId) { + switch (sceneId) { + case SCENE_DEKU_TREE: + case SCENE_DEKU_TREE_BOSS: + return 2; // Deku Tree + + case SCENE_DODONGOS_CAVERN: + case SCENE_DODONGOS_CAVERN_BOSS: + return 10; // Dodongo's + + case SCENE_JABU_JABU: + case SCENE_JABU_JABU_BOSS: + return 16; // Jabu Jabu + + case SCENE_FOREST_TEMPLE: + case SCENE_FOREST_TEMPLE_BOSS: + return 24; // Forest Temple + + case SCENE_FIRE_TEMPLE: + case SCENE_FIRE_TEMPLE_BOSS: + return 30; // Fire Temple + + case SCENE_WATER_TEMPLE: + case SCENE_WATER_TEMPLE_BOSS: + return 35; // Water Temple + + case SCENE_SPIRIT_TEMPLE: + case SCENE_SPIRIT_TEMPLE_BOSS: + return 45; // Spirit Temple + + case SCENE_SHADOW_TEMPLE: + case SCENE_SHADOW_TEMPLE_BOSS: + return 40; // Shadow Temple + + case SCENE_BOTTOM_OF_THE_WELL: + return 38; // Bottom of the Well + + case SCENE_ICE_CAVERN: + return 32; // Ice Cavern + + case SCENE_INSIDE_GANONS_CASTLE: + case SCENE_GANONS_TOWER: + case SCENE_GANONDORF_BOSS: + case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: + case SCENE_GANON_BOSS: + if (CVarGetInteger("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle", 0) == 1 && gSaveContext.inventory.gsTokens == 100) + return 120; + else + return 52; + + case SCENE_GERUDO_TRAINING_GROUND: + return 43; + + case SCENE_THIEVES_HIDEOUT: + return 42; + + case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: + case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: + return 53; + + case SCENE_MARKET_RUINS: + return 23; + + case SCENE_REDEAD_GRAVE: + return 10; + + case SCENE_ROYAL_FAMILYS_TOMB: + return 8; + + case SCENE_WINDMILL_AND_DAMPES_GRAVE: + return 22; + + case SCENE_HOUSE_OF_SKULLTULA: + return 99; + + case SCENE_HYRULE_FIELD: + if (LINK_IS_CHILD) + return 5; + else + return 20; + + case SCENE_GRAVEYARD: + if (LINK_IS_CHILD) + return 7; + else + return 20; + + case SCENE_ZORAS_RIVER: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_KOKIRI_FOREST: + if (LINK_IS_CHILD) + return 2; + else + return 21; + + case SCENE_SACRED_FOREST_MEADOW: + if (LINK_IS_CHILD) + return 9; + else + return 23; + + case SCENE_LAKE_HYLIA: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_ZORAS_FOUNTAIN: + if (LINK_IS_CHILD) + return 13; + else + return 30; + + case SCENE_GERUDO_VALLEY: + if (LINK_IS_CHILD) + return 10; + else + return 28; + + case SCENE_LOST_WOODS: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_DESERT_COLOSSUS: + return 42; + + case SCENE_GERUDOS_FORTRESS: + return 40; + + case SCENE_DEATH_MOUNTAIN_TRAIL: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_DEATH_MOUNTAIN_CRATER: + return 11; + + case SCENE_LON_LON_RANCH: + return 6; + + case SCENE_OUTSIDE_GANONS_CASTLE: + return 25; + + + // Debug only scenes + case SCENE_TEST01: + // Test Map + case SCENE_BESITU: + // Test Room + case SCENE_DEPTH_TEST: + // Depth Test + case SCENE_SYOTES: + // Stalfos Mini-Boss + case SCENE_SYOTES2: + // Stalfos Boss + case SCENE_SUTARU: + // Dark Link + case SCENE_HAIRAL_NIWA2: + // Castle Maze (Broken) + case SCENE_SASATEST: + // SRD Room + case SCENE_TESTROOM: + // Chest Room + return 45; + + default: + return -1; + } + return -1; +} \ No newline at end of file diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c new file mode 100644 index 00000000000..b8a352ca06e --- /dev/null +++ b/soh/src/code/leveled_overlays.c @@ -0,0 +1,902 @@ +#include "leveled_overlays.h" +#include "global.h" +#include "textures/icon_item_static/icon_item_static.h" +#include "textures/parameter_static/parameter_static.h" +#include "textures/do_action_static/do_action_static.h" +#include "textures/icon_item_24_static/icon_item_24_static.h" +#include "textures/message_static/message_static.h" +#include "textures/nes_font_static/nes_font_static.h" +#include "soh/frame_interpolation.h" + +#include "soh/ShipUtils.h" + + +Gfx* Gfx_Texture32(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock(displayListHead++, texture, G_IM_FMT_RGBA, G_IM_SIZ_32b, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, (s32)((dtdy))); + + return displayListHead; +} + +Gfx* Gfx_TextureIA4(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock_4b(displayListHead++, texture, G_IM_FMT_IA, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +Gfx* Gfx_TextureI4(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock_4b(displayListHead++, texture, G_IM_FMT_I, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +void Leveled_DrawTexI8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, + textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTexIA8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureIA8(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTexI4(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureI4(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, + textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex32(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_Texture32(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureIA4(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_OverlayDrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u16 alpha) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, alpha); + + OVERLAY_DISP = Gfx_TextureIA4(OVERLAY_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void ActorDamageNumber_New(Actor* actor, u16 damage) { + if (damage == 0 || (actor->category == ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.PlayerDamage", 1)) || (actor->category != ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.EnemyDamage", 1))) + return; + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -11 }; + + actor->floatingNumber[0] = damage; + actor->floatingNumberLife[0] = 18; + actor->floatingNumberPosition[0] = position; + actor->floatingNumberVelocity[0] = velocity; +} + +void ActorExperienceNumber_New(Actor* actor, u16 experience) { + if (experience == 0 || !CVarGetInteger("gLeveled.HUD.FloatingNumbers.ExpGain", 1)) + return; + + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumber[1] = experience; + actor->floatingNumberLife[1] = 40; + actor->floatingNumberPosition[1] = position; + actor->floatingNumberVelocity[1] = velocity; +} + +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff) { + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumberLife[2] = 100; + if (powerDiff > 0) + actor->floatingNumberLife[3] = 100; + if (courageDiff > 0) + actor->floatingNumberLife[4] = 100; + if (healthDiff > 0) + actor->floatingNumberLife[5] = 100; + if (magicDiff > 0) + actor->floatingNumberLife[6] = 100; + + actor->floatingNumberPosition[2] = position; + actor->floatingNumberVelocity[2] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -4; + velocity.y = -5; + actor->floatingNumberPosition[3] = position; + actor->floatingNumberVelocity[3] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 4; + velocity.y = -5; + actor->floatingNumberPosition[4] = position; + actor->floatingNumberVelocity[4] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -2; + velocity.y = -5; + actor->floatingNumberPosition[5] = position; + actor->floatingNumberVelocity[5] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 2; + velocity.y = -5; + actor->floatingNumberPosition[6] = position; + actor->floatingNumberVelocity[6] = velocity; +} + +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + s16 val = actor->floatingNumber[1]; + u8 digit[] = { 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + Vec3f spBC; + f32 spB4; + s32 j; + + if (actor->floatingNumberLife[1] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 32000) + val = 32000; + + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[1].x += actor->floatingNumberVelocity[1].x; + actor->floatingNumberPosition[1].y += actor->floatingNumberVelocity[1].y; + + if (actor->floatingNumberVelocity[1].y < 0) { + actor->floatingNumberVelocity[1].y += 0.4f; + } + + // Life + actor->floatingNumberLife[1]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[1].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + if (spBC.z <= -0) { + spBC.x = 160 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.y = 200 + actor->floatingNumberPosition[1].y; + } + + spBC.z = spBC.z * 1.0f; + + // Color + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 190, 190, 0, 255); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + // Draw + for (u8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width, (s16)spBC.y, + 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} +// Vertex / Matrix code taken mostly from the Health Bar code. +static Vtx sLeveledDamageNumberVtx[16]; + +void ActorDamageNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + u8 textureWidth = 8; + u8 textureHeight = 16; + s16 val = actor->floatingNumber[0]; + u8 digit[] = { 0, 0, 0, 0 }; + u8 digits = 1; + Vec3f projActorCenter; + f32 projActorCappedInvW; + s16 numbers_actorOffset = -20; + s32 j; + + if (actor->floatingNumberLife[0] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 9999) + val = 9999; + + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[0].x += actor->floatingNumberVelocity[0].x; + actor->floatingNumberPosition[0].y += actor->floatingNumberVelocity[0].y; + + actor->floatingNumberVelocity[0].y += 1.15f; + if (actor->floatingNumberPosition[0].y >= 0) { + actor->floatingNumberPosition[0].y = 0; + actor->floatingNumberVelocity[0].y = -actor->floatingNumberVelocity[0].y * 0.5f; + if (actor->floatingNumberVelocity[0].y >= -4.0f) + actor->floatingNumberVelocity[0].y = 0; + } + + // Life + actor->floatingNumberLife[0]--; + + // Position + func_8002BE04(play, &actor->focus.pos, &projActorCenter, &projActorCappedInvW); + + projActorCenter.x = (SCREEN_WIDTH / 2) * (projActorCenter.x * projActorCappedInvW) + actor->floatingNumberPosition[0].x - (digits - 1) * textureWidth / 2; + projActorCenter.x = projActorCenter.x * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1); + projActorCenter.x = CLAMP(projActorCenter.x, (-SCREEN_WIDTH / 2), (SCREEN_WIDTH / 2)); + + projActorCenter.y = (SCREEN_HEIGHT / 2) * (projActorCenter.y * projActorCappedInvW); + projActorCenter.y = projActorCenter.y + numbers_actorOffset - actor->floatingNumberPosition[0].y; + projActorCenter.y = CLAMP(projActorCenter.y, (-SCREEN_HEIGHT / 2), (SCREEN_HEIGHT / 2)); + + // Setup DL for overlay disp + Gfx_SetupDL_39Overlay(play->state.gfxCtx); + + Matrix_Translate(projActorCenter.x, projActorCenter.y, 0, MTXMODE_NEW); + Matrix_Scale(1.0f, -1.0, 1.0f, MTXMODE_APPLY); + Matrix_ToMtx(&actor->floatingDamageNumberMtx, __FILE__, __LINE__); + gSPMatrix(OVERLAY_DISP++, &actor->floatingDamageNumberMtx, G_MTX_MODELVIEW | G_MTX_LOAD); + + // Color + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 20, 255); + if (actor->category == ACTORCAT_PLAYER) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 100, 0, CLAMP(actor->floatingNumberLife[0] / 8.0 * 255, 0, 255)); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, CLAMP(actor->floatingNumberLife[0] / 8.0 * 255, 0, 255)); + } + + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + for (u8 i = 0; i < digits; i++) { + Ship_CreateQuadVertexGroup(&sLeveledDamageNumberVtx[4 * i], -textureWidth * i, 0, textureWidth, textureHeight, 0); + + gDPPipeSync(OVERLAY_DISP++); + + gSPVertex(OVERLAY_DISP++, sLeveledDamageNumberVtx, 16, 0); + + gDPLoadTextureBlock(OVERLAY_DISP++, digitTextures[digit[i]], G_IM_FMT_I, G_IM_SIZ_8b, textureWidth, + textureHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(OVERLAY_DISP++, 0 + 4 * i, 2 + 4 * i, 3 + 4 * i, 1 + 4 * i, 0); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Actor_LevelUpDraw(PlayState* play, Actor* actor) { + u8 width = 8; + Vec3f spBC; + f32 spB4; + for (s8 i = 2; i < 7; i++) { + if (actor->floatingNumberLife[i] <= 0) + continue; + + OPEN_DISPS(play->state.gfxCtx); + + // Velocity + actor->floatingNumberPosition[i].x += actor->floatingNumberVelocity[i].x; + actor->floatingNumberPosition[i].y += actor->floatingNumberVelocity[i].y; + + if (actor->floatingNumberVelocity[i].y < 0) { + actor->floatingNumberVelocity[i].y += 0.75f; + if (actor->floatingNumberVelocity[i].y > 0) + actor->floatingNumberVelocity[i].y = 0; + } + if (actor->floatingNumberVelocity[i].x < 0) { + actor->floatingNumberVelocity[i].x += 0.5f; + if (actor->floatingNumberVelocity[i].x > 0) + actor->floatingNumberVelocity[i].x = 0; + } + if (actor->floatingNumberVelocity[i].x > 0) { + actor->floatingNumberVelocity[i].x -= 0.5f; + if (actor->floatingNumberVelocity[i].x < 0) + actor->floatingNumberVelocity[i].x = 0; + } + + // Life + actor->floatingNumberLife[i]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 165 + actor->floatingNumberPosition[i].x - 24; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[i].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * 1.0f; + + if (spBC.z < 0) { + spBC.x = 160 + actor->floatingNumberPosition[i].x - 24; + spBC.y = 200 + actor->floatingNumberPosition[i].y; + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (i == 2) { + + for (u8 j = 0; j < 3; j++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 5, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar56LatinCapitalLetterVTex, 8, 16, (s16)spBC.x + 10, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 15, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x + 20, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar55LatinCapitalLetterUTex, 8, 16, (s16)spBC.x + 30, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar50LatinCapitalLetterPTex, 8, 16, (s16)spBC.x + 35, (s16)spBC.y, 8, 16, 2048, 2048); + } + } + + if (i == 3) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconSilverGauntletsTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + if (i == 4) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconShieldHylianTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + if (i == 5) { + + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 70, 0, 255); + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, dgHeartFullTex, 16, 16, (s16)spBC.x, (s16)spBC.y, 16, 16, 2048, 2048); + } + + if (i == 6) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgQuestIconMagicJarBigTex, 24, 24, (s16)spBC.x, (s16)spBC.y, 24, 24, 3072, 3072); + } + + CLOSE_DISPS(play->state.gfxCtx); + } +} + +extern const char* _gAmmoDigit0Tex[]; +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_OPA_DISP++); + + for (s8 i = 0; i < digits; i++) { + POLY_OPA_DISP = + Gfx_TextureIA8(POLY_OPA_DISP, (u8*)_gAmmoDigit0Tex[digit[i]], 8, 8, x - i * 6 + 6 * (digits - 1), y, 8, + 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_OverlayValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 direction, u8 r, u8 g, u8 b, u8 a) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, r, g, b, a); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + if (direction == 2) { + x -= 6 * (digits); + } + if (direction == 1) { + x -= 3 * (digits); + } + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(OVERLAY_DISP++); + + for (s8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[digit[i]], 8, 8, + x - i * 6 + 6 * (digits - 1), y, 8, 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + u8 width = 7; + + extern const char* digitTextures[]; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, a); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_OPA_DISP++); + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, 255); + + for (j = 0; j < 4; j++) { + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1) + (j % 2) * 2 - 1, y + (j / 2) * 2 - 1, 8, 16, 1 << 10, 1 << 10); + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1), y, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_KaleidoEquip_Stats(PlayState* play) { + extern const char* digitTextures[]; + Player* player = GET_PLAYER(play); + u16 statX = 92; + u16 statY = 76; + u8 attack = 1; + Color_RGBA8 textColor = { 255, 255, 255, 255 }; + + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 1) + attack = Leveled_GetHealthAttackMultiplier(); + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 2) + attack = Leveled_GetHealthAttackMultiplier() << 1; + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 3) { + attack = Leveled_GetHealthAttackMultiplier() << 2; + if (gBitFlags[3] & gSaveContext.inventory.equipment) + attack = Leveled_GetHealthAttackMultiplier(); + } + + // Values and Icons + // Level + Leveled_DrawTexI4(play, dgMsgChar4CLatinCapitalLetterLTex, 16, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI4(play, dgMsgChar76LatinSmallLetterVTex, 16, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); + + Leveled_BigValueNumberDraw(play, statX + 10, statY - 6, player->actor.level, 255, 255, 255, 255); + statY += 10; + // Health + Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); + u8 healthValX = gSaveContext.healthCapacity2 >= 1000 ? 12 : gSaveContext.healthCapacity2 >= 100 ? 6 : 0; + if (HealthMeter_IsCritical()) { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 127, 0); + } else { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); + } + Leveled_DrawTexI4(play, dgMsgChar2FSolidusTex, 16, 16, statX + 22 + healthValX, statY - 1, 12, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); + statY += 8; + // Magic + if (gSaveContext.magicCapacity > 0) { + healthValX = gSaveContext.magicCapacity >= 100 ? 6 : 0; + Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 24, 24, statX + 2, statY, 14, 14); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.magic, 255, 255, 255); + Leveled_DrawTexI4(play, dgMsgChar2FSolidusTex, 16, 16, statX + 22 + healthValX, statY - 1, 12, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.magicCapacity, 120, 255, 0); + statY += 8; + } + // Attack + Leveled_DrawTex32(play, dgItemIconSwordKokiriTex, 32, 32, statX, statY - 1, 22, 20); + Leveled_ValueNumberDraw(play, statX + 10, statY, GetActorStat_Attack(attack, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255)), 255, 255, 255); + statY += 8; + // Power + if (player->actor.powerModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.powerModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } + Leveled_DrawTex32(play, dgItemIconSilverGauntletsTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statY += 8; + // Courage + if (player->actor.courageModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.courageModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } else { + textColor.r = 255; + textColor.g = 255; + textColor.b = 255; + } + Leveled_DrawTex32(play, dgItemIconShieldHylianTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.courage + player->actor.courageModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statX = 90; + statY += 60; + if (gSaveContext.magicCapacity > 0) { + statY -= 8; + } + // EXP + Leveled_DrawTex32(play, dgItemIconGoronsBraceletTex, 32, 32, statX + 4, statY, 13, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.experience, 255, 255, 255); + statY += 8; + // Next LV + Leveled_DrawTex4b(play, dgNextDoActionENGTex, 48, 16, statX - 4, statY, 68, 15); + Leveled_ValueNumberDraw(play, statX + 22, statY, + GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience), 255, 255, 255); +} + +void Leveled_Interface_DrawNextLevel(PlayState* play) { + if (gSaveContext.showNeededExpTimer > 0){ + gSaveContext.showNeededExpTimer--; + } else { + return; + } + if (play->pauseCtx.state != 0 || !CVarGetInteger("gLeveled.HUD.ExperienceNextLevel", 1)) + return; + + Player* player = GET_PLAYER(play); + u16 posX = OTRGetRectDimensionFromLeftEdge(10); + u16 posY = 58; + extern const char* digitTextures[]; + s32 val = GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience); + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + s32 j; + + Leveled_OverlayDrawTex4b(play, dgNextDoActionENGTex, 48, 16, -192, -268, 48, 16, 255 - play->pauseCtx.alpha); // Load texture + Leveled_OverlayDrawTex4b(play, dgNextDoActionENGTex, 48, 16, posX, posY, 48, 16, 255 - play->pauseCtx.alpha); // Draw texture + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 999999) + val = 999999; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Color + + // Draw + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + s16 numbersPosX = 38; + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, 255 - play->pauseCtx.alpha); + + for (j = 0; j < 3; j++) { + for (u8 k = 0; k < 3; k++) { + if (j == 1 && k == 1) + continue; + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, + posX - i * width + width * (digits - 1) + numbersPosX - 1 + k, posY - 1 + j, 8, 16, 1024, 1024); + } + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255 - play->pauseCtx.alpha); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, posX - i * width + width * (digits - 1) + numbersPosX, posY, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} \ No newline at end of file diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c new file mode 100644 index 00000000000..e1ae153d9bc --- /dev/null +++ b/soh/src/code/leveled_stat_math.c @@ -0,0 +1,267 @@ +#include "global.h" + +static u32 sExpTable[] = { 0, 30, 64, 105, 155, 217, 292, 385, 497, 632, 792, + 981, 1202, 1458, 1753, 2089, 2471, 2903, 3388, 3930, 4534, 5203, + 5942, 6755, 7648, 8625, 9691, 10851, 12110, 13474, 14949, 16540, 18253, + 20094, 22070, 24188, 26453, 28874, 31456, 34209, 37139, 40254, 43562, 47073, + 50794, 54734, 58904, 63312, 67968, 72883, 78067, 83531, 89286, 95345, 101719, + 108421, 115463, 122859, 130623, 138770, 147314, 156270, 165655, 175485, 185778, 196552, + 207824, 219614, 231943, 244831, 258300, 272372, 287071, 302422, 318450, 335182, 352645, + 370870, 389885, 409724, 430419, 452005, 474520, 498001, 522489, 548027, 574659, 602434, + 631400, 661611, 693122, 725993, 760287, 796070, 833414, 872395, 913094, 955597, 999999 }; +/* Old table +{ 0, 30, 65, 110, 167, 240, 334, 452, 598, 777, 993, + 1251, 1555, 1910, 2320, 2791, 3329, 3937, 4621, 5388, 6242, 7189, + 8235, 9386, 10647, 12026, 13528, 15160, 16929, 18840, 20902, 23120, 25501, + 28054, 30784, 33700, 36809, 40119, 43637, 47371, 51329, 55520, 59951, 64630, + 69567, 74770, 80248, 86008, 92061, 98416, 105080, 112065, 119379, 127032, 135033, + 143392, 152119, 161225, 170719, 180611, 190912, 201632, 212783, 224374, 236417, 248922, + 261901, 275365, 289325, 303792, 318779, 334297, 350358, 366973, 384155, 401916, 420268, + 439224, 458796, 478997, 499839, 521336, 543501, 566346, 589885, 614131, 639098, 664799, + 691249, 718461, 746448, 775226, 804807, 835208, 866441, 898523, 931466, 965287, 999999 };*/ + +u8 Leveled_GetHealthAttackMultiplier() { + return CLAMP(CVarGetInteger("gLeveled.Enhancements.AttackAndHPMultiplier", 9), 1, 32); +} + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power) { + return GetActorStat_Attack(attack, power) / (1 + (float)power / 30.0f); +} + +u16 GetActorStat_Attack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.14f + (power - 2) * 0.0006f)); +} + +f32 GetActorStat_EnemyAttack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.007f + (power - 2) * 0.0002f)); +} + +u8 GetActorStat_Power(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u8 GetActorStat_Courage(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerPower(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerCourage(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level){ + return (u16)(CLAMP((f32)GetActorStat_Attack(baseHealth * Leveled_GetHealthAttackMultiplier(), GetActorStat_PlayerPower(level)) * CVarGetFloat("gLeveled.Difficulty.Enemy.HPPercent", 1.0f), 1, 0xffff)); +} + +u8 GetPlayerStat_BonusHearts(u8 level){ + if (CVarGetInteger("gLeveled.Player.Enhancements.HeartsWithLevelUp", 1) == 0){ + return 0; + } + + u8 bonusHearts = (level + 1) / 8; + if (bonusHearts > 10){ + bonusHearts = 10; + } + return bonusHearts; +} + +u8 GetPlayerStat_MagicUnits(u8 level){ + if (CVarGetInteger("gLeveled.Player.Enhancements.MagicWithLevelUp", 1) == 0){ + return 48; + } + + u8 maximumMagic = 12 + (u8)((f32)level / 2.8f) * 2; + if (maximumMagic > 72){ + maximumMagic = 72; + } + return maximumMagic; +} + +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level){ + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + u16 baseHearts = baseHealth / 16; + return (baseHearts + GetPlayerStat_BonusHearts(level)) * heartUnits; +} + +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - sExpTable[level - 1]; +} + +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - currentExp; + + if (nextLv < 0) + nextLv = 0; + return nextLv; +} + +u16 GetEnemyExperienceReward(u8 level, u16 expRate) { + if (expRate == 0) + return 0; + + return CLAMP(round((3 + CLAMP_MAX(floor(level / 6) * 4, 4) + (level - 1) * ((0.1 + (level / 95.0)) + pow(CLAMP_MIN(level - 8, 0), 1.25) / 80.0)) * expRate / 100.0), 1, 9999); +} + +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage) { + f32 damage = GetActorStat_Attack(attack, power); + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.01f; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.97f; + } + } + return damage; +} + +f32 Leveled_DamageFormulaOnPlayer(f32 attack, u8 power, u8 courage) { + f32 damage = attack; + + if (CVarGetInteger("gLeveled.Enemy.Enhancements.AttackScalesWithLevel", 1) == 1){ + damage = GetActorStat_EnemyAttack(attack, power); + + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + f32 multAddition = CLAMP_MIN((0.07f - (power * 0.0005f)) - (f32)i / (100.0f - (power * 0.33f)), 0); + damage *= 1.04f + multAddition; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } else { + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.05f + CLAMP_MIN(0.05f - (f32)i / 100.0f, 0); + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } + return damage; +} + +f32 Leveled_DamageModify(Actor* actor, Actor* attackingActor, f32 attack) { + f32 damage; + if (actor->category == ACTORCAT_PLAYER) { + damage = Leveled_DamageFormulaOnPlayer(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } else { + damage = Leveled_DamageFormula(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } + + + if (damage >= 1.25f) + damage += Rand_ZeroOne() - 0.2f; + + if (damage >= 6) + damage += Rand_ZeroFloat(damage * 0.12f) - damage * 0.06f; + + if (damage > 9999) + damage = 9999; + + damage = (u16)(damage + 0.5f); + + return CLAMP_MIN(damage, attack > 0 ? 1 : 0); +} + +u16 Leveled_GoldSkulltulaExperience(u8 tokens) { + u16 experience = 5; + u8 i; + + for (i = 0; i < tokens; i++) { + experience += 5 + 5 * i / (f32)10.0; + } + return (u16)((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.TokenRate", 1.0f)); +} + +void Leveled_SetPlayerModifiedStats(Player* player) { + s8 powerModifier = 0; + s8 courageModifier = 0; + + if (CVarGetInteger("gLeveled.Player.Enhancements.EquipmentStats", 1) == 1){ + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD)){ + case PLAYER_SWORD_MASTER: + courageModifier += 1; + break; + + case PLAYER_SWORD_BIGGORON: + if (gBitFlags[3] & gSaveContext.inventory.equipment){ + powerModifier -= 7; + courageModifier -= 12; + } else { + powerModifier += 2; + courageModifier -= 8; + } + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC) - 1){ + case PLAYER_TUNIC_GORON: + powerModifier += 3; + courageModifier -= 3; + break; + + case PLAYER_TUNIC_ZORA: + powerModifier -= 3; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD)){ + case PLAYER_SHIELD_DEKU: + courageModifier += 1; + break; + + case PLAYER_SHIELD_HYLIAN: + courageModifier += 2; + break; + + case PLAYER_SHIELD_MIRROR: + powerModifier -= 2; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) - 1){ + case PLAYER_BOOTS_IRON: + powerModifier += 2; + courageModifier += 1; + break; + + case PLAYER_BOOTS_HOVER: + courageModifier -= 1; + break; + + default: + break; + } + + powerModifier += Player_GetStrength(); + } + + player->actor.powerModifier = powerModifier; + player->actor.courageModifier = courageModifier; +} \ No newline at end of file diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 3ca67ad3b53..e9c703fe1cc 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1236,12 +1236,37 @@ void Actor_SetObjectDependency(PlayState* play, Actor* actor) { gSegments[6] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[actor->objBankIndex].segment); } +void Actor_RefreshLeveledStats(Actor* actor, Player* player) { + if (actor->category == ACTORCAT_PLAYER) { + actor->power = GetActorStat_PlayerPower(actor->level); + actor->courage = GetActorStat_PlayerCourage(actor->level); + gSaveContext.healthCapacity2 = + GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, actor->level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; + gSaveContext.magicUnits = GetPlayerStat_MagicUnits(actor->level); + Leveled_SetPlayerModifiedStats(player); + } else { + actor->power = GetActorStat_Power(actor->level); + actor->powerModifier = 1; + actor->courage = GetActorStat_Courage(actor->level); + actor->courageModifier = 0; + } +} + void Actor_Init(Actor* actor, PlayState* play) { Actor_SetWorldToHome(actor); Actor_SetShapeRotToWorld(actor); Actor_SetFocus(actor, 0.0f); Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); Actor_SetScale(actor, 0.01f); + actor->level = 0; + actor->exp = 0; + actor->ignoreExpReward = false; + for (u8 i = 0; i < 5; i++) { + actor->floatingNumber[i] = 0; + actor->floatingNumberLife[i] = 0; + } actor->targetMode = 3; actor->minVelocityY = -20.0f; actor->xyzDistToPlayerSq = FLT_MAX; @@ -1257,12 +1282,19 @@ void Actor_Init(Actor* actor, PlayState* play) { actor->init(actor, play); actor->init = NULL; + if (actor->category != ACTORCAT_PLAYER) { + Actor_GetLevelAndExperience(play, actor, 0); + actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + GameInteractor_ExecuteOnActorInit(actor); // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } + + Actor_RefreshLeveledStats(actor, GET_PLAYER(play)); } } @@ -2201,8 +2233,12 @@ s32 Actor_NotMounted(PlayState* play, Actor* horse) { void func_8002F698(PlayState* play, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5, u32 arg6) { Player* player = GET_PLAYER(play); + u16 damage = arg6; + if (actor != NULL && damage != 0) { + damage = Leveled_DamageModify(&player->actor, actor, arg6); + } - player->knockbackDamage = arg6; + player->knockbackDamage = damage; player->knockbackType = arg5; player->knockbackRot = arg3; player->knockbackSpeed = arg2; @@ -2618,12 +2654,19 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { actor->init(actor, play); actor->init = NULL; + if (actor->category != ACTORCAT_PLAYER) { + Actor_GetLevelAndExperience(play, actor, 0); + actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + GameInteractor_ExecuteOnActorInit(actor); // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } + + Actor_RefreshLeveledStats(actor, GET_PLAYER(play)); } actor = actor->next; } else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { @@ -3628,6 +3671,9 @@ Actor* Actor_Find(ActorContext* actorCtx, s32 actorId, s32 actorCategory) { */ void Enemy_StartFinishingBlow(PlayState* play, Actor* actor) { play->actorCtx.freezeFlashTimer = 5; + if (!actor->ignoreExpReward) { + Player_GainExperience(play, actor->exp); + } SoundSource_PlaySfxAtFixedWorldPos(play, &actor->world.pos, 20, NA_SE_EN_LAST_DAMAGE); } @@ -4740,12 +4786,15 @@ u8 func_800355E4(PlayState* play, Collider* collider) { } } -u8 Actor_ApplyDamage(Actor* actor) { +u16 Actor_ApplyDamage(Actor* actor) { if (actor->colChkInfo.damage >= actor->colChkInfo.health) { actor->colChkInfo.health = 0; } else { actor->colChkInfo.health -= actor->colChkInfo.damage; } + if (actor->colChkInfo.damage > 0) { + ActorDamageNumber_New(actor, actor->colChkInfo.damage); + } return actor->colChkInfo.health; } diff --git a/soh/src/code/z_collision_check.c b/soh/src/code/z_collision_check.c index aa466e412eb..0a63113ed04 100644 --- a/soh/src/code/z_collision_check.c +++ b/soh/src/code/z_collision_check.c @@ -3024,12 +3024,25 @@ void CollisionCheck_ApplyDamage(PlayState* play, CollisionCheckContext* colChkCt collider->actor->colChkInfo.damageEffect = tbl->table[i] >> 4 & 0xF; } if (!(collider->acFlags & AC_HARD)) { + Actor* attacker = collider->ac; + + if (collider->actor->category != ACTORCAT_PLAYER) { + damage *= Leveled_GetHealthAttackMultiplier(); + } else { + damage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); + } + + if (info->acHit->atFlags & AT_TYPE_PLAYER) + attacker = &GET_PLAYER(play)->actor; + + damage = (u16)Leveled_DamageModify(collider->actor, attacker, damage); collider->actor->colChkInfo.damage += damage; } if (CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0)) { collider->actor->colChkInfo.damage *= GET_PLAYER(play)->ivanDamageMultiplier; } + } /** diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index 0405494666f..0aa276a2d3e 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -1564,7 +1564,7 @@ s16 func_8001F404(s16 dropId) { } // clang-format on - if (dropId == ITEM00_HEART && gSaveContext.healthCapacity == gSaveContext.health) { + if (dropId == ITEM00_HEART && gSaveContext.healthCapacity2 == gSaveContext.health) { return ITEM00_RUPEE_GREEN; } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 451bdf9ab53..e92190e3269 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -390,12 +390,19 @@ void HealthMeter_Draw(PlayState* play) { f32 temp2; f32 temp3; f32 temp4; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (heartUnits < 4) { + heartUnits = 4; + CVarSetInteger("gLeveled.Difficulty.HeartUnits", 1); + } InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; Vtx* sp154 = interfaceCtx->beatingHeartVtx; - s32 curHeartFraction = gSaveContext.health % 0x10; - s16 totalHeartCount = gSaveContext.healthCapacity / 0x10; - s16 fullHeartCount = gSaveContext.health / 0x10; + s16 totalHeartCount = gSaveContext.healthCapacity2 / heartUnits; + s16 fullHeartCount = gSaveContext.health / heartUnits; + f32 heartUnit = (f32)gSaveContext.health / heartUnits * 16 - fullHeartCount * 16; + heartUnit = heartUnit > 0.0f && heartUnit < 1.0f ? 1.0f : heartUnit; + s32 curHeartFraction = (s32)heartUnit % 0x10; s32 pad2; f32 sp144 = interfaceCtx->unk_22A * 0.1f; s32 curCombineModeSet = 0; @@ -410,7 +417,7 @@ void HealthMeter_Draw(PlayState* play) { OPEN_DISPS(gfxCtx); - if (!(gSaveContext.health % 0x10)) { + if (!(gSaveContext.health % heartUnits)) { fullHeartCount--; } @@ -631,7 +638,7 @@ void HealthMeter_Draw(PlayState* play) { } offsetX += 10.0f; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (lineLength != 0 && (i + 1) % lineLength == 0) { offsetX = PosX_anchor; offsetY += 10.0f; @@ -639,6 +646,50 @@ void HealthMeter_Draw(PlayState* play) { FrameInterpolation_RecordCloseChild(); } + + // Draw Health Numbers + u32 healthNumbersType = CVarGetInteger("gLeveled.HUD.HealthNumbersType", 0); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); + s32 numberOffsetX = CLAMP(totalHeartCount, 0, lineLength) * 5; + s32 numberPosX = PosX_anchor + numberOffsetX; + s32 numberPosY = getHealthMeterYOffset() + 13; + + if (healthNumbersType == 0) { + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (HealthMeter_IsCritical()) { + Leveled_OverlayValueNumberDraw(play, numberPosX + 21, numberPosY, gSaveContext.health, 2, (u16)(255.0 * (1 - sp144 * 0.5)), (u16)(127.0 * (1 - sp144 * 0.5)), 0, (u8)interfaceCtx->magicAlpha); + } else { + Leveled_OverlayValueNumberDraw(play, numberPosX + 21, numberPosY, gSaveContext.health, 2, 255, 255, 255, (u8)interfaceCtx->magicAlpha); + } + + Leveled_OverlayValueNumberDraw(play, numberPosX + 28, numberPosY, gSaveContext.healthCapacity2, 0, 255, 255, 255, (u8)interfaceCtx->magicAlpha); + + extern const char* fontTbl[]; + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, fontTbl[15], G_IM_FMT_I, 16, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSPWideTextureRectangle(OVERLAY_DISP++, numberPosX + 22 << 2, numberPosY << 2, (numberPosX + 22 + 8) << 2, + (numberPosY + 16) << 2, G_TX_RENDERTILE, 0, 0, 16 << 7, 16 << 7); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, fontTbl[15], G_IM_FMT_I, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSPWideTextureRectangle(OVERLAY_DISP++, numberPosX + 22 << 2, numberPosY << 2, (numberPosX + 22 + 8) << 2, + (numberPosY + 16) << 2, G_TX_RENDERTILE, 0, 0, 16 << 7, 16 << 7); + } else if (healthNumbersType == 1) { + + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (HealthMeter_IsCritical()) { + Leveled_OverlayValueNumberDraw(play, numberPosX + 24, numberPosY, gSaveContext.health, 1, (u16)(255.0 * (1 - sp144 * 0.5)), (u16)(127.0 * (1 - sp144 * 0.5)), 0, (u8)interfaceCtx->magicAlpha); + } else { + Leveled_OverlayValueNumberDraw(play, numberPosX + 24, numberPosY, gSaveContext.health, 1, 255, 255, 255, (u8)interfaceCtx->magicAlpha); + } + } CLOSE_DISPS(gfxCtx); } @@ -669,15 +720,9 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { u32 HealthMeter_IsCritical(void) { s32 var; - if (gSaveContext.healthCapacity <= 0x50) { - var = 0x10; - } else if (gSaveContext.healthCapacity <= 0xA0) { - var = 0x18; - } else if (gSaveContext.healthCapacity <= 0xF0) { - var = 0x20; - } else { - var = 0x2C; - } + s32 heartValue = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + + var = (s32)(heartValue + (f32)(((gSaveContext.healthCapacity2 / heartValue) - 3) * heartValue) * 0.103f); if (GameInteractor_Should(VB_HEALTH_METER_BE_CRITICAL, var >= gSaveContext.health && gSaveContext.health > 0)) { return true; diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index fc63f96c8a5..c4fbf5c99b5 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -2823,6 +2823,12 @@ void Message_OpenText(PlayState* play, u16 textId) { } } msgCtx->msgLength = font->msgLength = GetEquipNowMessage(font->msgBuf, font->msgOffset, sizeof(font->msgBuf)); + } else if ((CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1) || + CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1)) && + (textId > 0x0600 && textId < 0x06FF) && play->actorCtx.targetCtx.targetedActor != NULL) { + Message_FindMessage(play, textId); + msgCtx->msgLength = font->msgLength = GetLeveledNaviEnemyInfo( + font->msgBuf, font->msgOffset, sizeof(font->msgBuf), play->actorCtx.targetCtx.targetedActor); } else { if (gSaveContext.language == LANGUAGE_JPN) { Message_FindMessageJPN(play, textId); @@ -4692,12 +4698,15 @@ void Message_Update(PlayState* play) { } if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health += heartUnits; } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health += heartUnits; } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index facbbfd818f..d1b767c89b9 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2318,19 +2318,23 @@ u8 Item_Give(PlayState* play, u8 item) { gSaveContext.ship.stats.heartPieces++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART_CONTAINER) { + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health += heartUnits; } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health -= heartUnits; } gSaveContext.ship.stats.heartContainers++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" if (play != NULL) { - Health_ChangeBy(play, 0x10); + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + Health_ChangeBy(play, heartUnits); } return Return_Item(item, MOD_NONE, item); } else if (item == ITEM_MAGIC_SMALL) { @@ -2411,6 +2415,9 @@ u8 Item_Give(PlayState* play, u8 item) { } } } else { + if (item == ITEM_LETTER_RUTO) { + Flags_SetRandomizerInf(RAND_INF_OBTAINED_RUTOS_LETTER); + } for (i = 0; i < 4; i++) { if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) { gSaveContext.inventory.items[temp + i] = item; @@ -2434,6 +2441,9 @@ u8 Item_Give(PlayState* play, u8 item) { } } else { Flags_SetRandomizerInf(item - ITEM_WEIRD_EGG + RAND_INF_CHILD_TRADES_HAS_WEIRD_EGG); + if (item == ITEM_WEIRD_EGG) { + Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG); + } } } @@ -2891,14 +2901,21 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { healthChange *= abs(giDefenseModifier); } } + + if (healthChange < 0) { + healthChange = (f32)healthChange * (f32)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f; + if (healthChange >= 0) + healthChange = -1; + ActorDamageNumber_New(GET_PLAYER(play), -healthChange); + } gSaveContext.health += healthChange; - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; } - heartCount = gSaveContext.health % 0x10; + heartCount = gSaveContext.health / CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; healthLevel = heartCount; if (heartCount != 0) { @@ -3035,7 +3052,7 @@ void Inventory_ChangeAmmo(s16 item, s16 ammoChange) { void Magic_Fill(PlayState* play) { if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; - gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; gSaveContext.magicState = MAGIC_STATE_FILL; } } @@ -3191,7 +3208,7 @@ void Interface_UpdateMagicBar(PlayState* play) { switch (gSaveContext.magicState) { case MAGIC_STATE_STEP_CAPACITY: - temp = gSaveContext.magicLevel * MAGIC_NORMAL_METER; + temp = gSaveContext.magicLevel * gSaveContext.magicUnits; if (gSaveContext.magicCapacity != temp) { if (gSaveContext.magicCapacity < temp) { gSaveContext.magicCapacity += 8; @@ -3219,6 +3236,9 @@ void Interface_UpdateMagicBar(PlayState* play) { // "Storage MAGIC_NOW=%d (%d)" osSyncPrintf("蓄電 MAGIC_NOW=%d (%d)\n", gSaveContext.magic, gSaveContext.magicFillTarget); + if (gSaveContext.magicFillTarget > gSaveContext.magicCapacity) { + gSaveContext.magicFillTarget = gSaveContext.magicCapacity; + } if (gSaveContext.magic >= gSaveContext.magicFillTarget) { gSaveContext.magic = gSaveContext.magicFillTarget; gSaveContext.magicState = gSaveContext.prevMagicState; @@ -3458,7 +3478,7 @@ void Interface_DrawMagicBar(PlayState* play) { s16 rMagicBarX; s16 PosX_MidEnd; s16 rMagicFillX; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) != ORIGINAL_LOCATION) { magicBarY = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0) + Y_Margins; if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == ANCHOR_LEFT) { @@ -3498,7 +3518,8 @@ void Interface_DrawMagicBar(PlayState* play) { } else if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == ANCHOR_TO_LIFE_METER) { magicBarY = R_MAGIC_BAR_SMALL_Y - 2 + - magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity - 1) / (0x10 * lineLength)) + + magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity2 - 1) / + ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength)) + CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0) + getHealthMeterYOffset(); s16 xPushover = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosX"), 0) + getHealthMeterXOffset() + R_MAGIC_BAR_X - 1; @@ -3509,10 +3530,10 @@ void Interface_DrawMagicBar(PlayState* play) { R_MAGIC_FILL_X - 1; } } else { - if ((gSaveContext.healthCapacity - 1) / 0x10 >= lineLength && lineLength != 0) { + if ((gSaveContext.healthCapacity2 - 1) / (CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) >= lineLength && lineLength != 0) { magicBarY = magicBarY_original_l + - magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity - 1) / (0x10 * lineLength) - 1)); + magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity2 - 1) / ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength) - 1)); } else { magicBarY = magicBarY_original_s; } @@ -3593,6 +3614,32 @@ void Interface_DrawMagicBar(PlayState* play) { (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); } + + // Draw Magic Numbers + u32 magicNumbersType = CVarGetInteger("gLeveled.HUD.MagicMeterNumbersType", 0); + + if (magicNumbersType == 0) { + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + Leveled_OverlayValueNumberDraw(play, rMagicFillX + (gSaveContext.magicCapacity >> 1) - 3, magicBarY + 1, gSaveContext.magic, 2, sMagicBorder.r, sMagicBorder.g, sMagicBorder.b, (u8)interfaceCtx->magicAlpha); + Leveled_OverlayValueNumberDraw(play, rMagicFillX + (gSaveContext.magicCapacity >> 1) + 3, magicBarY + 1, gSaveContext.magicCapacity, 0, sMagicBorder.r, sMagicBorder.g, sMagicBorder.b, (u8)interfaceCtx->magicAlpha); + + extern const char* fontTbl[]; + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha); + + OVERLAY_DISP = + Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, rMagicFillX + (gSaveContext.magicCapacity >> 1) - 2, magicBarY + 1, 8, 16, 8 << 7, 16 << 7); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); + + OVERLAY_DISP = + Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, rMagicFillX + (gSaveContext.magicCapacity >> 1) - 2, magicBarY + 1, 8, 16, 8 << 7, 16 << 7); + } else if (magicNumbersType == 1) { + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + Leveled_OverlayValueNumberDraw(play, rMagicFillX + (gSaveContext.magicCapacity >> 1), magicBarY + 1, gSaveContext.magic, 1, sMagicBorder.r, sMagicBorder.g, sMagicBorder.b, (u8)interfaceCtx->magicAlpha); + } } CLOSE_DISPS(play->state.gfxCtx); @@ -4106,7 +4153,8 @@ void Interface_DrawItemButtons(PlayState* play) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { X_Margins_CD = Left_HUD_Margin; }; - C_Down_BTN_Pos[0] = (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + X_Margins_CD); + C_Down_BTN_Pos[0] = + OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + X_Margins_CD); } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_RIGHT) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { X_Margins_CD = Right_HUD_Margin; @@ -5147,7 +5195,7 @@ void Interface_Draw(PlayState* play) { if (pauseCtx->debugState == 0) { Interface_InitVertices(play); func_8008A994(interfaceCtx); - if (fullUi || gSaveContext.health != gSaveContext.healthCapacity) { + if (fullUi || gSaveContext.health != gSaveContext.healthCapacity2) { HealthMeter_Draw(play); } @@ -5405,6 +5453,49 @@ void Interface_Draw(PlayState* play) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); } + // Draw Damage + Actor* currAct = play->actorCtx.actorLists[ACTORCAT_ENEMY].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_MISC].head; + if (currAct != NULL) { + while (currAct != NULL) { + if (currAct->id == ACTOR_EN_REEBA) + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_NPC].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_BOSS].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + ActorDamageNumber_Draw(play, GET_PLAYER(play)); + + // Draw Experience Gain + + ActorExperienceNumber_Draw(play, GET_PLAYER(play)); + + // Draw Level Up + Actor_LevelUpDraw(play, GET_PLAYER(play)); + // Render enemy health bar after Z-target to leverage set variables if (CVarGetInteger(CVAR_ENHANCEMENT("EnemyHealthBar"), 0)) { Interface_DrawEnemyHealthBar(&play->actorCtx.targetCtx, play); @@ -5509,6 +5600,8 @@ void Interface_Draw(PlayState* play) { gDPPipeSync(OVERLAY_DISP++); + Leveled_Interface_DrawNextLevel(play); // Draw next level + // C-Left Button Icon & Ammo Count if (gSaveContext.equips.buttonItems[1] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); @@ -5941,7 +6034,8 @@ void Interface_Draw(PlayState* play) { case 1: D_8015FFE2 = 20; D_8015FFE0 = 20; - gSaveContext.timerSeconds = gSaveContext.health >> 1; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.timerSeconds = (s32)((f32)gSaveContext.health / heartUnits * 8); gSaveContext.timerState = 2; break; case 2: @@ -6621,18 +6715,19 @@ void Interface_Update(PlayState* play) { Map_Update(play); if (gSaveContext.healthAccumulator != 0) { - gSaveContext.healthAccumulator -= 4; - gSaveContext.health += 4; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.healthAccumulator -= heartUnits >> 2; + gSaveContext.health += heartUnits >> 2; - if ((gSaveContext.health & 0xF) < 4) { + if ((gSaveContext.health % (heartUnits)) < heartUnits >> 2) { Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); } osSyncPrintf("now_life=%d max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); - if (gSaveContext.health >= gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health >= gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; osSyncPrintf("S_Private.now_life=%d S_Private.max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); gSaveContext.healthAccumulator = 0; @@ -6779,7 +6874,8 @@ void Interface_Update(PlayState* play) { } if (gSaveContext.timerState == 0) { - if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && ((gSaveContext.health >> 1) != 0)) { + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && (((s32)((f32)gSaveContext.health / heartUnits * 8)) != 0)) { gSaveContext.timerState = 1; gSaveContext.timerX[0] = 140; gSaveContext.timerY[0] = 80; diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index c5c600459e7..9d36e7a33cb 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -681,6 +681,7 @@ void Player_SetEquipmentData(PlayState* play, Player* this) { this->currentSwordItemId = B_BTN_ITEM; Player_SetModelGroup(this, Player_ActionToModelGroup(this, this->heldItemAction)); Player_SetBootData(play, this); + Leveled_SetPlayerModifiedStats(this); } } @@ -787,6 +788,57 @@ s32 Player_GetStrength(void) { } } +void Player_GainExperience(PlayState* play, u16 experience) { + Player* player = GET_PLAYER(play); + + if (player == NULL) + return; + + experience = (u16)(CLAMP(round((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.Rate", (f32)1.0)), 0, 0xffff)); + + bool levelUp = false; + u8 prevPower = player->actor.power; + u8 prevCourage = player->actor.courage; + u16 prevHealthCapacity = gSaveContext.healthCapacity2; + u16 prevMagicUnits = gSaveContext.magicUnits; + + if (player->actor.level == 99) + return; + + if (gSaveContext.experience < 999999) { + if (experience > 0) + gSaveContext.showNeededExpTimer = 60; + + gSaveContext.experience += experience; + ActorExperienceNumber_New(&player->actor, experience); + if (gSaveContext.experience > 999999) + gSaveContext.experience = 999999; + } + + while (GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience) <= 0 && player->actor.level < 99) { + player->actor.level += 1; + if (experience > 0) { + levelUp = true; + } + } + + Actor_RefreshLeveledStats(&player->actor, player); + + if (gSaveContext.magicLevel == 0) { + prevMagicUnits = gSaveContext.magicUnits; + } + + if (levelUp) { + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; + if (CVarGetInteger("gLeveled.HUD.LevelUp", 1) == 1) { + ActorLevelUp_New(&player->actor, player->actor.power - prevPower, player->actor.courage - prevCourage, gSaveContext.healthCapacity2 - prevHealthCapacity, gSaveContext.magicUnits - prevMagicUnits); + } + if (CVarGetInteger("gLeveled.HUD.LevelUpSound", 1) == 1) { + Audio_PlayFanfare(NA_BGM_ITEM_GET); + } + } +} + u8 Player_GetMask(PlayState* play) { Player* this = GET_PLAYER(play); diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 8fc6d1dde00..69928e11044 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -150,9 +150,8 @@ void Sram_OpenSave() { osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); osSyncPrintf(VT_RST); - if (gSaveContext.health < 0x30) { - gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + if (gSaveContext.health < 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) { + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } if (gSaveContext.scarecrowLongSongSet) { diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c index 2e3ad788843..86690fd6a3a 100644 --- a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c @@ -475,7 +475,7 @@ void BgDyYoseizo_HealPlayer_NoReward(BgDyYoseizo* this, PlayState* play) { } if (this->healingTimer == 110) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->refillTimer = 200; } @@ -724,7 +724,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { switch (actionIndex) { case FAIRY_UPGRADE_MAGIC: gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Interface_ChangeAlpha(9); break; case FAIRY_UPGRADE_DOUBLE_MAGIC: @@ -732,7 +732,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Interface_ChangeAlpha(9); break; @@ -743,7 +743,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } if (!this->healing) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; this->healing = true; if (actionIndex == 2) { Magic_Fill(play); @@ -771,7 +771,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } this->itemSpawned = true; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Interface_ChangeAlpha(9); gSaveContext.itemGetInf[1] |= sItemGetFlags[actionIndex]; Item_Give(play, sItemIds[actionIndex]); diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c index 04aff53beaf..692497415e2 100644 --- a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c @@ -6,6 +6,7 @@ #include "z_bg_mori_kaitenkabe.h" #include "objects/object_mori_objects/object_mori_objects.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -97,7 +98,9 @@ void BgMoriKaitenkabe_Wait(BgMoriKaitenkabe* this, PlayState* play) { if ((this->timer > (28 - CVarGetInteger(CVAR_ENHANCEMENT("FasterBlockPush"), 0) * 4)) && !Player_InCsMode(play)) { BgMoriKaitenkabe_SetupRotate(this); - Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8); + } Math_Vec3f_Copy(&this->lockedPlayerPos, &player->actor.world.pos); push.x = Math_SinS(this->dyna.unk_158); push.y = 0.0f; @@ -131,7 +134,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) { Math_StepToF(&this->rotSpeed, 0.6f, 0.02f); if (Math_StepToF(&this->rotYdeg, this->rotDirection * 45.0f, this->rotSpeed)) { BgMoriKaitenkabe_SetupWait(this); - Player_SetCsActionWithHaltedActors(play, thisx, 7); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Player_SetCsActionWithHaltedActors(play, thisx, 7); + } if (this->rotDirection > 0.0f) { thisx->home.rot.y += 0x2000; } else { @@ -148,7 +153,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) { this->dyna.unk_150 = 0.0f; player->stateFlags2 &= ~PLAYER_STATE2_MOVING_DYNAPOLY; } - Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); + } } void BgMoriKaitenkabe_Update(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c index 81542eaad3e..a11180a7b5d 100644 --- a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c @@ -131,8 +131,14 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) { Item_Give(play, ITEM_SWORD_MASTER); } play->csCtx.segment = D_808BB2F0; + + // Discover adult spawn + Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false); } else { play->csCtx.segment = D_808BB7A0; + + // Discover child spawn + Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false); } Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD); 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 acba457abfe..d491cc0a990 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 @@ -329,7 +329,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Animation_PlayLoop(&this->skelAnime, &object_kingdodongo_Anim_00F0D8); this->unk_1F8 = 1.0f; BossDodongo_SetupIntroCutscene(this, play); - this->health = 12; + Actor_GetLevelAndExperience(play, &this->actor, 0); + this->health = GetActorStat_EnemyMaxHealth(12, this->actor.level); this->colorFilterMin = 995.0f; this->actor.colChkInfo.mass = MASS_IMMOVABLE; this->colorFilterMax = 1000.0f; @@ -337,6 +338,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { this->unk_228 = 9200.0f; Collider_InitJntSph(play, &this->collider); Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items); + for (u8 i = 0; i < this->collider.count; i++) + this->collider.elements[i].info.toucher.damage <<= 1; // Double fire damage (to 1 heart) if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead // SOH [General] @@ -733,7 +736,9 @@ void BossDodongo_Explode(BossDodongo* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); func_80033E88(&this->actor, play, 4, 10); - this->health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + this->health -= damage; + ActorDamageNumber_New(&this->actor, damage); // make sure not to die from the bomb explosion if (this->health <= 0) { @@ -1509,12 +1514,14 @@ void BossDodongo_UpdateDamage(BossDodongo* this, PlayState* play) { item1 = this->collider.elements[0].info.acHitInfo; if ((this->actionFunc == BossDodongo_Vulnerable) || (this->actionFunc == BossDodongo_LayDown)) { swordDamage = damage = CollisionCheck_GetSwordDamage(item1->toucher.dmgFlags, play); + swordDamage = Leveled_DamageModify(&this->actor, this->collider.base.actor, swordDamage * Leveled_GetHealthAttackMultiplier()); if (damage != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); BossDodongo_SetupDamaged(this); this->unk_1C0 = 5; this->health -= swordDamage; + ActorDamageNumber_New(&this->actor, swordDamage); } } } diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c index 249b3afe965..807c4a05018 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -1295,12 +1295,14 @@ void BossFd_CollisionCheck(BossFd* this, PlayState* play) { if (headCollider->info.bumperFlags & BUMP_HIT) { headCollider->info.bumperFlags &= ~BUMP_HIT; hurtbox = headCollider->info.acHitInfo; - this->actor.colChkInfo.health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + if (hurtbox->toucher.dmgFlags & 0x1000) { - this->actor.colChkInfo.health -= 2; - } - if ((s8)this->actor.colChkInfo.health <= 2) { - this->actor.colChkInfo.health = 2; + if (this->actor.colChkInfo.health + 1 >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 2; + } } this->work[BFD_DAMAGE_FLASH_TIMER] = 10; this->work[BFD_INVINC_TIMER] = 20; @@ -1486,7 +1488,8 @@ void BossFd_UpdateEffects(BossFd* this, PlayState* play) { diff.z = player->actor.world.pos.z - effect->pos.z; if ((this->timers[3] == 0) && (sqrtf(SQ(diff.x) + SQ(diff.y) + SQ(diff.z)) < 20.0f)) { this->timers[3] = 50; - func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, 0x30); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 0x30); + func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, damage); if (player->bodyIsBurning == false) { for (i2 = 0; i2 < ARRAY_COUNT(player->bodyFlameTimers); i2++) { player->bodyFlameTimers[i2] = Rand_S16Offset(0, 200); diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c index 0299016b9c5..0097b74927f 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -207,7 +207,7 @@ void BossFd2_Destroy(Actor* thisx, PlayState* play) { void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; s16 temp_rand; - s8 health; + u16 health; osSyncPrintf("UP INIT 1\n"); Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaEmergeAnim); @@ -221,11 +221,11 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { this->timers[0] = 10; if (bossFd != NULL) { health = bossFd->actor.colChkInfo.health; - if (health >= 18) { + if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 0; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 1; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 2; } else { this->work[FD2_FAKEOUT_COUNT] = 3; @@ -234,7 +234,7 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { } void BossFd2_Emerge(BossFd2* this, PlayState* play) { - s8 health; + u16 health; BossFd* bossFd = (BossFd*)this->actor.parent; Player* player = GET_PLAYER(play); s16 i; @@ -258,13 +258,13 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { this->work[FD2_HOLE_COUNTER]++; this->actor.world.pos.y = -200.0f; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { holeTime = 30; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { holeTime = 25; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { holeTime = 20; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { holeTime = 10; } else { holeTime = 5; @@ -317,20 +317,20 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { void BossFd2_SetupIdle(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; - s8 health; + u16 health; s16 idleTime; osSyncPrintf("UP INIT 1\n"); Animation_PlayLoop(&this->skelAnime, &gHoleVolvagiaTurnAnim); this->actionFunc = BossFd2_Idle; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { idleTime = 50; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { idleTime = 40; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { idleTime = 40; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { idleTime = 30; } else { idleTime = 20; @@ -384,7 +384,8 @@ void BossFd2_Burrow(BossFd2* this, PlayState* play) { } else { Math_ApproachF(&this->actor.world.pos.y, -100.0f, 1.0f, 10.0f); if (this->timers[0] == 0) { - if ((this->work[FD2_HOLE_COUNTER] >= 3) && ((s8)bossFd->actor.colChkInfo.health < 24)) { + if ((this->work[FD2_HOLE_COUNTER] >= 3) && + (bossFd->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(24, this->actor.level))) { this->work[FD2_HOLE_COUNTER] = 0; this->actionFunc = BossFd2_Wait; bossFd->handoffSignal = FD2_SIGNAL_FLY; @@ -844,8 +845,12 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { hurtbox = this->collider.elements[0].info.acHitInfo; if (!bossFd->faceExposed) { if (hurtbox->toucher.dmgFlags & 0x40000040) { - bossFd->actor.colChkInfo.health -= 2; - if ((s8)bossFd->actor.colChkInfo.health <= 2) { + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (bossFd->actor.colChkInfo.health + 1 > damage) { + bossFd->actor.colChkInfo.health -= damage; + } else { bossFd->actor.colChkInfo.health = 1; } bossFd->faceExposed = true; @@ -872,7 +877,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { } } else { u8 canKill = false; - u8 damage; + u16 damage; if ((damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play)) == 0) { damage = (hurtbox->toucher.dmgFlags & 0x00001000) ? 4 : 2; @@ -882,15 +887,22 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { if (hurtbox->toucher.dmgFlags & 0x80) { damage = 0; } - if (((s8)bossFd->actor.colChkInfo.health > 2) || canKill) { - bossFd->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + + if ((bossFd->actor.colChkInfo.health > 2) || canKill) { + if (bossFd->actor.colChkInfo.health >= damage) { + ActorDamageNumber_New(&this->actor, damage); + bossFd->actor.colChkInfo.health -= damage; + } else { + bossFd->actor.colChkInfo.health = 0; + } osSyncPrintf(VT_FGCOL(GREEN)); osSyncPrintf("damage %d\n", damage); } osSyncPrintf(VT_RST); osSyncPrintf("hp %d\n", bossFd->actor.colChkInfo.health); - if ((s8)bossFd->actor.colChkInfo.health <= 0) { + if (bossFd->actor.colChkInfo.health <= 0) { bossFd->actor.colChkInfo.health = 0; BossFd2_SetupDeath(this, play); this->work[FD2_DAMAGE_FLASH_TIMER] = 10; diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index f845f323a20..4cfc1ec2d3a 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -584,7 +584,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { this->unk_198 = 2; this->timers[2] = 110; if (GameInteractor_Should(VB_GANON_HEAL_BEFORE_FIGHT, true)) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } Audio_QueueSeqCmd(NA_BGM_STOP); } else { @@ -800,7 +800,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { } if (this->csTimer == 25) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } if (this->csTimer == 100) { @@ -1218,7 +1218,7 @@ void BossGanon_SetupTowerCutscene(BossGanon* this, PlayState* play) { this->csState = 100; this->unk_198 = 1; gSaveContext.magic = gSaveContext.magicCapacity; - gSaveContext.health = gSaveContext.healthCapacity; + gSaveContext.health = gSaveContext.healthCapacity2; } else { this->actionFunc = BossGanon_SetupTowerCutscene; } @@ -2277,7 +2277,7 @@ void BossGanon_Wait(BossGanon* this, PlayState* play) { } else if ((this->timers[0] == 0) && !(player->stateFlags1 & PLAYER_STATE1_HANGING_OFF_LEDGE)) { this->timers[0] = (s16)Rand_ZeroFloat(30.0f) + 30; - if ((s8)this->actor.colChkInfo.health >= 20) { + if (this->actor.colChkInfo.health >= GetActorStat_EnemyMaxHealth(20, this->actor.level)) { BossGanon_SetupChargeLightBall(this, play); } else if (Rand_ZeroOne() >= 0.5f) { if ((Rand_ZeroOne() >= 0.5f) || (this->actor.xzDistToPlayer > 350.0f)) { @@ -2769,7 +2769,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } else if ((this->actionFunc == BossGanon_Vulnerable) && (this->unk_1C2 >= 3)) { if (!(acHitInfo->toucher.dmgFlags & 0x80)) { u8 hitWithSword = false; - u8 damage; + u16 damage; Vec3f sp50; u32 flags; @@ -2789,8 +2789,21 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { hitWithSword = true; } - if (((s8)this->actor.colChkInfo.health >= 3) || hitWithSword) { - this->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (hitWithSword) { + if (this->actor.colChkInfo.health >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + } else { + if (this->actor.colChkInfo.health > damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; + } } for (i = 0; i < ARRAY_COUNT(sBossGanonCape->strands); i++) { @@ -2800,7 +2813,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanon_SetupDeathCutscene(this, play); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DEAD); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DD_THUNDER); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index 1799dafd0c4..18a0e6fd53c 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -170,7 +170,7 @@ void BossGanon2_Init(Actor* thisx, PlayState* play) { } this->actor.colChkInfo.mass = MASS_IMMOVABLE; - this->actor.colChkInfo.health = 30; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(30, this->actor.level); Collider_InitJntSph(play, &this->unk_424); Collider_SetJntSph(play, &this->unk_424, &this->actor, &sJntSphInit1, this->unk_464); Collider_InitJntSph(play, &this->unk_444); @@ -1464,7 +1464,7 @@ void func_80900890(BossGanon2* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { func_808FFDB0(this, play); if (this->unk_334 == 0) { - this->actor.colChkInfo.health = 25; + this->actor.colChkInfo.health = (u16)(this->actor.maximumHealth * (25.0f / 30.0f)); } this->unk_336 = 1; } @@ -1931,10 +1931,10 @@ void func_80902348(BossGanon2* this, PlayState* play) { } void func_80902524(BossGanon2* this, PlayState* play) { - s8 temp_v0_4; + u16 temp_v0_4; ColliderInfo* acHitInfo; s16 i; - u8 phi_v1_2; + u16 phi_v1_2; osSyncPrintf("this->no_hit_time %d\n", this->unk_316); if (this->unk_316 != 0 || ((this->unk_334 == 0) && (this->actionFunc == func_80900890))) { @@ -1959,9 +1959,15 @@ void func_80902524(BossGanon2* this, PlayState* play) { this->unk_342 = 5; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); - this->actor.colChkInfo.health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + if (this->actor.colChkInfo.health >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; + } + ActorDamageNumber_New(&this->actor, damage); temp_v0_4 = this->actor.colChkInfo.health; - if (temp_v0_4 < 0x15 && this->unk_334 == 0) { + if (temp_v0_4 < (u16)(this->actor.maximumHealth * (21.0f / 30.0f)) && this->unk_334 == 0) { func_80900818(this, play); } else { if (temp_v0_4 <= 0) { @@ -1993,11 +1999,20 @@ void func_80902524(BossGanon2* this, PlayState* play) { phi_v1_2 = 2; } } - this->actor.colChkInfo.health -= phi_v1_2; + u8 baseDamage = phi_v1_2; + + phi_v1_2 = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, phi_v1_2 * Leveled_GetHealthAttackMultiplier()); + if (phi_v1_2 <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= phi_v1_2; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, phi_v1_2); + temp_v0_4 = this->actor.colChkInfo.health; - if ((temp_v0_4 < 0x15) && (this->unk_334 == 0)) { + if ((temp_v0_4 < (u16)(this->actor.maximumHealth * (21.0f / 30.0f))) && (this->unk_334 == 0)) { func_80900818(this, play); - } else if ((temp_v0_4 <= 0) && (phi_v1_2 >= 2)) { + } else if ((temp_v0_4 <= 0) && (baseDamage >= 2)) { func_80901020(this, play); } else { if (temp_v0_4 <= 0) { diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c index 8f74d05a851..a18ef5409cc 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c @@ -458,7 +458,8 @@ void BossGanondrof_Neutral(BossGanondrof* this, PlayState* play) { if (this->timers[0] == 0) { this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; rand01 = Rand_ZeroOne(); - if (thisx->colChkInfo.health < 5) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(5, this->actor.level); + if (thisx->colChkInfo.health < healthCheck) { if (rand01 < 0.25f) { BossGanondrof_SetupThrow(this, play); } else if (rand01 >= 0.8f) { @@ -629,8 +630,10 @@ void BossGanondrof_Throw(BossGanondrof* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { EnfHG* horseTemp = (EnfHG*)this->actor.child; - Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, + Actor* fireTemp = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, this->work[GND_ACTION_STATE], 0, 0, FHGFIRE_ENERGY_BALL); + fireTemp->level = this->actor.level; + Actor_RefreshLeveledStats(fireTemp, GET_PLAYER(play)); this->actor.child = &horseTemp->actor; } @@ -1199,7 +1202,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { this->colliderBody.base.acFlags &= ~AC_HIT; } else { acHit = this->colliderBody.base.acFlags & AC_HIT; - if ((acHit && ((s8)this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { + if ((acHit && (this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { if (acHit) { this->colliderBody.base.acFlags &= ~AC_HIT; hurtbox = this->colliderBody.info.acHitInfo; @@ -1210,7 +1213,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { osSyncPrintf("hit != 0 \n"); } else if (this->actionFunc != BossGanondrof_Charge) { if (this->returnCount == 0) { - u8 dmg; + u16 dmg; u8 canKill = false; s32 dmgFlags = hurtbox->toucher.dmgFlags; @@ -1219,11 +1222,21 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } dmg = CollisionCheck_GetSwordDamage(dmgFlags, play); (dmg == 0) ? (dmg = 2) : (canKill = true); - if (((s8)this->actor.colChkInfo.health > 2) || canKill) { - this->actor.colChkInfo.health -= dmg; + + dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, dmg * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, dmg); + + if ((this->actor.colChkInfo.health > 2) || canKill) { + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else if (this->actor.colChkInfo.health > 2) { + this->actor.colChkInfo.health = 1; + } else { + this->actor.colChkInfo.health = 0; + } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanondrof_SetupDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); GameInteractor_ExecuteOnBossDefeat(&this->actor); @@ -1242,7 +1255,15 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } } else if (acHit && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { this->work[GND_INVINC_TIMER] = 10; - this->actor.colChkInfo.health -= 2; + + u16 dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, dmg); + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else { + this->actor.colChkInfo.health = 0; + } + horse->hitTimer = 20; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); } diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c index 503e4147247..3caa68497a4 100644 --- a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c @@ -1837,9 +1837,16 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM2); } else if (this->actionFunc == BossGoma_FloorStunned && (damage = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags, play)) != 0) { - this->actor.colChkInfo.health -= damage; - if ((s8)this->actor.colChkInfo.health > 0) { + damage = Leveled_DamageModify(&this->actor, this->collider.elements[0].info.acHit->actor, damage * Leveled_GetHealthAttackMultiplier()); + if (damage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, damage); + + if (this->actor.colChkInfo.health > 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM1); BossGoma_SetupFloorDamaged(this); EffectSsSibuki_SpawnBurst(play, &this->actor.focus.pos); diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index b80c39f3b27..89207705990 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -1783,7 +1783,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { // "hit 2 !!" osSyncPrintf("Core_Damage_check 当り 2 !!\n"); if ((this->work[MO_TENT_ACTION_STATE] != MO_CORE_UNDERWATER) && (this->work[MO_TENT_INVINC_TIMER] == 0)) { - u8 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); + u16 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); if ((damage != 0) && (this->work[MO_TENT_ACTION_STATE] < MO_CORE_ATTACK)) { // "sword hit !!" @@ -1791,14 +1791,21 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { this->work[MO_TENT_ACTION_STATE] = MO_CORE_STUNNED; this->timers[0] = 25; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + this->actor.speedXZ = 15.0f; this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; this->work[MO_CORE_DMG_FLASH_TIMER] = 15; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_DAMAGE); - this->actor.colChkInfo.health -= damage; + if (damage < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } this->hitCount++; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) || ((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) { Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index 6e1db9049a4..07da8364bd1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -1895,8 +1895,9 @@ void BossSst_HandCrush(BossSst* this, PlayState* play) { } else { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - - play->damagePlayer(play, -8); + + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } if (Animation_OnFrame(&this->skelAnime, 0.0f)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CATCH); diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index 1fee8de966f..cb7fb5af9d1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -31,7 +31,7 @@ void BossTw_Update(Actor* thisx, PlayState* play); void BossTw_Draw(Actor* thisx, PlayState* play); void BossTw_Reset(void); -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 arg2); +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 arg2); void BossTw_TwinrovaSetupFly(BossTw* this, PlayState* play); void BossTw_DrawEffects(PlayState* play); void BossTw_TwinrovaLaugh(BossTw* this, PlayState* play); @@ -3091,7 +3091,7 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { if (info->toucher.dmgFlags & (DMG_SLINGSHOT | DMG_ARROW)) {} } } else if (this->collider.base.acFlags & AC_HIT) { - u8 damage; + u16 damage; u8 swordDamage; ColliderInfo* info = this->collider.info.acHitInfo; @@ -3105,8 +3105,11 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { swordDamage = true; } + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + if (!(info->toucher.dmgFlags & DMG_HOOKSHOT)) { - if (((s8)this->actor.colChkInfo.health < 3) && !swordDamage) { + if ((this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(3, this->actor.level)) && !swordDamage) { damage = 0; } @@ -5141,7 +5144,7 @@ void BossTw_TwinrovaChargeBlast(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { - if ((s8)this->actor.colChkInfo.health < 10) { + if (this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(10, this->actor.level)) { sTwinrovaBlastType = Rand_ZeroFloat(1.99f); } else { if (++sFixedBlatSeq >= 4) { @@ -5241,7 +5244,7 @@ void BossTw_TwinrovaDoneBlastShoot(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); } -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 damage) { if (this->actionFunc != BossTw_TwinrovaStun) { Animation_MorphToPlayOnce(&this->skelAnime, &gTwinrovaChargedAttackHitAnim, -15.0f); this->timers[0] = 150; @@ -5255,11 +5258,13 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&gTwinrovaDamageAnim); this->csState1 = 1; - if ((s8)(this->actor.colChkInfo.health -= damage) < 0) { + if (damage >= this->actor.colChkInfo.health) { this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossTw_TwinrovaSetupDeathCS(this, play); Enemy_StartFinishingBlow(play, &this->actor); Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD); diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index 3e8f05776e6..7bde37a38ce 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -403,7 +403,7 @@ static s16 sDoorState; static u8 sPhase3StopMoving; static Vec3s sZapperRot; static u16 sPhase2Timer; -static s8 sPhase4HP; +static u16 sPhase4HP; void BossVa_SetupAction(BossVa* this, BossVaActionFunc func) { this->actionFunc = func; @@ -1362,7 +1362,7 @@ void BossVa_SetupBodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; this->timer2 = (s16)(Rand_ZeroOne() * 150.0f) + 300; sBodyState = 1; - sPhase4HP = 4; + sPhase4HP = GetActorStat_EnemyMaxHealth(4, this->actor.level); if (this->actor.shape.yOffset != 0.0f) { this->timer = -30; } @@ -1400,11 +1400,21 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_DAMAGE); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 12); - sPhase4HP -= this->actor.colChkInfo.damage; + + u16 dmg = this->actor.colChkInfo.damage; + + if (dmg > sPhase4HP) { + sPhase4HP = 0; + } else { + sPhase4HP -= dmg; + } + + ActorDamageNumber_New(&this->actor, dmg); + if (sPhase4HP <= 0) { this->timer = 0; sFightPhase++; - sPhase4HP += 3; + sPhase4HP += GetActorStat_EnemyMaxHealth(3, this->actor.level); if (sFightPhase >= PHASE_DEATH) { BossVa_SetupBodyDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c index ad1c76a3dde..3d50bf3303b 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c @@ -7,6 +7,7 @@ #include "z_demo_kekkai.h" #include "objects/object_demo_kekkai/object_demo_kekkai.h" #include "scenes/dungeons/ganontika/ganontika_scene.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/ResourceManagerHelpers.h" #define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED) @@ -257,13 +258,15 @@ void DemoKekkai_TrialBarrierIdle(Actor* thisx, PlayState* play) { CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider1.base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider1.base); if (this->collider2.base.acFlags & AC_HIT) { - Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); - // "I got it" - LOG_STRING("当ったよ"); - this->actor.update = DemoKekkai_TrialBarrierDispel; - this->timer = 0; - play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); - gSaveContext.cutsceneTrigger = 1; + if (GameInteractor_Should(VB_PLAY_DISPEL_BARRIER_CS, true, this)) { + Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); + // "I got it" + LOG_STRING("当ったよ"); + this->actor.update = DemoKekkai_TrialBarrierDispel; + this->timer = 0; + play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); + gSaveContext.cutsceneTrigger = 1; + } } CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider2.base); func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG); diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h index 4e4a06c181b..a8df145c2fa 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h @@ -6,7 +6,7 @@ struct DemoKekkai; -typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* this, PlayState* play); +typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* thisx, PlayState* play); typedef struct DemoKekkai { /* 0x0000 */ Actor actor; diff --git a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c index 4f16a06083d..2763cd168bd 100644 --- a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c +++ b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c @@ -408,6 +408,7 @@ void func_809B75A0(EnBa* this, PlayState* play2) { Matrix_MultVec3f(&sp74, &this->unk_158[i + 1]); } this->unk_31A = 15; + Player_GainExperience(play, this->actor.exp); EnBa_SetupAction(this, EnBa_Die); } @@ -453,7 +454,14 @@ void EnBa_Update(Actor* thisx, PlayState* play) { if ((this->actor.params < EN_BA_DEAD_BLOB) && (this->collider.base.acFlags & 2)) { this->collider.base.acFlags &= ~2; - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (damage > this->actor.colChkInfo.health) { + this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; + } if (this->actor.colChkInfo.health == 0) { func_809B75A0(this, play); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c index 424f94a1c2d..c75d6061445 100644 --- a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c +++ b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c @@ -533,7 +533,9 @@ void EnBb_Damage(EnBb* this, PlayState* play) { Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); if (this->actor.speedXZ == 0.0f) { this->actor.shape.yOffset = 200.0f; - EnBb_SetupDown(this); + if (this->actor.params > ENBB_GREEN) { + EnBb_SetupDown(this); + } } } @@ -1187,7 +1189,7 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { if ((this->action != BB_DOWN) || (this->timer < 190)) { Actor_ApplyDamage(&this->actor); } - if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE)) { + if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE) && (this->actor.params > ENBB_GREEN)) { EnBb_SetupDown(this); } } else { @@ -1204,13 +1206,13 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { EnBb_KillFlameTrail(this); } EnBb_SetupDeath(this, play); + Player_GainExperience(play, this->actor.exp); //! @bug //! Because Din's Fire kills the bubble in a single hit, Actor_SetColorFilter is never called and //! colorFilterParams is never set. And because Din's Fire halts updating during its cutscene, //! EnBb_Death doesn't kill the bubble on the next frame like it should. This combines with //! the bug in EnBb_Draw below to crash the game. - } else if ((this->actor.params == ENBB_WHITE) && - ((this->action == BB_WHITE) || (this->action == BB_STUNNED))) { + } else if ((this->actor.params == ENBB_WHITE) && ((this->action == BB_WHITE) || (this->action == BB_STUNNED)) || (this->actor.params <= ENBB_GREEN)) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); this->actor.speedXZ = -8.0f; this->maxSpeed = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c index d06a234baa1..e85e2dbbfeb 100644 --- a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c +++ b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c @@ -181,7 +181,8 @@ void func_809BC598(EnBdfire* this, PlayState* play) { player->bodyFlameTimers[i] = Rand_S16Offset(0, 200); } player->bodyIsBurning = true; - func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 8); + // Buffed damage from 8 to 16. + func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 16); osSyncPrintf("POWER\n"); } } diff --git a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c index 368bf865b25..d5f947971b2 100644 --- a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c +++ b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c @@ -178,6 +178,7 @@ void EnBigokuta_Init(Actor* thisx, PlayState* play) { } CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, sColChkInfoInit); + Actor_GetLevelAndExperience(play, &this->actor, ACTOR_EN_BIGOKUTA); this->unk_194 = 1; diff --git a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c index d8ba3877386..01756d1c7f3 100644 --- a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c +++ b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c @@ -565,6 +565,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; } + if (this->actor.colChkInfo.health > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_DAMAGE); + } + damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == BIRI_DMGEFF_DEKUNUT) { @@ -580,7 +584,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } EnBili_SetupDischargeLightning(this); } else { - EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } } else if (damageEffect == BIRI_DMGEFF_FIRE) { EnBili_SetupBurnt(this); @@ -590,7 +597,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } else if (damageEffect == BIRI_DMGEFF_SLINGSHOT) { EnBili_SetupRecoil(this); } else { - EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F820) { // DMG_ARROW diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c index 712a9008d89..561c8f97dc2 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c @@ -206,7 +206,7 @@ void EnBomBowlPit_Reset(EnBomBowlPit* this, PlayState* play) { // "Normal termination"/"completion" osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); if (this->getItemId == GI_HEART_PIECE) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; // "Ah recovery!" (?) osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ あぁ回復! ☆☆☆☆☆ \n" VT_RST); } diff --git a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c index 6717b9788f8..4029ae466e6 100644 --- a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c +++ b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c @@ -116,7 +116,7 @@ u32 func_809CBCEC(EnBubble* this) { } void EnBubble_DamagePlayer(EnBubble* this, PlayState* play) { - s32 damage = -this->colliderSphere.elements[0].info.toucher.damage; + s32 damage = -Leveled_DamageModify(&GET_PLAYER(play)->actor, &this->actor, this->colliderSphere.elements[0].info.toucher.damage); play->damagePlayer(play, damage); func_8002F7A0(play, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); diff --git a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c index 38d3a9bf11c..1feb1aadd8f 100644 --- a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c +++ b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c @@ -675,6 +675,7 @@ void func_809D0424(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } @@ -718,6 +719,7 @@ void func_809D0584(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } else if ((this->unk_220 != 1) && (this->unk_220 != 6)) { diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c index 0b4f3bf0a55..3bc12bddfb1 100644 --- a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -372,10 +372,19 @@ void EnClearTag_Update(Actor* thisx, PlayState* play2) { this->acceleration.z = Rand_CenteredFloat(15.0f); Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND); - this->actor.colChkInfo.health--; - if ((s8)this->actor.colChkInfo.health <= 0) { + + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + if (damage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, damage); + + if (this->actor.colChkInfo.health <= 0) { this->state = CLEAR_TAG_STATE_CRASHING; this->actor.velocity.y = 0.0f; + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); goto state_crashing; } diff --git a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c index 7ab5fbd8410..99008b32edd 100644 --- a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c +++ b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c @@ -413,7 +413,7 @@ void EnCrow_Respawn(EnCrow* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, target, target * 0.1f)) { this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; this->actor.flags &= ~ACTOR_FLAG_UPDATE_CULLING_DISABLED; - this->actor.colChkInfo.health = 1; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(1, this->actor.level); EnCrow_SetupFlyIdle(this); } this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; @@ -428,10 +428,17 @@ void EnCrow_UpdateDamage(EnCrow* this, PlayState* play) { if (this->actor.colChkInfo.damageEffect == 1) { // Deku Nuts EnCrow_SetupTurnAway(this); } else { - Actor_ApplyDamage(&this->actor); - this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; - Enemy_StartFinishingBlow(play, &this->actor); - EnCrow_SetupDamaged(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + Enemy_StartFinishingBlow(play, &this->actor); + EnCrow_SetupDamaged(this, play); + } else { + EnCrow_SetupFlyIdle(this); + this->timer = (s16)Rand_ZeroFloat(25) + 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_DAMAGE); + this->actor.flags |= ACTOR_FLAG_UPDATE_CULLING_DISABLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); + } } } } diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c index b84103a4a0b..f42176016ec 100644 --- a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c @@ -1047,6 +1047,7 @@ void EnDekubaba_UpdateDamage(EnDekubaba* this, PlayState* play) { ((this->actor.colChkInfo.damageEffect != DEKUBABA_DMGEFF_NONE) || (this->actor.colChkInfo.damage != 0))) { phi_s0 = this->actor.colChkInfo.health - this->actor.colChkInfo.damage; + ActorDamageNumber_New(&this->actor, this->actor.colChkInfo.damage); if (this->actionFunc != EnDekubaba_StunnedVertical) { if ((this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_BOOMERANG) || diff --git a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c index ac2b5f7ee11..9acf20c5383 100644 --- a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c +++ b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c @@ -407,7 +407,11 @@ void EnDekunuts_Gasp(EnDekunuts* this, PlayState* play) { void EnDekunuts_BeDamaged(EnDekunuts* this, PlayState* play) { Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); if (SkelAnime_Update(&this->skelAnime)) { - EnDekunuts_SetupDie(this); + if (this->actor.colChkInfo.health == 0) { + EnDekunuts_SetupDie(this); + } else { + EnDekunuts_SetupRun(this); + } } } diff --git a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c index 5f09f29875e..685fe347998 100644 --- a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c +++ b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c @@ -493,9 +493,10 @@ void EnDh_CollisionCheck(EnDh* this, PlayState* play) { EnDh_SetupDeath(this); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { - if (((lastHealth >= 15) && (this->actor.colChkInfo.health < 15)) || - ((lastHealth >= 9) && (this->actor.colChkInfo.health < 9)) || - ((lastHealth >= 3) && (this->actor.colChkInfo.health < 3))) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(3, this->actor.level); + if (((lastHealth >= healthCheck * 5) && (this->actor.colChkInfo.health < healthCheck * 5)) || + ((lastHealth >= healthCheck * 3) && (this->actor.colChkInfo.health < healthCheck * 3)) || + ((lastHealth >= healthCheck) && (this->actor.colChkInfo.health < healthCheck))) { this->retreat++; } diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c index 07a244cd3cc..c336c8b8f06 100644 --- a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c @@ -665,6 +665,7 @@ void EnDodongo_SetupDeath(EnDodongo* this, PlayState* play) { Animation_MorphToPlayOnce(&this->skelAnime, &gDodongoDieAnim, -8.0f); this->timer = 0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_DEAD); + Player_GainExperience(play, this->actor.exp); this->actionState = DODONGO_DEATH; this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; this->actor.speedXZ = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c index 96a9297b969..1632ffd3b72 100644 --- a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -636,7 +636,7 @@ void func_80A0329C(EnElf* this, PlayState* play) { if ((heightDiff > 0.0f) && (heightDiff < 60.0f)) { if (!func_80A01F90(&this->actor.world.pos, &refActor->actor.world.pos, 10.0f)) { if (GameInteractor_Should(VB_FAIRY_HEAL, true, this)) { - Health_ChangeBy(play, 128); + Health_ChangeBy(play, (CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) << 3); } if (this->fairyFlags & FAIRY_FLAG_BIG) { Magic_Fill(play); diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c index a59c736afe2..41b5462e15f 100644 --- a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c @@ -224,10 +224,17 @@ s32 EnFd_SpawnCore(EnFd* this, PlayState* play) { return false; } - this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % 8; + u16 coreMaximumHealth = this->actor.maximumHealth / 3; + if (coreMaximumHealth * 3 != this->actor.maximumHealth) { + this->actor.colChkInfo.health = coreMaximumHealth * 3; + this->actor.maximumHealth = this->actor.colChkInfo.health; + } + + this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % coreMaximumHealth; + this->actor.child->maximumHealth = coreMaximumHealth; if (this->actor.child->colChkInfo.health == 0) { - this->actor.child->colChkInfo.health = 8; + this->actor.child->colChkInfo.health = coreMaximumHealth; } if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) { @@ -781,7 +788,7 @@ void EnFd_Draw(Actor* thisx, PlayState* play) { Matrix_Pop(); if (this->actionFunc != EnFd_Reappear && !(this->fadeAlpha < 0.9f)) { Gfx_SetupDL_25Xlu(play->state.gfxCtx); - clampedHealth = CLAMP(thisx->colChkInfo.health - 1, 0, 23); + clampedHealth = CLAMP((s32)((f32)thisx->colChkInfo.health / thisx->maximumHealth * 23), 0, 23); gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, primColors[clampedHealth / 8].r, primColors[clampedHealth / 8].g, primColors[clampedHealth / 8].b, (u8)this->fadeAlpha); gDPSetEnvColor(POLY_XLU_DISP++, envColors[clampedHealth / 8].r, envColors[clampedHealth / 8].g, diff --git a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c index ced99da23e2..9333bb5dbd0 100644 --- a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c +++ b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c @@ -630,20 +630,18 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { this->collider.base.acFlags &= ~AC_HIT; Actor_SetDropFlag(&this->actor, &this->collider.elements[0].info, 1); - if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { - if (Actor_ApplyDamage(&this->actor) == 0) { - Enemy_StartFinishingBlow(play, &this->actor); - this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; - } + if ((this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) && + ((this->actor.colorFilterTimer == 0) || ((this->actor.colorFilterParams & 0x4000) == 0))) { damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == 2) { // Din's Fire if (this->actor.params == KEESE_ICE_FLY) { - this->actor.colChkInfo.health = 0; - Enemy_StartFinishingBlow(play, &this->actor); - EnFirefly_Combust(this, play); - EnFirefly_SetupFall(this); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + EnFirefly_Combust(this, play); + EnFirefly_SetupFall(this); + } } else if (!this->onFire) { EnFirefly_Ignite(this); if (this->actionFunc == EnFirefly_Perch) { @@ -654,17 +652,38 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { if (this->actor.params == KEESE_ICE_FLY) { EnFirefly_SetupFall(this); } else { - EnFirefly_SetupFrozenFall(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + EnFirefly_SetupFrozenFall(this, play); + } } } else if (damageEffect == 1) { // Deku Nuts if (this->actionFunc != EnFirefly_Stunned) { EnFirefly_SetupStunned(this); } - } else { // Fire Arrows - if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { + } else if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { // Fire Arrows + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); EnFirefly_Combust(this, play); + } else { + EnFirefly_SetupFall(this); + } + } else { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + EnFirefly_SetupFall(this); + } + } + + if (this->actor.colChkInfo.health > 0 && damageEffect != 1) { + if (this->actionFunc == EnFirefly_Perch) { + EnFirefly_SetupFlyIdle(this); } - EnFirefly_SetupFall(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_DEAD); + this->actor.flags |= ACTOR_FLAG_UPDATE_CULLING_DISABLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); } } } diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c index b76f1fd9f09..5a667b05b97 100644 --- a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c @@ -740,6 +740,11 @@ void EnFloormas_SmDecideAction(EnFloormas* this, PlayState* play) { void EnFloormas_SmShrink(EnFloormas* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.0015f)) { EnFloormas_SetupSmWait(this); + EnFloormas* parent = (EnFloormas*)this->actor.parent; + EnFloormas* child = (EnFloormas*)this->actor.child; + if ((parent->actionFunc == EnFloormas_SmWait) && (child->actionFunc == EnFloormas_SmWait)) { + Player_GainExperience(play, this->actor.exp); + } } this->actor.scale.z = this->actor.scale.x; this->actor.scale.y = this->actor.scale.x; @@ -823,7 +828,8 @@ void EnFloormas_GrabLink(EnFloormas* this, PlayState* play) { } else { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } } diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c index 1cbe3bed2e7..63b6b6437a4 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -153,12 +153,23 @@ s32 EnFw_CheckCollider(EnFw* this, PlayState* play) { this->lastDmgHook = false; } this->collider.base.acFlags &= ~AC_HIT; - if (Actor_ApplyDamage(&this->actor) <= 0) { - if (this->actor.parent->colChkInfo.health <= 8) { + + u16 extraDamage = this->actor.maximumHealth / 9; + + if (this->actor.colChkInfo.damage + extraDamage >= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= this->actor.colChkInfo.damage + extraDamage; + } + + ActorDamageNumber_New(&this->actor, this->actor.colChkInfo.damage + extraDamage); + + if (this->actor.colChkInfo.health <= 0) { + if (this->actor.parent->colChkInfo.health <= this->actor.maximumHealth) { Enemy_StartFinishingBlow(play, &this->actor); this->actor.parent->colChkInfo.health = 0; } else { - this->actor.parent->colChkInfo.health -= 8; + this->actor.parent->colChkInfo.health -= this->actor.maximumHealth; } this->returnToParentTimer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c index 217afe31f29..1ebeb7999bb 100644 --- a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c @@ -365,6 +365,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { vec.z = this->actor.world.pos.z; EnFz_Damaged(this, play, &vec, 30, 10.0f); EnFz_SetupDespawn(this, play); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } } @@ -373,6 +374,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8); if (this->actor.colChkInfo.health == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD); + Player_GainExperience(play, this->actor.exp); EnFz_SetupMelt(this); } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE); @@ -718,17 +720,18 @@ void EnFz_Draw(Actor* thisx, PlayState* play) { EnFz* this = (EnFz*)thisx; s32 pad; s32 index; + + index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth * 5 + 0.99999f), 0, 6)) >> 1; - index = (6 - this->actor.colChkInfo.health) >> 1; - + // Leveled Mod - Already does this. // SOH [Enhancement] - With enemy health scaling, the Freezards health could cause an index out of bounds for the // displayLists, so we need to recompute the index based on the scaled health (using the maximum health value) and // clamp the final result for safety. - if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { + /* if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth) * 6); index = (6 - scaledHealth) >> 1; index = CLAMP(index, 0, 2); - } + }*/ OPEN_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 277677360b4..87e22131507 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -724,7 +724,7 @@ s32 EnGirlA_CanBuy_ZoraTunic(PlayState* play, EnGirlA* this) { } s32 EnGirlA_CanBuy_Health(PlayState* play, EnGirlA* this) { - if (gSaveContext.healthCapacity == gSaveContext.health) { + if (gSaveContext.healthCapacity2 == gSaveContext.health) { return CANBUY_RESULT_CANT_GET_NOW; } if (gSaveContext.rupees < this->basePrice) { diff --git a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c index fa95087bf71..b57666f3726 100644 --- a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c +++ b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c @@ -329,7 +329,7 @@ void EnGoma_SetupHurt(EnGoma* this, PlayState* play) { Animation_GetLastFrame(&gObjectGolDamagedAnim), ANIMMODE_ONCE, -2.0f); this->actionFunc = EnGoma_Hurt; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { this->actionTimer = 5; Enemy_StartFinishingBlow(play, &this->actor); } else { @@ -353,7 +353,7 @@ void EnGoma_Hurt(EnGoma* this, PlayState* play) { } if (this->actionTimer == 0) { - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { EnGoma_SetupDie(this); } else { EnGoma_SetupFlee(this); @@ -620,7 +620,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->hurtTimer--; } else { ColliderInfo* acHitInfo; - u8 swordDamage; + u16 swordDamage; if ((this->colCyl1.base.atFlags & 2) && this->actionFunc == EnGoma_Jump) { EnGoma_SetupLand(this); @@ -628,7 +628,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->actor.velocity.y = 0.0f; } - if ((this->colCyl2.base.acFlags & AC_HIT) && (s8)this->actor.colChkInfo.health > 0) { + if ((this->colCyl2.base.acFlags & AC_HIT) && this->actor.colChkInfo.health > 0) { acHitInfo = this->colCyl2.info.acHitInfo; this->colCyl2.base.acFlags &= ~AC_HIT; @@ -659,7 +659,14 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { swordDamage = 1; } - this->actor.colChkInfo.health -= swordDamage; + swordDamage = Leveled_DamageModify(&this->actor, &player->actor, swordDamage * Leveled_GetHealthAttackMultiplier()); + if (swordDamage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= swordDamage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, swordDamage); + EnGoma_SetupHurt(this, play); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5); this->hurtTimer = 13; diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c index 61a10b056bd..192d749d493 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c @@ -314,19 +314,21 @@ void func_80A5372C(EnHeishi2* this, PlayState* play) { f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); - this->unk_2F2[0] = 200; - this->cameraId = Play_CreateSubCamera(play); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); - Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); - this->unk_280.x = 947.0f; - this->unk_280.y = 1195.0f; - this->unk_280.z = 2682.0f; - - this->unk_28C.x = 1164.0f; - this->unk_28C.y = 1145.0f; - this->unk_28C.z = 3014.0f; - - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + this->unk_2F2[0] = 200; + this->cameraId = Play_CreateSubCamera(play); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); + Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); + this->unk_280.x = 947.0f; + this->unk_280.y = 1195.0f; + this->unk_280.z = 2682.0f; + + this->unk_28C.x = 1164.0f; + this->unk_28C.y = 1145.0f; + this->unk_28C.z = 3014.0f; + + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } this->actionFunc = func_80A53850; } @@ -334,11 +336,15 @@ void func_80A53850(EnHeishi2* this, PlayState* play) { BgSpot15Saku* gate; SkelAnime_Update(&this->skelAnime); - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } gate = (BgSpot15Saku*)this->gate; if ((this->unk_2F2[0] == 0) || (gate->unk_168 == 0)) { - Play_ClearCamera(play, this->cameraId); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) { + Play_ClearCamera(play, this->cameraId); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + } Message_CloseTextbox(play); this->unk_30C = 1; Player_SetCsActionWithHaltedActors(play, NULL, 7); @@ -479,23 +485,25 @@ void func_80A53DF8(EnHeishi2* this, PlayState* play) { f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); - this->unk_2F2[0] = 200; - this->cameraId = Play_CreateSubCamera(play); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); - Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); - this->unk_2BC.x = -71.0f; - this->unk_280.x = -71.0f; - this->unk_2BC.y = 571.0f; - this->unk_280.y = 571.0f; - this->unk_2BC.z = -1487.0f; - this->unk_280.z = -1487.0f; - this->unk_298.x = 181.0f; - this->unk_28C.x = 181.0f; - this->unk_298.y = 417.0f; - this->unk_28C.y = 417.0f; - this->unk_298.z = -1079.0f; - this->unk_28C.z = -1079.0f; - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + this->unk_2F2[0] = 200; + this->cameraId = Play_CreateSubCamera(play); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); + Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); + this->unk_2BC.x = -71.0f; + this->unk_280.x = -71.0f; + this->unk_2BC.y = 571.0f; + this->unk_280.y = 571.0f; + this->unk_2BC.z = -1487.0f; + this->unk_280.z = -1487.0f; + this->unk_298.x = 181.0f; + this->unk_28C.x = 181.0f; + this->unk_298.y = 417.0f; + this->unk_28C.y = 417.0f; + this->unk_298.z = -1079.0f; + this->unk_28C.z = -1079.0f; + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } this->actionFunc = func_80A53F30; } @@ -503,11 +511,15 @@ void func_80A53F30(EnHeishi2* this, PlayState* play) { BgGateShutter* gate; SkelAnime_Update(&this->skelAnime); - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } gate = (BgGateShutter*)this->gate; if ((this->unk_2F2[0] == 0) || (gate->openingState == 0)) { - Play_ClearCamera(play, this->cameraId); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) { + Play_ClearCamera(play, this->cameraId); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + } if ((this->unk_30A != 2)) { if (this->unk_30A == 0) { this->actor.textId = 0x2015; diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c index c4dbe3f3af0..0b1b2b824d4 100644 --- a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -686,7 +686,7 @@ void func_80A75C38(EnIk* this, PlayState* play) { f32 temp_f0; u8 pad; u8 pad2; - u8 prevHealth; + u16 prevHealth; s32 temp_v0_3; Vec3f sp38; @@ -720,20 +720,21 @@ void func_80A75C38(EnIk* this, PlayState* play) { } Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); prevHealth = this->actor.colChkInfo.health; + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); Actor_ApplyDamage(&this->actor); if (this->actor.params != 0) { - if ((prevHealth > 10) && (this->actor.colChkInfo.health <= 10)) { + if ((prevHealth > healthCheck) && (this->actor.colChkInfo.health <= healthCheck)) { this->unk_2FB = 1; BodyBreak_Alloc(&this->bodyBreak, 3, play); } - } else if (this->actor.colChkInfo.health <= 10) { + } else if (this->actor.colChkInfo.health <= healthCheck) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_BOSS); SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EN_LAST_DAMAGE); if (this->switchFlags != 0xFF) { Flags_SetSwitch(play, this->switchFlags); } return; - } else if (prevHealth == 50) { + } else if (prevHealth == GetActorStat_EnemyMaxHealth(50, this->actor.level)) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_ENEMY); } @@ -771,7 +772,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { this->unk_2FA = this->unk_2FB; func_80A75C38(this, play); - if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); + if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= healthCheck)) { func_80A781CC(&this->actor, play); return; } @@ -785,7 +787,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -64); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 64); + play->damagePlayer(play, -damage); this->unk_2FE = 0; } } diff --git a/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c index b7ba0617a19..6290d65bee7 100644 --- a/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c +++ b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c @@ -484,8 +484,17 @@ bool EnMag_ShouldDrawPressStart(Font* font, Gfx** gfxP, bool isActualText) { // Title logo is shifted to the left in Master Quest #define LOGO_X_SHIFT (isMQ ? 0 : -8) #define LOGO_TEX (isMQ ? gTitleZeldaShieldLogoMQTex : gTitleZeldaShieldLogoTex) +// Copyright texture is different depending on the version +// JPN CE displays two slightly different 2004 copyrights when lang is jpn or not +// Otherwise the other GC JPN versions either display 2002 for JPN or 2003 for others +// Else fallback to 2003 for GC or 1998 for N64 +#define COPYRIGHT_TEX \ + (isJpnGC_CE \ + ? (gSaveContext.language == LANGUAGE_JPN ? gTitleCopyright19982004JpnTex : gTitleCopyright19982004EngTex) \ + : (isGC ? ((isJpnGC_notCE || gSaveContext.language == LANGUAGE_JPN) ? gTitleCopyright19982002Tex \ + : gTitleCopyright19982003Tex) \ + : gTitleCopyright1998Tex)) // Copyright texture is larger on GC -#define COPYRIGHT_TEX (isGC ? gTitleCopyright19982003Tex : gTitleCopyright1998Tex) #define COPYRIGHT_TEX_WIDTH (isGC ? 160 : 128) #define COPYRIGHT_TEX_LEFT (isGC ? 78 : 94) @@ -515,6 +524,9 @@ void EnMag_DrawInner(Actor* thisx, PlayState* play, Gfx** gfxP) { u16 rectTop; bool isMQ = ResourceMgr_IsGameMasterQuest(); bool isGC = ResourceMgr_GetGamePlatform(0) == GAME_PLATFORM_GC; + bool isJpnGC_CE = isGC && ResourceMgr_GetGameVersion(0) == OOT_NTSC_JP_GC_CE; + bool isJpnGC_notCE = + isGC && (ResourceMgr_GetGameVersion(0) == OOT_NTSC_JP_GC || ResourceMgr_GetGameVersion(0) == OOT_NTSC_JP_MQ); gSPSegment(gfx++, 0x06, play->objectCtx.status[this->actor.objBankIndex].segment); diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c index 50ac7e11ba6..a2642de465d 100644 --- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -303,7 +303,7 @@ void EnMb_Init(Actor* thisx, PlayState* play) { relYawFromPlayer = this->actor.world.rot.y - Math_Vec3f_Yaw(&this->actor.world.pos, &player->actor.world.pos); - if (ABS(relYawFromPlayer) > 0x4000) { + if (ABS(relYawFromPlayer) > 0x4000 && !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0)) { this->actor.world.rot.y = thisx->world.rot.y + 0x8000; this->actor.shape.rot.y = thisx->world.rot.y; this->actor.world.pos.z = thisx->world.pos.z + 600.0f; @@ -625,6 +625,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); } else if (this->state == ENMB_STATE_CLUB_KNEELING) { /* dead code: the setup for this action sets state to something else */ EnMb_SetupClubDamagedWhileKneeling(this); @@ -634,6 +635,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); } else { EnMb_SetupSpearDamaged(this); } @@ -1446,6 +1448,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } else if (this->state != ENMB_STATE_CLUB_KNEELING) { EnMb_SetupClubDamaged(this); @@ -1453,6 +1456,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } else { EnMb_SetupSpearDamaged(this); diff --git a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c index ab8ca1a6f7d..ab379267eee 100644 --- a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c +++ b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c @@ -654,7 +654,7 @@ void EnPartner_Update(Actor* thisx, PlayState* play) { itemActor->params == ITEM00_ARROWS_MEDIUM || itemActor->params == ITEM00_ARROWS_LARGE || itemActor->params == ITEM00_BOMBCHU || itemActor->params == ITEM00_MAGIC_SMALL || itemActor->params == ITEM00_MAGIC_LARGE || itemActor->params == ITEM00_NUTS || - itemActor->params == ITEM00_STICK) { + itemActor->params == ITEM00_STICK || itemActor->params == ITEM00_SEEDS) { f32 distanceToObject = Actor_WorldDistXYZToActor(&this->actor, itemActor); if (distanceToObject <= 20.0f) { itemActor->world.pos = GET_PLAYER(play)->actor.world.pos; diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c index 33133be7ba7..1a25238108c 100644 --- a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c @@ -871,6 +871,7 @@ void EnPeehat_StateExplode(EnPeehat* this, PlayState* play) { if (this->animTimer == 5) { bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0x602, 0, true); + Player_GainExperience(play, this->actor.exp); if (bomb != NULL) { bomb->timer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c index e9b4ee2b6f9..7b6d6babbed 100644 --- a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -502,7 +502,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { case 1: Animation_PlayLoop(&this->skelAnime, &gGibdoRedeadGrabAttackAnim); this->unk_304++; - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xFF, 1, 0xC); this->unk_319 = 20; case 0: @@ -536,7 +537,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { this->unk_319--; if (this->unk_319 == 0) { - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xF0, 1, 0xC); this->unk_319 = 20; Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S + player->ageProperties->unk_92); @@ -730,6 +732,7 @@ void func_80AE3ECC(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); @@ -801,6 +804,7 @@ void func_80AE4114(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, 0, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c index 3797adafe55..0e2672fe0fd 100644 --- a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c @@ -335,6 +335,7 @@ void EnRr_SetupReleasePlayer(EnRr* this, PlayState* play) { EnRr_SetupDamage(this); } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } @@ -463,6 +464,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { } else { this->dropType = dropType; EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } return; case RR_DMG_FIRE: // Fire Arrow and Din's Fire @@ -507,7 +509,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { this->collider2.base.ocFlags1 &= ~OC1_HIT; // "catch" osSyncPrintf(VT_FGCOL(GREEN) "キャッチ(%d)!!" VT_RST "\n", this->frameCount); - if (play->grabPlayer(play, player)) { + if (GameInteractor_Should(VB_LIKE_LIKE_GRAB_PLAYER, true, this) && play->grabPlayer(play, player)) { player->actor.parent = &this->actor; this->stopScroll = false; EnRr_SetupGrabPlayer(this, player); @@ -756,6 +758,7 @@ void EnRr_Stunned(EnRr* this, PlayState* play) { this->actionFunc = EnRr_Approach; } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } } diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index bd926454504..90b766f714f 100644 --- a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -142,6 +142,8 @@ void func_80AFB950(EnSi* this, PlayState* play) { player->actor.freezeTimer = 10; } else { SET_GS_FLAGS((this->actor.params & 0x1F00) >> 8, this->actor.params & 0xFF); + u16 experience = Leveled_GoldSkulltulaExperience(gSaveContext.inventory.gsTokens); + Player_GainExperience(play, experience); GameInteractor_ExecuteOnFlagSet(FLAG_GS_TOKEN, this->actor.params); Actor_Kill(&this->actor); } diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c index b7612dd9179..0520e1ffdb4 100644 --- a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c @@ -420,6 +420,7 @@ void func_80AFD7B4(EnSkb* this, PlayState* play) { this->unk_283 |= 4; EffectSsDeadSound_SpawnStationary(play, &this->actor.projectedPos, NA_SE_EN_STALKID_DEAD, 1, 1, 0x28); EnSkb_SetupAction(this, func_80AFD880); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.c b/soh/src/overlays/actors/ovl_En_St/z_en_st.c index 99f4617bb9a..456e83b67fd 100644 --- a/soh/src/overlays/actors/ovl_En_St/z_en_st.c +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.c @@ -401,7 +401,8 @@ s32 EnSt_CheckHitLink(EnSt* this, PlayState* play) { } this->gaveDamageSpinTimer = 30; - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); return true; diff --git a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c index c5c8e6167fa..13d960365d8 100644 --- a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c +++ b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -334,6 +334,11 @@ s32 func_80B0C9F0(EnSw* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, this->unk_392); if (Actor_ApplyDamage(&this->actor) != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DAMAGE); + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + if (this->actor.colChkInfo.health > 1) { + this->actor.colChkInfo.health >>= 1; + } + } return true; } Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c index 72969cc2e83..130c3329f93 100644 --- a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c +++ b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c @@ -317,6 +317,8 @@ void EnTest_Destroy(Actor* thisx, PlayState* play) { if ((this->actor.params != STALFOS_TYPE_2) && !Actor_FindNearby(play, &this->actor, ACTOR_EN_TEST, ACTORCAT_ENEMY, 8000.0f)) { func_800F5B58(); + if (this->actor.ignoreExpReward) + Player_GainExperience(play, this->actor.exp); } Effect_Delete(play, this->effectIndex); @@ -1510,7 +1512,7 @@ void func_80862E6C(EnTest* this, PlayState* play) { } } else { if (this->actor.home.rot.x == 0) { - this->actor.colChkInfo.health = 10; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(10, this->actor.level); if (this->actor.params == STALFOS_TYPE_4) { this->actor.params = -1; diff --git a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c index a7574e17919..1cfc528f669 100644 --- a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c +++ b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c @@ -726,6 +726,7 @@ void EnTite_Stunned(EnTite* this, PlayState* play) { this->actor.world.rot.y = this->actor.shape.rot.y; if (this->actor.colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else if (this->flipState == TEKTITE_FLIPPED) { EnTite_SetupFlipUpright(this); } else if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) && @@ -867,6 +868,7 @@ void EnTite_CheckDamage(Actor* thisx, PlayState* play) { } if (thisx->colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else { // Flip tektite back up if it's on its back Audio_PlayActorSound2(thisx, NA_SE_EN_TEKU_DAMAGE); diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c index 4925e73cb31..ceca597df8b 100644 --- a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c +++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c @@ -706,7 +706,7 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) { sStaggerCount = 0; } } - if (player->linearVelocity == -18.0f) { + if (GameInteractor_Should(VB_TORCH2_HANDLE_CLANKING, player->linearVelocity == -18.0f, this)) { if (this->actor.xzDistToPlayer > 80.0f) { player->linearVelocity = 1.2f; } else if (this->actor.xzDistToPlayer < 70.0f) { diff --git a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c index 7d9861dae62..d28355d6978 100644 --- a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c +++ b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c @@ -601,6 +601,7 @@ void EnTp_UpdateDamage(EnTp* this, PlayState* play) { if (head->actor.params <= TAILPASARAN_HEAD) { EnTp_SetupDie(head); + Player_GainExperience(play, this->actor.exp); head->damageEffect = this->actor.colChkInfo.damageEffect; head->actor.params = TAILPASARAN_HEAD_DYING; } diff --git a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c index a26e3eab97e..935f458dc7e 100644 --- a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c +++ b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c @@ -388,6 +388,7 @@ void EnVm_Die(EnVm* this, PlayState* play) { bomb->timer = 0; } + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xA0); Actor_Kill(&this->actor); } @@ -397,7 +398,10 @@ void EnVm_CheckHealth(EnVm* this, PlayState* play) { EnBom* bomb; if (Actor_GetCollidedExplosive(play, &this->colliderCylinder.base) != NULL) { - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + this->actor.colChkInfo.damage += damage; + ActorDamageNumber_New(&this->actor, damage); + Actor_ApplyDamage(&this->actor); osSyncPrintf("hp down %d\n", this->actor.colChkInfo.health); } else { if (!(this->colliderQuad2.base.acFlags & AC_HIT) || this->unk_21C == 2) { diff --git a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c index 99d9fba48a6..942285d8461 100644 --- a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c +++ b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c @@ -1945,6 +1945,7 @@ void EnZf_Die(EnZf* this, PlayState* play) { if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (D_80B4A1B4 == -1)) { Flags_SetSwitch(play, this->clearFlag); func_800F5B58(); + Player_GainExperience(play, this->actor.exp); } else { D_80B4A1B4 = -1; } diff --git a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c index 6a38b626428..99de9ae436b 100644 --- a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c +++ b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c @@ -2537,7 +2537,7 @@ void func_80B59828(EnZl3* this, PlayState* play) { if (func_80B59698(this, play) != 0) { func_80088AA0(180); func_80B53468(); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); if (Flags_GetSwitch(play, 0x20)) { Flags_UnsetSwitch(play, 0x20); @@ -2586,7 +2586,7 @@ void func_80B59AD0(EnZl3* this, PlayState* play) { func_80B53614(this, play); Flags_UnsetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO); func_80B56F10(this, play); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->action = 27; this->drawConfig = 1; diff --git a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c index 3c83a1d51e0..4143a1cd755 100644 --- a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c +++ b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c @@ -617,7 +617,8 @@ void EnfHG_Damage(EnfHG* this, PlayState* play) { this->timers[0] = 140; this->actionFunc = EnfHG_Retreat; Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); - if (bossGnd->actor.colChkInfo.health > 24) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(24, bossGnd->actor.level); + if (bossGnd->actor.colChkInfo.health > healthCheck) { this->bossGndSignal = FHG_RIDE; } else { bossGnd->flyMode = GND_FLY_NEUTRAL; diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index 3c2028ce017..4d42de65f58 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -5170,7 +5170,9 @@ void Fishing_HandleOwnerDialog(Fishing* this, PlayState* play) { if (Actor_HasParent(&this->actor, play)) { this->stateAndTimer = 24; } else { - Actor_OfferGetItem(&this->actor, play, GI_SCALE_GOLDEN, 2000.0f, 1000.0f); + if (!GameInteractor_Should(VB_GIVE_RANDO_GLITCH_FISHING_PRIZE, false, this)) { + Actor_OfferGetItem(&this->actor, play, GI_SCALE_GOLDEN, 2000.0f, 1000.0f); + } } break; diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index ddee9d0af37..b6a2f7e1458 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4545,7 +4545,7 @@ void func_80837C0C(PlayState* play, Player* this, s32 damageResponseType, f32 sp Player_PlaySfx(this, NA_SE_PL_DAMAGE); - if (!func_80837B18(play, this, 0 - this->actor.colChkInfo.damage)) { + if (!func_80837B18_modified(play, this, 0 - this->actor.colChkInfo.damage, false)) { this->stateFlags2 &= ~PLAYER_STATE2_GRABBED_BY_ENEMY; if (!(this->actor.bgCheckFlags & 1) && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER)) { func_80837B9C(this, play); @@ -4764,7 +4764,9 @@ s32 func_808382DC(Player* this, PlayState* play) { gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw; } - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } } Player_PlayVoiceSfx(this, NA_SE_VO_LI_TAKEN_AWAY); @@ -4784,7 +4786,7 @@ s32 func_808382DC(Player* this, PlayState* play) { this->bodyShockTimer = 40; } - this->actor.colChkInfo.damage += this->knockbackDamage; + this->actor.colChkInfo.damage += this->knockbackDamage << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, knockbackResponse[this->knockbackType - 1], this->knockbackSpeed, this->knockbackYVelocity, this->knockbackRot, 20); } else { @@ -4886,7 +4888,7 @@ s32 func_808382DC(Player* this, PlayState* play) { ((this->currentTunic != PLAYER_TUNIC_GORON && CVarGetInteger(CVAR_CHEAT("SuperTunic"), 0) == 0) || (this->floorTypeTimer >= D_808544F4[sp48])))) { this->floorTypeTimer = 0; - this->actor.colChkInfo.damage = 4; + this->actor.colChkInfo.damage = 4 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_NONE, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } else { return 0; @@ -5129,7 +5131,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol } if (exitIndex == 0) { - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } Scene_SetTransitionForNextEntrance(play); } else { play->nextEntranceIndex = play->setupExitList[exitIndex - 1]; @@ -5163,7 +5167,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2, play->setupExitList[exitIndex - 1])) { gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex; - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } gSaveContext.respawnFlag = -2; } gSaveContext.retainWeatherMode = 1; @@ -5226,7 +5232,7 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol if (this->actor.bgCheckFlags & 1) { if (this->floorProperty == 5) { Play_TriggerRespawn(play); - } else { + } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { Play_TriggerVoidOut(play); } play->transitionType = TRANS_TYPE_FADE_BLACK_FAST; @@ -9279,7 +9285,7 @@ s32 func_80842DF4(PlayState* play, Player* this) { func_80842B7C(play, this); if (this->actor.colChkInfo.atHitEffect == 1) { - this->actor.colChkInfo.damage = 8; + this->actor.colChkInfo.damage = 8 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_ELECTRIC_SHOCK, 0.0f, 0.0f, this->actor.shape.rot.y, 20); return 1; @@ -9536,7 +9542,7 @@ void func_80843AE8(PlayState* play, Player* this) { LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_derth_rebirth, 1.0f, 99.0f, Animation_GetLastFrame(&gPlayerAnim_link_derth_rebirth), ANIMMODE_ONCE, 0.0f); } - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; this->av2.actionVar2 = -1; } } else if (gSaveContext.healthAccumulator == 0) { @@ -9613,6 +9619,10 @@ static FallImpactInfo D_80854600[] = { s32 func_80843E64(PlayState* play, Player* this) { s32 sp34; + if (!GameInteractor_Should(VB_RECIEVE_FALL_DAMAGE, true, this)) { + return 0; + } + if ((sFloorType == 6) || (sFloorType == 9)) { sp34 = 0; } else { @@ -9914,7 +9924,7 @@ void Player_Action_80844A44(Player* this, PlayState* play) { Math_StepToF(&this->linearVelocity, 0.0f, 0.05f); if (this->actor.bgCheckFlags & 1) { - this->actor.colChkInfo.damage = 0x10; + this->actor.colChkInfo.damage = 0x10 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_KNOCKBACK_LARGE, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } } @@ -10800,6 +10810,7 @@ void Player_InitCommon(Player* this, PlayState* play, FlexSkeletonHeader* skelHe Collider_SetQuad(play, &this->meleeWeaponQuads[1], &this->actor, &D_80854650); Collider_InitQuad(play, &this->shieldQuad); Collider_SetQuad(play, &this->shieldQuad, &this->actor, &D_808546A0); + Player_GainExperience(play, 0); this->ivanDamageMultiplier = 1; } @@ -12773,9 +12784,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) { temp2 += sControlInput->rel.stick_y * 240.0f * invertYAxisMulti * yAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_y) > 15.0f) { - temp2 += sControlInput->cur.right_stick_y * 240.0f * invertYAxisMulti * yAxisMulti; + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp2 += sControlInput->rel.right_stick_y * 240.0f * invertYAxisMulti * yAxisMulti; } if (fabsf(sControlInput->cur.gyro_x) > 0.01f) { temp2 += (-sControlInput->cur.gyro_x) * 750.0f; @@ -12792,9 +12802,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) { temp2 += sControlInput->rel.stick_x * -16.0f * invertXAxisMulti * xAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_x) > 15.0f) { - temp2 += sControlInput->cur.right_stick_x * -16.0f * invertXAxisMulti * xAxisMulti; + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp2 += sControlInput->rel.right_stick_x * -16.0f * invertXAxisMulti * xAxisMulti; } if (fabsf(sControlInput->cur.gyro_y) > 0.01f) { temp2 += (sControlInput->cur.gyro_y) * 750.0f * invertXAxisMulti; @@ -12810,10 +12819,9 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { (s32)((1.0f - Math_CosS(sControlInput->rel.stick_y * 200)) * 1500.0f) * invertYAxisMulti * yAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_y) > 15.0f) { - temp3 += ((sControlInput->cur.right_stick_y >= 0) ? 1 : -1) * - (s32)((1.0f - Math_CosS(sControlInput->cur.right_stick_y * 200)) * 1500.0f) * invertYAxisMulti * + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp3 += ((sControlInput->rel.right_stick_y >= 0) ? 1 : -1) * + (s32)((1.0f - Math_CosS(sControlInput->rel.right_stick_y * 200)) * 1500.0f) * invertYAxisMulti * yAxisMulti; } if (fabsf(sControlInput->cur.gyro_x) > 0.01f) { @@ -12831,10 +12839,9 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { (s32)((1.0f - Math_CosS(sControlInput->rel.stick_x * 200)) * -1500.0f) * invertXAxisMulti * xAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_x) > 15.0f) { - temp3 += ((sControlInput->cur.right_stick_x >= 0) ? 1 : -1) * - (s32)((1.0f - Math_CosS(sControlInput->cur.right_stick_x * 200)) * -1500.0f) * invertXAxisMulti * + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp3 += ((sControlInput->rel.right_stick_x >= 0) ? 1 : -1) * + (s32)((1.0f - Math_CosS(sControlInput->rel.right_stick_x * 200)) * -1500.0f) * invertXAxisMulti * xAxisMulti; } if (fabsf(sControlInput->cur.gyro_y) > 0.01f) { @@ -14610,25 +14617,27 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { if (this->itemAction == PLAYER_IA_BOTTLE_POE) { s32 rand = Rand_S16Offset(-1, 3); + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (rand == 0) { rand = 3; } - if ((rand < 0) && (gSaveContext.health <= 0x10)) { + if ((rand < 0) && (gSaveContext.health <= heartUnits)) { rand = 3; } if (rand < 0) { - Health_ChangeBy(play, -0x10); + Health_ChangeBy(play, -heartUnits); } else { - gSaveContext.healthAccumulator = rand * 0x10; + gSaveContext.healthAccumulator = rand * heartUnits; } } else { s32 sp28 = D_808549FC[this->itemAction - PLAYER_IA_BOTTLE_POTION_RED]; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (sp28 & 1) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = heartUnits * 20; } if (sp28 & 2) { @@ -14636,7 +14645,7 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } if (sp28 & 4) { - gSaveContext.healthAccumulator = 0x50; + gSaveContext.healthAccumulator = heartUnits * 5; } } @@ -14691,7 +14700,7 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { if (this->av1.bottleCatchType != BOTTLE_CATCH_NONE) { if (!this->av2.startedTextbox) { - if (CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { this->av1.bottleCatchType = BOTTLE_CATCH_NONE; } else { // 1 is subtracted because `sBottleCatchInfo` does not have an entry for `BOTTLE_CATCH_NONE` @@ -14734,13 +14743,13 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { this->av1.bottleCatchType = i + 1; this->av2.startedTextbox = false; - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; } this->interactRangeActor->parent = &this->actor; Player_UpdateBottleHeld(play, this, catchInfo->itemId, ABS(catchInfo->itemAction)); - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { Player_AnimPlayOnceAdjusted(play, this, swingEntry->catchAnimation); func_80835EA4(play, 4); } @@ -14772,7 +14781,8 @@ void Player_Action_8084EED8(Player* this, PlayState* play) { Player_PlaySfx(this, NA_SE_EV_BOTTLE_CAP_OPEN); Player_PlaySfx(this, NA_SE_EV_FIATY_HEAL - SFX_FLAG); } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { - gSaveContext.healthAccumulator = 0x140; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.healthAccumulator = heartUnits * 20; } } @@ -15013,7 +15023,7 @@ void Player_Action_8084F88C(Player* this, PlayState* play) { if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { Grotto_ForceRegularVoidOut(); } - } else { + } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { Play_TriggerVoidOut(play); } 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 6dae312ac4a..e55c9d76a49 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 @@ -3,6 +3,7 @@ #include #include +#include "textures/nes_font_static/nes_font_static.h" #include "textures/title_static/title_static.h" #include "textures/parameter_static/parameter_static.h" #include @@ -26,6 +27,7 @@ #include "soh/SaveManager.h" #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/ShipUtils.h" typedef struct { s16 left; @@ -1053,20 +1055,27 @@ void FileChoose_UpdateRandomizer() { if (!SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && !CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { CVarSetString(CVAR_GENERAL("SpoilerLog"), ""); + Randomizer_SetSpoilerLoaded(false); } if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0 || !(Randomizer_IsSeedGenerated() || Randomizer_IsSpoilerLoaded()) && SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && !fileSelectSpoilerFileLoaded) { if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0) { - CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), "")); - Audio_PlayFanfare(NA_BGM_HORSE_GOAL); + if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""))) { + CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), "")); + Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); + } else { + Sfx_PlaySfxCentered(NA_SE_SY_ERROR); + } } const char* fileLoc = CVarGetString(CVAR_GENERAL("SpoilerLog"), ""); CVarSetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0); CVarSetString(CVAR_GENERAL("RandomizerDroppedFile"), ""); - Randomizer_ParseSpoiler(fileLoc); - fileSelectSpoilerFileLoaded = true; + if (!Ship_IsCStringEmpty(fileLoc)) { + Randomizer_ParseSpoiler(fileLoc); + fileSelectSpoilerFileLoaded = true; + } if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { @@ -1891,9 +1900,9 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { s16 phi_ra; s16 temp_t1; - this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x288 * sizeof(Vtx)); + this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x298 * sizeof(Vtx)); - for (phi_t2 = 0; phi_t2 < 0x288; phi_t2 += 4) { + for (phi_t2 = 0; phi_t2 < 0x298; phi_t2 += 4) { this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = 0x12C; this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = this->windowContentVtx[phi_t2].v.ob[0] + 0x10; @@ -2051,7 +2060,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] - WREG(43); } - phi_t0 = this->windowPosX - 14; + phi_t0 = this->windowPosX - 26; temp_t1 -= 0x16; for (phi_a1 = 0; phi_a1 < 4; phi_a1++, phi_t2 += 4) { @@ -2061,7 +2070,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = this->windowContentVtx[phi_t2].v.ob[1] - D_80812828[phi_a1]; - phi_t0 += D_80812818[phi_a1]; + phi_t0 += D_80812818[phi_a1] - 1; } this->windowContentVtx[phi_t2 - 15].v.tc[0] = this->windowContentVtx[phi_t2 - 13].v.tc[0] = 0x400; @@ -2158,6 +2167,55 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2 + 6].v.ob[1] = this->windowContentVtx[phi_t2 + 7].v.ob[1] = this->windowContentVtx[phi_t2 + 4].v.ob[1] - 0x10; this->windowContentVtx[phi_t2 + 5].v.tc[0] = this->windowContentVtx[phi_t2 + 7].v.tc[0] = 0x1000; + + phi_t2 += 8; + // Level count + if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) == 0) { + phi_t0 = this->windowPosX + 30; + temp_t1 = 24; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 5; + } + phi_t0 += 3; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 9; + } + } else { + phi_t0 = this->windowPosX + 33; + temp_t1 = 14; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 5; + } + phi_t0 -= 13; + temp_t1 -= 10; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 9; + } + } } static u16 D_8081284C[] = { 0x007C, 0x0124, 0x01CC }; @@ -2270,6 +2328,31 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { } } + // draw level + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[648], 32, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + + char lvText[] = { 21, 57 }; + + for (i = 0, vtxOffset = 0; i < 2; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + lvText[i] * FONT_CHAR_TEX_SIZE, vtxOffset); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[656], 12, 0); + + FileChoose_SplitNumber(Save_GetSaveMetaInfo(fileIndex)->level, &deathCountSplit[0], &deathCountSplit[1], + &deathCountSplit[2]); + + for (i = 1, vtxOffset = 0; i < 3; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + deathCountSplit[i] * FONT_CHAR_TEX_SIZE, + vtxOffset); + } + // end draw level + gDPPipeSync(POLY_OPA_DISP++); heartType = (Save_GetSaveMetaInfo(fileIndex)->defense == 0) ? 0 : 1; diff --git a/soh/src/overlays/gamestates/ovl_select/z_select.c b/soh/src/overlays/gamestates/ovl_select/z_select.c index 3e1fcf220d4..e08ba7c97b8 100644 --- a/soh/src/overlays/gamestates/ovl_select/z_select.c +++ b/soh/src/overlays/gamestates/ovl_select/z_select.c @@ -40,7 +40,7 @@ void Select_LoadGame(SelectContext* this, s32 entranceIndex) { Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); gSaveContext.entranceIndex = entranceIndex; - // Check the entrance to see if the exit should be overriden to a grotto return point for entrance rando + // Check the entrance to see if the exit should be overridden to a grotto return point for entrance rando if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { // Ignore return value as we want to load into the entrance specified by the debug menu Grotto_OverrideSpecialEntrance(Entrance_GetOverride(entranceIndex)); @@ -105,7 +105,7 @@ void Select_Grotto_LoadGame(SelectContext* this, s32 grottoIndex) { gSaveContext.respawn[RESPAWN_MODE_RETURN].playerParams = 0x4ff; gSaveContext.respawn[RESPAWN_MODE_RETURN].pos = this->betterGrottos[grottoIndex].pos; - // Check the entrance to see if the exit should be overriden to a grotto return point for entrance rando + // Check the entrance to see if the exit should be overridden to a grotto return point for entrance rando if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { // Use grotto content and parent scene num to identify the right grotto s16 grottoEntrance = Grotto_GetRenamedGrottoIndexFromOriginal(this->betterGrottos[grottoIndex].data, diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c index 17f1aa89eda..ade62acc3e7 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c @@ -8,51 +8,6 @@ extern const char* digitTextures[]; void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { - Color_RGB8 aButtonColor = { 80, 150, 255 }; - if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.Changed"), 0)) { - aButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.AButton.Value"), aButtonColor); - } else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) { - aButtonColor = (Color_RGB8){ 80, 255, 150 }; - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D4, true)) { - aButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cButtonsColor = { 255, 255, 50 }; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CButtons.Changed"), 0)) { - cButtonsColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor); - } - Color_RGB8 cUpButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CUpButton.Changed"), 0)) { - cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CUpButton.Value"), cUpButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true)) { - cUpButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cDownButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.Changed"), 0)) { - cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CDownButton.Value"), cDownButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_F4, true)) { - cDownButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cLeftButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CLeftButton.Changed"), 0)) { - cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CLeftButton.Value"), cLeftButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true)) { - cLeftButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cRightButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.Changed"), 0)) { - cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CRightButton.Value"), cRightButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true)) { - cRightButtonColor = (Color_RGB8){ 191, 191, 191 }; - } static s16 D_8082A070[][4] = { { 255, 0, 0, 255 }, @@ -124,12 +79,22 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { s16 pad2; s16 phi_s0_2; s16 sp208[3]; - bool dpad = CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0); OPEN_DISPS(gfxCtx); if (((pauseCtx->unk_1E4 == 0) || (pauseCtx->unk_1E4 == 5) || (pauseCtx->unk_1E4 == 8)) && (pauseCtx->pageIndex == PAUSE_QUEST)) { + if (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0)) { + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { + pauseCtx->stickRelX = -35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { + pauseCtx->stickRelX = 35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + pauseCtx->stickRelY = -35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + pauseCtx->stickRelY = 35; + } + } pauseCtx->cursorColorSet = 0; if (pauseCtx->cursorSpecialPos == 0) { @@ -140,7 +105,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } else { phi_s3 = pauseCtx->cursorPoint[PAUSE_QUEST]; - if ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + if ((pauseCtx->stickRelX < -30)) { phi_s0 = D_8082A1AC[phi_s3][2]; if (phi_s0 == -3) { KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_LEFT); @@ -153,7 +118,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { phi_s0 = D_8082A1AC[phi_s0][2]; } } - } else if ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + } else if ((pauseCtx->stickRelX > 30)) { phi_s0 = D_8082A1AC[phi_s3][3]; if (phi_s0 == -2) { KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_RIGHT); @@ -168,7 +133,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } } - if ((pauseCtx->stickRelY < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DDOWN))) { + if ((pauseCtx->stickRelY < -30)) { phi_s0 = D_8082A1AC[phi_s3][1]; while (phi_s0 >= 0) { if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { @@ -176,7 +141,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } phi_s0 = D_8082A1AC[phi_s0][1]; } - } else if ((pauseCtx->stickRelY > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DUP))) { + } else if ((pauseCtx->stickRelY > 30)) { phi_s0 = D_8082A1AC[phi_s3][0]; while (phi_s0 >= 0) { if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { @@ -267,7 +232,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } } } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { - if ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + if ((pauseCtx->stickRelX > 30)) { pauseCtx->cursorPoint[PAUSE_QUEST] = 0x15; pauseCtx->nameDisplayTimer = 0; pauseCtx->cursorSpecialPos = 0; @@ -285,7 +250,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { pauseCtx->cursorSlot[pauseCtx->pageIndex] = sp216; } } else { - if ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + if ((pauseCtx->stickRelX < -30)) { pauseCtx->cursorPoint[PAUSE_QUEST] = 0; pauseCtx->nameDisplayTimer = 0; pauseCtx->cursorSpecialPos = 0; @@ -506,6 +471,52 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } if (pauseCtx->state == 6) { + Color_RGB8 aButtonColor = { 80, 150, 255 }; + if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.Changed"), 0)) { + aButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.AButton.Value"), aButtonColor); + } else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) { + aButtonColor = (Color_RGB8){ 80, 255, 150 }; + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D4, true)) { + aButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cButtonsColor = { 255, 255, 50 }; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CButtons.Changed"), 0)) { + cButtonsColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor); + } + Color_RGB8 cUpButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CUpButton.Changed"), 0)) { + cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CUpButton.Value"), cUpButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true)) { + cUpButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cDownButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.Changed"), 0)) { + cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CDownButton.Value"), cDownButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_F4, true)) { + cDownButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cLeftButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CLeftButton.Changed"), 0)) { + cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CLeftButton.Value"), cLeftButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true)) { + cLeftButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cRightButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.Changed"), 0)) { + cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CRightButton.Value"), cRightButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true)) { + cRightButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + gDPPipeSync(POLY_OPA_DISP++); gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c index 2eb5e15bbd3..1e636d7f2e5 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c @@ -372,12 +372,14 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { if (gSaveContext.healthCapacity < 0x30) { gSaveContext.healthCapacity = 0x30; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { gSaveContext.healthCapacity += 0x10; if (gSaveContext.healthCapacity >= 0x140) { gSaveContext.healthCapacity = 0x140; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } break; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index aa18912eab1..eb1070447f1 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -183,10 +183,6 @@ void KaleidoScope_DrawEquipment(PlayState* play) { s16 cursorX; s16 cursorY; s16 oldCursorPoint; - bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); - bool pauseAnyCursor = - (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || - (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON); OPEN_DISPS(play->state.gfxCtx); @@ -204,6 +200,11 @@ void KaleidoScope_DrawEquipment(PlayState* play) { } if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_EQUIP)) { + bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); + bool pauseAnyCursor = + (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || + (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON); + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_EQUIP]; pauseCtx->cursorColorSet = 0; @@ -527,6 +528,7 @@ void KaleidoScope_DrawEquipment(PlayState* play) { // Equip success sound Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + Leveled_SetPlayerModifiedStats(GET_PLAYER(play)); // Wait 10 frames before accepting input again pauseCtx->unk_1E4 = 7; sEquipTimer = 10; @@ -638,6 +640,7 @@ void KaleidoScope_DrawEquipment(PlayState* play) { Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + Leveled_SetPlayerModifiedStats(GET_PLAYER(play)); pauseCtx->unk_1E4 = 7; sEquipTimer = 10; } else if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) != 0) { @@ -868,6 +871,10 @@ void KaleidoScope_DrawEquipment(PlayState* play) { Gfx_SetupDL_42Opa(play->state.gfxCtx); KaleidoScope_DrawEquipmentImage(play, pauseCtx->playerSegment, PAUSE_EQUIP_PLAYER_WIDTH, PAUSE_EQUIP_PLAYER_HEIGHT); + if (pauseCtx->pageIndex == PAUSE_EQUIP && (pauseCtx->unk_1E4 == 0 || sEquipTimer > 0) && pauseCtx->alpha == 255) { + Leveled_KaleidoEquip_Stats(play); + } + if (gUpgradeMasks[0]) {} CLOSE_DISPS(play->state.gfxCtx); 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 beef301c411..7f0cfcc7a1a 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 @@ -421,11 +421,6 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { s16 cursorY; s16 oldCursorPoint; s16 moveCursorResult; - bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); - bool pauseAnyCursor = - pauseCtx->cursorSpecialPos == 0 && - ((CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || - (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON)); OPEN_DISPS(play->state.gfxCtx); @@ -437,6 +432,12 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { pauseCtx->nameColorSet = 0; if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_ITEM)) { + bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); + bool pauseAnyCursor = + pauseCtx->cursorSpecialPos == 0 && + ((CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || + (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON)); + moveCursorResult = 0 || IsItemCycling(); oldCursorPoint = pauseCtx->cursorPoint[PAUSE_ITEM]; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c index 3ff61d7833b..3e79eb6ad04 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c @@ -59,11 +59,11 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { s16 stepG; s16 stepB; u16 rgba16; - bool dpad = CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0); OPEN_DISPS(gfxCtx); if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_MAP)) { + bool dpad = CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0); pauseCtx->cursorColorSet = 0; oldCursorPoint = pauseCtx->cursorPoint[PAUSE_MAP]; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 14f749d43da..46ecd847def 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -4774,7 +4774,7 @@ void KaleidoScope_Update(PlayState* play) { play->gameplayFrames = 0; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK; gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); gSaveContext.healthAccumulator = 0; gSaveContext.magicState = MAGIC_STATE_IDLE;