From 8216cebf11912a894410eb28c35bcecdec652f8f Mon Sep 17 00:00:00 2001 From: Waterdish Date: Sat, 16 Mar 2024 00:47:39 -0700 Subject: [PATCH] SOH 8.0.5 update --- app/build.gradle | 4 +- app/jni/CMakeLists.txt | 4 +- app/jni/src/CMakeLists.txt | 4 +- app/jni/src/libultraship | 2 +- app/jni/src/soh/CMakeLists.txt | 2 +- app/jni/src/soh/include/z64.h | 5 +- app/jni/src/soh/macosx/soh-macos.sh.in | 234 ------------------ app/jni/src/soh/properties.h | 8 +- .../controls/GameControlEditor.cpp | 24 +- .../cosmetics/authenticGfxPatches.cpp | 16 ++ .../game-interactor/GameInteractor.cpp | 11 +- .../game-interactor/GameInteractor.h | 2 +- app/jni/src/soh/soh/Enhancements/mods.cpp | 27 +- app/jni/src/soh/soh/Enhancements/presets.cpp | 11 + app/jni/src/soh/soh/Enhancements/presets.h | 16 +- .../location_access/locacc_fire_temple.cpp | 2 +- .../location_access/locacc_forest_temple.cpp | 2 +- .../Enhancements/randomizer/3drando/logic.cpp | 2 +- .../Enhancements/randomizer/randomizer.cpp | 24 +- .../randomizer/randomizer_check_tracker.cpp | 183 +++++++++----- .../randomizer/randomizer_entrance.c | 5 + .../randomizer/randomizer_item_tracker.cpp | 42 ++-- .../soh/Enhancements/randomizer/savefile.cpp | 6 +- app/jni/src/soh/soh/Enhancements/tts/tts.cpp | 2 +- app/jni/src/soh/soh/OTRGlobals.cpp | 25 +- app/jni/src/soh/soh/OTRGlobals.h | 2 + app/jni/src/soh/soh/SaveManager.cpp | 99 +++++--- app/jni/src/soh/soh/SohGui.cpp | 9 + app/jni/src/soh/soh/SohGui.hpp | 2 + app/jni/src/soh/soh/SohMenuBar.cpp | 2 +- app/jni/src/soh/soh/SohModals.cpp | 54 ++++ app/jni/src/soh/soh/SohModals.h | 15 ++ app/jni/src/soh/soh/z_scene_otr.cpp | 50 ++-- app/jni/src/soh/src/code/z_actor.c | 20 +- app/jni/src/soh/src/code/z_bgcheck.c | 2 +- app/jni/src/soh/src/code/z_camera.c | 2 +- app/jni/src/soh/src/code/z_camera_data.inc | 8 +- app/jni/src/soh/src/code/z_map_exp.c | 1 - app/jni/src/soh/src/code/z_message_PAL.c | 5 +- app/jni/src/soh/src/code/z_scene.c | 7 +- app/jni/src/soh/src/code/z_sram.c | 117 ++++----- .../actors/ovl_Boss_Dodongo/z_boss_dodongo.c | 33 ++- .../overlays/actors/ovl_Demo_Gj/z_demo_gj.c | 6 +- .../actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c | 3 + .../overlays/actors/ovl_En_GirlA/z_en_girla.c | 7 +- .../overlays/actors/ovl_En_Part/z_en_part.c | 6 +- .../src/overlays/actors/ovl_En_Ru2/z_en_ru2.c | 13 - .../src/overlays/actors/ovl_En_Si/z_en_si.c | 8 +- .../ovl_file_choose/z_file_choose.c | 6 +- .../ovl_kaleido_scope/z_kaleido_map_PAL.c | 13 +- .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 32 +-- app/src/main/AndroidManifest.xml | 4 +- app/src/main/assets/soh.otr | Bin 439359 -> 439361 bytes 53 files changed, 605 insertions(+), 584 deletions(-) create mode 100644 app/jni/src/soh/soh/SohModals.cpp create mode 100644 app/jni/src/soh/soh/SohModals.h diff --git a/app/build.gradle b/app/build.gradle index 4a56533..4c73f37 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { } minSdkVersion 18 targetSdkVersion 31 - versionCode 4 - versionName "1.1.5" + versionCode 5 + versionName "1.2.0" externalNativeBuild { //ndkBuild { // arguments "APP_PLATFORM=android-23" diff --git a/app/jni/CMakeLists.txt b/app/jni/CMakeLists.txt index d6526f4..4d28364 100644 --- a/app/jni/CMakeLists.txt +++ b/app/jni/CMakeLists.txt @@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 ") -project(Game VERSION 8.0.4 LANGUAGES C CXX) -set(PROJECT_BUILD_NAME "MacReady Echo" CACHE STRING "") +project(Game VERSION 8.0.5 LANGUAGES C CXX) +set(PROJECT_BUILD_NAME "MacReady Foxtrot" CACHE STRING "") set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "") add_subdirectory(src) diff --git a/app/jni/src/CMakeLists.txt b/app/jni/src/CMakeLists.txt index 5b14dd3..72d0eb6 100644 --- a/app/jni/src/CMakeLists.txt +++ b/app/jni/src/CMakeLists.txt @@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 8.0.4 LANGUAGES C CXX) -set(PROJECT_BUILD_NAME "MacReady Echo" CACHE STRING "") +project(Ship VERSION 8.0.5 LANGUAGES C CXX) +set(PROJECT_BUILD_NAME "MacReady Foxtrot" CACHE STRING "") set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "") set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh) diff --git a/app/jni/src/libultraship b/app/jni/src/libultraship index d665dc8..f56885c 160000 --- a/app/jni/src/libultraship +++ b/app/jni/src/libultraship @@ -1 +1 @@ -Subproject commit d665dc889d9a15a88f4009aebea629e90620f27c +Subproject commit f56885cad59319376eadcf6a8e4a2f189d0b763c diff --git a/app/jni/src/soh/CMakeLists.txt b/app/jni/src/soh/CMakeLists.txt index 89c56d1..bf7a1a4 100644 --- a/app/jni/src/soh/CMakeLists.txt +++ b/app/jni/src/soh/CMakeLists.txt @@ -332,7 +332,7 @@ endif() include(FetchContent) FetchContent_Declare( Boost - URL https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.gz + URL https://archives.boost.io/release/1.81.0/source/boost_1_81_0.tar.gz URL_HASH SHA256=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6 SOURCE_SUBDIR "null" # Set to a nonexistent directory so boost is not built (we don't need to build it) DOWNLOAD_EXTRACT_TIMESTAMP false # supress timestamp warning, not needed since the url wont change diff --git a/app/jni/src/soh/include/z64.h b/app/jni/src/soh/include/z64.h index 23ffcfb..f790ddd 100644 --- a/app/jni/src/soh/include/z64.h +++ b/app/jni/src/soh/include/z64.h @@ -744,7 +744,6 @@ typedef struct { /* 0x0134 */ char** doActionSegment; /* 0x0138 */ u8* iconItemSegment; /* 0x013C */ char** mapSegment; - char** mapSegmentName; /* 0x0140 */ u8 mapPalette[32]; /* 0x0160 */ DmaRequest dmaRequest_160; /* 0x0180 */ DmaRequest dmaRequest_180; @@ -815,6 +814,10 @@ typedef struct { /* 0x026C */ u8 dinsNayrus; // "m_magic"; din's fire and nayru's love /* 0x026D */ u8 all; // "another"; enables all item restrictions } restrictions; + // #region SOH [General] + /* */ char* mapSegmentName[2]; // Tracks the map segment texture by OTR sig name + /* */ u8 mapPalettesPulse[40][32]; // Used to have unique pointers per map pulse color for the shader backend. 40 for map pulse timer x2 + // #endregion } InterfaceContext; // size = 0x270 typedef struct { diff --git a/app/jni/src/soh/macosx/soh-macos.sh.in b/app/jni/src/soh/macosx/soh-macos.sh.in index 0983f63..217496c 100755 --- a/app/jni/src/soh/macosx/soh-macos.sh.in +++ b/app/jni/src/soh/macosx/soh-macos.sh.in @@ -7,68 +7,6 @@ export RESPATH="${SNAME%/MacOS*}/Resources" export LIBPATH="${SNAME%/MacOS*}/Frameworks" export DYLD_FALLBACK_LIBRARY_PATH="$LIBPATH" -remap_hashes () -{ - # Remap v64 and n64 hashes to their z64 hash equivalent - # ZAPD will handle converting the data into z64 format - case "$ROMHASH" in - a9059b56e761c9034fbe02fe4c24985aaa835dac) # v64 - ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099 - ;; - 24708102dc504d3f375a37f4ae4e149c167dc515) # n64 - ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099 - ;; - 580dd0bd1b6d2c51cc20a764eece84dba558964c) # v64 - ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4 - ;; - d6342c59007e57c1194661ec6880b2f078403f4e) # n64 - ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4 - ;; - d0bdc2eb320668b4ba6893b9aefe4040a73123ff) # v64 - ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5 - ;; - 4946ab250f6ac9b32d76b21f309ebb8ebc8103d2) # n64 - ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5 - ;; - 663c34f1b2c05a09e5beffe4d0dcd440f7d49dc7) # v64 - ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012 - ;; - 24c73d378b0620a380ce5ef9f2b186c6c157a68b) # n64 - ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012 - ;; - 8ebf2e29313f44f2d49e5b4191971d09919e8e48) # v64 - ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2 - ;; - 4264bf7b875737b8fae77d52322a5099d051fc11) # n64 - ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2 - ;; - 973bc6fe56010a8d646166a1182a81b4f13b8cf9) # v64 - ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03 - ;; - d327752c46edc70ff3668b9514083dbbee08927c) # v64 - ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03 - ;; - ecdeb1747560834e079c22243febea7f6f26ba3b) # v64 - ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da - ;; - f19f8662ec7abee29484a272a6fda53e39efe0f1) # n64 - ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da - ;; - ab519ce04a33818ce2c39b3c514a751d807a494a) # v64 - ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6 - ;; - c19a34f7646305e1755249fca2071e178bd7cd00) # n64 - ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6 - ;; - 25e8ae79ea0839ca5c984473f7460d8040c36f9c) # v64 - ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f - ;; - 166c02770d67fcc3954c443eb400a6a3573d3fc0) # n64 - ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f - ;; - esac -} - if [ ! -e "$SHIP_HOME" ]; then mkdir "$SHIP_HOME"; fi if [ ! -e "$SHIP_HOME"/mods ]; then @@ -76,178 +14,6 @@ if [ ! -e "$SHIP_HOME"/mods ]; then touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt fi -# If either OTR doesn't exist kick off the OTR gen process -if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then - - # If no ROMs exist kick off the file selection prompts - while [ ! -e "$SHIP_HOME"/*.*64 ] && [ ! -e "$SHIP_HOME"/oot*.otr ]; do - - SHOULD_PROMPT_FOR_ROM=1 - while [ $SHOULD_PROMPT_FOR_ROM -eq 1 ]; do - SHOULD_PROMPT_FOR_ROM=0 - # Use osascript to prompt the user to chose a file - DROPROM=`osascript <<-EOF - set romFile to choose file of type {"b64","n64","v64","z64"} with prompt "Please select your ROM:" - return POSIX path of romFile - EOF` - - # If no rom was selected, the user cancelled, so exit - if [[ -z $DROPROM ]] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then - echo "No ROM selected. Exiting..." - exit 1 - elif [[ -z $DROPROM ]]; then - break; - fi - - # If an invalid rom was selected, let the user know and ask to try again - ROMHASH="$(shasum "$DROPROM" | awk '{ print $1 }')" - - remap_hashes - - case "$ROMHASH" in - cee6bc3c2a634b41728f2af8da54d9bf8cc14099) - ROM_TYPE=0;; - 0227d7c0074f2d0ac935631990da8ec5914597b4) - ROM_TYPE=0;; - 328a1f1beba30ce5e178f031662019eb32c5f3b5) - ROM_TYPE=0;; - cfbb98d392e4a9d39da8285d10cbef3974c2f012) - ROM_TYPE=0;; - f46239439f59a2a594ef83cf68ef65043b1bffe2) - ROM_TYPE=1;; - 50bebedad9e0f10746a52b07239e47fa6c284d03) - ROM_TYPE=1;; - 079b855b943d6ad8bd1eb026c0ed169ecbdac7da) - ROM_TYPE=1;; - cfecfdc58d650e71a200c81f033de4e6d617a9f6) - ROM_TYPE=1;; - 517bd9714c73cb96c21e7c2ef640d7b55186102f) - ROM_TYPE=1;; - *) - TRY_AGAIN_RESULT=`osascript <<-EOF - set alertText to "Incompatible ROM hash" - set alertMessage to "Incompatible ROM provided, would you like to try again?" - return display alert alertText \ - message alertMessage \ - as critical \ - buttons {"Cancel", "Try Again"} - EOF` - if [[ "$TRY_AGAIN_RESULT" == "button returned:Try Again" ]]; then - SHOULD_PROMPT_FOR_ROM=1 - continue; - else - echo "No ROM selected. Exiting..." - exit 1 - fi - esac - - cp "$DROPROM" "$SHIP_HOME" - - # Ask user if they would also like to select the other variant (MQ/Vanilla) - if [ $ROM_TYPE -eq 0 ] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then - UPLOAD_ANOTHER_RESULT=`osascript <<-EOF - set alertText to "Success" - set alertMessage to "Would you also like to provide a Master Quest ROM?" - return display alert alertText \ - message alertMessage \ - buttons {"No", "Yes"} - EOF` - elif [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then - UPLOAD_ANOTHER_RESULT=`osascript <<-EOF - set alertText to "Success" - set alertMessage to "Would you also like to provide a Vanilla (Non Master Quest) ROM?" - return display alert alertText \ - message alertMessage \ - buttons {"No", "Yes"} - EOF` - fi - - if [[ "$UPLOAD_ANOTHER_RESULT" == "button returned:Yes" ]]; then - UPLOAD_ANOTHER_RESULT="button returned:No" - SHOULD_PROMPT_FOR_ROM=1 - continue; - fi - break - done - done - - # At this point we should now have 1 or more valid roms in $SHIP_HOME directory - - # Prepare tmp dir - for ROMPATH in "$SHIP_HOME"/*.*64 - do - ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)" - export ASSETDIR - cp -r "$RESPATH/assets" "$ASSETDIR" - mkdir -p "$ASSETDIR"/tmp - cp "$ROMPATH" "$ASSETDIR"/tmp/rom.z64 - cd "$ASSETDIR" || return - - # If an invalid rom was detected, let the user know - ROMHASH="$(shasum "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')" - - remap_hashes - - case "$ROMHASH" in - cee6bc3c2a634b41728f2af8da54d9bf8cc14099) - ROM=GC_NMQ_D - OTRNAME="oot.otr";; - 0227d7c0074f2d0ac935631990da8ec5914597b4) - ROM=GC_NMQ_PAL_F - OTRNAME="oot.otr";; - 328a1f1beba30ce5e178f031662019eb32c5f3b5) - ROM=N64_PAL_10 - OTRNAME="oot.otr";; - cfbb98d392e4a9d39da8285d10cbef3974c2f012) - ROM=N64_PAL_11 - OTRNAME="oot.otr";; - f46239439f59a2a594ef83cf68ef65043b1bffe2) - ROM=GC_MQ_PAL_F - OTRNAME="oot-mq.otr";; - 50bebedad9e0f10746a52b07239e47fa6c284d03) - ROM=GC_MQ_D - OTRNAME="oot-mq.otr";; - 079b855b943d6ad8bd1eb026c0ed169ecbdac7da) - ROM=GC_MQ_D - OTRNAME="oot-mq.otr";; - cfecfdc58d650e71a200c81f033de4e6d617a9f6) - ROM=GC_MQ_D - OTRNAME="oot-mq.otr";; - 517bd9714c73cb96c21e7c2ef640d7b55186102f) - ROM=GC_MQ_D - OTRNAME="oot-mq.otr";; - *) - osascript -e 'display notification "One or more invalid ROM provided" with title "Ship Of Harkinian"' - rm -r "$ASSETDIR" - cd "$SNAME" - continue; - esac - - # Only generate OTR if we don't have on of this type yet - if [ -e "$SHIP_HOME"/"$OTRNAME" ]; then - rm -r "$ASSETDIR" - cd "$SNAME" - continue; - fi - - osascript -e 'display notification "Generating OTR..." with title "Ship Of Harkinian"' - assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --portVer "@CMAKE_PROJECT_VERSION@" - if [ -e "$ASSETDIR"/oot.otr ]; then - osascript -e 'display notification "OTR successfully generated" with title "Ship Of Harkinian"' - cp "$ASSETDIR"/oot.otr "$SHIP_HOME"/"$OTRNAME" - rm -r "$ASSETDIR" - cd "$SNAME" - fi - done - - if [ ! -e "$SHIP_HOME"/oot*.otr ]; then - osascript -e 'display notification "OTR failed to generate" with title "Ship Of Harkinian"' - exit 1; - fi -fi - -cd "$SNAME" - arch_name="$(uname -m)" launch_arch="arm64" if [ "${arch_name}" = "x86_64" ] && [ "$(sysctl -in sysctl.proc_translated)" != "1" ]; then diff --git a/app/jni/src/soh/properties.h b/app/jni/src/soh/properties.h index 592d007..b9e6c03 100644 --- a/app/jni/src/soh/properties.h +++ b/app/jni/src/soh/properties.h @@ -1,9 +1,9 @@ -#define VER_FILEVERSION 8, 0, 4, 0 -#define VER_FILEVERSION_STR "8.0.4\0" +#define VER_FILEVERSION 8, 0, 5, 0 +#define VER_FILEVERSION_STR "8.0.5\0" -#define VER_PRODUCTVERSION 8, 0, 4, 0 -#define VER_PRODUCTVERSION_str "8.0.4\0" +#define VER_PRODUCTVERSION 8, 0, 5, 0 +#define VER_PRODUCTVERSION_str "8.0.5\0" #define VER_COMPANYNAME_STR "github.com/harbourmasters\0" #define VER_PRODUCTNAME_STR "Ship of Harkinian\0" diff --git a/app/jni/src/soh/soh/Enhancements/controls/GameControlEditor.cpp b/app/jni/src/soh/soh/Enhancements/controls/GameControlEditor.cpp index eb69f3c..976912b 100644 --- a/app/jni/src/soh/soh/Enhancements/controls/GameControlEditor.cpp +++ b/app/jni/src/soh/soh/Enhancements/controls/GameControlEditor.cpp @@ -258,24 +258,28 @@ namespace GameControlEditor { window->EndGroupPanelPublic(0); UIWidgets::Spacer(0); - window->BeginGroupPanelPublic("Third-Person Camera", ImGui::GetContentRegionAvail()); + window->BeginGroupPanelPublic("Free Look/Third-person Camera", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("Free Camera", "gFreeCamera"); - DrawHelpIcon("Enables free camera control\nNote: You must remap C buttons off of the right stick in the " + UIWidgets::PaddedEnhancementCheckbox("Enable Free Look", "gFreeCamera"); + DrawHelpIcon("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."); - UIWidgets::PaddedEnhancementCheckbox("Invert Camera X Axis", "gInvertXAxis"); - DrawHelpIcon("Inverts the Camera X Axis in:\n-Free camera"); - UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); - DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free camera"); + UIWidgets::PaddedEnhancementCheckbox("Invert X Axis", "gInvertXAxis"); + DrawHelpIcon("Inverts the Camera X Axis in:\n-Free Look"); + UIWidgets::PaddedEnhancementCheckbox("Invert Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); + DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free Look"); UIWidgets::Spacer(0); - UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal", + UIWidgets::PaddedEnhancementSliderFloat("Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal", "gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true); - UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical", + DrawHelpIcon("Changes the sensitivity of the X axis control for Free Look"); + UIWidgets::PaddedEnhancementSliderFloat("Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical", "gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true); + DrawHelpIcon("Changes the sensitivity of the Y axis control for Free Look"); UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist", "gFreeCameraDistMax", 100, 900, "", 185, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed", + DrawHelpIcon("How far the camera sits from Link while in Free Look mode"); + UIWidgets::PaddedEnhancementSliderInt("Transition Speed: %d", "##CamTranSpeed", "gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true); + DrawHelpIcon("How quickly the camera changes to the distance specified above"); window->EndGroupPanelPublic(0); } diff --git a/app/jni/src/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/app/jni/src/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp index de97f38..709854c 100644 --- a/app/jni/src/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp +++ b/app/jni/src/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp @@ -8,6 +8,7 @@ extern "C" { #include "objects/object_gi_soldout/object_gi_soldout.h" #include "objects/object_ik/object_ik.h" #include "objects/object_link_child/object_link_child.h" +#include "objects/object_ru2/object_ru2.h" uint32_t ResourceMgr_GameHasMasterQuest(); uint32_t ResourceMgr_GameHasOriginal(); @@ -187,10 +188,25 @@ void PatchIronKnuckleTextureOverflow() { } } +void PatchPrincessRutoEaring() { + // FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings. + // Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so + // it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the + // earring texture loads into the same place in TMEM as the brick texture, so when it comes to rendering, TEXEL1 + // uses the earring texture with different clamp settings, and it displays without noticeable error. However, both + // texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses + // our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces + // TEXEL1 with TEXEL0, which is most likely the original intention, and all is well. + ResourceMgr_PatchGfxByName(gAdultRutoHeadDL, "RutoEaringTileFix", 162, + gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED, + TEXEL0, 0, PRIM_LOD_FRAC, COMBINED)); +} + void ApplyAuthenticGfxPatches() { PatchDekuStickTextureOverflow(); PatchFreezardTextureOverflow(); PatchIronKnuckleTextureOverflow(); + PatchPrincessRutoEaring(); } // Patches the Sold Out GI DL to render the texture in the mirror boundary diff --git a/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.cpp b/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.cpp index eb33094..5a0d0ef 100644 --- a/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.cpp +++ b/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.cpp @@ -37,12 +37,19 @@ GameInteractionEffectQueryResult GameInteractor::RemoveEffect(GameInteractionEff // MARK: - Helpers -bool GameInteractor::IsSaveLoaded() { +bool GameInteractor::IsSaveLoaded(bool allowDbgSave) { Player* player; if (gPlayState != NULL) { player = GET_PLAYER(gPlayState); } - return (gPlayState == NULL || player == NULL || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) ? false : true; + + // Checking for normal game mode prevents debug saves from reporting true on title screen + if (gPlayState == NULL || player == NULL || gSaveContext.gameMode != GAMEMODE_NORMAL) { + return false; + } + + // Valid save file or debug save + return (gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2) || (allowDbgSave && gSaveContext.fileNum == 0xFF); } bool GameInteractor::IsGameplayPaused() { diff --git a/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.h b/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.h index 4f8ef25..b56a001 100644 --- a/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/app/jni/src/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -198,7 +198,7 @@ public: DEFINE_HOOK(OnAssetAltChange, void()); // Helpers - static bool IsSaveLoaded(); + static bool IsSaveLoaded(bool allowDbgSave = false); static bool IsGameplayPaused(); static bool CanSpawnActor(); static bool CanAddOrTakeAmmo(int16_t amount, int16_t item); diff --git a/app/jni/src/soh/soh/Enhancements/mods.cpp b/app/jni/src/soh/soh/Enhancements/mods.cpp index 6692dd5..258fbba 100644 --- a/app/jni/src/soh/soh/Enhancements/mods.cpp +++ b/app/jni/src/soh/soh/Enhancements/mods.cpp @@ -47,7 +47,7 @@ void ReloadSceneTogglingLinkAge() { void RegisterInfiniteMoney() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gInfiniteMoney", 0) != 0) { if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) { gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); @@ -58,7 +58,7 @@ void RegisterInfiniteMoney() { void RegisterInfiniteHealth() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gInfiniteHealth", 0) != 0) { if (gSaveContext.health < gSaveContext.healthCapacity) { gSaveContext.health = gSaveContext.healthCapacity; @@ -69,7 +69,7 @@ void RegisterInfiniteHealth() { void RegisterInfiniteAmmo() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gInfiniteAmmo", 0) != 0) { // Deku Sticks if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) { @@ -106,7 +106,7 @@ void RegisterInfiniteAmmo() { void RegisterInfiniteMagic() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gInfiniteMagic", 0) != 0) { if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) { gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30; @@ -117,7 +117,7 @@ void RegisterInfiniteMagic() { void RegisterInfiniteNayrusLove() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gInfiniteNayru", 0) != 0) { gSaveContext.nayrusLoveTimer = 0x44B; } @@ -126,7 +126,7 @@ void RegisterInfiniteNayrusLove() { void RegisterMoonJumpOnL() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gMoonJumpOnL", 0) != 0) { Player* player = GET_PLAYER(gPlayState); @@ -141,7 +141,7 @@ void RegisterMoonJumpOnL() { void RegisterInfiniteISG() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gEzISG", 0) != 0) { Player* player = GET_PLAYER(gPlayState); @@ -153,7 +153,7 @@ void RegisterInfiniteISG() { //Permanent quick put away (QPA) glitched damage value void RegisterEzQPA() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gEzQPA", 0) != 0) { Player* player = GET_PLAYER(gPlayState); @@ -165,7 +165,7 @@ void RegisterEzQPA() { void RegisterUnrestrictedItems() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; if (CVarGetInteger("gNoRestrictItems", 0) != 0) { u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong; @@ -193,11 +193,14 @@ void RegisterFreezeTime() { /// Switches Link's age and respawns him at the last entrance he entered. void RegisterSwitchAge() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) { + static bool warped = false; + + if (!GameInteractor::IsSaveLoaded(true)) { CVarClear("gSwitchAge"); + warped = false; return; } - static bool warped = false; + static Vec3f playerPos; static int16_t playerYaw; static RoomContext* roomCtx; @@ -231,7 +234,7 @@ void RegisterSwitchAge() { void RegisterOcarinaTimeTravel() { GameInteractor::Instance->RegisterGameHook([]() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { CVarClear("gTimeTravel"); return; } diff --git a/app/jni/src/soh/soh/Enhancements/presets.cpp b/app/jni/src/soh/soh/Enhancements/presets.cpp index 7b2ca65..726863e 100644 --- a/app/jni/src/soh/soh/Enhancements/presets.cpp +++ b/app/jni/src/soh/soh/Enhancements/presets.cpp @@ -12,6 +12,14 @@ void clearCvars(std::vector cvarsToClear) { } } +std::string FormatLocations(std::vector locs) { + std::string locString = ""; + for (auto loc: locs) { + locString += std::to_string(loc) + ","; + } + return locString; +} + void applyPreset(std::vector entries) { for(auto& [cvar, type, value] : entries) { switch (type) { @@ -24,6 +32,9 @@ void applyPreset(std::vector entries) { case PRESET_ENTRY_TYPE_STRING: CVarSetString(cvar, std::get(value)); break; + case PRESET_ENTRY_TYPE_CPP_STRING: + CVarSetString(cvar, std::get(value).c_str()); + break; } } } diff --git a/app/jni/src/soh/soh/Enhancements/presets.h b/app/jni/src/soh/soh/Enhancements/presets.h index 22c9dd7..cac60cd 100644 --- a/app/jni/src/soh/soh/Enhancements/presets.h +++ b/app/jni/src/soh/soh/Enhancements/presets.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -11,6 +12,7 @@ enum PresetEntryType { PRESET_ENTRY_TYPE_S32, PRESET_ENTRY_TYPE_FLOAT, PRESET_ENTRY_TYPE_STRING, + PRESET_ENTRY_TYPE_CPP_STRING, }; enum PresetType { @@ -36,15 +38,19 @@ enum RandomizerPreset { typedef struct PresetEntry { const char* cvar; PresetEntryType type; - std::variant value; + std::variant value; } PresetEntry; +std::string FormatLocations(std::vector locs); + #define PRESET_ENTRY_S32(cvar, value) \ { cvar, PRESET_ENTRY_TYPE_S32, value } #define PRESET_ENTRY_FLOAT(cvar, value) \ { cvar, PRESET_ENTRY_TYPE_FLOAT, value } #define PRESET_ENTRY_STRING(cvar, value) \ { cvar, PRESET_ENTRY_TYPE_STRING, value } +#define PRESET_ENTRY_CPP_STRING(cvar, value) \ + { cvar, PRESET_ENTRY_TYPE_CPP_STRING, value } void DrawPresetSelector(PresetType presetType); void clearCvars(std::vector cvarsToClear); @@ -866,7 +872,8 @@ const std::vector spockRacePresetEntries = { PRESET_ENTRY_S32("gRandomizeDampeHint", 1), PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN), PRESET_ENTRY_S32("gRandomizeEnableBombchuDrops", 1), - PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "78,143,144,229,"), + PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations( + { RC_MARKET_10_BIG_POES, RC_KAK_40_GOLD_SKULLTULA_REWARD, RC_KAK_50_GOLD_SKULLTULA_REWARD, RC_ZR_FROGS_OCARINA_GAME })), PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_OPEN), PRESET_ENTRY_S32("gRandomizeFullWallets", 1), PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP), @@ -958,7 +965,8 @@ const std::vector spockRaceNoLogicPresetEntries = { PRESET_ENTRY_S32("gRandomizeDampeHint", 1), PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN), PRESET_ENTRY_S32("gRandomizeEnableBombchuDrops", 1), - PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "78,143,144,229,"), + PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations( + { RC_MARKET_10_BIG_POES, RC_KAK_40_GOLD_SKULLTULA_REWARD, RC_KAK_50_GOLD_SKULLTULA_REWARD, RC_ZR_FROGS_OCARINA_GAME })), PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_OPEN), PRESET_ENTRY_S32("gRandomizeFullWallets", 1), PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP), @@ -1011,7 +1019,7 @@ const std::vector s6PresetEntries = { PRESET_ENTRY_S32("gRandomizeBigPoeTargetCount", 1), PRESET_ENTRY_S32("gRandomizeCuccosToReturn", 4), PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN), - PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "48,"), + PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations({ RC_DEKU_THEATER_MASK_OF_TRUTH })), PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_CLOSED_DEKU), PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP), PRESET_ENTRY_S32("gRandomizeGerudoFortress", RO_GF_FAST), diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp b/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp index 440483a..ff2fb88 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp +++ b/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp @@ -40,7 +40,7 @@ void AreaTable_Init_FireTemple() { }, { //Exits Entrance(FIRE_TEMPLE_FIRST_ROOM, {[]{return true;}}), - Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && LogicFireBossDoorJump) || CanUse(HOVER_BOOTS) || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}));}}), + Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && (LogicFireBossDoorJump || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}))) || CanUse(HOVER_BOOTS));}}), }); areaTable[FIRE_TEMPLE_LOOP_ENEMIES] = Area("Fire Temple Loop Enemies", "Fire Temple", FIRE_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, { diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp b/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp index fea9fda..5e64aee 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp +++ b/app/jni/src/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp @@ -172,7 +172,7 @@ void AreaTable_Init_ForestTemple() { Entrance(FOREST_TEMPLE_WEST_CORRIDOR, {[]{return true;}}), Entrance(FOREST_TEMPLE_NW_OUTDOORS_UPPER, {[]{return CanUse(HOVER_BOOTS) || (LogicForestOutsideBackdoor && CanJumpslash && GoronBracelet);}}), Entrance(FOREST_TEMPLE_NW_CORRIDOR_TWISTED, {[]{return IsAdult && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}), - Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}), + Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return IsAdult && (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}), }); areaTable[FOREST_TEMPLE_NW_CORRIDOR_TWISTED] = Area("Forest Temple NW Corridor Twisted", "Forest Temple", FOREST_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, { diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/3drando/logic.cpp b/app/jni/src/soh/soh/Enhancements/randomizer/3drando/logic.cpp index 580c687..df127e6 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/3drando/logic.cpp +++ b/app/jni/src/soh/soh/Enhancements/randomizer/3drando/logic.cpp @@ -536,7 +536,7 @@ namespace Logic { Fish = HasBottle && FishAccess; Fairy = HasBottle && FairyAccess; - FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20); + FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20) && (BombBag || BombchusInLogic); CanPlayBowling = (BombchusInLogic && FoundBombchus) || (!BombchusInLogic && BombBag); HasBombchus = (BuyBombchus10 || BuyBombchus20 || (AmmoDrops.Is(AMMODROPS_BOMBCHU) && FoundBombchus)); diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer.cpp b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer.cpp index 4b9a84e..835621f 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -4207,7 +4207,7 @@ void RandomizerSettingsWindow::DrawElement() { break; case RO_LACS_GREG_REWARD: UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount", - "gRandomizeLacsStoneCount", 1, 4, "", 4, true, true, false); + "gRandomizeLacsStoneCount", 1, 4, "", 3, true, true, false); break; case RO_LACS_WILDCARD_REWARD: UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount", @@ -4236,7 +4236,7 @@ void RandomizerSettingsWindow::DrawElement() { break; case RO_LACS_GREG_REWARD: UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount", - "gRandomizeLacsMedallionCount", 1, 7, "", 7, true, true, false); + "gRandomizeLacsMedallionCount", 1, 7, "", 6, true, true, false); break; case RO_LACS_WILDCARD_REWARD: UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount", @@ -4265,7 +4265,7 @@ void RandomizerSettingsWindow::DrawElement() { break; case RO_LACS_GREG_REWARD: UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount", - "gRandomizeLacsRewardCount", 1, 10, "", 10, true, true, false); + "gRandomizeLacsRewardCount", 1, 10, "", 9, true, true, false); break; case RO_LACS_WILDCARD_REWARD: UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount", @@ -4294,7 +4294,7 @@ void RandomizerSettingsWindow::DrawElement() { break; case RO_LACS_GREG_REWARD: UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount", - "gRandomizeLacsDungeonCount", 1, 9, "", 9, true, true, false); + "gRandomizeLacsDungeonCount", 1, 9, "", 8, true, true, false); break; case RO_LACS_WILDCARD_REWARD: UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount", @@ -4689,7 +4689,11 @@ void RandomizerSettingsWindow::DrawElement() { excludedLocationString += std::to_string(excludedLocationIt); excludedLocationString += ","; } - CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str()); + if (excludedLocationString == "") { + CVarClear("gRandomizeExcludedLocations"); + } else { + CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str()); + } LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); } ImGui::SameLine(); @@ -4866,7 +4870,7 @@ void RandomizerSettingsWindow::DrawElement() { enabledTrickString += std::to_string(enabledTrickIt); enabledTrickString += ","; } - CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str()); + CVarClear("gRandomizeEnabledTricks"); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); } ImGui::SameLine(); @@ -5070,7 +5074,7 @@ void RandomizerSettingsWindow::DrawElement() { enabledTrickString += std::to_string(enabledTrickIt); enabledTrickString += ","; } - CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str()); + CVarClear("gRandomizeEnabledTricks"); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); } @@ -5108,7 +5112,11 @@ void RandomizerSettingsWindow::DrawElement() { enabledTrickString += std::to_string(enabledTrickIt); enabledTrickString += ","; } - CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str()); + if (enabledTrickString == "") { + CVarClear("gRandomizeEnabledTricks"); + } else { + CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str()); + } LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); } DrawTagChips(*rtObject.rtTags); diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index d9c2424..b9ca97a 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -107,8 +107,9 @@ std::map> checksByArea; bool areasFullyChecked[RCAREA_INVALID]; u32 areasSpoiled = 0; bool showVOrMQ; -s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)" -bool optCollapseAll; // A bool that will collapse all checks once +s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)" +s8 areaCheckTotals[RCAREA_INVALID]; +bool optCollapseAll; // A bool that will collapse all checks once bool optExpandAll; // A bool that will expand all checks once RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK; RandomizerCheckArea previousArea = RCAREA_INVALID; @@ -225,6 +226,26 @@ void TrySetAreas() { } } +void RecalculateAreaTotals() { + for (auto [rcArea, rcObjects] : checksByArea) { + if (rcArea == RCAREA_INVALID) { + return; + } + areaChecksGotten[rcArea] = 0; + areaCheckTotals[rcArea] = 0; + for (auto rcObj : rcObjects) { + if (!IsVisibleInCheckTracker(rcObj)) { + continue; + } + areaCheckTotals[rcArea]++; + if (gSaveContext.checkTrackerData[rcObj.rc].skipped || gSaveContext.checkTrackerData[rcObj.rc].status == RCSHOW_COLLECTED + || gSaveContext.checkTrackerData[rcObj.rc].status == RCSHOW_SAVED) { + areaChecksGotten[rcArea]++; + } + } + } +} + void SetCheckCollected(RandomizerCheck rc) { gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED; RandomizerCheckObject rcObj; @@ -233,10 +254,12 @@ void SetCheckCollected(RandomizerCheck rc) { } else { rcObj = RandomizerCheckObjects::GetAllRCObjects().find(rc)->second; } - if (!gSaveContext.checkTrackerData[rc].skipped) { - areaChecksGotten[rcObj.rcArea]++; - } else { - gSaveContext.checkTrackerData[rc].skipped = false; + if (IsVisibleInCheckTracker(rcObj)) { + if (!gSaveContext.checkTrackerData[rc].skipped) { + areaChecksGotten[rcObj.rcArea]++; + } else { + gSaveContext.checkTrackerData[rc].skipped = false; + } } SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); @@ -346,6 +369,7 @@ void ClearAreaChecksAndTotals() { for (auto& [rcArea, vec] : checksByArea) { vec.clear(); areaChecksGotten[rcArea] = 0; + areaCheckTotals[rcArea] = 0; } } @@ -425,9 +449,9 @@ void CheckTrackerLoadGame(int32_t fileNum) { TrySetAreas(); for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) { RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc]; - if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET || - !RandomizerCheckObjects::GetAllRCObjects().contains(rc)) + if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET || !RandomizerCheckObjects::GetAllRCObjects().contains(rc)) { continue; + } RandomizerCheckObject realRcObj; if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) { @@ -437,8 +461,11 @@ void CheckTrackerLoadGame(int32_t fileNum) { } checksByArea.find(realRcObj.rcArea)->second.push_back(realRcObj); - if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) { - areaChecksGotten[realRcObj.rcArea]++; + if (IsVisibleInCheckTracker(realRcObj)) { + areaCheckTotals[realRcObj.rcArea]++; + if (rcTrackerData.status == RCSHOW_COLLECTED || rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) { + areaChecksGotten[realRcObj.rcArea]++; + } } if (areaChecksGotten[realRcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(realRcObj.rcArea)) { @@ -463,6 +490,7 @@ void CheckTrackerLoadGame(int32_t fileNum) { checksByArea.find(startingArea)->second.push_back(linksPocket); areaChecksGotten[startingArea]++; + areaCheckTotals[startingArea]++; } showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_RANDOM_NUMBER || @@ -514,10 +542,6 @@ void CheckTrackerTransition(uint32_t sceneNum) { } void CheckTrackerFrame() { - if (IS_RANDO) { - hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1); - alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0); - } if (!GameInteractor::IsSaveLoaded()) { return; } @@ -787,9 +811,13 @@ void Teardown() { void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) { auto area = RandomizerCheckObjects::GetAllRCObjects().find(static_cast(check))->second.rcArea; - if (!gSaveContext.checkTrackerData[check].skipped && data.skipped) { + if ((!gSaveContext.checkTrackerData[check].skipped && data.skipped) || + ((gSaveContext.checkTrackerData[check].status != RCSHOW_COLLECTED && gSaveContext.checkTrackerData[check].status != RCSHOW_SAVED) && + (data.status == RCSHOW_COLLECTED || data.status == RCSHOW_SAVED))) { areaChecksGotten[area]++; - } else if (gSaveContext.checkTrackerData[check].skipped && !data.skipped) { + } else if ((gSaveContext.checkTrackerData[check].skipped && !data.skipped) || + ((gSaveContext.checkTrackerData[check].status == RCSHOW_COLLECTED || gSaveContext.checkTrackerData[check].status == RCSHOW_SAVED) && + (data.status != RCSHOW_COLLECTED && data.status != RCSHOW_SAVED))) { areaChecksGotten[area]--; } gSaveContext.checkTrackerData[check] = data; @@ -902,8 +930,7 @@ void CheckTrackerWindow::DrawElement() { for (auto& [rcArea, objs] : checksByArea) { RandomizerCheckArea thisArea = currentArea; - const int areaChecksTotal = static_cast(objs.size()); - thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaChecksTotal); + thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaCheckTotals[rcArea]); //Last Area needs to be cleaned up if (lastArea != RCAREA_INVALID && doDraw) { UIWidgets::PaddedSeparator(); @@ -940,10 +967,11 @@ void CheckTrackerWindow::DrawElement() { stemp = RandomizerCheckObjects::GetRCAreaName(rcArea) + "##TreeNode"; ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f)); - if (doingCollapseOrExpand) + if (doingCollapseOrExpand) { ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always); - else + } else { ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once); + } doDraw = ImGui::TreeNode(stemp.c_str()); ImGui::PopStyleColor(); ImGui::SameLine(); @@ -958,12 +986,14 @@ void CheckTrackerWindow::DrawElement() { if (isThisAreaSpoiled) { if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) { - if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(DungeonSceneLookupByArea(rcArea))) - ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaChecksTotal); - else - ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaChecksTotal); + if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains( + DungeonSceneLookupByArea(rcArea))) { + ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); + } else { + ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); + } } else { - ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaChecksTotal); + ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); } } else { ImGui::Text("???"); @@ -977,11 +1007,13 @@ void CheckTrackerWindow::DrawElement() { doAreaScroll = false; } for (auto rco : objs) { - if (doDraw && isThisAreaSpoiled && IsVisibleInCheckTracker(rco)) + if (IsVisibleInCheckTracker(rco) && doDraw && isThisAreaSpoiled) { DrawLocation(rco); + } } - if (doDraw) + if (doDraw) { ImGui::TreePop(); + } } areaMask <<= 1; } @@ -1200,8 +1232,9 @@ void UpdateAreaFullyChecked(RandomizerCheckArea area) { void UpdateAreas(RandomizerCheckArea area) { areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size(); - if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area)) + if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area)) { areasSpoiled |= (1 << area); + } } void UpdateAllOrdering() { @@ -1229,30 +1262,36 @@ bool CompareChecks(RandomizerCheckObject i, RandomizerCheckObject j) { bool iSaved = iShow.status == RCSHOW_SAVED; bool jCollected = jShow.status == RCSHOW_COLLECTED || jShow.status == RCSHOW_SAVED; bool jSaved = jShow.status == RCSHOW_SAVED; - if (!iCollected && jCollected) - return true; - else if (iCollected && !jCollected) - return false; - if (!iSaved && jSaved) + if (!iCollected && jCollected) { return true; - else if (iSaved && !jSaved) + } else if (iCollected && !jCollected) { return false; + } - if (!iShow.skipped && jShow.skipped) + if (!iSaved && jSaved) { return true; - else if (iShow.skipped && !jShow.skipped) + } else if (iSaved && !jSaved) { return false; + } - if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType)) + if (!iShow.skipped && jShow.skipped) { return true; - else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType)) + } else if (iShow.skipped && !jShow.skipped) { return false; + } - if (i.rc < j.rc) + if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType)) { return true; - else if (i.rc > j.rc) + } else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType)) { return false; + } + + if (i.rc < j.rc) { + return true; + } else if (i.rc > j.rc) { + return false; + } return false; } @@ -1270,47 +1309,54 @@ void DrawLocation(RandomizerCheckObject rcObj) { RandomizerCheckStatus status = checkData.status; bool skipped = checkData.skipped; if (status == RCSHOW_COLLECTED) { - if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) + if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) { return; + } mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default) : - CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default); + CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default); } else if (status == RCSHOW_SAVED) { - if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) + if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) { return; - mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default) : - CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default); + } + mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default) : + CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default); } else if (skipped) { - if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) + if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) { return; - mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default) : - CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default); + } + mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default) : + CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default); } else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) { - if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) + if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) { return; - mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default) : - CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default); + } + mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default) : + CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default); } else if (status == RCSHOW_SCUMMED) { - if (!showHidden && CVarGetInteger("gCheckTrackerKnownHide", 0)) + if (!showHidden && CVarGetInteger("gCheckTrackerScummedHide", 0)) { return; - mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) : - CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default); + } + mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) : + CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default); } else if (status == RCSHOW_UNCHECKED) { - if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) + if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) { return; - mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default) : - CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default); + } + mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default) : + CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default); } //Main Text txt = rcObj.rcShortName; - if (lastLocationChecked == rcObj.rc) + if (lastLocationChecked == rcObj.rc) { txt = "* " + txt; + } // Draw button - for Skipped/Seen/Scummed/Unchecked only if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) { @@ -1385,8 +1431,9 @@ void DrawLocation(RandomizerCheckObject rcObj) { break; } } - if (txt == "" && skipped) - txt = "Skipped"; //TODO language + if (txt == "" && skipped) { + txt = "Skipped"; // TODO language + } if (txt != "") { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f)); @@ -1412,8 +1459,9 @@ int hue = 0; void RainbowTick() { float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat("gCosmetics.RainbowSpeed", 0.6f)); for (auto& cvar : rainbowCVars) { - if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) + if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) { continue; + } Color_RGBA8 newColor; newColor.r = sin(freqHue + 0) * 127 + 128; @@ -1520,9 +1568,15 @@ void CheckTrackerSettingsWindow::DrawElement() { } UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers"); UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked."); - UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true); + if (UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true)) { + hideShopRightChecks = !hideShopRightChecks; + RecalculateAreaTotals(); + } UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops."); - UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, ""); + if (UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "")) { + alwaysShowGS = !alwaysShowGS; + RecalculateAreaTotals(); + } UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings."); ImGui::TableNextColumn(); @@ -1577,6 +1631,9 @@ void CheckTrackerWindow::InitElement() { GameInteractor::Instance->RegisterGameHook(CheckTrackerSceneFlagSet); GameInteractor::Instance->RegisterGameHook(CheckTrackerFlagSet); + hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1); + alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0); + LocationTable_Init(); } diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_entrance.c index e6ec3fe..eda9764 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -398,6 +398,11 @@ void Entrance_SetSavewarpEntrance(void) { gSaveContext.entranceIndex = 0x0486; // Gerudo Fortress -> Thieve's Hideout spawn 0 } else if (scene == SCENE_LINKS_HOUSE) { gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); + } else if (CVarGetInteger("gRememberSaveLocation", 0) && scene != SCENE_FAIRYS_FOUNTAIN && scene != SCENE_GROTTOS && + // Use the saved entrance value with remember save location, except when in grottos/fairy fountains or if + // the entrance index is -1 (new save) + gSaveContext.entranceIndex != -1) { + return; } else if (LINK_IS_CHILD) { gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); // Child Overworld Spawn } else { diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index f24ab81..f63b37b 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/app/jni/src/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -30,6 +30,8 @@ void DrawBottle(ItemTrackerItem item); void DrawQuest(ItemTrackerItem item); void DrawSong(ItemTrackerItem item); +int itemTrackerSectionId; + bool shouldUpdateVectors = true; std::vector mainWindowItems = {}; @@ -282,11 +284,6 @@ void ItemTrackerOnFrame() { } } -void SaveNotes(uint32_t fileNum) { - CVarSetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), std::string(std::begin(itemTrackerNotes), std::end(itemTrackerNotes)).c_str()); - LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); -} - bool IsValidSaveFile() { bool validSave = gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2; return validSave; @@ -734,7 +731,7 @@ void DrawNotes(bool resizeable = false) { } if ((ImGui::IsItemDeactivatedAfterEdit() || (notesNeedSave && notesIdleFrames > notesMaxIdleFrames)) && IsValidSaveFile()) { notesNeedSave = false; - SaveNotes(gSaveContext.fileNum); + SaveManager::Instance->SaveSection(gSaveContext.fileNum, itemTrackerSectionId, true); } ImGui::EndGroup(); } @@ -959,6 +956,26 @@ void UpdateVectors() { shouldUpdateVectors = false; } +void ItemTrackerInitFile(bool isDebug) { + itemTrackerNotes.clear(); + itemTrackerNotes.push_back(0); +} + +void ItemTrackerSaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { + SaveManager::Instance->SaveData("personalNotes", std::string(std::begin(itemTrackerNotes), std::end(itemTrackerNotes)).c_str()); +} + +void ItemTrackerLoadFile() { + std::string initialTrackerNotes = ""; + SaveManager::Instance->LoadData("personalNotes", initialTrackerNotes); + itemTrackerNotes.resize(initialTrackerNotes.length() + 1); + if (initialTrackerNotes != "") { + SohUtils::CopyStringToCharArray(itemTrackerNotes.Data, initialTrackerNotes.c_str(), itemTrackerNotes.size()); + } else { + itemTrackerNotes.push_back(0); + } +} + void ItemTrackerWindow::DrawElement() { UpdateVectors(); @@ -1223,14 +1240,9 @@ void ItemTrackerWindow::InitElement() { itemTrackerNotes.push_back(0); } - GameInteractor::Instance->RegisterGameHook([](uint32_t fileNum) { - const char* initialTrackerNotes = CVarGetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), ""); - itemTrackerNotes.resize(strlen(initialTrackerNotes) + 1); - strcpy(itemTrackerNotes.Data, initialTrackerNotes); - }); - GameInteractor::Instance->RegisterGameHook([](uint32_t fileNum) { - CVarSetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), ""); - LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); - }); + SaveManager::Instance->AddInitFunction(ItemTrackerInitFile); + itemTrackerSectionId = SaveManager::Instance->AddSaveFunction("itemTrackerData", 1, ItemTrackerSaveFile, true, -1); + SaveManager::Instance->AddLoadFunction("itemTrackerData", 1, ItemTrackerLoadFile); + GameInteractor::Instance->RegisterGameHook(ItemTrackerOnFrame); } diff --git a/app/jni/src/soh/soh/Enhancements/randomizer/savefile.cpp b/app/jni/src/soh/soh/Enhancements/randomizer/savefile.cpp index 33278d0..fb04eca 100644 --- a/app/jni/src/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/app/jni/src/soh/soh/Enhancements/randomizer/savefile.cpp @@ -207,6 +207,9 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.randomizerInf[i] = 0; } + // Reset triforce pieces collected + gSaveContext.triforcePiecesCollected = 0; + gSaveContext.cutsceneIndex = 0; // no intro cutscene // Starts pending ice traps out at 0 before potentially incrementing them down the line. gSaveContext.pendingIceTrapCount = 0; @@ -442,8 +445,5 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth } - // Reset triforce pieces collected - gSaveContext.triforcePiecesCollected = 0; - SetStartingItems(); } diff --git a/app/jni/src/soh/soh/Enhancements/tts/tts.cpp b/app/jni/src/soh/soh/Enhancements/tts/tts.cpp index 0b46cd1..9e5ff4a 100644 --- a/app/jni/src/soh/soh/Enhancements/tts/tts.cpp +++ b/app/jni/src/soh/soh/Enhancements/tts/tts.cpp @@ -168,7 +168,7 @@ void RegisterOnInterfaceUpdateHook() { prevTimer = timer; - if (!GameInteractor::IsSaveLoaded()) return; + if (!GameInteractor::IsSaveLoaded(true)) return; static int16_t lostHealth = 0; static int16_t prevHealth = 0; diff --git a/app/jni/src/soh/soh/OTRGlobals.cpp b/app/jni/src/soh/soh/OTRGlobals.cpp index 4c564af..64d70da 100644 --- a/app/jni/src/soh/soh/OTRGlobals.cpp +++ b/app/jni/src/soh/soh/OTRGlobals.cpp @@ -297,6 +297,7 @@ OTRGlobals::OTRGlobals() { }; // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) context = LUS::Context::CreateInstance("Ship of Harkinian", appShortName, "shipofharkinian.json", OTRFiles, {}, 3); + SPDLOG_INFO("Starting Ship of Harkinian version {}", (char*)gBuildVersion); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Animation, "Animation", std::make_shared()); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared()); @@ -1143,8 +1144,7 @@ extern "C" uint64_t GetUnixTimestamp() { auto time = std::chrono::system_clock::now(); auto since_epoch = time.time_since_epoch(); auto millis = std::chrono::duration_cast(since_epoch); - long now = millis.count(); - return now; + return (uint64_t)millis.count(); } // C->C++ Bridge @@ -2486,8 +2486,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { randoInf = RAND_INF_MERCHANTS_CARPET_SALESMAN; } messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(randoInf, textId, Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_ON_HINT); - } else if (Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) && - (textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)) { + } else if (textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); } else if (textId == TEXT_CURSED_SKULLTULA_PEOPLE) { actorParams = GET_PLAYER(play)->targetActor->params; @@ -2597,6 +2596,24 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla gfx_register_blended_texture(name, mask, replacement); } +extern "C" void Gfx_UnregisterBlendedTexture(const char* name) { + gfx_unregister_blended_texture(name); +} + +extern "C" void Gfx_TextureCacheDelete(const uint8_t* texAddr) { + char* imgName = (char*)texAddr; + + if (texAddr == nullptr) { + return; + } + + if (ResourceMgr_OTRSigCheck(imgName)) { + texAddr = (const uint8_t*)GetResourceDataByNameHandlingMQ(imgName); + } + + gfx_texture_cache_delete(texAddr); +} + void SoH_ProcessDroppedFiles(std::string filePath) { try { std::ifstream configStream(filePath); diff --git a/app/jni/src/soh/soh/OTRGlobals.h b/app/jni/src/soh/soh/OTRGlobals.h index ada030c..469f679 100644 --- a/app/jni/src/soh/soh/OTRGlobals.h +++ b/app/jni/src/soh/soh/OTRGlobals.h @@ -175,6 +175,8 @@ void Entrance_InitEntranceTrackingData(void); void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex); void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex); void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement); +void Gfx_UnregisterBlendedTexture(const char* name); +void Gfx_TextureCacheDelete(const uint8_t* addr); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); diff --git a/app/jni/src/soh/soh/SaveManager.cpp b/app/jni/src/soh/soh/SaveManager.cpp index e61cf92..d800398 100644 --- a/app/jni/src/soh/soh/SaveManager.cpp +++ b/app/jni/src/soh/soh/SaveManager.cpp @@ -9,6 +9,7 @@ #include #include "soh/Enhancements/boss-rush/BossRush.h" #include +#include "SohGui.hpp" #define NOGDI // avoid various windows defines that conflict with things in z64.h #include @@ -1025,51 +1026,67 @@ void SaveManager::SaveGlobal() { void SaveManager::LoadFile(int fileNum) { SPDLOG_INFO("Load File - fileNum: {}", fileNum); - assert(std::filesystem::exists(GetFileName(fileNum))); + std::filesystem::path fileName = GetFileName(fileNum); + assert(std::filesystem::exists(fileName)); InitFile(false); - std::ifstream input(GetFileName(fileNum)); - - saveBlock = nlohmann::json::object(); - input >> saveBlock; - if (!saveBlock.contains("version")) { - SPDLOG_ERROR("Save at " + GetFileName(fileNum).string() + " contains no version"); - assert(false); - } - switch (saveBlock["version"].get()) { - case 1: - for (auto& block : saveBlock["sections"].items()) { - int sectionVersion = block.value()["version"]; - std::string sectionName = block.key(); - if (!sectionLoadHandlers.contains(sectionName)) { - // Unloadable sections aren't necessarily errors, they are probably mods that were unloaded - // TODO report in a more noticeable manner - SPDLOG_WARN("Save " + GetFileName(fileNum).string() + " contains unloadable section " + sectionName); - continue; - } - SectionLoadHandler& handler = sectionLoadHandlers[sectionName]; - if (!handler.contains(sectionVersion)) { - // A section that has a loader without a handler for the specific version means that the user has a mod - // at an earlier version than the save has. In this case, the user probably wants to load the save. - // Report the error so that the user can rectify the error. - // TODO report in a more noticeable manner - SPDLOG_ERROR("Save " + GetFileName(fileNum).string() + " contains section " + sectionName + - " with an unloadable version " + std::to_string(sectionVersion)); - assert(false); - continue; - } - currentJsonContext = &block.value()["data"]; - handler[sectionVersion](); - } - break; - default: - SPDLOG_ERROR("Unrecognized save version " + std::to_string(saveBlock["version"].get()) + " in " + - GetFileName(fileNum).string()); + std::ifstream input(fileName); + + try { + saveBlock = nlohmann::json::object(); + input >> saveBlock; + if (!saveBlock.contains("version")) { + SPDLOG_ERROR("Save at " + fileName.string() + " contains no version"); assert(false); - break; + } + switch (saveBlock["version"].get()) { + case 1: + for (auto& block : saveBlock["sections"].items()) { + int sectionVersion = block.value()["version"]; + std::string sectionName = block.key(); + if (!sectionLoadHandlers.contains(sectionName)) { + // Unloadable sections aren't necessarily errors, they are probably mods that were unloaded + // TODO report in a more noticeable manner + SPDLOG_WARN("Save " + GetFileName(fileNum).string() + " contains unloadable section " + + sectionName); + continue; + } + SectionLoadHandler& handler = sectionLoadHandlers[sectionName]; + if (!handler.contains(sectionVersion)) { + // A section that has a loader without a handler for the specific version means that the user + // has a mod at an earlier version than the save has. In this case, the user probably wants to + // load the save. Report the error so that the user can rectify the error. + // TODO report in a more noticeable manner + SPDLOG_ERROR("Save " + GetFileName(fileNum).string() + " contains section " + sectionName + + " with an unloadable version " + std::to_string(sectionVersion)); + assert(false); + continue; + } + currentJsonContext = &block.value()["data"]; + handler[sectionVersion](); + } + break; + default: + SPDLOG_ERROR("Unrecognized save version " + std::to_string(saveBlock["version"].get()) + " in " + + GetFileName(fileNum).string()); + assert(false); + break; + } + InitMeta(fileNum); + GameInteractor::Instance->ExecuteHooks(fileNum); + } catch (const std::exception& e) { + input.close(); + std::filesystem::path newFile(LUS::Context::GetPathRelativeToAppDirectory("Save") + ("/file" + std::to_string(fileNum + 1) + "-" + std::to_string(GetUnixTimestamp()) + ".bak")); +#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("Error loading save file", "A problem occurred loading the save in slot " + std::to_string(fileNum + 1) + ".\nSave file corruption is suspected.\n" + + "The file has been renamed to prevent further issues."); } - InitMeta(fileNum); - GameInteractor::Instance->ExecuteHooks(fileNum); } void SaveManager::ThreadPoolWait() { diff --git a/app/jni/src/soh/soh/SohGui.cpp b/app/jni/src/soh/soh/SohGui.cpp index 8782915..8bd7430 100644 --- a/app/jni/src/soh/soh/SohGui.cpp +++ b/app/jni/src/soh/soh/SohGui.cpp @@ -125,6 +125,7 @@ namespace SohGui { std::shared_ptr mItemTrackerSettingsWindow; std::shared_ptr mItemTrackerWindow; std::shared_ptr mRandomizerSettingsWindow; + std::shared_ptr mModalWindow; void SetupGuiElements() { auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); @@ -183,9 +184,13 @@ namespace SohGui { gui->AddGuiWindow(mItemTrackerSettingsWindow); mRandomizerSettingsWindow = std::make_shared("gRandomizerSettingsEnabled", "Randomizer Settings"); gui->AddGuiWindow(mRandomizerSettingsWindow); + mModalWindow = std::make_shared("gOpenWindows.modalWindowEnabled", "Modal Window"); + gui->AddGuiWindow(mModalWindow); + mModalWindow->Show(); } void Destroy() { + mModalWindow = nullptr; mRandomizerSettingsWindow = nullptr; mItemTrackerWindow = nullptr; mItemTrackerSettingsWindow = nullptr; @@ -205,4 +210,8 @@ namespace SohGui { mConsoleWindow = nullptr; mSohMenuBar = nullptr; } + + void RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function button1callback, std::function button2callback) { + mModalWindow->RegisterPopup(title, message, button1, button2, button1callback, button2callback); + } } diff --git a/app/jni/src/soh/soh/SohGui.hpp b/app/jni/src/soh/soh/SohGui.hpp index 59333fd..dead44b 100644 --- a/app/jni/src/soh/soh/SohGui.hpp +++ b/app/jni/src/soh/soh/SohGui.hpp @@ -22,6 +22,7 @@ #include "Enhancements/randomizer/randomizer_entrance_tracker.h" #include "Enhancements/randomizer/randomizer_item_tracker.h" #include "Enhancements/randomizer/randomizer_settings_window.h" +#include "SohModals.h" #ifdef __cplusplus extern "C" { @@ -37,6 +38,7 @@ namespace SohGui { void SetupGuiElements(); void Draw(); void Destroy(); + void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function button1callback = nullptr, std::function button2callback = nullptr); } #endif /* SohGui_hpp */ diff --git a/app/jni/src/soh/soh/SohMenuBar.cpp b/app/jni/src/soh/soh/SohMenuBar.cpp index 5d4e5a2..ed7c88b 100644 --- a/app/jni/src/soh/soh/SohMenuBar.cpp +++ b/app/jni/src/soh/soh/SohMenuBar.cpp @@ -562,7 +562,7 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song."); UIWidgets::PaddedEnhancementCheckbox("Remember Save Location", "gRememberSaveLocation", true, false); UIWidgets::Tooltip("When loading a save, places Link at the last entrance he went through.\n" - "This doesn't work if the save was made in a grotto."); + "This doesn't work if the save was made in grottos/fairy fountains or dungeons."); UIWidgets::PaddedEnhancementCheckbox("Skip Magic Arrow Equip Animation", "gSkipArrowAnimation", true, false); UIWidgets::PaddedEnhancementCheckbox("Skip save confirmation", "gSkipSaveConfirmation", true, false); UIWidgets::Tooltip("Skip the \"Game saved.\" confirmation screen"); diff --git a/app/jni/src/soh/soh/SohModals.cpp b/app/jni/src/soh/soh/SohModals.cpp new file mode 100644 index 0000000..087bc8a --- /dev/null +++ b/app/jni/src/soh/soh/SohModals.cpp @@ -0,0 +1,54 @@ +#include "SohModals.h" +#include "ImGui/imgui.h" +#include +#include +#include +#include +#include "UIWidgets.hpp" +#include "OTRGlobals.h" +#include "z64.h" + +extern "C" PlayState* gPlayState; +struct SohModal { + std::string title_; + std::string message_; + std::string button1_; + std::string button2_; + std::function button1callback_; + std::function button2callback_; +}; +std::vector modals; + +void SohModalWindow::DrawElement() { + if (modals.size() > 0) { + SohModal curModal = modals.at(0); + if (!ImGui::IsPopupOpen(curModal.title_.c_str())) { + ImGui::OpenPopup(curModal.title_.c_str()); + } + if (ImGui::BeginPopupModal(curModal.title_.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) { + ImGui::Text(curModal.message_.c_str()); + if (ImGui::Button(curModal.button1_.c_str())) { + if (curModal.button1callback_ != nullptr) { + curModal.button1callback_(); + } + ImGui::CloseCurrentPopup(); + modals.erase(modals.begin()); + } + ImGui::SameLine(); + if (curModal.button2_ != "") { + if (ImGui::Button(curModal.button2_.c_str())) { + if (curModal.button2callback_ != nullptr) { + curModal.button2callback_(); + } + ImGui::CloseCurrentPopup(); + modals.erase(modals.begin()); + } + } + } + ImGui::EndPopup(); + } +} + +void SohModalWindow::RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function button1callback, std::function button2callback) { + modals.push_back({ title, message, button1, button2, button1callback, button2callback }); +} \ No newline at end of file diff --git a/app/jni/src/soh/soh/SohModals.h b/app/jni/src/soh/soh/SohModals.h new file mode 100644 index 0000000..6f5acb2 --- /dev/null +++ b/app/jni/src/soh/soh/SohModals.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "window/gui/GuiMenuBar.h" +#include "window/gui/GuiElement.h" + +class SohModalWindow : public LUS::GuiWindow { + public: + using LUS::GuiWindow::GuiWindow; + + void InitElement() override {}; + void DrawElement() override; + void UpdateElement() override {}; + void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function button1callback = nullptr, std::function button2callback = nullptr); +}; \ No newline at end of file diff --git a/app/jni/src/soh/soh/z_scene_otr.cpp b/app/jni/src/soh/soh/z_scene_otr.cpp index ef98a4b..be1840e 100644 --- a/app/jni/src/soh/soh/z_scene_otr.cpp +++ b/app/jni/src/soh/soh/z_scene_otr.cpp @@ -149,6 +149,13 @@ bool Scene_CommandMeshHeader(PlayState* play, LUS::ISceneCommand* cmd) { extern "C" void* func_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId); +bool OTRfunc_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId) { + + objectCtx->status[bankIndex].id = -objectId; + + return false; +} + bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) { // LUS::SetObjectList* cmdObj = static_pointer_cast(cmd); LUS::SetObjectList* cmdObj = (LUS::SetObjectList*)cmd; @@ -164,49 +171,30 @@ bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) { void* nextPtr; k = 0; - // i = play->objectCtx.unk_09; - i = 0; + i = play->objectCtx.unk_09; firstStatus = &play->objectCtx.status[0]; status = &play->objectCtx.status[i]; - for (int i = 0; i < cmdObj->objects.size(); i++) { - bool alreadyIncluded = false; - - for (int j = 0; j < play->objectCtx.num; j++) { - if (play->objectCtx.status[j].id == cmdObj->objects[i]) { - alreadyIncluded = true; - break; + // Loop until a mismatch in the object lists + // Then clear all object ids past that in the context object list and kill actors for those objects + for (i = play->objectCtx.unk_09, k = 0; i < play->objectCtx.num; i++, k++) { + if (k >= cmdObj->objects.size() || play->objectCtx.status[i].id != cmdObj->objects[k]) { + for (j = i; j < play->objectCtx.num; j++) { + play->objectCtx.status[j].id = OBJECT_INVALID; } - } - - if (!alreadyIncluded) { - play->objectCtx.status[play->objectCtx.num++].id = cmdObj->objects[i]; func_80031A28(play, &play->actorCtx); + break; } } - /* - while (i < play->objectCtx.num) { - if (status->id != *objectEntry) { - status2 = &play->objectCtx.status[i]; - for (j = i; j < play->objectCtx.num; j++) { - status2->id = OBJECT_INVALID; - status2++; - } - play->objectCtx.num = i; - func_80031A28(play, &play->actorCtx); - - continue; + // Continuing from the last index, add the remaining object ids from the command object list + for (; k < cmdObj->objects.size(); k++, i++) { + if (i < OBJECT_EXCHANGE_BANK_MAX - 1) { + OTRfunc_800982FC(&play->objectCtx, i, cmdObj->objects[k]); } - - i++; - k++; - objectEntry++; - status++; } play->objectCtx.num = i; - */ return false; } diff --git a/app/jni/src/soh/src/code/z_actor.c b/app/jni/src/soh/src/code/z_actor.c index 79a3c8a..c1dc66e 100644 --- a/app/jni/src/soh/src/code/z_actor.c +++ b/app/jni/src/soh/src/code/z_actor.c @@ -1219,8 +1219,7 @@ void Actor_Init(Actor* actor, PlayState* play) { CollisionCheck_InitInfo(&actor->colChkInfo); actor->floorBgId = BGCHECK_SCENE; ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f); - //if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) - { + if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { //Actor_SetObjectDependency(play, actor); actor->init(actor, play); actor->init = NULL; @@ -2861,11 +2860,19 @@ s32 func_800314D4(PlayState* play, Actor* actor, Vec3f* arg2, f32 arg3) { if ((arg2->z > -actor->uncullZoneScale) && (arg2->z < (actor->uncullZoneForward + actor->uncullZoneScale))) { var = (arg3 < 1.0f) ? 1.0f : 1.0f / arg3; - if ((((fabsf(arg2->x) - actor->uncullZoneScale) * var) < 2.0f) && - (((arg2->y + actor->uncullZoneDownward) * var) > -2.0f) && - (((arg2->y - actor->uncullZoneScale) * var) < 2.0f)) { + // #region SoH [Widescreen support] + // Doors will cull quite noticeably on wider screens. For these actors the zone is increased + f32 limit = 1.0f; + if (((actor->id == ACTOR_EN_DOOR) || (actor->id == ACTOR_DOOR_SHUTTER)) && CVarGetInteger("gIncreaseDoorUncullZones", 1)) { + limit = 2.0f; + } + + if ((((fabsf(arg2->x) - actor->uncullZoneScale) * var) < limit) && + (((arg2->y + actor->uncullZoneDownward) * var) > -limit) && + (((arg2->y - actor->uncullZoneScale) * var) < limit)) { return true; } + // #endregion } return false; @@ -3129,6 +3136,9 @@ void Actor_FreeOverlay(ActorDBEntry* dbEntry) { osSyncPrintf(VT_RST); } +// SoH: Flag to check if actors are being spawned from the actor entry list +// This flag is checked against to allow actors which dont have an objectBankIndex in the objectCtx slot/status array to spawn +// An example of what this fixes, is that it allows hookshot to be used as child int gMapLoading = 0; Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 posX, f32 posY, f32 posZ, diff --git a/app/jni/src/soh/src/code/z_bgcheck.c b/app/jni/src/soh/src/code/z_bgcheck.c index 4acc68e..7c99e7c 100644 --- a/app/jni/src/soh/src/code/z_bgcheck.c +++ b/app/jni/src/soh/src/code/z_bgcheck.c @@ -1902,7 +1902,7 @@ s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResul s32 bgId2; f32 nx, ny, nz; // unit normal of polygon - if (CVarGetInteger("gNoClip", 0) != 0) { + if (CVarGetInteger("gNoClip", 0) && actor != NULL && actor->id == ACTOR_PLAYER) { return false; } diff --git a/app/jni/src/soh/src/code/z_camera.c b/app/jni/src/soh/src/code/z_camera.c index a177e29..4353d39 100644 --- a/app/jni/src/soh/src/code/z_camera.c +++ b/app/jni/src/soh/src/code/z_camera.c @@ -7887,7 +7887,7 @@ s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags) { } } - // Clear free camera if an action is performed that would move the camera (targeting, first person, talking) + // Clear free look if an action is performed that would move the camera (targeting, first person, talking) if (CVarGetInteger("gFreeCamera", 0) && SetCameraManual(camera) == 1 && ((mode >= CAM_MODE_TARGET && mode <= CAM_MODE_BATTLE) || (mode >= CAM_MODE_FIRSTPERSON && mode <= CAM_MODE_CLIMBZ) || mode == CAM_MODE_HANGZ || diff --git a/app/jni/src/soh/src/code/z_camera_data.inc b/app/jni/src/soh/src/code/z_camera_data.inc index b9e0ba8..90b1874 100644 --- a/app/jni/src/soh/src/code/z_camera_data.inc +++ b/app/jni/src/soh/src/code/z_camera_data.inc @@ -16,9 +16,11 @@ typedef struct { union { u32 unk_00; struct { - u32 unk_bit0 : 1; - u32 unk_bit1 : 1; - u32 validModes : 30; + // SoH [Port] These bitfield values are unused and led to shifting in validModes for little endian systems + // Removing those so that validModes can be a complete 32 bit value + // u32 unk_bit0 : 1; + // u32 unk_bit1 : 1; + u32 validModes; }; }; CameraMode* cameraModes; diff --git a/app/jni/src/soh/src/code/z_map_exp.c b/app/jni/src/soh/src/code/z_map_exp.c index c4db009..213ee01 100644 --- a/app/jni/src/soh/src/code/z_map_exp.c +++ b/app/jni/src/soh/src/code/z_map_exp.c @@ -524,7 +524,6 @@ void Map_Init(PlayState* play) { interfaceCtx->unk_25A = -1; interfaceCtx->mapSegment = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*)); - interfaceCtx->mapSegmentName = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*)); // "MAP texture initialization scene_data_ID=%d mapSegment=%x" osSyncPrintf("\n\n\nMAP テクスチャ初期化 scene_data_ID=%d\nmapSegment=%x\n\n", play->sceneNum, interfaceCtx->mapSegment, play); diff --git a/app/jni/src/soh/src/code/z_message_PAL.c b/app/jni/src/soh/src/code/z_message_PAL.c index 0988cf0..1196dd7 100644 --- a/app/jni/src/soh/src/code/z_message_PAL.c +++ b/app/jni/src/soh/src/code/z_message_PAL.c @@ -8,6 +8,7 @@ #include "textures/message_static/message_static.h" #include "textures/message_texture_static/message_texture_static.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/OTRGlobals.h" @@ -3084,7 +3085,9 @@ void Message_Draw(PlayState* play) { POLY_OPA_DISP = plusOne; } plusOne = Graph_GfxPlusOne(polyOpaP = POLY_OPA_DISP); - gSPDisplayList(OVERLAY_DISP++, plusOne); + if (!GameInteractor_NoUIActive()) { + gSPDisplayList(OVERLAY_DISP++, plusOne); + } Message_DrawMain(play, &plusOne); gSPEndDisplayList(plusOne++); Graph_BranchDlist(polyOpaP, plusOne); diff --git a/app/jni/src/soh/src/code/z_scene.c b/app/jni/src/soh/src/code/z_scene.c index 2a0a5d5..579e84e 100644 --- a/app/jni/src/soh/src/code/z_scene.c +++ b/app/jni/src/soh/src/code/z_scene.c @@ -83,9 +83,10 @@ void Object_UpdateBank(ObjectContext* objectCtx) { RomFile* objectFile; size_t size; - /* + for (i = 0; i < objectCtx->num; i++) { if (status->id < 0) { + /* if (status->dmaRequest.vromAddr == 0) { osCreateMesgQueue(&status->loadQueue, &status->loadMsg, 1); objectFile = &gObjectTable[-status->id]; @@ -96,10 +97,12 @@ void Object_UpdateBank(ObjectContext* objectCtx) { } else if (!osRecvMesg(&status->loadQueue, NULL, OS_MESG_NOBLOCK)) { status->id = -status->id; } + */ + status->id = -status->id; } status++; } - */ + } s32 Object_GetIndex(ObjectContext* objectCtx, s16 objectId) { diff --git a/app/jni/src/soh/src/code/z_sram.c b/app/jni/src/soh/src/code/z_sram.c index 1ce20ab..0152ecf 100644 --- a/app/jni/src/soh/src/code/z_sram.c +++ b/app/jni/src/soh/src/code/z_sram.c @@ -59,65 +59,68 @@ void Sram_OpenSave() { Save_LoadFile(); - if (!CVarGetInteger("gRememberSaveLocation", 0) || gSaveContext.savedSceneNum == SCENE_FAIRYS_FOUNTAIN || - gSaveContext.savedSceneNum == SCENE_GROTTOS) { - switch (gSaveContext.savedSceneNum) { - case SCENE_DEKU_TREE: - case SCENE_DODONGOS_CAVERN: - case SCENE_JABU_JABU: - case SCENE_FOREST_TEMPLE: - case SCENE_FIRE_TEMPLE: - case SCENE_WATER_TEMPLE: - case SCENE_SPIRIT_TEMPLE: - case SCENE_SHADOW_TEMPLE: - case SCENE_BOTTOM_OF_THE_WELL: - case SCENE_ICE_CAVERN: - case SCENE_GANONS_TOWER: - case SCENE_GERUDO_TRAINING_GROUND: - case SCENE_THIEVES_HIDEOUT: - case SCENE_INSIDE_GANONS_CASTLE: - gSaveContext.entranceIndex = dungeonEntrances[gSaveContext.savedSceneNum]; - break; - case SCENE_DEKU_TREE_BOSS: - gSaveContext.entranceIndex = 0; - break; - case SCENE_DODONGOS_CAVERN_BOSS: - gSaveContext.entranceIndex = 4; - break; - case SCENE_JABU_JABU_BOSS: - gSaveContext.entranceIndex = 0x28; - break; - case SCENE_FOREST_TEMPLE_BOSS: - gSaveContext.entranceIndex = 0x169; - break; - case SCENE_FIRE_TEMPLE_BOSS: - gSaveContext.entranceIndex = 0x165; - break; - case SCENE_WATER_TEMPLE_BOSS: - gSaveContext.entranceIndex = 0x10; - break; - case SCENE_SPIRIT_TEMPLE_BOSS: - gSaveContext.entranceIndex = 0x82; - break; - case SCENE_SHADOW_TEMPLE_BOSS: - gSaveContext.entranceIndex = 0x37; - break; - case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: - case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: - case SCENE_GANONDORF_BOSS: - case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: - case SCENE_GANON_BOSS: - gSaveContext.entranceIndex = 0x41B; - break; + switch (gSaveContext.savedSceneNum) { + case SCENE_DEKU_TREE: + case SCENE_DODONGOS_CAVERN: + case SCENE_JABU_JABU: + case SCENE_FOREST_TEMPLE: + case SCENE_FIRE_TEMPLE: + case SCENE_WATER_TEMPLE: + case SCENE_SPIRIT_TEMPLE: + case SCENE_SHADOW_TEMPLE: + case SCENE_BOTTOM_OF_THE_WELL: + case SCENE_ICE_CAVERN: + case SCENE_GANONS_TOWER: + case SCENE_GERUDO_TRAINING_GROUND: + case SCENE_THIEVES_HIDEOUT: + case SCENE_INSIDE_GANONS_CASTLE: + gSaveContext.entranceIndex = dungeonEntrances[gSaveContext.savedSceneNum]; + break; + case SCENE_DEKU_TREE_BOSS: + gSaveContext.entranceIndex = 0; + break; + case SCENE_DODONGOS_CAVERN_BOSS: + gSaveContext.entranceIndex = 4; + break; + case SCENE_JABU_JABU_BOSS: + gSaveContext.entranceIndex = 0x28; + break; + case SCENE_FOREST_TEMPLE_BOSS: + gSaveContext.entranceIndex = 0x169; + break; + case SCENE_FIRE_TEMPLE_BOSS: + gSaveContext.entranceIndex = 0x165; + break; + case SCENE_WATER_TEMPLE_BOSS: + gSaveContext.entranceIndex = 0x10; + break; + case SCENE_SPIRIT_TEMPLE_BOSS: + gSaveContext.entranceIndex = 0x82; + break; + case SCENE_SHADOW_TEMPLE_BOSS: + gSaveContext.entranceIndex = 0x37; + break; + case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: + case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: + case SCENE_GANONDORF_BOSS: + case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: + case SCENE_GANON_BOSS: + gSaveContext.entranceIndex = 0x41B; + break; - default: - if (gSaveContext.savedSceneNum != SCENE_LINKS_HOUSE) { - gSaveContext.entranceIndex = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? 0xBB : 0x5F4; - } else { - gSaveContext.entranceIndex = 0xBB; - } + default: + // Use the saved entrance value with remember save location, except when in grottos/fairy fountains + if (CVarGetInteger("gRememberSaveLocation", 0) && gSaveContext.savedSceneNum != SCENE_FAIRYS_FOUNTAIN && + gSaveContext.savedSceneNum != SCENE_GROTTOS) { break; - } + } + + if (gSaveContext.savedSceneNum != SCENE_LINKS_HOUSE) { + gSaveContext.entranceIndex = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? 0xBB : 0x5F4; + } else { + gSaveContext.entranceIndex = 0xBB; + } + break; } osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); diff --git a/app/jni/src/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/app/jni/src/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index deb7616..1f27915 100644 --- a/app/jni/src/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/app/jni/src/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -10,10 +10,6 @@ #include // malloc #include // memcpy -// OTRTODO: Replace usage of this method when we can clear the cache -// for a single texture without the need of a DL opcode in the render code -void gfx_texture_cache_clear(); - #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define LAVA_TEX_WIDTH 32 @@ -123,7 +119,9 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() { sMaskTexLava[i] = maskVal; } } + Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, NULL); + Gfx_TextureCacheDelete(sMaskTexLava); return; } @@ -165,7 +163,9 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() { } } - gfx_texture_cache_clear(); + Gfx_TextureCacheDelete(sMaskTexLava); + Gfx_TextureCacheDelete(sLavaWavyTex); + Gfx_TextureCacheDelete(sLavaFloorModifiedTex); } void func_808C12C4(u8* arg1, s16 arg2) { @@ -228,6 +228,7 @@ void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) { } free(sp54); + Gfx_TextureCacheDelete(sLavaWavyTexRaw); } // Modified to support CPU modified texture with the resource system @@ -255,6 +256,8 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) { temp_s3[i + temp2] = sp54[i + i2]; } } + + Gfx_TextureCacheDelete(sLavaWavyTex); } void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) { @@ -373,6 +376,13 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL); + // Clear cache for masks + Gfx_TextureCacheDelete(sMaskTex8x16); + Gfx_TextureCacheDelete(sMaskTex8x32); + Gfx_TextureCacheDelete(sMaskTex16x16); + Gfx_TextureCacheDelete(sMaskTex16x32); + Gfx_TextureCacheDelete(sMaskTex32x16); + BossDodongo_RegisterBlendedLavaTextureUpdate(); // Register alt listener to update the blended lava for the replacement texture based on alt path @@ -1206,6 +1216,7 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) { } } else { sMaskTexLava[new_var] = 1; + Gfx_TextureCacheDelete(sMaskTexLava); } this->unk_1C2 += 37; @@ -1345,18 +1356,6 @@ void BossDodongo_Draw(Actor* thisx, PlayState* play) { gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTex32x16); } - gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTexLava); - - // Using WORK_DISP to invalidate these textures as they are used in drawing the scene textures which happens - // before actors are drawn. WORK_DISP comes before POLAY_OPA_DISP. It is probably not meant for this, but it - // at least works for now. - // Alternatively, having a way to invalidate just these pointers from the Update func should be sufficient. - if (sLavaFloorModifiedTexRaw != NULL) { - gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTexRaw); - } else { - gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTex); - } - if ((this->unk_1C0 >= 2) && (this->unk_1C0 & 1)) { POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 255, 255, 0, 900, 1099); } else { diff --git a/app/jni/src/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c b/app/jni/src/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c index 5f3737b..357b2c0 100644 --- a/app/jni/src/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c +++ b/app/jni/src/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c @@ -192,11 +192,11 @@ void DemoGj_Explode(DemoGj* this, PlayState* play, Vec3f* initialPos, Vec3f* dir phi_s0 = 0x21; } - Gfx* gfx = ResourceMgr_LoadGfxByName(gGanonRubbleDL); - + // SoH [Port] Changed from &gGanonsCastleRubbleAroundArenaDL[28] to gGanonRubbleDL as it seems this was an error in the original rom/decomp + // Other calls to EffectSsKakera_Spawn with OBJECT_GEFF use gGanonRubbleDL, so this change is to match that EffectSsKakera_Spawn(play, &explosionPos, &velocity, initialPos, -200, phi_s0, 10, 10, 0, Rand_ZeroOne() * 20.0f + 20.0f, 20, 300, (s32)(Rand_ZeroOne() * 30.0f) + 30, -1, - OBJECT_GEFF, gfx); + OBJECT_GEFF, gGanonRubbleDL); theta += 0x2AAA; } diff --git a/app/jni/src/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c b/app/jni/src/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c index ae09cb4..9ce27f4 100644 --- a/app/jni/src/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c +++ b/app/jni/src/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c @@ -101,6 +101,9 @@ void EnDntJiji_Destroy(Actor* thisx, PlayState* play) { } void EnDntJiji_SetFlower(EnDntJiji* this, PlayState* play) { + // SOH: Due to removed object dependencies, parent was still NULL when Init was called. In order to properly set + // stage, redo it here now that we are a frame later. + this->stage = (EnDntDemo*)this->actor.parent; if (this->actor.bgCheckFlags & 1) { this->flowerPos = this->actor.world.pos; this->actionFunc = EnDntJiji_SetupWait; diff --git a/app/jni/src/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/app/jni/src/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 037b4b5..6aa1b7d 100644 --- a/app/jni/src/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/app/jni/src/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -1027,8 +1027,8 @@ void EnGirlA_BuyEvent_ObtainBombchuPack(PlayState* play, EnGirlA* this) { Rupees_ChangeBy(-this->basePrice); // Normally, buying a bombchu pack sets a flag indicating the pack is now sold out - // If they're in logic for rando, skip setting that flag so they can be purchased repeatedly - if (IS_RANDO && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC)) { + // If we're in rando, skip setting that flag so they can be purchased repeatedly + if (IS_RANDO) { return; } @@ -1255,8 +1255,7 @@ void EnGirlA_InitializeItemAction(EnGirlA* this, PlayState* play) { this->itemGiveFunc = itemEntry->itemGiveFunc; this->buyEventFunc = itemEntry->buyEventFunc; // If chus are in logic, make the 10 pack affordable without a wallet upgrade - if (IS_RANDO && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) && - this->getItemId == GI_BOMBCHUS_10) { + if (IS_RANDO && this->getItemId == GI_BOMBCHUS_10) { this->basePrice = 99; } else { this->basePrice = itemEntry->price; diff --git a/app/jni/src/soh/src/overlays/actors/ovl_En_Part/z_en_part.c b/app/jni/src/soh/src/overlays/actors/ovl_En_Part/z_en_part.c index e1b65ef..be1143f 100644 --- a/app/jni/src/soh/src/overlays/actors/ovl_En_Part/z_en_part.c +++ b/app/jni/src/soh/src/overlays/actors/ovl_En_Part/z_en_part.c @@ -8,6 +8,8 @@ #include "objects/object_tite/object_tite.h" #include "objects/object_ik/object_ik.h" +#include // strcmp + #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED void EnPart_Init(Actor* thisx, PlayState* play); @@ -297,11 +299,11 @@ void EnPart_Draw(Actor* thisx, PlayState* play) { gSPSegment(POLY_OPA_DISP++, 0x08, func_80ACEAC0(play->state.gfxCtx, 255, 255, 255, 180, 180, 180)); gSPSegment(POLY_OPA_DISP++, 0x09, func_80ACEAC0(play->state.gfxCtx, 225, 205, 115, 25, 20, 0)); gSPSegment(POLY_OPA_DISP++, 0x0A, func_80ACEAC0(play->state.gfxCtx, 225, 205, 115, 25, 20, 0)); - } else if ((thisx->params == 9) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) { + } else if ((thisx->params == 9) && (strcmp((const char*)this->displayList, object_tite_DL_002FF0) == 0)) { gSPSegment(POLY_OPA_DISP++, 0x08, object_tite_Tex_001300); gSPSegment(POLY_OPA_DISP++, 0x09, object_tite_Tex_001700); gSPSegment(POLY_OPA_DISP++, 0x0A, object_tite_Tex_001900); - } else if ((thisx->params == 10) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) { + } else if ((thisx->params == 10) && (strcmp((const char*)this->displayList, object_tite_DL_002FF0) == 0)) { gSPSegment(POLY_OPA_DISP++, 0x08, object_tite_Tex_001B00); gSPSegment(POLY_OPA_DISP++, 0x09, object_tite_Tex_001F00); gSPSegment(POLY_OPA_DISP++, 0x0A, object_tite_Tex_002100); diff --git a/app/jni/src/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c b/app/jni/src/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c index 6be42b4..fe4a777 100644 --- a/app/jni/src/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c +++ b/app/jni/src/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c @@ -821,19 +821,6 @@ void func_80AF3F20(EnRu2* this, PlayState* play) { void EnRu2_Draw(Actor* thisx, PlayState* play) { EnRu2* this = (EnRu2*)thisx; - // FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings. - // Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so - // it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the - // earring texture loads into the same place in tmem as the brick texture, so when it comes to rendering, TEXEL1 - // uses the earring texture with diffrent clamp settings, and it displays without noticeable error. However, both - // texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses - // our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces - // TEXEL1 with TEXEL0, which is most likely the original intention, and all is well. - Gfx* gfx = ResourceMgr_LoadGfxByName(gAdultRutoHeadDL); - Gfx patch = gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED, TEXEL0, 0, - PRIM_LOD_FRAC, COMBINED); - gfx[0xA2] = patch; - if ((this->drawConfig < 0) || (this->drawConfig >= ARRAY_COUNT(sDrawFuncs)) || (sDrawFuncs[this->drawConfig] == 0)) { // "Draw Mode is improper!" diff --git a/app/jni/src/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/app/jni/src/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index 49e88f9..f6c8707 100644 --- a/app/jni/src/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/app/jni/src/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -16,6 +16,7 @@ void EnSi_Init(Actor* thisx, PlayState* play); void EnSi_Destroy(Actor* thisx, PlayState* play); void EnSi_Update(Actor* thisx, PlayState* play); void EnSi_Draw(Actor* thisx, PlayState* play); +void EnSi_Reset(); s32 func_80AFB748(EnSi* this, PlayState* play); void func_80AFB768(EnSi* this, PlayState* play); @@ -61,7 +62,7 @@ const ActorInit En_Si_InitVars = { (ActorFunc)EnSi_Destroy, (ActorFunc)EnSi_Update, (ActorFunc)EnSi_Draw, - NULL, + (ActorResetFunc)EnSi_Reset, }; void EnSi_Init(Actor* thisx, PlayState* play) { @@ -224,6 +225,11 @@ void EnSi_Draw(Actor* thisx, PlayState* play) { } } +void EnSi_Reset() { + textId = 0xB4; + giveItemId = ITEM_SKULL_TOKEN; +} + void Randomizer_UpdateSkullReward(EnSi* this, PlayState* play) { Player* player = GET_PLAYER(play); diff --git a/app/jni/src/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/app/jni/src/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index bb8dd14..59b6367 100644 --- a/app/jni/src/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/app/jni/src/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -3036,11 +3036,7 @@ void FileChoose_LoadGame(GameState* thisx) { Entrance_Init(); // Handle randomized spawn positions after the save context has been setup from load - // When remeber save location is on, set save warp if the save was in an a grotto, or - // the entrance index is -1 from shuffle overwarld spawn - if (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES) && ((!CVarGetInteger("gRememberSaveLocation", 0) || - gSaveContext.savedSceneNum == SCENE_FAIRYS_FOUNTAIN || gSaveContext.savedSceneNum == SCENE_GROTTOS) || - (CVarGetInteger("gRememberSaveLocation", 0) && Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_SPAWNS) && gSaveContext.entranceIndex == -1))) { + if (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { Entrance_SetSavewarpEntrance(); } } diff --git a/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c b/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c index 28723d3..34dc47a 100644 --- a/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c +++ b/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c @@ -312,6 +312,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gQuestIconGoldSkulltulaTex, 24, 24, 8); } + // Unique index for both pulse phases + uint8_t palettePulseIdx = (mapBgPulseStage ? 40 : 20) - mapBgPulseTimer; + if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) { stepR = (mapBgPulseR - mapBgPulseColors[mapBgPulseStage][0]) / mapBgPulseTimer; stepG = (mapBgPulseG - mapBgPulseColors[mapBgPulseStage][1]) / mapBgPulseTimer; @@ -324,6 +327,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { interfaceCtx->mapPalette[28] = (rgba16 & 0xFF00) >> 8; interfaceCtx->mapPalette[29] = rgba16 & 0xFF; + interfaceCtx->mapPalettesPulse[palettePulseIdx][28] = (rgba16 & 0xFF00) >> 8; + interfaceCtx->mapPalettesPulse[palettePulseIdx][29] = rgba16 & 0xFF; + mapBgPulseTimer--; if (mapBgPulseTimer == 0) { mapBgPulseStage ^= 1; @@ -335,7 +341,8 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_POINT); gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); - gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalette); + // Use a unique palette address per frame so the renderer/shader can cache all variations + gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalettesPulse[palettePulseIdx]); gDPSetTextureLUT(POLY_KAL_DISP++, G_TT_RGBA16); u8 mirroredWorld = CVarGetInteger("gMirroredWorld", 0); @@ -349,10 +356,6 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[60], 8, 0); - // The dungeon map textures are recreated each frame, so always invalidate them - gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[0]); - gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[1]); - gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH, MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); diff --git a/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 877ea7e..1475472 100644 --- a/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/app/jni/src/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -1205,8 +1205,6 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, void** textures) { return gfx; } -static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT]; - void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { static Color_RGB8 D_8082ACF4[12] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 255, 255, 0 }, { 0, 0, 0 }, @@ -1375,10 +1373,6 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { } } - // Need to invalidate the blend mask every frame. Ideally this would be done in KaleidoScope_DrawDungeonMap - // but the reference is not shared between files - gSPInvalidateTexCache(POLY_KAL_DISP++, mapBlendMask); - if (pauseCtx->pageIndex) { // pageIndex != PAUSE_ITEM gDPPipeSync(OVERLAY_DISP++); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); @@ -3326,6 +3320,7 @@ static uint8_t mapLeftTexModified[MAP_48x85_TEX_SIZE]; static uint8_t mapRightTexModified[MAP_48x85_TEX_SIZE]; static uint8_t* mapLeftTexModifiedRaw = NULL; static uint8_t* mapRightTexModifiedRaw = NULL; +static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT]; // Load dungeon maps into the interface context // SoH [General] - Modified to account for our resource system and HD textures @@ -3357,19 +3352,16 @@ void KaleidoScope_LoadDungeonMap(PlayState* play) { size_t size = (width * height) / 2; // account for CI4 size // Resource size being larger than the calculated CI size means it is most likely not a CI4 texture - // Abort early end undo the blended effect by clearing the mask to avoid crashing + // Abort early and unregister the blended effect to avoid crashing if (size < ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0])) { - if (mapBlendMask[0] != 0) { - for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) { - mapBlendMask[i] = 0; - } - } - interfaceCtx->mapSegment[0] = NULL; interfaceCtx->mapSegment[1] = NULL; - Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, NULL); - Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, NULL); + Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[0]); + Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[1]); + + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]); return; } @@ -3404,6 +3396,11 @@ void KaleidoScope_LoadDungeonMap(PlayState* play) { Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, interfaceCtx->mapSegment[0]); Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, interfaceCtx->mapSegment[1]); + + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegment[0]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegment[1]); } static uint8_t registeredDungeonMapTextureHook = false; @@ -3444,6 +3441,11 @@ void KaleidoScope_UpdateDungeonMap(PlayState* play) { KaleidoScope_LoadDungeonMap(play); Map_SetFloorPalettesData(play, pauseCtx->dungeonMapSlot - 3); + // Copy the map palette values to all pulse palettes + for (uint8_t i = 0; i < ARRAY_COUNT(interfaceCtx->mapPalettesPulse); i++) { + memcpy(interfaceCtx->mapPalettesPulse[i], interfaceCtx->mapPalette, sizeof(interfaceCtx->mapPalette)); + } + s32 size = MAP_48x85_TEX_SIZE; if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9f6a7e1..b724dde 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,8 +4,8 @@ --> diff --git a/app/src/main/assets/soh.otr b/app/src/main/assets/soh.otr index f39c3fc865b7744ad23d4953a6240306145713f4..9ffa296e14c9c92145f0b346f123d5dd5eb6a398 100644 GIT binary patch delta 2307 zcmdo0Lh9fPDN)~mKq(yt1_sA1Yz&MH%nXieSr{BAimEcQHd?n@GqzeYwOTW8wPs0N zQh#BVX>(M5*89-K9Jx73OSWvC{V1~|bg3D?S7>&KQ*iaw4zs@WrC3UR+JokO?#67LA?=Rupm)`g>v3{@hY7f8L zDYAEZ9eLEY%}MS2ezAC~R_@Kl1vU#SCH{mouQt%BKP1ljGCgfUgCEQNy|diXeq9u0 zkoGHA*72CmeJI4>=8Z{#>(7bk9Se08J@-Sor13>emxbikEk930mF#-;rd|C0tE$i* z=FF z-|kT?OUmWfKDAQnEL4jtOnDzU^|yE28=G&_tslwRZoR5BUF+8E%=(fY@{NZtu4eIn z!FJuxN=*82U81<~FZZ_jA~Wt=zkIUd%|D&A7Y&~xFO_p?DKqEIH?`3CnOJsk;e_A8 zQ|>hdWSV`5Si`w&$Db8H_H6sR>Cyf5H=Nw6eww>|-5rrL^=St0oWARI4xJTii~nwn zYOQ*~bve!dvGI!s5ufb;*U9@b)~CFGDF5%F{J;8&i!+|YcXMYk-mDJ#Rn0QvwOU`$ zGe))z%qPoc2c~4KyWXw*^`z_1-%6W;3>C7ByyxatHJ_b(5&-590k^Krh-&YyB`7yg=Z_^8ZumtBihb+#-B*54~U(=@)$clnCcOFob5 zmA_7WC-FR(>3GZ|hvPYgeyO2?&x`k8Rk~WZsY|x`fcu1Kj)>W27Rq`PCw0&NcA#N1 z+uh|imvT6|)g~@~z|cMQ?U#?&T8|ue4P*}W`hJuB?zPPq@4uftYn`X~rAsG%2*zwm zTUn)CGOc7KD@P{R=M1}(HUIl6y*U%_bIq<_B7LNOvIX;&=Kl-UUOz3k{`qB3p6%Dq z88q)Z_ElcGY`K+F*`hboUBmy)_WH=ru{L;4bF-Y771vSDv$s}#Tj%}h%-j4dwsIx@ znum+B&)E5H++DsUf5q&kmggu)>ln?GE1iK(8=A0&3i+S>qI?Gh~Di} zqQ1!KK;_mSlCk1GkF#d^Y|uO(+Ar~K!ju_7;*C$MPm1iUh*;M0==y5$$DZ|#CEL=IRt7&_ z*g#_~I-|uvhqX=Q{)`nhF=sA+a#;RPW3$Ndliqh{9QYpj z+3zI%XVFo4UsFHtUh(;dM%4C{JrV1)wGB2_KKR0arE$?_K_8(*cZCGz>mRfz zc2=Le|3sh2-76gZGPP{0&wSv$_PGA@+{*J$%X-Ta->ADpyXYB~=d(WZp6Bf)-&&&_ z+@28TwC&DfRwL~lis!GoPglEX{fN0SHtWr`&j}y)88a?*+jL+5$^KJM4aKTsLjBen zX8itn@zJw}#eAnEj*7{;++CIU|7P2tH^()rqW{(&K2>5R;cQTRto77955Ab^zgEQt z)_^^n%w{x0=tgh)cvXYD6T>ln(yRBmV{ zdwYrH=l%3j+bS8Af8d>vL-jW&fdiRO%uhVae;g=nTlc_0J@d8f+mkCTI>OE?d#{-K z!gBM5yt-MDyS}S$EI`-GW=qGp6)9dxs*1Pf)KFm4Ae|U0lO^bocQIQKL zX1ofY;v262U~aHq;{UdipT%pYsjl8$v+=cgRqAA(B{Ou(_N;t6L4@)3idj!S=Kp#3 z?!S$ILcN7Sk;daphTqYzZm^2nIeA;{(-On^udUlTFCM#{pWdjpi~sJ?ibdZPz5d%j zYkkP)@_A|7jhgxg`K%HpeVY8T9GfSbU98PsD?X+BexG1bz8H5Zo0UMk_tQg}GdfPl z9^N(eSo4NeZl~qgW=%iDl&7!I(>BHH-j2sxzfXLnlNWB%Ke3~3;jLFsN<$=1Yg|1R z_CjoOxNwc*|H*tigXWc-nxinc_?O-hE4f+I%|FfAo;L67*|I%4D)owLDL-c|w^b>9 zv*~Twjl`$9*Lu#K7k@7MrNp;j(#qDh@U*Og*Rop|zTEch$o9u}{Hr;>Pvr^;KN%!w zdOa)c7KfnT@v;*Ezcc>CZvExv%>K#recLN@$#}kn|vTd?4 z_24`@OAA$v>-|{@Ofe zGpYE+?dj{PTmCLl{f5EK1!taZ^O8zmy31eav-{*fS8p?X*WXlsY(AAgFCCAJ#enRKGk5UtY?!N$F!F%;m=vNoN7<~ wt}L}_o^Sk}`v1(ePa?9~?bouj+plHaZoii83meareJpZMw7*J}J#;ky0BAIZ&j0`b delta 2305 zcmX^3LTdjDDN)~mKq(yt1_t{rYz&MH%nbHxSs3gmimEcQG+MV>GqzeYwOTW8wPs0N zQr|PnH2L$g6_K0WayNEP4063F;_tjhYU#6Juh48Ylda*l-zt)W7k^u&mUX31ZQF(K zA6MHy_`CAD<@cX4-+#uvzVn^`x-u6R*R6Vf0k6X+LN|PL7J6SS_(AK-`aj*DmRyhS zVg7CSp|RyzH_ybltZibgxzPeG#wzT+v)7ahHnjb&SMdGyBGY4m@xDLXgF8y%SJ@=| z{aLr;<-MEH@|N{Wu{d*o<(dWgGvXhud3-JWbj~LK zU?IzeGKaZV)UBy!QcbuZ+a#{o_9{_T+UWH@hLB4&rKeTcvR=)dbSY!niibug95~-k z5Ipc<>XDah-3z`M2k}?QbXsbKG%#D2AMR}xzqfz-nTG=X(>&9ov!!;+cF&5gzHF_% z{;+QqU*PwcwxbvPTBP~T+|4$sKgkt;>|arHbim|jpUo$oY-OvC*S9~6(Ak=FVMkiP z*%s%Cc3;HP6!+yT(N11x_*;q+0+2fbLigGZPzYnTk6|+ug zlh^jEcRw&08O-`}e*b;eI_8@KP+=EQxxZQk6^o}XkK z*sD~orP(%docvZ*xyWdv!ta29C>u5xzoZpgJ=bopo3LqOcBIAJ#N64v9^4C#Y3*+b zQe)$BU4QWUDHe}9;dCMSz;|BfBj@yP?5yU0$NAxW_7%@}ZYgJ`7G{W;`Ae*(YX zCX-s*mp;??MgDxvQ>#|!r1XA@)eezT*1C1+Lmk^rw&tsv&6getdEDHgS#DyHd?2?g zQbvBob?GX;Z|1RgJ!P|J`d36r+>BrOVM67BQf1+-e=b}oh~3mM$?25%`#FZr*QAyi zE%rLu>^?QTvp=2p!Td?LH; zeaLq0qX$#WFXee=eBH09V*h_a)XzwX6N?Wt+V|BTb6c72+*ACmz*~0(bKw(a;i%>H zmn3%oclf)lOVJ`NS5UcCeD8PV+E?Q58)CB8U0R-}FBbdIxYPIeoVNH0`ve@4x6I!i z!{xZj=3YI=@u;*CIq5>@tc+OCyFZqv7j8TFk)vmay~@J%CRw+XnD5?OzSv6m5&JpK z{j;u>xS6lnJB`ce$)1;Thh^r-1V8eLJeT`5=8)f&)3c7>Ih(XBH0u89X=OhZsy6WZ zNVqRLc0+(`N$HV~>Y-h%u{ranJTqOKH1R@7;LrEw>yN!>4XD2v!?E0_Z>h}V72o0y zuX|`&$dkW-nz7s?OMOg|Hd1WP0X6V{n%Ju;rguWqTibdoXhhs z9c~p`l_;pJzd$t8&N1qaUHxVGn=zIe&)w^1t>8YTk|MvxskUQt=SpQY`;I4$D^&OM z{`Q!6MXT&W@!OAEIiGzCXFTASnzq`~;Kv2d{*Zf{t#uykJo(H{syZstZQZrZzdvt= zEV}Q=ys|%~i%%s#-1vWvYt7eG(Ov65ZLBNR674xAZJFr2(uV8b0j3T z+pYgrVz5rnb?xk9eB6q=H)wNM+1qV+r5Q&Bu#8qeUZ() zdz1M@`5CPjx2NcIAG%|l{AHSM(ND+P>k7PzIn9#UIkxw>+qZ@L75p;Q+5OX%l_917 zcYFU*o6-gK-Se%Qo2+(3vCcPrvPpNdgut4;wdKz>7gYz&-I9K4zS7qJ$uRfdtDn~Fl=iYHfH5H2lld%vSXAT99djk>wyQl9s9IqAaBhWJPg7sRjjL;8b1k^m z<`}$Qwy;9^!0TU^TEeD>9270z_0Q0$_2AdGTbfMwj>iX2-O{L&DxnyC`@{W(pR-!d zX4hX-2s`m^?&ag&X>*^mNAGQV@`T5|F!a3P&RacG*^VEtXVEXLxHWT0Pu%vKoVM$B zoS$*+iX-tt=Z%Ktm&c5OBjxnHaMx>VgJ`>na!TS?zdh2P|+ zr(c8VH3B5mc3 zJ3g&^F?Z|Q-;H6O(aq+{>-L#{EPVEF_MiG9iA{Gc+;a`~v&x*lEc@uS$@`D$XxsHs|~I$OkJ)yHPgDM`StCZ(He^c2~87g0!^?Hxsstww0;mnSgWec^|G>F~2aK^5p_E`7I p)xY1@|MwC5+wQoQrQLBY>vqSrY+u-TChcR9d!+qU!sMZ=0RTT!asdDU