diff --git a/.github/workflows/apt-deps.txt b/.github/workflows/apt-deps.txt index 2a12d368c..43286f69e 100644 --- a/.github/workflows/apt-deps.txt +++ b/.github/workflows/apt-deps.txt @@ -1 +1 @@ -libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev ninja-build +libusb-dev libusb-1.0-0-dev libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev ninja-build diff --git a/.github/workflows/generate-builds.yml b/.github/workflows/generate-builds.yml index c54d017a2..6f7134282 100644 --- a/.github/workflows/generate-builds.yml +++ b/.github/workflows/generate-builds.yml @@ -13,22 +13,37 @@ jobs: with: submodules: true - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1.2.11 with: - key: ${{ runner.os }}-soh-otr-ccache + key: ${{ runner.os }}-otr-ccache-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-otr-ccache-${{ github.ref }} + ${{ runner.os }}-otr-ccache- - name: Install dependencies if: ${{ !vars.LINUX_RUNNER }} run: | sudo apt-get update sudo apt-get install -y $(cat .github/workflows/apt-deps.txt) + - name: Cache build folders + uses: actions/cache@v4 + with: + key: ${{ runner.os }}-otr-build-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-otr-build-${{ github.ref }} + ${{ runner.os }}-otr-build- + path: | + build-cmake + SDL2-2.28.5 - name: Install latest SDL if: ${{ !vars.LINUX_RUNNER }} run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz - tar -xzf SDL2-2.26.1.tar.gz - cd SDL2-2.26.1 - ./configure + if [ ! -d "SDL2-2.28.5" ]; then + wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz + tar -xzf SDL2-2.28.5.tar.gz + fi + cd SDL2-2.28.5 + ./configure --enable-hidapi-libusb make -j 10 sudo make install sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ @@ -37,7 +52,7 @@ jobs: export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release cmake --build build-cmake --config Release --target GenerateSohOtr - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: soh.otr path: soh.otr @@ -50,9 +65,12 @@ jobs: with: submodules: true - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1.2.11 with: - key: ${{ runner.os }}-ccache + key: ${{ runner.os }}-ccache-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-ccache-${{ github.ref }} + ${{ runner.os }}-ccache- - name: Install gtar wrapper if: ${{ !vars.MAC_RUNNER }} run: | @@ -85,7 +103,7 @@ jobs: sudo port install $(cat .github/workflows/macports-deps.txt) brew install ninja - name: Download soh.otr - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: soh.otr - name: Build SoH @@ -99,7 +117,7 @@ jobs: mv _packages/*.dmg SoH.dmg mv README.md readme.txt - name: Upload build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: soh-mac path: | @@ -128,17 +146,32 @@ jobs: sudo apt-get update sudo apt-get install -y $(cat .github/workflows/apt-deps.txt) - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1.2.11 with: - key: ${{ matrix.os }}-ccache + key: ${{ matrix.os }}-ccache-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ matrix.os }}-ccache-${{ github.ref }} + ${{ matrix.os }}-ccache- + - name: Cache build folders + uses: actions/cache@v4 + with: + key: ${{ matrix.os }}-build-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ matrix.os }}-build-${{ github.ref }} + ${{ matrix.os }}-build- + path: | + SDL2-2.28.5 + SDL2_net-2.2.0 - name: Install latest SDL if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }} run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz - tar -xzf SDL2-2.26.1.tar.gz - cd SDL2-2.26.1 - ./configure + if [ ! -d "SDL2-2.28.5" ]; then + wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz + tar -xzf SDL2-2.28.5.tar.gz + fi + cd SDL2-2.28.5 + ./configure --enable-hidapi-libusb make -j 10 sudo make install sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ @@ -146,15 +179,17 @@ jobs: if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }} run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz - tar -xzf SDL2_net-2.2.0.tar.gz + if [ ! -d "SDL2_net-2.2.0" ]; then + wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz + tar -xzf SDL2_net-2.2.0.tar.gz + fi cd SDL2_net-2.2.0 ./configure make -j 10 sudo make install sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ - name: Download soh.otr - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: soh.otr - name: Build SoH @@ -170,7 +205,7 @@ jobs: CC: gcc-${{ matrix.gcc }} CXX: g++-${{ matrix.gcc }} - name: Upload build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: soh-linux-${{ matrix.archive-suffix }} path: | @@ -180,7 +215,7 @@ jobs: needs: generate-soh-otr runs-on: ${{ (vars.LINUX_RUNNER && fromJSON(vars.LINUX_RUNNER)) || 'ubuntu-latest' }} container: - image: devkitpro/devkita64:latest + image: devkitpro/devkita64:20240120 steps: - name: Install dependencies run: | @@ -193,9 +228,12 @@ jobs: with: submodules: true - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1.2.11 with: - key: ${{ runner.os }}-switch-ccache + key: ${{ runner.os }}-switch-ccache-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-switch-ccache-${{ github.ref }} + ${{ runner.os }}-switch-ccache- - name: Build SoH run: | cmake -H. -Bbuild-switch -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache @@ -204,11 +242,11 @@ jobs: mv build-switch/soh/*.nro soh.nro mv README.md readme.txt - name: Download soh.otr - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: soh.otr - name: Upload build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: soh-switch path: | @@ -230,9 +268,12 @@ jobs: with: submodules: true - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1.2.11 with: - key: ${{ runner.os }}-wiiu-ccache + key: ${{ runner.os }}-wiiu-ccache-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-wiiu-ccache-${{ github.ref }} + ${{ runner.os }}-wiiu-ccache- - name: Build SoH run: | cmake -H. -Bbuild-wiiu -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache @@ -245,11 +286,11 @@ jobs: DEVKITPRO: /opt/devkitpro DEVKITPPC: /opt/devkitpro/devkitPPC - name: Download soh.otr - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: soh.otr - name: Upload build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: soh-wiiu path: | @@ -270,15 +311,25 @@ jobs: with: submodules: true - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1.2.11 with: - key: ${{ runner.os }}-ccache - - name: vcpkg - uses: johnwason/vcpkg-action@v5 + variant: sccache + max-size: "1G" + key: ${{ runner.os }}-ccache-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-ccache-${{ github.ref }} + ${{ runner.os }}-ccache- + - name: Cache build folder + uses: actions/cache@v4 with: - pkgs: zlib bzip2 libpng sdl2 sdl2-net glew glfw3 - token: ${{ github.token }} - triplet: 'x64-windows-static' + save-always: true + key: ${{ runner.os }}-build-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-build-${{ github.ref }} + ${{ runner.os }}-build- + path: | + build-windows + vcpkg - name: Configure Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1 - name: Build SoH @@ -286,7 +337,7 @@ jobs: VCPKG_ROOT: ${{github.workspace}}/vcpkg run: | set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH" - cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DBUILD_REMOTE_CONTROL=1 + cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DBUILD_REMOTE_CONTROL=1 cmake --build build-windows --config Release --parallel 10 mkdir soh-windows @@ -299,12 +350,12 @@ jobs: mv ./build-windows/gamecontrollerdb.txt ./soh-windows/gamecontrollerdb.txt mv ./x64/Release/assets ./soh-windows - name: Download soh.otr - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: soh.otr path: soh-windows - name: Upload build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: soh-windows path: soh-windows diff --git a/CMake/DefaultCXX.cmake b/CMake/DefaultCXX.cmake index e87721511..6ed89d9e0 100644 --- a/CMake/DefaultCXX.cmake +++ b/CMake/DefaultCXX.cmake @@ -8,5 +8,9 @@ if(MSVC) set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") set_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc") - set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi") + if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache") + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Z7") + else() + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi") + endif() endif() \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cabaef20..9669c5f0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,13 +14,17 @@ add_compile_options($<$:/MP>) add_compile_options($<$:/utf-8>) if (CMAKE_SYSTEM_NAME STREQUAL "Windows") -include(CMake/automate-vcpkg.cmake) + include(CMake/automate-vcpkg.cmake) -set(VCPKG_TRIPLET x64-windows-static) -set(VCPKG_TARGET_TRIPLET x64-windows-static) + set(VCPKG_TRIPLET x64-windows-static) + set(VCPKG_TARGET_TRIPLET x64-windows-static) -vcpkg_bootstrap() -vcpkg_install_packages(zlib bzip2 libpng sdl2 sdl2-net glew glfw3) + vcpkg_bootstrap() + vcpkg_install_packages(zlib bzip2 libpng sdl2 sdl2-net glew glfw3) + + if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache") + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded) + endif() endif() ################################################################################ diff --git a/OTRExporter b/OTRExporter index 04b85b95f..44adc47b4 160000 --- a/OTRExporter +++ b/OTRExporter @@ -1 +1 @@ -Subproject commit 04b85b95fab07a394b62dcd28a502a3040f08e0c +Subproject commit 44adc47b4da529e72d968b14cab94aefd8260f22 diff --git a/docs/MODDING.md b/docs/MODDING.md index ebd7cf071..75293b64a 100644 --- a/docs/MODDING.md +++ b/docs/MODDING.md @@ -188,4 +188,4 @@ Assuming all went well, you can now push your changes to your fork with the foll ```bash git push origin -``` \ No newline at end of file +``` diff --git a/libultraship b/libultraship index 15d57d806..0833afad6 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 15d57d806e39d7f19783e26acc1a062d402169c7 +Subproject commit 0833afad66e96d2ec4bbc410186d7247dc243ee2 diff --git a/soh/CMake/DefaultCXX.cmake b/soh/CMake/DefaultCXX.cmake index 7b052b9cc..032326a13 100644 --- a/soh/CMake/DefaultCXX.cmake +++ b/soh/CMake/DefaultCXX.cmake @@ -8,5 +8,9 @@ if(MSVC) set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") set_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc") - set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi") + if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache") + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Z7") + else() + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi") + endif() endif() \ No newline at end of file diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index ffc128bcd..228303558 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -328,7 +328,7 @@ endif() include(FetchContent) FetchContent_Declare( Boost - URL https://sourceforge.net/projects/boost/files/boost/1.81.0/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/soh/assets/.gitignore b/soh/assets/.gitignore index dce395e6f..d75078bf1 100644 --- a/soh/assets/.gitignore +++ b/soh/assets/.gitignore @@ -4,4 +4,4 @@ *.cfg *.vtx.inc *.dlist.inc -*.txt \ No newline at end of file +!*.png diff --git a/soh/assets/sources/triforce-hunt/paths.txt b/soh/assets/sources/triforce-hunt/paths.txt new file mode 100644 index 000000000..40b397186 --- /dev/null +++ b/soh/assets/sources/triforce-hunt/paths.txt @@ -0,0 +1,15 @@ +Complete triforce: + DL name: gTriforcePieceCompletedDL + Export Path: objects/object_triforce_completed + +Shard 0: + DL name: gTriforcePiece0DL + Export Path: objects/object_triforce_piece_0 + +Shard 1: + DL name: gTriforcePiece1DL + Export Path: objects/object_triforce_piece_1 + +Shard 2: + DL name: gTriforcePiece2DL + Export Path: objects/object_triforce_piece_2 diff --git a/soh/assets/sources/triforce-hunt/textures/noise_tex.png b/soh/assets/sources/triforce-hunt/textures/noise_tex.png new file mode 100644 index 000000000..e4329f5d1 Binary files /dev/null and b/soh/assets/sources/triforce-hunt/textures/noise_tex.png differ diff --git a/soh/assets/sources/triforce-hunt/triforce_complete.blend b/soh/assets/sources/triforce-hunt/triforce_complete.blend new file mode 100644 index 000000000..e5e3fbd13 Binary files /dev/null and b/soh/assets/sources/triforce-hunt/triforce_complete.blend differ diff --git a/soh/assets/sources/triforce-hunt/triforce_shard_0.blend b/soh/assets/sources/triforce-hunt/triforce_shard_0.blend new file mode 100644 index 000000000..cd13e8859 Binary files /dev/null and b/soh/assets/sources/triforce-hunt/triforce_shard_0.blend differ diff --git a/soh/assets/sources/triforce-hunt/triforce_shard_1.blend b/soh/assets/sources/triforce-hunt/triforce_shard_1.blend new file mode 100644 index 000000000..aca2f0600 Binary files /dev/null and b/soh/assets/sources/triforce-hunt/triforce_shard_1.blend differ diff --git a/soh/assets/sources/triforce-hunt/triforce_shard_2.blend b/soh/assets/sources/triforce-hunt/triforce_shard_2.blend new file mode 100644 index 000000000..0ead9e5a6 Binary files /dev/null and b/soh/assets/sources/triforce-hunt/triforce_shard_2.blend differ diff --git a/soh/include/z64.h b/soh/include/z64.h index e668b2e7a..bad2e1ca7 100644 --- a/soh/include/z64.h +++ b/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/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 654446005..8c0d415a3 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -12,6 +12,7 @@ #include #include "../../UIWidgets.hpp" #include "AudioCollection.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" Vec3f pos = { 0.0f, 0.0f, 0.0f }; f32 freqScale = 1.0f; @@ -78,7 +79,12 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) { void RandomizeGroup(SeqType type) { std::vector values; - + + // An empty IncludedSequences set means that the AudioEditor window has never been drawn + if (AudioCollection::Instance->GetIncludedSequences().empty()) { + AudioCollection::Instance->InitializeShufflePool(); + } + // use a while loop to add duplicates if we don't have enough included sequences while (values.size() < AuthenticCountBySequenceType(type)) { for (const auto& seqData : AudioCollection::Instance->GetIncludedSequences()) { @@ -123,6 +129,34 @@ void ResetGroup(const std::map& map, SeqType type) { } } +void LockGroup(const std::map& map, SeqType type) { + for (const auto& [defaultValue, seqData] : map) { + if (seqData.category == type) { + // Only save authentic sequence CVars + if (seqData.category == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) { + continue; + } + const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey); + const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey); + CVarSetInteger(cvarLockKey.c_str(), 1); + } + } +} + +void UnlockGroup(const std::map& map, SeqType type) { + for (const auto& [defaultValue, seqData] : map) { + if (seqData.category == type) { + // Only save authentic sequence CVars + if (seqData.category == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) { + continue; + } + const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey); + const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey); + CVarSetInteger(cvarLockKey.c_str(), 0); + } + } +} + void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequenceType) { const std::string cvarKey = AudioCollection::Instance->GetCvarKey(sfxKey); const std::string hiddenKey = "##" + cvarKey; @@ -163,6 +197,8 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { const std::string hiddenTabId = "##" + tabId; const std::string resetAllButton = "Reset All" + hiddenTabId; const std::string randomizeAllButton = "Randomize All" + hiddenTabId; + const std::string lockAllButton = "Lock All" + hiddenTabId; + const std::string unlockAllButton = "Unlock All" + hiddenTabId; if (ImGui::Button(resetAllButton.c_str())) { auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); @@ -184,6 +220,28 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { ReplayCurrentBGM(); } } + ImGui::SameLine(); + if (ImGui::Button(lockAllButton.c_str())) { + auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); + LockGroup(map, type); + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); + if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) { + ReplayCurrentBGM(); + } + } + ImGui::SameLine(); + if (ImGui::Button(unlockAllButton.c_str())) { + auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); + UnlockGroup(map, type); + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); + if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) { + ReplayCurrentBGM(); + } + } ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); @@ -350,6 +408,19 @@ void DrawTypeChip(SeqType type) { ImGui::EndDisabled(); } + +void AudioEditorRegisterOnSceneInitHook() { + GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { + if (CVarGetInteger("gAudioEditor.RandomizeAllOnNewScene", 0)) { + AudioEditor_RandomizeAll(); + } + }); +} + +void AudioEditor::InitElement() { + AudioEditorRegisterOnSceneInitHook(); +} + void AudioEditor::DrawElement() { AudioCollection::Instance->InitializeShufflePool(); @@ -359,6 +430,28 @@ void AudioEditor::DrawElement() { return; } + float buttonSegments = ImGui::GetContentRegionAvail().x / 4; + if (ImGui::Button("Randomize All Groups", ImVec2(buttonSegments, 30.0f))) { + AudioEditor_RandomizeAll(); + } + UIWidgets::Tooltip("Randomizes all unlocked music and sound effects across tab groups"); + ImGui::SameLine(); + if (ImGui::Button("Reset All Groups", ImVec2(buttonSegments, 30.0f))) { + AudioEditor_ResetAll(); + } + UIWidgets::Tooltip("Resets all unlocked music and sound effects across tab groups"); + ImGui::SameLine(); + if (ImGui::Button("Lock All Groups", ImVec2(buttonSegments, 30.0f))) { + AudioEditor_LockAll(); + } + UIWidgets::Tooltip("Locks all music and sound effects across tab groups"); + ImGui::SameLine(); + if (ImGui::Button("Unlock All Groups", ImVec2(buttonSegments, 30.0f))) { + AudioEditor_UnlockAll(); + } + UIWidgets::Tooltip("Unlocks all music and sound effects across tab groups"); + + if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("Background Music")) { Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD); @@ -431,6 +524,10 @@ void AudioEditor::DrawElement() { LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); } + ImGui::NewLine(); + UIWidgets::EnhancementCheckbox("Randomize All Music and Sound Effects on New Scene", "gAudioEditor.RandomizeAllOnNewScene"); + UIWidgets::Tooltip("Enables randomizing all unlocked music and sound effects when you enter a new scene."); + ImGui::NewLine(); ImGui::PushItemWidth(-FLT_MIN); UIWidgets::PaddedSeparator(); @@ -625,3 +722,19 @@ void AudioEditor_ResetAll() { LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); ReplayCurrentBGM(); } + +void AudioEditor_LockAll() { + for (auto type : allTypes) { + LockGroup(AudioCollection::Instance->GetAllSequences(), type); + } + + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); +} + +void AudioEditor_UnlockAll() { + for (auto type : allTypes) { + UnlockGroup(AudioCollection::Instance->GetAllSequences(), type); + } + + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); +} diff --git a/soh/soh/Enhancements/audio/AudioEditor.h b/soh/soh/Enhancements/audio/AudioEditor.h index 766006ecc..9cca94efe 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.h +++ b/soh/soh/Enhancements/audio/AudioEditor.h @@ -14,13 +14,15 @@ class AudioEditor : public LUS::GuiWindow { using LUS::GuiWindow::GuiWindow; void DrawElement() override; - void InitElement() override {}; + void InitElement() override; void UpdateElement() override {}; ~AudioEditor() {}; }; void AudioEditor_RandomizeAll(); void AudioEditor_ResetAll(); +void AudioEditor_LockAll(); +void AudioEditor_UnlockAll(); extern "C" { #endif diff --git a/soh/soh/Enhancements/controls/GameControlEditor.cpp b/soh/soh/Enhancements/controls/GameControlEditor.cpp deleted file mode 100644 index 46366135f..000000000 --- a/soh/soh/Enhancements/controls/GameControlEditor.cpp +++ /dev/null @@ -1,355 +0,0 @@ -#include "GameControlEditor.h" - -#include -#include -#include -#include -#include -#include - -#ifndef IMGUI_DEFINE_MATH_OPERATORS -#define IMGUI_DEFINE_MATH_OPERATORS -#endif -#include -#include -#include -#include -#include -#include - -#include "macros.h" - -#include "../../UIWidgets.hpp" - -namespace GameControlEditor { - const ImGuiTableFlags PANEL_TABLE_FLAGS = - ImGuiTableFlags_BordersH | - ImGuiTableFlags_BordersV; - const ImGuiTableColumnFlags PANEL_TABLE_COLUMN_FLAGS = - ImGuiTableColumnFlags_IndentEnable | - ImGuiTableColumnFlags_NoSort; - - namespace TableHelper { - void InitHeader(bool has_header = true) { - if (has_header) { - ImGui::TableHeadersRow(); - } - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); //This is to adjust Vertical pos of item in a cell to be normlized. - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - } - - void NextCol() { - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - } - - void NextLine() { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - } - } - - void DrawHelpIcon(const std::string& helptext) { - // place the ? button to the most of the right side of the cell it is using. - ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 22); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 15); - ImGui::SmallButton("?"); - UIWidgets::Tooltip(helptext.c_str()); - } - - typedef uint32_t N64ButtonMask; - - // Used together for an incomplete linked hash map implementation in order to - // map button masks to their names and original mapping on N64 - static std::list> buttons; - static std::unordered_map buttonNames; - - void addButtonName(N64ButtonMask mask, const char* name) { - buttons.push_back(std::make_pair(mask, name)); - buttonNames[mask] = std::prev(buttons.end()); - } - - typedef struct { - const char* label; - const char* cVarName; - N64ButtonMask defaultBtn; - } CustomButtonMap; - - // Ocarina button maps - static CustomButtonMap ocarinaD5 = {"D5", "gOcarinaD5BtnMap", BTN_CUP}; - static CustomButtonMap ocarinaB4 = {"B4", "gOcarinaB4BtnMap", BTN_CLEFT}; - static CustomButtonMap ocarinaA4 = {"A4", "gOcarinaA4BtnMap", BTN_CRIGHT}; - static CustomButtonMap ocarinaF4 = {"F4", "gOcarinaF4BtnMap", BTN_CDOWN}; - static CustomButtonMap ocarinaD4 = {"D4", "gOcarinaD4BtnMap", BTN_A}; - static CustomButtonMap ocarinaSongDisable = {"Disable songs", "gOcarinaDisableBtnMap", BTN_L}; - static CustomButtonMap ocarinaSharp = {"Pitch up", "gOcarinaSharpBtnMap", BTN_R}; - static CustomButtonMap ocarinaFlat = {"Pitch down", "gOcarinaFlatBtnMap", BTN_Z}; - - void GameControlEditorWindow::InitElement() { - addButtonName(BTN_A, "A"); - addButtonName(BTN_B, "B"); - addButtonName(BTN_CUP, "C Up"); - addButtonName(BTN_CDOWN, "C Down"); - addButtonName(BTN_CLEFT, "C Left"); - addButtonName(BTN_CRIGHT, "C Right"); - addButtonName(BTN_L, "L"); - addButtonName(BTN_Z, "Z"); - addButtonName(BTN_R, "R"); - addButtonName(BTN_START, "Start"); - addButtonName(BTN_DUP, "D-pad up"); - addButtonName(BTN_DDOWN, "D-pad down"); - addButtonName(BTN_DLEFT, "D-pad left"); - addButtonName(BTN_DRIGHT, "D-pad right"); - addButtonName(0, "None"); - } - - // Draw a button mapping setting consisting of a padded label and button dropdown. - // excludedButtons indicates which buttons are unavailable to choose from. - void DrawMapping(CustomButtonMap& mapping, float labelWidth, N64ButtonMask excludedButtons) { - N64ButtonMask currentButton = CVarGetInteger(mapping.cVarName, mapping.defaultBtn); - - const char* preview; - if (buttonNames.contains(currentButton)) { - preview = buttonNames[currentButton]->second; - } else { - preview = "Unknown"; - } - - UIWidgets::Spacer(0); - ImVec2 cursorPos = ImGui::GetCursorPos(); - ImVec2 textSize = ImGui::CalcTextSize(mapping.label); - ImGui::SetCursorPosY(cursorPos.y + textSize.y / 4); - ImGui::SetCursorPosX(cursorPos.x + abs(textSize.x - labelWidth)); - ImGui::Text("%s", mapping.label); - ImGui::SameLine(); - ImGui::SetCursorPosY(cursorPos.y); - - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); - if (ImGui::BeginCombo(StringHelper::Sprintf("##%s", mapping.cVarName).c_str(), preview)) { - for (auto i = buttons.begin(); i != buttons.end(); i++) { - if ((i->first & excludedButtons) != 0) { - continue; - } - if (ImGui::Selectable(i->second, i->first == currentButton)) { - CVarSetInteger(mapping.cVarName, i->first); - LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); - } - } - ImGui::EndCombo(); - } - UIWidgets::Spacer(0); - } - - void DrawOcarinaControlPanel(GameControlEditorWindow* window) { - if (!ImGui::CollapsingHeader("Ocarina Controls")) { - return; - } - - if (!ImGui::BeginTable("tableCustomOcarinaControls", 1, PANEL_TABLE_FLAGS)) { - return; - } - - ImGui::TableSetupColumn("Custom Ocarina Controls", PANEL_TABLE_COLUMN_FLAGS | ImGuiTableColumnFlags_WidthStretch); - TableHelper::InitHeader(false); - - ImVec2 cursor = ImGui::GetCursorPos(); - ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); - UIWidgets::EnhancementCheckbox("Customize Ocarina Controls", "gCustomOcarinaControls"); - - if (CVarGetInteger("gCustomOcarinaControls", 0) == 1) { - if (ImGui::BeginTable("tableCustomMainOcarinaControls", 2, ImGuiTableFlags_SizingStretchProp)) { - float labelWidth; - N64ButtonMask disableMask = BTN_B; - if (CVarGetInteger("gDpadOcarina", 0)) { - disableMask |= BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT; - } - - ImGui::TableSetupColumn("Notes##CustomOcarinaNotes", PANEL_TABLE_COLUMN_FLAGS); - ImGui::TableSetupColumn("Modifiers##CustomOcaranaModifiers", PANEL_TABLE_COLUMN_FLAGS); - TableHelper::InitHeader(false); - - window->BeginGroupPanelPublic("Notes", ImGui::GetContentRegionAvail()); - labelWidth = ImGui::CalcTextSize("D5").x + 10; - DrawMapping(ocarinaD5, labelWidth, disableMask); - DrawMapping(ocarinaB4, labelWidth, disableMask); - DrawMapping(ocarinaA4, labelWidth, disableMask); - DrawMapping(ocarinaF4, labelWidth, disableMask); - DrawMapping(ocarinaD4, labelWidth, disableMask); - ImGui::Dummy(ImVec2(0, 5)); - float cursorY = ImGui::GetCursorPosY(); - window->EndGroupPanelPublic(0); - - TableHelper::NextCol(); - - window->BeginGroupPanelPublic("Modifiers", ImGui::GetContentRegionAvail()); - labelWidth = ImGui::CalcTextSize(ocarinaSongDisable.label).x + 10; - DrawMapping(ocarinaSongDisable, labelWidth, disableMask); - DrawMapping(ocarinaSharp, labelWidth, disableMask); - DrawMapping(ocarinaFlat, labelWidth, disableMask); - window->EndGroupPanelPublic(cursorY - ImGui::GetCursorPosY() + 2); - - ImGui::EndTable(); - } - } else { - UIWidgets::Spacer(0); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); - ImGui::TextWrapped("To modify the main ocarina controls, select the \"Customize Ocarina Controls\" checkbox."); - UIWidgets::Spacer(0); - } - - window->BeginGroupPanelPublic("Alternate controls", ImGui::GetContentRegionAvail()); - if (ImGui::BeginTable("tableOcarinaAlternateControls", 2, ImGuiTableFlags_SizingFixedSame)) { - ImGui::TableSetupColumn("D-pad", PANEL_TABLE_COLUMN_FLAGS); - ImGui::TableSetupColumn("Right stick", PANEL_TABLE_COLUMN_FLAGS); - TableHelper::InitHeader(false); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); - UIWidgets::EnhancementCheckbox("Play with D-pad", "gDpadOcarina"); - TableHelper::NextCol(); - UIWidgets::EnhancementCheckbox("Play with camera stick", "gRStickOcarina"); - UIWidgets::Spacer(0); - ImGui::EndTable(); - } - window->EndGroupPanelPublic(0); - - ImGui::EndTable(); - } - - void DrawCameraControlPanel(GameControlEditorWindow* window) { - if (!ImGui::CollapsingHeader("Camera Controls")) { - return; - } - - UIWidgets::Spacer(0); - window->BeginGroupPanelPublic("Aiming/First-Person Camera", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("Right Stick Aiming", "gRightStickAiming"); - DrawHelpIcon("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming"); - if (CVarGetInteger("gRightStickAiming", 0)) { - UIWidgets::PaddedEnhancementCheckbox("Allow moving while in first person mode", "gMoveWhileFirstPerson"); - DrawHelpIcon("Changes the left stick to move the player while in first person mode"); - } - UIWidgets::PaddedEnhancementCheckbox("Invert Aiming X Axis", "gInvertAimingXAxis"); - DrawHelpIcon("Inverts the Camera X Axis in:\n-First-Person/C-Up view\n-Weapon Aiming"); - UIWidgets::PaddedEnhancementCheckbox("Invert Aiming Y Axis", "gInvertAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); - DrawHelpIcon("Inverts the Camera Y Axis in:\n-First-Person/C-Up view\n-Weapon Aiming"); - UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming Y Axis", "gInvertShieldAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); - DrawHelpIcon("Inverts the Shield Aiming Y Axis"); - UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming X Axis", "gInvertShieldAimingXAxis"); - DrawHelpIcon("Inverts the Shield Aiming X Axis"); - UIWidgets::PaddedEnhancementCheckbox("Invert Z-Weapon Aiming Y Axis", "gInvertZAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); - DrawHelpIcon("Inverts the Camera Y Axis in:\n-Z-Weapon Aiming"); - UIWidgets::PaddedEnhancementCheckbox("Disable Auto-Centering in First-Person View", "gDisableAutoCenterViewFirstPerson"); - DrawHelpIcon("Prevents the C-Up view from auto-centering, allowing for Gyro Aiming"); - if (UIWidgets::PaddedEnhancementCheckbox("Enable Custom Aiming/First-Person sensitivity", "gEnableFirstPersonSensitivity", true, false)) { - if (!CVarGetInteger("gEnableFirstPersonSensitivity", 0)) { - CVarClear("gFirstPersonCameraSensitivityX"); - CVarClear("gFirstPersonCameraSensitivityY"); - } - } - if (CVarGetInteger("gEnableFirstPersonSensitivity", 0)) { - UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %.0f %%", "##FirstPersonSensitivity Horizontal", - "gFirstPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true); - UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %.0f %%", "##FirstPersonSensitivity Vertical", - "gFirstPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true); - } - UIWidgets::Spacer(0); - window->EndGroupPanelPublic(0); - - UIWidgets::Spacer(0); - window->BeginGroupPanelPublic("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 " - "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::Spacer(0); - UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %.0f %%", "##ThirdPersonSensitivity Horizontal", - "gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true); - UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %.0f %%", "##ThirdPersonSensitivity Vertical", - "gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist", - "gFreeCameraDistMax", 100, 900, "", 185, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed", - "gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true); - window->EndGroupPanelPublic(0); - } - - void DrawDpadControlPanel(GameControlEditorWindow* window) { - if (!ImGui::CollapsingHeader("D-Pad Controls")) { - return; - } - - ImVec2 cursor = ImGui::GetCursorPos(); - ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); - window->BeginGroupPanelPublic("D-Pad Options", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("D-pad Support on Pause Screen", "gDpadPause"); - DrawHelpIcon("Navigate Pause with the D-pad\nIf used with D-pad as Equip Items, you must hold C-Up to equip instead of navigate\n" - "To make the cursor only move a single space no matter how long a direction is held, manually set gDpadHoldChange to 0"); - UIWidgets::PaddedEnhancementCheckbox("D-pad Support in Text Boxes", "gDpadText"); - DrawHelpIcon("Navigate choices in text boxes, shop item selection, and the file select / name entry screens with the D-pad\n" - "To make the cursor only move a single space during name entry no matter how long a direction is held, manually set gDpadHoldChange to 0"); - UIWidgets::PaddedEnhancementCheckbox("D-pad as Equip Items", "gDpadEquips"); - DrawHelpIcon("Equip items and equipment on the D-pad\nIf used with D-pad on Pause Screen, you must hold C-Up to equip instead of navigate"); - window->EndGroupPanelPublic(0); - } - - void DrawMiscControlPanel(GameControlEditorWindow* window) { - if (!ImGui::CollapsingHeader("Miscellaneous Controls")) { - return; - } - - ImVec2 cursor = ImGui::GetCursorPos(); - ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); - window->BeginGroupPanelPublic("Misc Controls", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedText("Allow the cursor to be on any slot"); - static const char* cursorOnAnySlot[3] = { "Only in Rando", "Always", "Never" }; - UIWidgets::EnhancementCombobox("gPauseAnyCursor", cursorOnAnySlot, PAUSE_ANY_CURSOR_RANDO_ONLY); - DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select " - "certain items."); - UIWidgets::Spacer(0); - ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0)); - UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false); - DrawHelpIcon("Hold the assigned button to change the maximum walking speed\nTo change the assigned button, go into the Ports tabs above"); - if (CVarGetInteger("gEnableWalkModify", 0)) { - UIWidgets::Spacer(5); - window->BeginGroupPanelPublic("Walk Modifier", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", "gWalkSpeedToggle", true, false); - UIWidgets::PaddedEnhancementCheckbox("Don't affect jump distance/velocity", "gWalkModifierDoesntChangeJump", true, false); - UIWidgets::PaddedEnhancementSliderFloat("Modifier 1: %.0f %%", "##WalkMod1", "gWalkModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true); - UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %.0f %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true); - window->EndGroupPanelPublic(0); - } - ImGui::EndDisabled(); - UIWidgets::Spacer(0); - UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL"); - DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up"); - window->EndGroupPanelPublic(0); - } - - - void GameControlEditorWindow::DrawElement() { - ImGui::SetNextWindowSize(ImVec2(465, 430), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Game Controls Configuration", &mIsVisible)) { - DrawOcarinaControlPanel(this); - DrawCameraControlPanel(this); - DrawDpadControlPanel(this); - DrawMiscControlPanel(this); - } - ImGui::End(); - } - - void GameControlEditorWindow::BeginGroupPanelPublic(const char* name, const ImVec2& size) { - BeginGroupPanel(name, size); - } - - void GameControlEditorWindow::EndGroupPanelPublic(float minHeight) { - EndGroupPanel(minHeight); - } -} diff --git a/soh/soh/Enhancements/controls/GameControlEditor.h b/soh/soh/Enhancements/controls/GameControlEditor.h deleted file mode 100644 index 7cf306741..000000000 --- a/soh/soh/Enhancements/controls/GameControlEditor.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace GameControlEditor { -class GameControlEditorWindow : public LUS::GuiWindow { - public: - using LUS::GuiWindow::GuiWindow; - - void BeginGroupPanelPublic(const char* name, const ImVec2& size); - void EndGroupPanelPublic(float minHeight); - - void InitElement() override; - void DrawElement() override; - void UpdateElement() override {}; -}; - -static int CurrentPort = 0; -static int BtnReading = -1; - -} // namespace GameControlEditor diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index a5ecc39dc..a1290c238 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -22,6 +22,22 @@ void SohInputEditorWindow::InitElement() { mButtonsBitmasks = { BTN_A, BTN_B, BTN_START, BTN_L, BTN_R, BTN_Z, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT }; mDpadBitmasks = { BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; mModifierButtonsBitmasks = { BTN_MODIFIER1, BTN_MODIFIER2 }; + + addButtonName(BTN_A, "A"); + addButtonName(BTN_B, "B"); + addButtonName(BTN_CUP, "C Up"); + addButtonName(BTN_CDOWN, "C Down"); + addButtonName(BTN_CLEFT, "C Left"); + addButtonName(BTN_CRIGHT, "C Right"); + addButtonName(BTN_L, "L"); + addButtonName(BTN_Z, "Z"); + addButtonName(BTN_R, "R"); + addButtonName(BTN_START, "Start"); + addButtonName(BTN_DUP, "D-pad up"); + addButtonName(BTN_DDOWN, "D-pad down"); + addButtonName(BTN_DLEFT, "D-pad left"); + addButtonName(BTN_DRIGHT, "D-pad right"); + addButtonName(0, "None"); } #define INPUT_EDITOR_WINDOW_GAME_INPUT_BLOCK_ID 95237929 @@ -1017,14 +1033,6 @@ void SohInputEditorWindow::DrawAddLEDMappingButton(uint8_t port) { } } -void SohInputEditorWindow::DrawHelpIcon(const std::string& helptext) { - // place the ? button to the most of the right side of the cell it is using. - ImGui::SetCursorPosY(ImGui::GetCursorPosY() - SCALE_IMGUI_SIZE(22)); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - SCALE_IMGUI_SIZE(15)); - ImGui::SmallButton("?"); - UIWidgets::Tooltip(helptext.c_str()); -} - void SohInputEditorWindow::DrawLEDSection(uint8_t port) { for (auto [id, mapping] : LUS::Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetLED()->GetAllLEDMappings()) { @@ -1063,11 +1071,11 @@ void SohInputEditorWindow::DrawLEDSection(uint8_t port) { }; UIWidgets::PaddedText("Source"); UIWidgets::EnhancementCombobox("gLedColorSource", ledSources, LED_SOURCE_TUNIC_ORIGINAL); - DrawHelpIcon("Health\n- Red when health critical (13-20% depending on max health)\n- Yellow when " - "health < 40%. Green otherwise.\n\n" - "Tunics: colors will mirror currently equipped tunic, whether original or the current " - "values in Cosmetics Editor.\n\n" - "Custom: single, solid color"); + UIWidgets::Tooltip("Health\n- Red when health critical (13-20% depending on max health)\n- Yellow when " + "health < 40%. Green otherwise.\n\n" + "Tunics: colors will mirror currently equipped tunic, whether original or the current " + "values in Cosmetics Editor.\n\n" + "Custom: single, solid color"); if (CVarGetInteger("gLedColorSource", 1) == LED_SOURCE_CUSTOM) { UIWidgets::Spacer(3); auto port1Color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 }); @@ -1087,12 +1095,12 @@ void SohInputEditorWindow::DrawLEDSection(uint8_t port) { } UIWidgets::PaddedEnhancementSliderFloat("Brightness: %.1f %%", "##LED_Brightness", "gLedBrightness", 0.0f, 1.0f, "", 1.0f, true, true); - DrawHelpIcon("Sets the brightness of controller LEDs. 0% brightness = LEDs off."); + UIWidgets::Tooltip("Sets the brightness of controller LEDs. 0% brightness = LEDs off."); UIWidgets::PaddedEnhancementCheckbox( "Critical Health Override", "gLedCriticalOverride", true, true, CVarGetInteger("gLedColorSource", LED_SOURCE_TUNIC_ORIGINAL) == LED_SOURCE_HEALTH, "Override redundant for health source.", UIWidgets::CheckboxGraphics::Cross, true); - DrawHelpIcon("Shows red color when health is critical, otherwise displays according to color source."); + UIWidgets::Tooltip("Shows red color when health is critical, otherwise displays according to color source."); } ImGui::TreePop(); } @@ -1430,6 +1438,273 @@ void SohInputEditorWindow::DrawLEDDeviceIcons(uint8_t portIndex) { } } +const ImGuiTableFlags PANEL_TABLE_FLAGS = + ImGuiTableFlags_BordersH | + ImGuiTableFlags_BordersV; +const ImGuiTableColumnFlags PANEL_TABLE_COLUMN_FLAGS = + ImGuiTableColumnFlags_IndentEnable | + ImGuiTableColumnFlags_NoSort; + +namespace TableHelper { + void InitHeader(bool has_header = true) { + if (has_header) { + ImGui::TableHeadersRow(); + } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); //This is to adjust Vertical pos of item in a cell to be normlized. + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + } + + void NextCol() { + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + } + + void NextLine() { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + } +} + +typedef uint32_t N64ButtonMask; + +void SohInputEditorWindow::addButtonName(N64ButtonMask mask, const char* name) { + buttons.push_back(std::make_pair(mask, name)); + buttonNames[mask] = std::prev(buttons.end()); +} + +// Ocarina button maps +static CustomButtonMap ocarinaD5 = {"D5", "gOcarinaD5BtnMap", BTN_CUP}; +static CustomButtonMap ocarinaB4 = {"B4", "gOcarinaB4BtnMap", BTN_CLEFT}; +static CustomButtonMap ocarinaA4 = {"A4", "gOcarinaA4BtnMap", BTN_CRIGHT}; +static CustomButtonMap ocarinaF4 = {"F4", "gOcarinaF4BtnMap", BTN_CDOWN}; +static CustomButtonMap ocarinaD4 = {"D4", "gOcarinaD4BtnMap", BTN_A}; +static CustomButtonMap ocarinaSongDisable = {"Disable songs", "gOcarinaDisableBtnMap", BTN_L}; +static CustomButtonMap ocarinaSharp = {"Pitch up", "gOcarinaSharpBtnMap", BTN_R}; +static CustomButtonMap ocarinaFlat = {"Pitch down", "gOcarinaFlatBtnMap", BTN_Z}; + +// Draw a button mapping setting consisting of a padded label and button dropdown. +// excludedButtons indicates which buttons are unavailable to choose from. +void SohInputEditorWindow::DrawMapping(CustomButtonMap& mapping, float labelWidth, N64ButtonMask excludedButtons) { + N64ButtonMask currentButton = CVarGetInteger(mapping.cVarName, mapping.defaultBtn); + + const char* preview; + if (buttonNames.contains(currentButton)) { + preview = buttonNames[currentButton]->second; + } else { + preview = "Unknown"; + } + + UIWidgets::Spacer(0); + ImVec2 cursorPos = ImGui::GetCursorPos(); + ImVec2 textSize = ImGui::CalcTextSize(mapping.label); + ImGui::SetCursorPosY(cursorPos.y + textSize.y / 4); + ImGui::SetCursorPosX(cursorPos.x + abs(textSize.x - labelWidth)); + ImGui::Text("%s", mapping.label); + ImGui::SameLine(); + ImGui::SetCursorPosY(cursorPos.y); + + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + if (ImGui::BeginCombo(StringHelper::Sprintf("##%s", mapping.cVarName).c_str(), preview)) { + for (auto i = buttons.begin(); i != buttons.end(); i++) { + if ((i->first & excludedButtons) != 0) { + continue; + } + if (ImGui::Selectable(i->second, i->first == currentButton)) { + CVarSetInteger(mapping.cVarName, i->first); + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + } + } + ImGui::EndCombo(); + } + UIWidgets::Spacer(0); +} + +void SohInputEditorWindow::DrawOcarinaControlPanel() { + if (!ImGui::BeginTable("tableCustomOcarinaControls", 1, PANEL_TABLE_FLAGS)) { + return; + } + + ImGui::TableSetupColumn("Custom Ocarina Controls", PANEL_TABLE_COLUMN_FLAGS | ImGuiTableColumnFlags_WidthStretch); + TableHelper::InitHeader(false); + + ImVec2 cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); + UIWidgets::EnhancementCheckbox("Customize Ocarina Controls", "gCustomOcarinaControls"); + + if (CVarGetInteger("gCustomOcarinaControls", 0) == 1) { + if (ImGui::BeginTable("tableCustomMainOcarinaControls", 2, ImGuiTableFlags_SizingStretchProp)) { + float labelWidth; + N64ButtonMask disableMask = BTN_B; + if (CVarGetInteger("gDpadOcarina", 0)) { + disableMask |= BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT; + } + + ImGui::TableSetupColumn("Notes##CustomOcarinaNotes", PANEL_TABLE_COLUMN_FLAGS); + ImGui::TableSetupColumn("Modifiers##CustomOcaranaModifiers", PANEL_TABLE_COLUMN_FLAGS); + TableHelper::InitHeader(false); + + LUS::GuiWindow::BeginGroupPanel("Notes", ImGui::GetContentRegionAvail()); + labelWidth = ImGui::CalcTextSize("D5").x + 10; + DrawMapping(ocarinaD5, labelWidth, disableMask); + DrawMapping(ocarinaB4, labelWidth, disableMask); + DrawMapping(ocarinaA4, labelWidth, disableMask); + DrawMapping(ocarinaF4, labelWidth, disableMask); + DrawMapping(ocarinaD4, labelWidth, disableMask); + ImGui::Dummy(ImVec2(0, 5)); + float cursorY = ImGui::GetCursorPosY(); + LUS::GuiWindow::EndGroupPanel(0); + + TableHelper::NextCol(); + + LUS::GuiWindow::BeginGroupPanel("Modifiers", ImGui::GetContentRegionAvail()); + labelWidth = ImGui::CalcTextSize(ocarinaSongDisable.label).x + 10; + DrawMapping(ocarinaSongDisable, labelWidth, disableMask); + DrawMapping(ocarinaSharp, labelWidth, disableMask); + DrawMapping(ocarinaFlat, labelWidth, disableMask); + LUS::GuiWindow::EndGroupPanel(cursorY - ImGui::GetCursorPosY() + 2); + + ImGui::EndTable(); + } + } else { + UIWidgets::Spacer(0); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); + ImGui::TextWrapped("To modify the main ocarina controls, select the \"Customize Ocarina Controls\" checkbox."); + UIWidgets::Spacer(0); + } + + LUS::GuiWindow::BeginGroupPanel("Alternate controls", ImGui::GetContentRegionAvail()); + if (ImGui::BeginTable("tableOcarinaAlternateControls", 2, ImGuiTableFlags_SizingFixedSame)) { + ImGui::TableSetupColumn("D-pad", PANEL_TABLE_COLUMN_FLAGS); + ImGui::TableSetupColumn("Right stick", PANEL_TABLE_COLUMN_FLAGS); + TableHelper::InitHeader(false); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); + UIWidgets::EnhancementCheckbox("Play with D-pad", "gDpadOcarina"); + TableHelper::NextCol(); + UIWidgets::EnhancementCheckbox("Play with camera stick", "gRStickOcarina"); + UIWidgets::Spacer(0); + ImGui::EndTable(); + } + LUS::GuiWindow::EndGroupPanel(0); + + ImGui::EndTable(); +} + +void SohInputEditorWindow::DrawCameraControlPanel() { + ImVec2 cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); + LUS::GuiWindow::BeginGroupPanel("Aiming/First-Person Camera", ImGui::GetContentRegionAvail()); + UIWidgets::PaddedEnhancementCheckbox("Right Stick Aiming", "gRightStickAiming"); + UIWidgets::Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming"); + if (CVarGetInteger("gRightStickAiming", 0)) { + UIWidgets::PaddedEnhancementCheckbox("Allow moving while in first person mode", "gMoveWhileFirstPerson"); + UIWidgets::Tooltip("Changes the left stick to move the player while in first person mode"); + } + UIWidgets::PaddedEnhancementCheckbox("Invert Aiming X Axis", "gInvertAimingXAxis"); + UIWidgets::Tooltip("Inverts the Camera X Axis in:\n-First-Person/C-Up view\n-Weapon Aiming"); + UIWidgets::PaddedEnhancementCheckbox("Invert Aiming Y Axis", "gInvertAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); + UIWidgets::Tooltip("Inverts the Camera Y Axis in:\n-First-Person/C-Up view\n-Weapon Aiming"); + UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming Y Axis", "gInvertShieldAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); + UIWidgets::Tooltip("Inverts the Shield Aiming Y Axis"); + UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming X Axis", "gInvertShieldAimingXAxis"); + UIWidgets::Tooltip("Inverts the Shield Aiming X Axis"); + UIWidgets::PaddedEnhancementCheckbox("Invert Z-Weapon Aiming Y Axis", "gInvertZAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); + UIWidgets::Tooltip("Inverts the Camera Y Axis in:\n-Z-Weapon Aiming"); + UIWidgets::PaddedEnhancementCheckbox("Disable Auto-Centering in First-Person View", "gDisableAutoCenterViewFirstPerson"); + UIWidgets::Tooltip("Prevents the C-Up view from auto-centering, allowing for Gyro Aiming"); + if (UIWidgets::PaddedEnhancementCheckbox("Enable Custom Aiming/First-Person sensitivity", "gEnableFirstPersonSensitivity", true, false)) { + if (!CVarGetInteger("gEnableFirstPersonSensitivity", 0)) { + CVarClear("gFirstPersonCameraSensitivityX"); + CVarClear("gFirstPersonCameraSensitivityY"); + } + } + if (CVarGetInteger("gEnableFirstPersonSensitivity", 0)) { + UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %.0f %%", "##FirstPersonSensitivity Horizontal", + "gFirstPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true); + UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %.0f %%", "##FirstPersonSensitivity Vertical", + "gFirstPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true); + } + UIWidgets::Spacer(0); + LUS::GuiWindow::EndGroupPanel(0); + + UIWidgets::Spacer(0); + cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); + LUS::GuiWindow::BeginGroupPanel("Third-Person Camera", ImGui::GetContentRegionAvail()); + + UIWidgets::PaddedEnhancementCheckbox("Free Camera", "gFreeCamera"); + UIWidgets::Tooltip("Enables free 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"); + UIWidgets::Tooltip("Inverts the Camera X Axis in:\n-Free camera"); + UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); + UIWidgets::Tooltip("Inverts the Camera Y Axis in:\n-Free camera"); + UIWidgets::Spacer(0); + UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %.0f %%", "##ThirdPersonSensitivity Horizontal", + "gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true); + UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %.0f %%", "##ThirdPersonSensitivity Vertical", + "gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true); + UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist", + "gFreeCameraDistMax", 100, 900, "", 185, true, false, true); + UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed", + "gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true); + LUS::GuiWindow::EndGroupPanel(0); +} + +void SohInputEditorWindow::DrawDpadControlPanel() { + ImVec2 cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); + LUS::GuiWindow::BeginGroupPanel("D-Pad Options", ImGui::GetContentRegionAvail()); + UIWidgets::PaddedEnhancementCheckbox("D-pad Support on Pause Screen", "gDpadPause"); + UIWidgets::Tooltip("Navigate Pause with the D-pad\nIf used with D-pad as Equip Items, you must hold C-Up to equip instead of navigate\n" + "To make the cursor only move a single space no matter how long a direction is held, manually set gDpadHoldChange to 0"); + UIWidgets::PaddedEnhancementCheckbox("D-pad Support in Text Boxes", "gDpadText"); + UIWidgets::Tooltip("Navigate choices in text boxes, shop item selection, and the file select / name entry screens with the D-pad\n" + "To make the cursor only move a single space during name entry no matter how long a direction is held, manually set gDpadHoldChange to 0"); + UIWidgets::PaddedEnhancementCheckbox("D-pad as Equip Items", "gDpadEquips"); + UIWidgets::Tooltip("Equip items and equipment on the D-pad\nIf used with D-pad on Pause Screen, you must hold C-Up to equip instead of navigate"); + LUS::GuiWindow::EndGroupPanel(0); +} + +void SohInputEditorWindow::DrawMiscControlPanel() { + ImVec2 cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); + LUS::GuiWindow::BeginGroupPanel("Misc Controls", ImGui::GetContentRegionAvail()); + UIWidgets::PaddedText("Allow the cursor to be on any slot"); + static const char* cursorOnAnySlot[3] = { "Only in Rando", "Always", "Never" }; + UIWidgets::EnhancementCombobox("gPauseAnyCursor", cursorOnAnySlot, PAUSE_ANY_CURSOR_RANDO_ONLY); + UIWidgets::Tooltip("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select " + "certain items."); + UIWidgets::Spacer(0); + ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0)); + UIWidgets::PaddedEnhancementCheckbox("Enable speed modifiers", "gEnableWalkModify", true, false); + UIWidgets::Tooltip("Hold the assigned button to change the maximum walking or swimming speed"); + if (CVarGetInteger("gEnableWalkModify", 0)) { + UIWidgets::Spacer(5); + LUS::GuiWindow::BeginGroupPanel("Speed Modifier", ImGui::GetContentRegionAvail()); + UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", "gWalkSpeedToggle", true, false); + LUS::GuiWindow::BeginGroupPanel("Walk Modifier", ImGui::GetContentRegionAvail()); + UIWidgets::PaddedEnhancementCheckbox("Don't affect jump distance/velocity", "gWalkModifierDoesntChangeJump", true, false); + UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 1: %.0f %%", "##WalkMod1", "gWalkModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true); + UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 2: %.0f %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true); + LUS::GuiWindow::EndGroupPanel(0); + LUS::GuiWindow::BeginGroupPanel("Swim Modifier", ImGui::GetContentRegionAvail()); + UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 1: %.0f %%", "##SwimMod1", "gSwimModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true); + UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 2: %.0f %%", "##SwimMod2", "gSwimModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true); + LUS::GuiWindow::EndGroupPanel(0); + LUS::GuiWindow::EndGroupPanel(0); + } + ImGui::EndDisabled(); + UIWidgets::Spacer(0); + UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL"); + UIWidgets::Tooltip("Speak to Navi with L but enter first-person camera with C-Up"); + LUS::GuiWindow::EndGroupPanel(0); +} + void SohInputEditorWindow::DrawLinkTab() { uint8_t portIndex = 0; if (ImGui::BeginTabItem(StringHelper::Sprintf("Link (P1)###port%d", portIndex).c_str())) { @@ -1516,6 +1791,46 @@ void SohInputEditorWindow::DrawLinkTab() { DrawButtonDeviceIcons(portIndex, mModifierButtonsBitmasks); } + if (ImGui::CollapsingHeader("Ocarina Controls")) { + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + DrawOcarinaControlPanel(); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + } + + if (ImGui::CollapsingHeader("Camera Controls")) { + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + DrawCameraControlPanel(); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + } + + if (ImGui::CollapsingHeader("D-Pad Controls")) { + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + DrawDpadControlPanel(); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + } + + if (ImGui::CollapsingHeader("Miscellaneous Controls")) { + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + DrawMiscControlPanel(); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + } + ImGui::PopStyleColor(); ImGui::PopStyleColor(); ImGui::PopStyleColor(); diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.h b/soh/soh/Enhancements/controls/SohInputEditorWindow.h index 32443bb54..e96b5b09a 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.h +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.h @@ -10,6 +10,15 @@ #include #include #include +#include + +typedef uint32_t N64ButtonMask; + +typedef struct { + const char* label; + const char* cVarName; + N64ButtonMask defaultBtn; +} CustomButtonMap; class SohInputEditorWindow : public LUS::GuiWindow { public: @@ -51,6 +60,17 @@ class SohInputEditorWindow : public LUS::GuiWindow { void DrawRemoveGyroMappingButton(uint8_t port, std::string id); void DrawAddGyroMappingButton(uint8_t port); + // Used together for an incomplete linked hash map implementation in order to + // map button masks to their names and original mapping on N64 + std::list> buttons; + std::unordered_map buttonNames; + void addButtonName(N64ButtonMask mask, const char* name); + void DrawMapping(CustomButtonMap& mapping, float labelWidth, N64ButtonMask excludedButtons); + void DrawOcarinaControlPanel(); + void DrawCameraControlPanel(); + void DrawDpadControlPanel(); + void DrawMiscControlPanel(); + int32_t mGameInputBlockTimer; int32_t mMappingInputBlockTimer; int32_t mRumbleTimer; @@ -84,6 +104,4 @@ class SohInputEditorWindow : public LUS::GuiWindow { bool mInputEditorPopupOpen; void DrawSetDefaultsButton(uint8_t portIndex); void DrawClearAllButton(uint8_t portIndex); - - void DrawHelpIcon(const std::string& helptext); }; diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index 50b5c5b27..240bf5894 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -67,6 +67,7 @@ typedef enum { GROUP_EQUIPMENT, GROUP_CONSUMABLE, GROUP_HUD, + GROUP_KALEIDO, GROUP_TITLE, GROUP_NPC, GROUP_WORLD, @@ -75,6 +76,7 @@ typedef enum { GROUP_SPIN_ATTACK, GROUP_TRAILS, GROUP_NAVI, + GROUP_IVAN, } CosmeticGroup; std::map groupLabels = { @@ -85,6 +87,7 @@ std::map groupLabels = { { GROUP_EQUIPMENT, "Equipment" }, { GROUP_CONSUMABLE, "Consumables" }, { GROUP_HUD, "HUD" }, + { GROUP_KALEIDO, "Pause Menu" }, { GROUP_TITLE, "Title Screen" }, { GROUP_NPC, "NPCs" }, { GROUP_WORLD, "World" }, @@ -93,6 +96,7 @@ std::map groupLabels = { { GROUP_SPIN_ATTACK, "Spin Attack" }, { GROUP_TRAILS, "Trails" }, { GROUP_NAVI, "Navi" }, + { GROUP_IVAN, "Ivan" } }; typedef struct { @@ -265,6 +269,38 @@ static std::map cosmeticOptions = { COSMETIC_OPTION("Hud_NameTagActorText", "Nametag Text", GROUP_HUD, ImVec4(255, 255, 255, 255), true, true, false), COSMETIC_OPTION("Hud_NameTagActorBackground", "Nametag Background", GROUP_HUD, ImVec4(0, 0, 0, 80), true, false, true), + COSMETIC_OPTION("Kal_ItemSelA", "Item Select Color A", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, false), + COSMETIC_OPTION("Kal_ItemSelB", "Item Select Color B", GROUP_KALEIDO, ImVec4(70, 100, 130, 255), false, true, false), + COSMETIC_OPTION("Kal_ItemSelC", "Item Select Color C", GROUP_KALEIDO, ImVec4(70, 100, 130, 255), false, true, false), + COSMETIC_OPTION("Kal_ItemSelD", "Item Select Color D", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, false), + + COSMETIC_OPTION("Kal_EquipSelA", "Equip Select Color A", GROUP_KALEIDO, ImVec4(10, 50, 40, 255), false, true, false), + COSMETIC_OPTION("Kal_EquipSelB", "Equip Select Color B", GROUP_KALEIDO, ImVec4(90, 100, 60, 255), false, true, false), + COSMETIC_OPTION("Kal_EquipSelC", "Equip Select Color C", GROUP_KALEIDO, ImVec4(90, 100, 60, 255), false, true, false), + COSMETIC_OPTION("Kal_EquipSelD", "Equip Select Color D", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, false), + + COSMETIC_OPTION("Kal_MapSelDunA", "Map Dungeon Color A", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false), + COSMETIC_OPTION("Kal_MapSelDunB", "Map Dungeon Color B", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false), + COSMETIC_OPTION("Kal_MapSelDunC", "Map Dungeon Color C", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false), + COSMETIC_OPTION("Kal_MapSelDunD", "Map Dungeon Color D", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false), + + COSMETIC_OPTION("Kal_QuestStatusA", "Quest StatusColor A", GROUP_KALEIDO, ImVec4(80, 80, 50, 255), false, true, false), + COSMETIC_OPTION("Kal_QuestStatusB", "Quest StatusColor B", GROUP_KALEIDO, ImVec4(120, 120, 70, 255), false, true, false), + COSMETIC_OPTION("Kal_QuestStatusC", "Quest StatusColor C", GROUP_KALEIDO, ImVec4(120, 120, 70, 255), false, true, false), + COSMETIC_OPTION("Kal_QuestStatusD", "Quest StatusColor D", GROUP_KALEIDO, ImVec4(80, 80, 50, 255), false, true, false), + + COSMETIC_OPTION("Kal_MapSelectA", "Map Color A", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false), + COSMETIC_OPTION("Kal_MapSelectB", "Map Color B", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false), + COSMETIC_OPTION("Kal_MapSelectC", "Map Color C", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false), + COSMETIC_OPTION("Kal_MapSelectD", "Map Color D", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false), + + COSMETIC_OPTION("Kal_SaveA", "Save A", GROUP_KALEIDO, ImVec4(50, 50, 50, 255), false, true, false), + COSMETIC_OPTION("Kal_SaveB", "Save B", GROUP_KALEIDO, ImVec4(110, 110, 110, 255), false, true, false), + COSMETIC_OPTION("Kal_SaveC", "Save C", GROUP_KALEIDO, ImVec4(110, 110, 110, 255), false, true, false), + COSMETIC_OPTION("Kal_SaveD", "Save D", GROUP_KALEIDO, ImVec4(50, 50, 50, 255), false, true, false), + + COSMETIC_OPTION("Kal_NamePanel", "Name Panel", GROUP_KALEIDO, ImVec4(90,100,130,255), true, true, false), + COSMETIC_OPTION("Title_FileChoose", "File Choose", GROUP_TITLE, ImVec4(100, 150, 255, 255), false, true, false), COSMETIC_OPTION("Title_NintendoLogo", "Nintendo Logo", GROUP_TITLE, ImVec4( 0, 0, 255, 255), false, true, true), COSMETIC_OPTION("Title_N64LogoRed", "N64 Red", GROUP_TITLE, ImVec4(150, 0, 0, 255), false, true, true), @@ -316,6 +352,9 @@ static std::map cosmeticOptions = { COSMETIC_OPTION("Navi_EnemySecondary", "Enemy Secondary", GROUP_NAVI, ImVec4(200, 155, 0, 0), false, true, true), COSMETIC_OPTION("Navi_PropsPrimary", "Props Primary", GROUP_NAVI, ImVec4( 0, 255, 0, 255), false, true, false), COSMETIC_OPTION("Navi_PropsSecondary", "Props Secondary", GROUP_NAVI, ImVec4( 0, 255, 0, 0), false, true, true), + + COSMETIC_OPTION("Ivan_IdlePrimary", "Ivan Idle Primary", GROUP_IVAN, ImVec4(255, 255, 255, 255), false, true, false), + COSMETIC_OPTION("Ivan_IdleSecondary", "Ivan Idle Secondary", GROUP_IVAN, ImVec4( 0, 255, 0, 255), false, true, true), COSMETIC_OPTION("NPC_FireKeesePrimary", "Fire Keese Primary", GROUP_NPC, ImVec4(255, 255, 255, 255), false, true, false), COSMETIC_OPTION("NPC_FireKeeseSecondary", "Fire Keese Secondary", GROUP_NPC, ImVec4(255, 255, 255, 255), false, true, true), @@ -1626,6 +1665,8 @@ void RandomizeColor(CosmeticOption& cosmeticOption) { CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_NPCSecondary"), 1.0f); } else if (cosmeticOption.label == "Props Primary") { CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_PropsSecondary"), 1.0f); + } else if (cosmeticOption.label == "Ivan Idle Primary") { + CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Ivan_IdleSecondary"), 0.5f); } else if (cosmeticOption.label == "Level 1 Secondary") { CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("SpinAttack_Level1Primary"), 2.0f); } else if (cosmeticOption.label == "Level 2 Secondary") { @@ -1794,14 +1835,11 @@ void CosmeticsEditorWindow::DrawElement() { } UIWidgets::EnhancementCheckbox("Sync Rainbow colors", "gCosmetics.RainbowSync"); UIWidgets::EnhancementSliderFloat("Rainbow Speed: %.3f", "##rainbowSpeed", "gCosmetics.RainbowSpeed", 0.03f, 1.0f, "", 0.6f, false, true); + UIWidgets::EnhancementCheckbox("Randomize All on New Scene", "gCosmetics.RandomizeAllOnNewScene"); + UIWidgets::Tooltip("Enables randomizing all unlocked cosmetics when you enter a new scene."); + if (ImGui::Button("Randomize All", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) { - for (auto& [id, cosmeticOption] : cosmeticOptions) { - if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && (!cosmeticOption.advancedOption || CVarGetInteger("gCosmetics.AdvancedMode", 0))) { - RandomizeColor(cosmeticOption); - } - } - ApplyOrResetCustomGfxPatches(); - LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + CosmeticsEditor_RandomizeAll(); } ImGui::SameLine(); if (ImGui::Button("Reset All", ImVec2(ImGui::GetContentRegionAvail().x, 30.0f))) { @@ -1861,6 +1899,7 @@ void CosmeticsEditorWindow::DrawElement() { if (ImGui::BeginTabItem("World & NPCs")) { DrawCosmeticGroup(GROUP_WORLD); DrawCosmeticGroup(GROUP_NAVI); + DrawCosmeticGroup(GROUP_IVAN); DrawCosmeticGroup(GROUP_NPC); ImGui::EndTabItem(); } @@ -1873,10 +1912,16 @@ void CosmeticsEditorWindow::DrawElement() { DrawCosmeticGroup(GROUP_TITLE); ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("HUD Placement")) { Draw_Placements(); ImGui::EndTabItem(); } + + if (ImGui::BeginTabItem("Pause Menu")) { + DrawCosmeticGroup(GROUP_KALEIDO); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } ImGui::End(); @@ -1894,6 +1939,14 @@ void RegisterOnGameFrameUpdateHook() { }); } +void Cosmetics_RegisterOnSceneInitHook() { + GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { + if (CVarGetInteger("gCosmetics.RandomizeAllOnNewScene", 0)) { + CosmeticsEditor_RandomizeAll(); + } + }); +} + void CosmeticsEditorWindow::InitElement() { // Convert the `current color` into the format that the ImGui color picker expects for (auto& [id, cosmeticOption] : cosmeticOptions) { @@ -1911,6 +1964,7 @@ void CosmeticsEditorWindow::InitElement() { RegisterOnLoadGameHook(); RegisterOnGameFrameUpdateHook(); + Cosmetics_RegisterOnSceneInitHook(); } void CosmeticsEditor_RandomizeAll() { diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp index de97f3840..709854c35 100644 --- a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp +++ b/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/soh/soh/Enhancements/custom-message/CustomMessageTypes.h b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h index d93bae983..47729c129 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h @@ -38,6 +38,7 @@ typedef enum { TEXT_CARPET_SALESMAN_1 = 0x6077, TEXT_CARPET_SALESMAN_2 = 0x6078, TEXT_MARKET_GUARD_NIGHT = 0x7003, + TEXT_FISHERMAN_LEAVE = 0x409E, TEXT_SHEIK_NEED_HOOK = 0x700F, TEXT_SHEIK_HAVE_HOOK = 0x7010, TEXT_SCRUB_RANDOM = 0x9000, diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 14c9354b5..90dbc2a09 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -730,6 +730,62 @@ void RegisterPatchNoMSHandler() { }); } +void UpdatePatchHand() { + if ((CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && LINK_IS_CHILD) { + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1", 92, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2", 93, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1", 84, gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2", 85, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1", 51, gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2", 52, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1", 104, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2", 105, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1", 79, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2", 80, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1", 76, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2", 77, gsSPEndDisplayList()); + + } else { + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2"); + } + if ((CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && LINK_IS_ADULT) { + ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword", 13, gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot", 13, gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang", 50, gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield", 49, gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL)); + } else { + ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword"); + ResourceMgr_UnpatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot"); + ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang"); + ResourceMgr_UnpatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield"); + } +} + +void RegisterPatchHandHandler() { + GameInteractor::Instance->RegisterGameHook([](int32_t sceneNum) { + UpdatePatchHand(); + }); +} + +void RegisterResetNaviTimer() { + GameInteractor::Instance->RegisterGameHook([](int32_t sceneNum) { + if (CVarGetInteger("gEnhancements.ResetNaviTimer", 0)) { + gSaveContext.naviTimer = 0; + } + }); +} + f32 triforcePieceScale; void RegisterTriforceHunt() { @@ -1133,6 +1189,29 @@ void RegisterRandomizerSheikSpawn() { }); } +void UpdateHurtContainerModeState(bool newState) { + static bool hurtEnabled = false; + if (hurtEnabled == newState) { + return; + } + + hurtEnabled = newState; + uint16_t getHeartPieces = gSaveContext.sohStats.heartPieces / 4; + uint16_t getHeartContainers = gSaveContext.sohStats.heartContainers; + + if (hurtEnabled) { + gSaveContext.healthCapacity = 320 - ((getHeartPieces + getHeartContainers) * 16); + } else { + gSaveContext.healthCapacity = 48 + ((getHeartPieces + getHeartContainers) * 16); + } +} + +void RegisterHurtContainerModeHandler() { + GameInteractor::Instance->RegisterGameHook([](int32_t fileNum) { + UpdateHurtContainerModeState(CVarGetInteger("gHurtContainer", 0)); + }); +} + void RegisterRandomizedEnemySizes() { GameInteractor::Instance->RegisterGameHook([](void* refActor) { // Randomized Enemy Sizes @@ -1291,6 +1370,7 @@ void InitMods() { RegisterBonkDamage(); RegisterMenuPathFix(); RegisterMirrorModeHandler(); + RegisterResetNaviTimer(); RegisterTriforceHunt(); RegisterGrantGanonsBossKey(); RegisterEnemyDefeatCounts(); @@ -1300,4 +1380,6 @@ void InitMods() { RegisterToTMedallions(); NameTag_RegisterHooks(); RegisterPatchNoMSHandler(); + RegisterPatchHandHandler(); + RegisterHurtContainerModeHandler(); } diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 46123f968..57ebedfd9 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -9,9 +9,11 @@ extern "C" { void UpdateDirtPathFixState(int32_t sceneNum); void UpdateMirrorModeState(int32_t sceneNum); +void UpdateHurtContainerModeState(bool newState); void PatchToTMedallions(); void UpdatePermanentHeartLossState(); void InitMods(); +void UpdatePatchHand(); #ifdef __cplusplus } diff --git a/soh/soh/Enhancements/presets.cpp b/soh/soh/Enhancements/presets.cpp index 7b2ca6595..726863e05 100644 --- a/soh/soh/Enhancements/presets.cpp +++ b/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/soh/soh/Enhancements/presets.h b/soh/soh/Enhancements/presets.h index d7efeb4f3..8a9b963e8 100644 --- a/soh/soh/Enhancements/presets.h +++ b/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); @@ -70,6 +76,7 @@ const std::vector enhancementsCvars = { "gForgeTime", "gClimbSpeed", "gFasterBlockPush", + "gCrawlSpeed", "gFasterHeavyBlockLift", "gNoForcedNavi", "gSkulltulaFreeze", @@ -136,6 +143,7 @@ const std::vector enhancementsCvars = { "gInjectItemCounts", "gDayGravePull", "gDampeAllNight", + "gQuitFishingAtDoor", "gSkipSwimDeepEndAnim", "gSkipScarecrow", "gBlueFireArrows", @@ -184,6 +192,14 @@ const std::vector enhancementsCvars = { "gBombchuBowlingNoSmallCucco", "gBombchuBowlingNoBigCucco", "gBombchuBowlingAmmunition", + "gCustomizeOcarinaGame", + "gInstantOcarinaGameWin", + "gOcarinaGameNoteSpeed", + "gOcarinaUnlimitedFailTime", + "gOcarinaGameStartingNotes", + "gOcarinaGameRoundOneNotes", + "gOcarinaGameRoundTwoNotes", + "gOcarinaGameRoundThreeNotes", "gCreditsFix", "gSilverRupeeJingleExtend", "gStaticExplosionRadius", @@ -256,6 +272,8 @@ const std::vector cheatCvars = { "gWalkSpeedToggle", "gWalkModifierOne", "gWalkModifierTwo", + "gSwimModifierOne", + "gSwimModifierTwo", "gGoronPot", "gDampeWin", "gCustomizeShootingGallery", @@ -783,6 +801,13 @@ const std::vector randomizerPresetEntries = { // Adult Minimum Weight (8 to 13) PRESET_ENTRY_S32("gAdultMinimumWeightFish", 6), + // Customize Lost Woods Ocarina Game Behavior + PRESET_ENTRY_S32("gCustomizeOcarinaGame", 1), + // Start With Five Notes + PRESET_ENTRY_S32("gOcarinaGameStartingNotes", 5), + // Round One Notes + PRESET_ENTRY_S32("gOcarinaGameRoundOneNotes", 5), + // Visual Stone of Agony PRESET_ENTRY_S32("gVisualAgony", 1), // Pull grave during the day @@ -871,7 +896,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), @@ -963,7 +989,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), @@ -1016,7 +1043,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/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp index 440483af6..ff2fb885c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp +++ b/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/soh/soh/Enhancements/randomizer/3drando/logic.cpp b/soh/soh/Enhancements/randomizer/3drando/logic.cpp index 580c687d0..df127e699 100644 --- a/soh/soh/Enhancements/randomizer/3drando/logic.cpp +++ b/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/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 24c258506..984c20bb2 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -4221,7 +4221,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", @@ -4250,7 +4250,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", @@ -4279,7 +4279,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", @@ -4308,7 +4308,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", diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index f24ab818b..f158b4f3b 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -412,10 +412,31 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) { void DrawItemCount(ItemTrackerItem item) { int iconSize = CVarGetInteger("gItemTrackerIconSize", 36); + int textSize = CVarGetInteger("gTrackers.ItemTracker.ItemTrackerTextSize", 13); ItemTrackerNumbers currentAndMax = GetItemCurrentAndMax(item); ImVec2 p = ImGui::GetCursorScreenPos(); int32_t trackerNumberDisplayMode = CVarGetInteger("gItemTrackerCapacityTrack", ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY); int32_t trackerKeyNumberDisplayMode = CVarGetInteger("gItemTrackerKeyTrack", KEYS_COLLECTED_MAX); + float textScalingFactor = static_cast(iconSize) / 36.0f; + uint32_t actualItemId = INV_CONTENT(item.id); + bool hasItem = actualItemId != ITEM_NONE; + + if (CVarGetInteger("gTrackers.ItemTracker.HookshotIdentifier", 0)) { + if ((actualItemId == ITEM_HOOKSHOT || actualItemId == ITEM_LONGSHOT) && hasItem) { + + // Calculate the scaled position for the text + ImVec2 textPos = ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(item.id == ITEM_HOOKSHOT ? "H" : "L").x * + textScalingFactor / 2) + 8 * textScalingFactor, p.y - 22 * textScalingFactor); + + ImGui::SetCursorScreenPos(textPos); + ImGui::SetWindowFontScale(textScalingFactor); + + ImGui::Text(item.id == ITEM_HOOKSHOT ? "H" : "L"); + ImGui::SetWindowFontScale(1.0f); // Reset font scale to the original state + } + } + + ImGui::SetWindowFontScale(textSize / 13.0f); if (item.id == ITEM_KEY_SMALL && IsValidSaveFile()) { std::string currentString = ""; @@ -671,7 +692,7 @@ void DrawDungeonItem(ItemTrackerItem item) { ImVec2 p = ImGui::GetCursorScreenPos(); std::string dungeonName = itemTrackerDungeonShortNames[item.data]; - ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + 16))); + ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + CVarGetInteger("gTrackers.ItemTracker.ItemTrackerTextSize", 13) + 3))); ImGui::PushStyleColor(ImGuiCol_Text, dungeonColor); ImGui::Text("%s", dungeonName.c_str()); ImGui::PopStyleColor(); @@ -800,7 +821,7 @@ void DrawItemsInACircle(std::vector items) { float angle = (float)i / items.size() * 2.0f * M_PI; float x = (radius / 2.0f) * cos(angle) + max.x / 2.0f; float y = (radius / 2.0f) * sin(angle) + max.y / 2.0f; - ImGui::SetCursorPos(ImVec2(x - 14, y + 4)); + ImGui::SetCursorPos(ImVec2(x - (CVarGetInteger("gItemTrackerIconSize", 36) - 8) / 2.0f, y + 4)); items[i].drawFunc(items[i]); } } @@ -1127,6 +1148,7 @@ void ItemTrackerSettingsWindow::DrawElement() { UIWidgets::PaddedSeparator(); UIWidgets::EnhancementSliderInt("Icon size : %dpx", "##ITEMTRACKERICONSIZE", "gItemTrackerIconSize", 25, 128, "", 36); UIWidgets::EnhancementSliderInt("Icon margins : %dpx", "##ITEMTRACKERSPACING", "gItemTrackerIconSpacing", -5, 50, "", 12); + UIWidgets::EnhancementSliderInt("Text size : %dpx", "##ITEMTRACKERTEXTSIZE", "gTrackers.ItemTracker.ItemTrackerTextSize", 1, 30, "", 13); UIWidgets::Spacer(0); @@ -1167,7 +1189,7 @@ void ItemTrackerSettingsWindow::DrawElement() { shouldUpdateVectors = true; } if (CVarGetInteger("gItemTrackerDungeonRewardsDisplayType", SECTION_DISPLAY_MAIN_WINDOW) == SECTION_DISPLAY_SEPARATE) { - if (UIWidgets::PaddedEnhancementCheckbox("Circle display", "gItemTrackerDungeonRewardsCircle", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true)) { + if (UIWidgets::PaddedEnhancementCheckbox("Circle display", "gItemTrackerDungeonRewardsCircle", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, false)) { shouldUpdateVectors = true; } } @@ -1200,6 +1222,10 @@ void ItemTrackerSettingsWindow::DrawElement() { shouldUpdateVectors = true; } } + UIWidgets::EnhancementCheckbox("Show Hookshot Identifiers", "gTrackers.ItemTracker.HookshotIdentifier"); + UIWidgets::InsertHelpHoverText("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot."); + + UIWidgets::Spacer(0); ImGui::PopStyleVar(1); ImGui::EndTable(); diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 29f2ee9a4..74d3853c4 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/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/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp index 11a7cd75a..892b5798a 100644 --- a/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp +++ b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp @@ -115,7 +115,7 @@ void AdvancedResolutionSettingsWindow::DrawElement() { const bool disabled_resolutionSlider = (CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0) && CVarGetInteger("gAdvancedResolution.Enabled", 0)) || CVarGetInteger("gLowResMode", 0); - if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, + if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %.1f%%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) { LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier( CVarGetFloat("gInternalResolution", 1)); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 63001a2b2..283d35e53 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -29,7 +29,6 @@ #include #include #include "Enhancements/speechsynthesizer/SpeechSynthesizer.h" -#include "Enhancements/controls/GameControlEditor.h" #include "Enhancements/controls/SohInputEditorWindow.h" #include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/audio/AudioCollection.h" @@ -307,7 +306,7 @@ OTRGlobals::OTRGlobals() { // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) context->InitResourceManager(OTRFiles, {}, 3); - + context->InitControlDeck({BTN_MODIFIER1, BTN_MODIFIER2}); context->GetControlDeck()->SetSinglePlayerMappingMode(true); @@ -317,7 +316,9 @@ OTRGlobals::OTRGlobals() { auto sohInputEditorWindow = std::make_shared("gControllerConfigurationEnabled", "Input Editor"); context->InitWindow(sohInputEditorWindow); context->InitAudio(); - + + 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()); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Room, "Room", std::make_shared()); // Is room scene? maybe? @@ -1213,8 +1214,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 @@ -2563,8 +2563,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; @@ -2629,6 +2628,9 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { if (textId == TEXT_MARKET_GUARD_NIGHT && CVarGetInteger("gMarketSneak", 0) && play->sceneNum == SCENE_MARKET_ENTRANCE_NIGHT) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_MARKET_GUARD_NIGHT); } + if (textId == TEXT_FISHERMAN_LEAVE && CVarGetInteger("gQuitFishingAtDoor", 0)) { + messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_FISHERMAN_LEAVE); + } font->charTexBuf[0] = (messageEntry.GetTextBoxType() << 4) | messageEntry.GetTextBoxPosition(); switch (gSaveContext.language) { case LANGUAGE_FRA: @@ -2674,6 +2676,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/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 518aac8d1..db42341c2 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -176,6 +176,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/soh/soh/SohGui.cpp b/soh/soh/SohGui.cpp index fa7072f5a..49f84068a 100644 --- a/soh/soh/SohGui.cpp +++ b/soh/soh/SohGui.cpp @@ -116,7 +116,6 @@ namespace SohGui { std::shared_ptr mInputEditorWindow; std::shared_ptr mAudioEditorWindow; - std::shared_ptr mGameControlEditorWindow; std::shared_ptr mCosmeticsEditorWindow; std::shared_ptr mActorViewerWindow; std::shared_ptr mColViewerWindow; @@ -164,8 +163,6 @@ namespace SohGui { mAudioEditorWindow = std::make_shared("gAudioEditor.WindowOpen", "Audio Editor"); gui->AddGuiWindow(mAudioEditorWindow); - mGameControlEditorWindow = std::make_shared("gGameControlEditorEnabled", "Game Control Editor"); - gui->AddGuiWindow(mGameControlEditorWindow); mCosmeticsEditorWindow = std::make_shared("gCosmeticsEditorEnabled", "Cosmetics Editor"); gui->AddGuiWindow(mCosmeticsEditorWindow); mActorViewerWindow = std::make_shared("gActorViewerEnabled", "Actor Viewer"); @@ -211,7 +208,6 @@ namespace SohGui { mColViewerWindow = nullptr; mActorViewerWindow = nullptr; mCosmeticsEditorWindow = nullptr; - mGameControlEditorWindow = nullptr; mAudioEditorWindow = nullptr; mInputEditorWindow = nullptr; mStatsWindow = nullptr; diff --git a/soh/soh/SohGui.hpp b/soh/soh/SohGui.hpp index fccc29c1f..73a3addd4 100644 --- a/soh/soh/SohGui.hpp +++ b/soh/soh/SohGui.hpp @@ -11,7 +11,6 @@ #include #include "SohMenuBar.h" #include "Enhancements/audio/AudioEditor.h" -#include "Enhancements/controls/GameControlEditor.h" #include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/debugger/actorViewer.h" #include "Enhancements/debugger/colViewer.h" diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index e656a3a90..f59e3a343 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -21,7 +21,6 @@ #include "Enhancements/audio/AudioEditor.h" -#include "Enhancements/controls/GameControlEditor.h" #include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/debugger/actorViewer.h" #include "Enhancements/debugger/colViewer.h" @@ -180,7 +179,6 @@ void DrawShipMenu() { } extern std::shared_ptr mInputEditorWindow; -extern std::shared_ptr mGameControlEditorWindow; extern std::shared_ptr mAdvancedResolutionSettingsWindow; void DrawSettingsMenu() { @@ -241,11 +239,6 @@ void DrawSettingsMenu() { mInputEditorWindow->ToggleVisibility(); } } - if (mGameControlEditorWindow) { - if (ImGui::Button(GetWindowButtonText("Additional Controller Options", CVarGetInteger("gGameControlEditorEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mGameControlEditorWindow->ToggleVisibility(); - } - } UIWidgets::PaddedSeparator(); ImGui::PopStyleColor(1); ImGui::PopStyleVar(3); @@ -273,8 +266,10 @@ void DrawSettingsMenu() { 2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) { LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1)); } - UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing"); - #endif + UIWidgets::Tooltip("Resolution scale. Multiplies output resolution by this value, on each axis relative to window size.\n" + "Lower values may improve performance.\n" + "Values above 100% can be used for super-sampling, as an intensive but highly effective form of anti-aliasing.\n\n" + "Default: 100%"); if (mAdvancedResolutionSettingsWindow) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); @@ -288,14 +283,28 @@ void DrawSettingsMenu() { ImGui::PopStyleColor(1); ImGui::PopStyleVar(3); } - - #ifndef __WIIU__ - if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false)) { - LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1)); - }; - UIWidgets::Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel"); + #else + // macOS: Internal resolution is currently disabled in libultraship. + ImGui::BeginGroup(); + ImGui::Text("Internal Resolution: 100.0%%"); + UIWidgets::Spacer(0); + ImGui::Text(" " ICON_FA_INFO_CIRCLE " Not available on this system."); + UIWidgets::Spacer(0); + ImGui::EndGroup(); #endif + #ifndef __WIIU__ + if (UIWidgets::PaddedEnhancementSliderInt( + (CVarGetInteger("gMSAAValue", 1) == 1) ? "Anti-aliasing (MSAA): Off" : "Anti-aliasing (MSAA): %d", + "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false)) { + LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1)); + } + UIWidgets::Tooltip("Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x, to smooth the edges of rendered geometry.\n" + "Higher sample count will result in smoother edges on models, but may reduce performance.\n\n" + "Recommended: 2x or 4x"); + #endif + + UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); { // FPS Slider const int minFps = 20; static int maxFps; @@ -369,26 +378,27 @@ void DrawSettingsMenu() { bool matchingRefreshRate = CVarGetInteger("gMatchRefreshRate", 0) && LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() != LUS::WindowBackend::DX11; UIWidgets::PaddedEnhancementSliderInt( - (currentFps == 20) ? "FPS: Original (20)" : "FPS: %d", + (currentFps == 20) ? "Frame Rate: Original (20 fps)" : "Frame Rate: %d fps", "##FPSInterpolation", "gInterpolationFPS", minFps, maxFps, "", 20, true, true, false, matchingRefreshRate); #endif if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { UIWidgets::Tooltip( - "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely " - "visual and does not impact game logic, execution of glitches etc.\n\n" - "A higher target FPS than your monitor's refresh rate will waste resources, and might give a worse result." - ); + "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics.\n" + "This is purely visual and does not impact game logic, execution of glitches etc.\n" + "Higher frame rate settings may impact CPU performance." + "\n\n " ICON_FA_INFO_CIRCLE + " There is no need to set this above your monitor's refresh rate. Doing so will waste resources and may give a worse result."); } else { UIWidgets::Tooltip( - "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely " - "visual and does not impact game logic, execution of glitches etc." - ); + "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics.\n" + "This is purely visual and does not impact game logic, execution of glitches etc.\n" + "Higher frame rate settings may impact CPU performance."); } } // END FPS Slider if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { UIWidgets::Spacer(0); - if (ImGui::Button("Match Refresh Rate")) { + if (ImGui::Button("Match Frame Rate to Refresh Rate")) { int hz = LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); if (hz >= 20 && hz <= 360) { CVarSetInteger("gInterpolationFPS", hz); @@ -396,17 +406,22 @@ void DrawSettingsMenu() { } } } else { - UIWidgets::PaddedEnhancementCheckbox("Match Refresh Rate", "gMatchRefreshRate", true, false); + UIWidgets::PaddedEnhancementCheckbox("Match Frame Rate to Refresh Rate", "gMatchRefreshRate", true, false); } - UIWidgets::Tooltip("Matches interpolation value to the current game's window refresh rate"); + UIWidgets::Tooltip("Matches interpolation value to the game window's current refresh rate."); if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { UIWidgets::PaddedEnhancementSliderInt(CVarGetInteger("gExtraLatencyThreshold", 80) == 0 ? "Jitter fix: Off" : "Jitter fix: >= %d FPS", "##ExtraLatencyThreshold", "gExtraLatencyThreshold", 0, 360, "", 80, true, true, false); - UIWidgets::Tooltip("When Interpolation FPS setting is at least this threshold, add one frame of input lag (e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the CPU to work on one frame while GPU works on the previous frame.\nThis setting should be used when your computer is too slow to do CPU + GPU work in time."); + UIWidgets::Tooltip( + "(For DirectX backend only)\n\n" + "When Interpolation FPS (Frame Rate) setting is at least this threshold, add one frame of delay (e.g. 16.6 ms for 60 FPS) in order to avoid jitter." + "This setting allows the CPU to work on one frame while GPU works on the previous frame.\n" + "This setting should be used when your computer is too slow to do CPU + GPU work in time."); } UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); + ImGui::Text("ImGui Menu Scale"); ImGui::SameLine(); ImGui::TextColored({ 0.85f, 0.35f, 0.0f, 1.0f }, "(Experimental)"); @@ -455,6 +470,7 @@ void DrawSettingsMenu() { if (LUS::Context::GetInstance()->GetWindow()->CanDisableVerticalSync()) { UIWidgets::PaddedEnhancementCheckbox("Enable Vsync", "gVsyncEnabled", true, false); + UIWidgets::Tooltip("Activate vertical sync, to prevent screen tearing."); } if (LUS::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen()) { @@ -462,17 +478,21 @@ void DrawSettingsMenu() { } if (LUS::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports()) { - UIWidgets::PaddedEnhancementCheckbox("Allow multi-windows", "gEnableMultiViewports", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); + UIWidgets::PaddedEnhancementCheckbox("Allow multi-windows (Needs reload)", "gEnableMultiViewports", true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); UIWidgets::Tooltip("Allows windows to be able to be dragged off of the main game window. Requires a reload to take effect."); } // If more filters are added to LUS, make sure to add them to the filters list here - ImGui::Text("Texture Filter (Needs reload)"); - + ImGui::Text("Texture Filtering (Needs reload)"); UIWidgets::EnhancementCombobox("gTextureFilter", filters, FILTER_THREE_POINT); + UIWidgets::Tooltip("Texture filtering, aka texture smoothing. Requires a reload to take effect.\n\n" + "Three-Point: Replicates real N64 texture filtering.\n" + "Bilinear: If Three-Point causes poor performance, try this.\n" + "Nearest: Disables texture smoothing. (Not recommended)"); - UIWidgets::Spacer(0); + UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); + // Draw LUS settings menu (such as Overlays Text Font) LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->DrawSettings(); ImGui::EndMenu(); @@ -542,6 +562,7 @@ void DrawEnhancementsMenu() { UIWidgets::PaddedEnhancementSliderInt("King Zora Speed: %dx", "##MWEEPSPEED", "gMweepSpeed", 1, 5, "", 1, true, false, true); UIWidgets::PaddedEnhancementSliderInt("Vine/Ladder Climb speed +%d", "##CLIMBSPEED", "gClimbSpeed", 0, 12, "", 0, true, false, true); UIWidgets::PaddedEnhancementSliderInt("Block pushing speed +%d", "##BLOCKSPEED", "gFasterBlockPush", 0, 5, "", 0, true, false, true); + UIWidgets::PaddedEnhancementSliderInt("Crawl speed %dx", "##CRAWLSPEED", "gCrawlSpeed", 1, 5, "", 1, true, false, true); UIWidgets::PaddedEnhancementCheckbox("Faster Heavy Block Lift", "gFasterHeavyBlockLift", false, false); UIWidgets::Tooltip("Speeds up lifting silver rocks and obelisks"); UIWidgets::PaddedEnhancementCheckbox("Skip Pickup Messages", "gFastDrops", true, false); @@ -573,6 +594,8 @@ void DrawEnhancementsMenu() { "This doesn't work if the save was made in a grotto."); UIWidgets::PaddedEnhancementCheckbox("No Forced Navi", "gNoForcedNavi", true, false); UIWidgets::Tooltip("Prevent forced Navi conversations"); + UIWidgets::PaddedEnhancementCheckbox("Navi Timer Resets", "gEnhancements.ResetNaviTimer", true, false); + UIWidgets::Tooltip("Resets the Navi timer on scene change. If you have already talked to her, she will try and talk to you again, instead of needing a save warp or death. "); UIWidgets::PaddedEnhancementCheckbox("No Skulltula Freeze", "gSkulltulaFreeze", true, false); UIWidgets::Tooltip("Stops the game from freezing the player when picking up Gold Skulltulas"); UIWidgets::PaddedEnhancementCheckbox("Nighttime GS Always Spawn", "gNightGSAlwaysSpawn", true, false); @@ -610,6 +633,8 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("Allows exiting Hyrule Castle Market Town to Hyrule Field at night by speaking to the guard next to the gate."); UIWidgets::PaddedEnhancementCheckbox("Link as default file name", "gLinkDefaultName", true, false); UIWidgets::Tooltip("Allows you to have \"Link\" as a premade file name"); + UIWidgets::PaddedEnhancementCheckbox("Quit Fishing At Door", "gQuitFishingAtDoor", true, false); + UIWidgets::Tooltip("Fisherman asks if you want to quit at the door when you still have the rod"); UIWidgets::PaddedText("Time Travel with the Song of Time", true, false); UIWidgets::EnhancementCombobox("gTimeTravel", timeTravelOptions, 0); UIWidgets::Tooltip("Allows Link to freely change age by playing the Song of Time.\n" @@ -814,6 +839,40 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("The minimum weight for the unique fishing reward as an adult"); ImGui::EndMenu(); } + UIWidgets::Spacer(0); + + if (ImGui::BeginMenu("Lost Woods Ocarina Game")) { + UIWidgets::EnhancementCheckbox("Customize Behavior", "gCustomizeOcarinaGame"); + UIWidgets::Tooltip("Turn on/off changes to the lost woods ocarina game behavior"); + bool disabled = !CVarGetInteger("gCustomizeOcarinaGame", 0); + static const char* disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off"; + UIWidgets::PaddedEnhancementCheckbox("Instant Win", "gInstantOcarinaGameWin", true, false, disabled, disabledTooltip); + UIWidgets::Tooltip("Skips the lost woods ocarina game"); + UIWidgets::PaddedEnhancementSliderInt("Note Play Speed: %dx", "##OcarinaGameNoteSpeed", "gOcarinaGameNoteSpeed", 1, 5, "", 1, true, true, false, disabled, disabledTooltip); + UIWidgets::Tooltip("Adjust the speed that the skull kids play notes"); + UIWidgets::PaddedEnhancementCheckbox("Unlimited Playback Time", "gOcarinaUnlimitedFailTime", true, false, disabled, disabledTooltip); + UIWidgets::Tooltip("Removes the timer to play back the song"); + UIWidgets::PaddedEnhancementSliderInt("Number of Starting Notes: %d", "##OcarinaGameStartingNotes", "gOcarinaGameStartingNotes", 1, 8, "", 3, true, true, false, + disabled, disabledTooltip); + UIWidgets::Tooltip("Adjust the number of notes the skull kids play to start the first round"); + int roundMin = CVarGetInteger("gOcarinaGameStartingNotes", 3); + UIWidgets::PaddedEnhancementSliderInt("Round One Notes: %d", "##OcarinaGameRoundOne", + "gOcarinaGameRoundOneNotes", roundMin, 8, "", 5, true, true, + false, + disabled, disabledTooltip); + UIWidgets::Tooltip("Adjust the number of notes you need to play to end the first round"); + UIWidgets::PaddedEnhancementSliderInt("Round Two Notes: %d", "##OcarinaGameRoundTwoNotes", + "gOcarinaGameRoundTwoNotes", roundMin, 8, "", 6, true, true, + false, + disabled, disabledTooltip); + UIWidgets::Tooltip("Adjust the number of notes you need to play to end the second round"); + UIWidgets::PaddedEnhancementSliderInt("Round Three Notes: %d", "##OcarinaGameRoundThreeNotes", + "gOcarinaGameRoundThreeNotes", roundMin, 8, "", 8, true, true, + false, + disabled, disabledTooltip); + UIWidgets::Tooltip("Adjust the number of notes you need to play to end the third round"); + ImGui::EndMenu(); + } UIWidgets::Spacer(0); @@ -1046,6 +1105,15 @@ void DrawEnhancementsMenu() { UIWidgets::PaddedEnhancementCheckbox("Kokiri Draw Distance", "gDisableKokiriDrawDistance", true, false); UIWidgets::Tooltip("The Kokiri are mystical beings that fade into view when approached\nEnabling this will remove their draw distance"); } + if (UIWidgets::PaddedEnhancementCheckbox("Show Age-Dependent Equipment", "gEnhancements.EquimentAlwaysVisible", true, + false)) { + UpdatePatchHand(); + } + UIWidgets::Tooltip("Makes all equipment visible, regardless of Age."); + if (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0) == 1) { + UIWidgets::PaddedEnhancementCheckbox("Scale Adult Equipment as Child", "gEnhancements.ScaleAdultEquimentAsChild", true, false); + UIWidgets::Tooltip("Scales all of the Adult Equipment, as well and moving some a bit, to fit on Child Link Better. May not work properly with some mods."); + } UIWidgets::PaddedEnhancementCheckbox("N64 Mode", "gLowResMode", true, false); UIWidgets::Tooltip("Sets aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution"); UIWidgets::PaddedEnhancementCheckbox("Glitch line-up tick", "gDrawLineupTick", true, false); @@ -1064,6 +1132,7 @@ void DrawEnhancementsMenu() { PatchToTMedallions(); } UIWidgets::Tooltip("When medallions are collected, the medallion imprints around the Master Sword pedestal in the Temple of Time will become colored"); + UIWidgets::PaddedEnhancementCheckbox("Show locked door chains on both sides of locked doors", "gShowDoorLocksOnBothSides", true, false); UIWidgets::PaddedText("Fix Vanishing Paths", true, false); if (UIWidgets::EnhancementCombobox("gSceneSpecificDirtPathFix", zFightingOptions, ZFIGHT_FIX_DISABLED) && gPlayState != NULL) { UpdateDirtPathFixState(gPlayState->sceneNum); @@ -1257,6 +1326,14 @@ void DrawEnhancementsMenu() { } } + UIWidgets::Spacer(0); + if (UIWidgets::PaddedEnhancementCheckbox("Hurt Container Mode", "gHurtContainer", true, false)) { + UpdateHurtContainerModeState(CVarGetInteger("gHurtContainer", 0)); + } + UIWidgets::Tooltip("Changes Heart Piece and Heart Container functionality.\n\n" + "- Each Heart Container or full Heart Piece reduces Links hearts by 1.\n" + "- Can be enabled retroactively after a File has already started."); + ImGui::EndMenu(); } diff --git a/soh/soh/UIWidgets.cpp b/soh/soh/UIWidgets.cpp index ca104f945..8de4e9e9d 100644 --- a/soh/soh/UIWidgets.cpp +++ b/soh/soh/UIWidgets.cpp @@ -449,7 +449,7 @@ namespace UIWidgets { if (changed && !(abs(oldVal - val) < 0.000001f)) { std::stringstream ss; - ss << std::setprecision(ticks) << val; + ss << std::setprecision(ticks + 1) << val; val = std::stof(ss.str()); CVarSetFloat(cvarName, val); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); diff --git a/soh/soh/z_message_OTR.cpp b/soh/soh/z_message_OTR.cpp index 10e6b0cfe..a4255d08a 100644 --- a/soh/soh/z_message_OTR.cpp +++ b/soh/soh/z_message_OTR.cpp @@ -182,4 +182,9 @@ extern "C" void OTRMessage_Init() CustomMessage("You look bored. Wanna go out for a&walk?\x1B&%gYes&No%w", "Du siehst gelangweilt aus.&Willst du einen Spaziergang machen?\x1B&%gJa&Nein%w", "Tu as l'air de t'ennuyer. Tu veux&aller faire un tour?\x1B&%gOui&Non%w")); + CustomMessageManager::Instance->CreateMessage( + customMessageTableID, TEXT_FISHERMAN_LEAVE, + CustomMessage("Hey! Hey!&You can't take the rod out of here!&I'm serious!^Do you want to quit?&\x1B&%gYes&No%w", + "Hey! Hey!&Du kannst die Angel doch nicht&einfach mitnehmen!&Ganz im Ernst!^Möchtest du aufhören?&\x1B&%gJa&Nein%w", //TODO Used AI translation as placeholder + "Holà! Holà!&Les cannes ne sortent pas d'ici!&Je suis sérieux!^Voulez-vous arrêter?&\x1B&%gOui&Non%w")); //TODO Used AI translation as placeholder } diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp index 6926f313f..8bfb13308 100644 --- a/soh/soh/z_scene_otr.cpp +++ b/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 (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/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c index 27de9b2d5..41d2a8ec3 100644 --- a/soh/src/code/code_800EC960.c +++ b/soh/src/code/code_800EC960.c @@ -2065,16 +2065,40 @@ void func_800EE404(void) { void Audio_OcaMemoryGameStart(u8 minigameRound) { u8 i; + + // #region SOH [Enhancement] + if (CVarGetInteger("gCustomizeOcarinaGame", 0)) { + u8 startingNotes = 3; + u8 roundOneCount = CVarGetInteger("gOcarinaGameRoundOneNotes", 5); + u8 roundTwoCount = CVarGetInteger("gOcarinaGameRoundTwoNotes", 6); + u8 roundThreeCount = CVarGetInteger("gOcarinaGameRoundThreeNotes", 8); + u8 modMinigameNoteCnts[] = { roundOneCount, roundTwoCount, roundThreeCount }; - if (minigameRound > 2) { - minigameRound = 2; - } - sOcaMinigameAppendPos = 0; - sOcaMinigameEndPos = sOcaMinigameNoteCnts[minigameRound]; + startingNotes = CVarGetInteger("gOcarinaGameStartingNotes", 3); - for (i = 0; i < 3; i++) { - Audio_OcaMemoryGameGenNote(); + if (minigameRound > 2) { + minigameRound = 2; + } + + sOcaMinigameAppendPos = 0; + sOcaMinigameEndPos = modMinigameNoteCnts[minigameRound]; + + for (i = 0; i < startingNotes; i++) { + Audio_OcaMemoryGameGenNote(); + } + // #endregion + } else { + if (minigameRound > 2) { + minigameRound = 2; + } + + sOcaMinigameAppendPos = 0; + sOcaMinigameEndPos = sOcaMinigameNoteCnts[minigameRound]; + + for (i = 0; i < 3; i++) { + Audio_OcaMemoryGameGenNote(); + } } } @@ -2093,11 +2117,24 @@ s32 Audio_OcaMemoryGameGenNote(void) { rndNote = sOcarinaNoteValues[(rnd + 1) % 5]; } - sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].noteIdx = rndNote; - sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].unk_02 = 0x2D; - sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].volume = 0x50; - sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].vibrato = 0; - sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].tone = 0; + // #region SOH [Enhancement] + if (CVarGetInteger("gCustomizeOcarinaGame", 0)) { + int noteSpeed = 0x2D; + noteSpeed = noteSpeed / CVarGetInteger("gOcarinaGameNoteSpeed", 1); + + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].noteIdx = rndNote; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].unk_02 = noteSpeed; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].volume = 0x50; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].vibrato = 0; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].tone = 0; + // #endregion + } else { + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].noteIdx = rndNote; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].unk_02 = 0x2D; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].volume = 0x50; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].vibrato = 0; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].tone = 0; + } sOcaMinigameAppendPos++; diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index cd17e1756..089ed475a 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1239,8 +1239,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; @@ -2881,11 +2880,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; @@ -3149,6 +3156,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/soh/src/code/z_bgcheck.c b/soh/src/code/z_bgcheck.c index 4acc68e55..7c99e7ca2 100644 --- a/soh/src/code/z_bgcheck.c +++ b/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/soh/src/code/z_camera.c b/soh/src/code/z_camera.c index 2b5281037..ec3981397 100644 --- a/soh/src/code/z_camera.c +++ b/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/soh/src/code/z_map_exp.c b/soh/src/code/z_map_exp.c index c4db0098b..213ee018d 100644 --- a/soh/src/code/z_map_exp.c +++ b/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/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 7bb3803fc..4c661aad7 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -3361,8 +3361,13 @@ void Message_Update(PlayState* play) { } if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; - gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + if (!CVarGetInteger("gHurtContainer", 0)) { + gSaveContext.healthCapacity += 0x10; + gSaveContext.health += 0x10; + } else { + gSaveContext.healthCapacity -= 0x10; + gSaveContext.health -= 0x10; + } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { if (sLastPlayedSong == OCARINA_SONG_SARIAS) { diff --git a/soh/src/code/z_onepointdemo.c b/soh/src/code/z_onepointdemo.c index ab62692cc..9489eefcd 100644 --- a/soh/src/code/z_onepointdemo.c +++ b/soh/src/code/z_onepointdemo.c @@ -69,7 +69,13 @@ s32 OnePointCutscene_SetInfo(PlayState* play, s16 camIdx, s16 csId, Actor* actor PosRot sp8C; f32 tempRand; Unique9OnePointCs* csInfo = ONEPOINT_CS_INFO(csCam); - + + // #region SOH [Enhancement] + //the default is 90, lower values necessary to prevent camera swing as animation speeds up + s16 camCrawlTemp = CVarGetInteger("gCrawlSpeed", 1); + s16 camCrawlTimer = D_8012042C / camCrawlTemp; + // #endregion + switch (csId) { case 1020: if (timer < 20) { @@ -330,13 +336,26 @@ s32 OnePointCutscene_SetInfo(PlayState* play, s16 camIdx, s16 csId, Actor* actor case 9601: Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3); Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting); - OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120398); + if (CVarGetInteger("gCrawlSpeed", 1) > 1) { + OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, camCrawlTimer, D_80120308, D_80120398); + } else { + OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120398); + } break; case 9602: - Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3); - Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting); - OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120434); - break; + // #region SOH [Enhancement] + if (CVarGetInteger("gCrawlSpeed", 1) > 1) { + Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3); + Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting); + OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, camCrawlTimer, D_80120308, D_80120434); + break; + // #endregion + } else { + Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3); + Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting); + OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120434); + break; + } case 4175: csInfo->keyFrames = D_8012147C; csInfo->keyFrameCnt = 4; diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index f9a54eb2c..6efa962b6 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2278,8 +2278,13 @@ u8 Item_Give(PlayState* play, u8 item) { gSaveContext.sohStats.heartPieces++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART_CONTAINER) { - gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + if (!CVarGetInteger("gHurtContainer", 0)) { + gSaveContext.healthCapacity += 0x10; + gSaveContext.health += 0x10; + } else { + gSaveContext.healthCapacity -= 0x10; + gSaveContext.health -= 0x10; + } gSaveContext.sohStats.heartContainers++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART) { diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index e20362fd3..3edaaca5b 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -600,13 +600,23 @@ void Player_SetModelsForHoldingShield(Player* this) { if ((CVarGetInteger("gShieldTwoHanded", 0) && (this->heldItemAction != PLAYER_IA_DEKU_STICK) || !Player_HoldsTwoHandedWeapon(this)) && !Player_IsChildWithHylianShield(this)) { this->rightHandType = PLAYER_MODELTYPE_RH_SHIELD; - this->rightHandDLists = &sPlayerDListGroups[PLAYER_MODELTYPE_RH_SHIELD][gSaveContext.linkAge]; + if (LINK_IS_CHILD && (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && (this->currentShield == PLAYER_SHIELD_MIRROR)) { + this->rightHandDLists = &sPlayerDListGroups[PLAYER_MODELTYPE_RH_SHIELD][0]; + } else if (LINK_IS_ADULT && (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && (this->currentShield == PLAYER_SHIELD_DEKU)) { + this->rightHandDLists = &sPlayerDListGroups[PLAYER_MODELTYPE_RH_SHIELD][1]; + } else { + this->rightHandDLists = &sPlayerDListGroups[PLAYER_MODELTYPE_RH_SHIELD][gSaveContext.linkAge]; + } if (this->sheathType == PLAYER_MODELTYPE_SHEATH_18) { this->sheathType = PLAYER_MODELTYPE_SHEATH_16; } else if (this->sheathType == PLAYER_MODELTYPE_SHEATH_19) { this->sheathType = PLAYER_MODELTYPE_SHEATH_17; } this->sheathDLists = &sPlayerDListGroups[this->sheathType][gSaveContext.linkAge]; + if ((CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && LINK_IS_CHILD && + gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI) { + this->sheathDLists = &sPlayerDListGroups[this->sheathType][0]; + } this->modelAnimType = PLAYER_ANIMTYPE_2; this->itemAction = -1; } @@ -617,12 +627,40 @@ void Player_SetModels(Player* this, s32 modelGroup) { // Left hand this->leftHandType = gPlayerModelTypes[modelGroup][PLAYER_MODELGROUPENTRY_LEFT_HAND]; this->leftHandDLists = &sPlayerDListGroups[this->leftHandType][gSaveContext.linkAge]; - + + if (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) { + if (LINK_IS_CHILD && + (this->leftHandType == PLAYER_MODELTYPE_LH_HAMMER || + ((this->leftHandType == PLAYER_MODELTYPE_LH_SWORD || this->leftHandType == PLAYER_MODELTYPE_LH_BGS) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI)))) { + this->leftHandDLists = &sPlayerDListGroups[this->leftHandType][0]; + } + + if (LINK_IS_ADULT && (this->leftHandType == PLAYER_MODELTYPE_LH_BOOMERANG || + (this->leftHandType == PLAYER_MODELTYPE_LH_SWORD && gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KOKIRI))) { + this->leftHandDLists = &sPlayerDListGroups[this->leftHandType][1]; + } + } + // Right hand this->rightHandType = gPlayerModelTypes[modelGroup][PLAYER_MODELGROUPENTRY_RIGHT_HAND]; this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][gSaveContext.linkAge]; - if (CVarGetInteger("gBowSlingShotAmmoFix", 0) && this->rightHandType == 11) { // If holding Bow/Slingshot + this->rightHandType = gPlayerModelTypes[modelGroup][PLAYER_MODELGROUPENTRY_RIGHT_HAND]; + this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][gSaveContext.linkAge]; + + if (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) { + if (LINK_IS_CHILD && + (this->rightHandType == PLAYER_MODELTYPE_RH_HOOKSHOT || + (this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD && this->currentShield == PLAYER_SHIELD_MIRROR))) { + this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][0]; + } + if (LINK_IS_ADULT && + (this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD && this->currentShield == PLAYER_SHIELD_DEKU)) { + this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][1]; + } + } + if ((CVarGetInteger("gBowSlingShotAmmoFix", 0) || CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && this->rightHandType == 11) { // If holding Bow/Slingshot this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][Player_HoldsSlingshot(this)]; } @@ -630,6 +668,23 @@ void Player_SetModels(Player* this, s32 modelGroup) { this->sheathType = gPlayerModelTypes[modelGroup][PLAYER_MODELGROUPENTRY_SHEATH]; this->sheathDLists = &sPlayerDListGroups[this->sheathType][gSaveContext.linkAge]; + if (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) { + if (LINK_IS_CHILD && + (this->currentShield == PLAYER_SHIELD_HYLIAN || this->currentShield == PLAYER_SHIELD_MIRROR) && + ((gSaveContext.equips.buttonItems[0] == ITEM_SWORD_MASTER) || + (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_BGS))) { + this->sheathDLists = &sPlayerDListGroups[this->sheathType][0]; + } else if (LINK_IS_CHILD && this->currentShield == PLAYER_SHIELD_MIRROR && gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KOKIRI && + this->sheathType == PLAYER_MODELTYPE_SHEATH_18) { + this->sheathDLists = &sPlayerDListGroups[this->sheathType][0]; + } else if (LINK_IS_ADULT && this->currentShield == PLAYER_SHIELD_DEKU) { + this->sheathDLists = &sPlayerDListGroups[this->sheathType][1]; + } else if (LINK_IS_CHILD && this->sheathType == PLAYER_MODELTYPE_SHEATH_17 && + ((gSaveContext.equips.buttonItems[0] == ITEM_SWORD_MASTER) || (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_BGS))) { + this->sheathDLists = &sPlayerDListGroups[this->sheathType][0]; + } + } + // Waist this->waistDLists = &sPlayerDListGroups[gPlayerModelTypes[modelGroup][4]][gSaveContext.linkAge]; @@ -1187,6 +1242,42 @@ void func_8008F87C(PlayState* play, Player* this, SkelAnime* skelAnime, Vec3f* p s32 Player_OverrideLimbDrawGameplayCommon(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { Player* this = (Player*)thisx; + + if (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0) && CVarGetInteger("gEnhancements.ScaleAdultEquimentAsChild", 0) && LINK_IS_CHILD) { + if (limbIndex == PLAYER_LIMB_L_HAND) { + if ((gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI && sLeftHandType == PLAYER_MODELTYPE_LH_SWORD) || + (sLeftHandType == PLAYER_MODELTYPE_LH_BGS) || (sLeftHandType == PLAYER_MODELTYPE_LH_HAMMER)) { + Matrix_Scale(0.8, 0.8, 0.8, MTXMODE_APPLY); + } + } + if (limbIndex == PLAYER_LIMB_R_HAND) { + if ((this->currentShield == PLAYER_SHIELD_MIRROR && sRightHandType == PLAYER_MODELTYPE_RH_SHIELD) || + (this->currentShield == PLAYER_SHIELD_HYLIAN && (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_MASTER || + gSaveContext.equips.buttonItems[0] == ITEM_SWORD_BGS)) || (sRightHandType == PLAYER_MODELTYPE_RH_HOOKSHOT) || + (sRightHandType == PLAYER_MODELTYPE_RH_BOW_SLINGSHOT && Player_HoldsBow(this))) { + Matrix_Scale(0.8, 0.8, 0.8, MTXMODE_APPLY); + } + } + if (limbIndex == PLAYER_LIMB_SHEATH) { + if ((this->currentShield == PLAYER_SHIELD_MIRROR || + (this->currentShield == PLAYER_SHIELD_HYLIAN && + (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_MASTER || + gSaveContext.equips.buttonItems[0] == ITEM_SWORD_BGS))) && + ((this->sheathType == PLAYER_MODELTYPE_SHEATH_16) || (this->sheathType == PLAYER_MODELTYPE_SHEATH_17) || + (this->sheathType == PLAYER_MODELTYPE_SHEATH_18) || + (this->sheathType == PLAYER_MODELTYPE_SHEATH_19))) { + Matrix_Translate(218, -100, 62, MTXMODE_APPLY); + Matrix_Scale(0.8, 0.8, 0.8, MTXMODE_APPLY); + } + if ((this->currentShield == PLAYER_SHIELD_DEKU && + gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI && + (this->sheathType == PLAYER_MODELTYPE_SHEATH_16 || + this->sheathType == PLAYER_MODELTYPE_SHEATH_17))) { + Matrix_Translate(218, -100, 62, MTXMODE_APPLY); + Matrix_Scale(0.8, 0.8, 0.8, MTXMODE_APPLY); + } + } + } if (limbIndex == PLAYER_LIMB_ROOT) { sLeftHandType = this->leftHandType; @@ -1305,9 +1396,11 @@ s32 Player_OverrideLimbDrawGameplayDefault(PlayState* play, s32 limbIndex, Gfx** (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI)) { dLists += PLAYER_SHIELD_MAX * 4; } - } else if (!LINK_IS_ADULT && ((this->sheathType == PLAYER_MODELTYPE_SHEATH_16) || (this->sheathType == PLAYER_MODELTYPE_SHEATH_17)) && - (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI)) { - dLists = &sSheathWithSwordDLs[PLAYER_SHIELD_MAX * 4]; + } else if (!CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) { + if (!LINK_IS_ADULT && ((this->sheathType == PLAYER_MODELTYPE_SHEATH_16) || (this->sheathType == PLAYER_MODELTYPE_SHEATH_17)) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI)) { + dLists = &sSheathWithSwordDLs[PLAYER_SHIELD_MAX * 4]; + } } if (dLists[sDListsLodOffset] != NULL) { @@ -1345,7 +1438,7 @@ s32 Player_OverrideLimbDrawGameplayFirstPerson(PlayState* play, s32 limbIndex, G *dList = sFirstPersonLeftForearmDLs[gSaveContext.linkAge]; } else if (limbIndex == PLAYER_LIMB_L_HAND) { s32 handOutDlIndex = gSaveContext.linkAge; - if (CVarGetInteger("gBowSlingShotAmmoFix", 0) && LINK_IS_ADULT && Player_HoldsSlingshot(this)) { + if ((CVarGetInteger("gBowSlingShotAmmoFix", 0) || CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && LINK_IS_ADULT && Player_HoldsSlingshot(this)) { handOutDlIndex = 1; } *dList = sFirstPersonLeftHandDLs[handOutDlIndex]; @@ -1355,7 +1448,7 @@ s32 Player_OverrideLimbDrawGameplayFirstPerson(PlayState* play, s32 limbIndex, G *dList = sFirstPersonForearmDLs[gSaveContext.linkAge]; } else if (limbIndex == PLAYER_LIMB_R_HAND) { s32 firstPersonWeaponIndex = gSaveContext.linkAge; - if (CVarGetInteger("gBowSlingShotAmmoFix", 0)) { + if (CVarGetInteger("gBowSlingShotAmmoFix", 0) || CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) { if (Player_HoldsBow(this)) { firstPersonWeaponIndex = 0; } else if (Player_HoldsSlingshot(this)) { @@ -1755,7 +1848,7 @@ void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList, Ve Matrix_Get(&this->shieldMf); } else if ((this->rightHandType == PLAYER_MODELTYPE_RH_BOW_SLINGSHOT) || (this->rightHandType == PLAYER_MODELTYPE_RH_BOW_SLINGSHOT_2)) { s32 stringModelToUse = gSaveContext.linkAge; - if(CVarGetInteger("gBowSlingShotAmmoFix", 0)){ + if (CVarGetInteger("gBowSlingShotAmmoFix", 0) || CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) { stringModelToUse = Player_HoldsSlingshot(this); } BowStringData* stringData = &sBowStringData[stringModelToUse]; diff --git a/soh/src/code/z_scene.c b/soh/src/code/z_scene.c index 1702dc3d9..eeff423d7 100644 --- a/soh/src/code/z_scene.c +++ b/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/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index b077d8ef7..30455e8e5 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -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/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c b/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c index 5f3737b44..357b2c0ca 100644 --- a/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c +++ b/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/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c b/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c index ae09cb438..9ce27f4c1 100644 --- a/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c +++ b/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/soh/src/overlays/actors/ovl_En_Door/z_en_door.c b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c index f0442821a..329462c06 100644 --- a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c +++ b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c @@ -349,7 +349,15 @@ void EnDoor_Draw(Actor* thisx, PlayState* play) { } } if (this->lockTimer != 0) { + if (CVarGetInteger("gShowDoorLocksOnBothSides", 0)) { + Matrix_Push(); + } Actor_DrawDoorLock(play, this->lockTimer, DOORLOCK_NORMAL); + if (CVarGetInteger("gShowDoorLocksOnBothSides", 0)) { + Matrix_Pop(); + Matrix_RotateZYX(0, 0x8000, 0, MTXMODE_APPLY); + Actor_DrawDoorLock(play, this->lockTimer, DOORLOCK_NORMAL); + } } CLOSE_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 037b4b594..6aa1b7d41 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -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/soh/src/overlays/actors/ovl_En_Part/z_en_part.c b/soh/src/overlays/actors/ovl_En_Part/z_en_part.c index e1b65efca..be1143ff1 100644 --- a/soh/src/overlays/actors/ovl_En_Part/z_en_part.c +++ b/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/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c index 51e3e1499..6a001d07b 100644 --- a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c +++ b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c @@ -745,6 +745,28 @@ void EnPartner_Update(Actor* thisx, PlayState* play) { CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); } + if (CVarGetInteger("gCosmetics.Ivan_IdlePrimary.Changed", 0)) { + Color_RGB8 ivanColor1 = CVarGetColor24("gCosmetics.Ivan_IdlePrimary.Value", (Color_RGB8){ 255, 255, 255 }); + this->innerColor.r = ivanColor1.r; + this->innerColor.g = ivanColor1.g; + this->innerColor.b = ivanColor1.b; + } else { + this->innerColor.r = 255; + this->innerColor.g = 255; + this->innerColor.b = 255; + } + + if (CVarGetInteger("gCosmetics.Ivan_IdleSecondary.Changed", 0)) { + Color_RGB8 ivanColor2 = CVarGetColor24("gCosmetics.Ivan_IdleSecondary.Value", (Color_RGB8){ 0, 255, 0 }); + this->outerColor.r = ivanColor2.r; + this->outerColor.g = ivanColor2.g; + this->outerColor.b = ivanColor2.b; + } else { + this->outerColor.r = 0; + this->outerColor.g = 255; + this->outerColor.b = 0; + } + SkelAnime_Update(&this->skelAnime); EnPartner_UpdateLights(this, play); diff --git a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c index 6be42b42d..fe4a77724 100644 --- a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c +++ b/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/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c index 3492d6de9..207d9612b 100644 --- a/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c +++ b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c @@ -1412,12 +1412,20 @@ void EnSkj_StartOcarinaMinigame(EnSkj* this, PlayState* play) { EnSkj_TurnPlayer(this, player); if (dialogState == TEXT_STATE_CLOSING) { - func_8010BD58(play, OCARINA_ACTION_MEMORY_GAME); - if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) { - sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_PLAY_NOTES; + // #region SOH [Enhancement] + if (CVarGetInteger("gInstantOcarinaGameWin", 0) && CVarGetInteger("gCustomizeOcarinaGame", 0)) { + play->msgCtx.ocarinaMode = OCARINA_MODE_0F; + this->songFailTimer = 160; + this->actionFunc = EnSkj_WaitForPlayback; + // #endregion + } else { + func_8010BD58(play, OCARINA_ACTION_MEMORY_GAME); + if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_PLAY_NOTES; + this->songFailTimer = 160; + this->actionFunc = EnSkj_WaitForPlayback; + } } - this->songFailTimer = 160; - this->actionFunc = EnSkj_WaitForPlayback; } } @@ -1466,7 +1474,14 @@ void EnSkj_WaitForPlayback(EnSkj* this, PlayState* play) { break; case MSGMODE_MEMORY_GAME_PLAYER_PLAYING: if (this->songFailTimer != 0) { - this->songFailTimer--; + // #region SOH [Enhancement] + if (CVarGetInteger("gOcarinaUnlimitedFailTime", 0) == 1 && + CVarGetInteger("gCustomizeOcarinaGame", 0) == 1) { + // don't decrement timer + // #endregion + } else { + this->songFailTimer--; + } } else { // took too long, game failed func_80078884(NA_SE_SY_OCARINA_ERROR); Message_CloseTextbox(play); diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index 3f8de83ba..727a20730 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -5174,6 +5174,37 @@ static Vec3s sSinkingLureLocationPos[] = { { 553, -48, -508 }, }; +// #region SOH [Enhancement] +void Fishing_QuitAtDoor(Fishing* this, PlayState* play) { + if ((Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) { + Message_CloseTextbox(play); + + switch (play->msgCtx.choiceIndex) { + case 0: + if (D_80B7E084 == 0) { + Message_ContinueTextbox(play, 0x4085); + } else if (sLinkAge == 1) { + Message_ContinueTextbox(play, 0x4092); + } + + if (Message_GetState(&play->msgCtx) == TEXT_STATE_DONE_FADING) { + + if (D_80B7A68C != 0) { + D_80B7A688 = 1; + D_80B7A68C = 0; + } + D_80B7E0AC = 0; + play->interfaceCtx.unk_260 = 0; + } + break; + case 1: + func_800A9F6C(0.0f, 150, 10, 10); + break; + } + } +} +// #endregion + void Fishing_UpdateOwner(Actor* thisx, PlayState* play2) { PlayState* play = play2; Fishing* this = (Fishing*)thisx; @@ -5480,6 +5511,12 @@ void Fishing_UpdateOwner(Actor* thisx, PlayState* play2) { case 11: player->actor.world.pos.z = 1360.0f; player->actor.speedXZ = 0.0f; + + // #region SOH [Enhancement] + if (CVarGetInteger("gQuitFishingAtDoor", 0)) { + Fishing_QuitAtDoor(this, play); + } + // #endregion if (Message_GetState(&play->msgCtx) == TEXT_STATE_NONE) { Camera* camera = Play_GetCamera(play, MAIN_CAM); diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 523e7cf11..145abcdd0 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -2911,7 +2911,11 @@ s32 func_808356E8(Player* this, PlayState* play) { } void func_808357E8(Player* this, Gfx** dLists) { - this->leftHandDLists = &dLists[gSaveContext.linkAge]; + if (LINK_IS_ADULT && (CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0))) { + this->leftHandDLists = &dLists[1]; + } else { + this->leftHandDLists = &dLists[gSaveContext.linkAge]; + } } s32 func_80835800(Player* this, PlayState* play) { @@ -6982,9 +6986,19 @@ s32 Player_TryEnteringCrawlspace(Player* this, PlayState* play, u32 interactWall this->actor.world.pos.z = zVertex1 + (distToInteractWall * wallPolyNormZ); func_80832224(this); this->actor.prevPos = this->actor.world.pos; - Player_AnimPlayOnce(play, this, &gPlayerAnim_link_child_tunnel_start); - Player_AnimReplaceApplyFlags(play, this, 0x9D); - + // #region SOH [Enhancement] + if (CVarGetInteger("gCrawlSpeed", 1) > 1) { + // increase animation speed when entering a tunnel + LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_start, + ((CVarGetInteger("gCrawlSpeed", 1) + 1.0f) / 2.0f), 0.0f, + Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), ANIMMODE_ONCE, + 0.0f); + Player_AnimReplaceApplyFlags(play, this, 0x9D); + // #endregion + } else { + Player_AnimPlayOnce(play, this, &gPlayerAnim_link_child_tunnel_start); + Player_AnimReplaceApplyFlags(play, this, 0x9D); + } return true; } } @@ -7065,16 +7079,39 @@ s32 Player_TryLeavingCrawlspace(Player* this, PlayState* play) { if (this->linearVelocity > 0.0f) { this->actor.shape.rot.y = this->actor.wallYaw + 0x8000; - Player_AnimPlayOnce(play, this, &gPlayerAnim_link_child_tunnel_end); - Player_AnimReplaceApplyFlags(play, this, 0x9D); - OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM); + // #region SOH [Enhancement] + if (CVarGetInteger("gCrawlSpeed", 1) > 1) { + // animation when exiting a tunnel forward + LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_end, + ((CVarGetInteger("gCrawlSpeed", 1) + 1.0f) / 2.0f), 0.0f, + Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_end), ANIMMODE_ONCE, + 0.0f); + Player_AnimReplaceApplyFlags(play, this, 0x9D); + OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM); + // #endregion + } else { + Player_AnimPlayOnce(play, this, &gPlayerAnim_link_child_tunnel_end); + Player_AnimReplaceApplyFlags(play, this, 0x9D); + OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM); + } } else { this->actor.shape.rot.y = this->actor.wallYaw; - LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_start, -1.0f, - Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), 0.0f, ANIMMODE_ONCE, - 0.0f); - Player_AnimReplaceApplyFlags(play, this, 0x9D); - OnePointCutscene_Init(play, 9602, 999, NULL, MAIN_CAM); + // #region SOH [Enhancement] + // animation when exiting a tunnel backward + if (CVarGetInteger("gCrawlSpeed",1) > 1) { + LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_start, + -1.0f * ((CVarGetInteger("gCrawlSpeed", 1) + 1.0f) / 2.0f), + Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), 0.0f, ANIMMODE_ONCE, 0.0f); + Player_AnimReplaceApplyFlags(play, this, 0x9D); + OnePointCutscene_Init(play, 9602, 999, NULL, MAIN_CAM); + // #endregion + } + else { + LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_start, -1.0f, + Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), 0.0f, ANIMMODE_ONCE, 0.0f); + Player_AnimReplaceApplyFlags(play, this, 0x9D); + OnePointCutscene_Init(play, 9602, 999, NULL, MAIN_CAM); + } } this->currentYaw = this->actor.shape.rot.y; @@ -11926,6 +11963,68 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { void func_8084AEEC(Player* this, f32* arg1, f32 arg2, s16 arg3) { f32 temp1; f32 temp2; + + // #region SOH [Enhancement] + f32 swimMod = 1.0f; + + if (CVarGetInteger("gEnableWalkModify", 0) == 1) { + if (CVarGetInteger("gWalkSpeedToggle", 0) == 1) { + if (gWalkSpeedToggle1) { + swimMod *= CVarGetFloat("gSwimModifierOne", 1.0f); + } else if (gWalkSpeedToggle2) { + swimMod *= CVarGetFloat("gSwimModifierTwo", 1.0f); + } + } else { + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_MODIFIER1)) { + swimMod *= CVarGetFloat("gSwimModifierOne", 1.0f); + } else if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_MODIFIER2)) { + swimMod *= CVarGetFloat("gSwimModifierTwo", 1.0f); + } + } + temp1 = this->skelAnime.curFrame - 10.0f; + + temp2 = (R_RUN_SPEED_LIMIT / 100.0f) * 0.8f * swimMod; + if (*arg1 > temp2) { + *arg1 = temp2; + } + + if ((0.0f < temp1) && (temp1 < 10.0f)) { + temp1 *= 6.0f; + } else { + temp1 = 0.0f; + arg2 = 0.0f; + } + + Math_AsymStepToF(arg1, arg2 * 0.8f * swimMod, temp1, (fabsf(*arg1) * 0.02f) + 0.05f); + Math_ScaledStepToS(&this->currentYaw, arg3, 1600); + // #endregion + } else { + + temp1 = this->skelAnime.curFrame - 10.0f; + + temp2 = (R_RUN_SPEED_LIMIT / 100.0f) * 0.8f; + if (*arg1 > temp2) { + *arg1 = temp2; + } + + if ((0.0f < temp1) && (temp1 < 10.0f)) { + temp1 *= 6.0f; + } else { + temp1 = 0.0f; + arg2 = 0.0f; + } + + Math_AsymStepToF(arg1, arg2 * 0.8f, temp1, (fabsf(*arg1) * 0.02f) + 0.05f); + Math_ScaledStepToS(&this->currentYaw, arg3, 1600); + } +} + +// #region SOH [Enhancement] +//Diving uses function func_8084AEEC to calculate changes both xz and y velocity (via func_8084DBC4) +//Provide original calculation for y velocity when swim speed mod is active +void SurfaceWithoutSwimMod(Player* this, f32* arg1, f32 arg2, s16 arg3) { + f32 temp1; + f32 temp2; temp1 = this->skelAnime.curFrame - 10.0f; @@ -11944,6 +12043,7 @@ void func_8084AEEC(Player* this, f32* arg1, f32 arg2, s16 arg3) { Math_AsymStepToF(arg1, arg2 * 0.8f, temp1, (fabsf(*arg1) * 0.02f) + 0.05f); Math_ScaledStepToS(&this->currentYaw, arg3, 1600); } +// #endregion void func_8084B000(Player* this) { f32 phi_f18; @@ -12562,8 +12662,15 @@ void func_8084C760(Player* this, PlayState* play) { return; } + // player speed in a tunnel if (!Player_TryLeavingCrawlspace(this, play)) { - this->linearVelocity = sControlInput->rel.stick_y * 0.03f; + // #region SOH [Enhancement] + if (CVarGetInteger("gCrawlSpeed", 1) > 1) { + this->linearVelocity = sControlInput->rel.stick_y * 0.03f * CVarGetInteger("gCrawlSpeed", 1); + // #endregion + } else { + this->linearVelocity = sControlInput->rel.stick_y * 0.03f; + } } } return; @@ -13098,7 +13205,14 @@ void func_8084DBC4(PlayState* play, Player* this, f32 arg2) { Player_GetMovementSpeedAndYaw(this, &sp2C, &sp2A, 0.0f, play); func_8084AEEC(this, &this->linearVelocity, sp2C * 0.5f, sp2A); - func_8084AEEC(this, &this->actor.velocity.y, arg2, this->currentYaw); + // Original implementation of func_8084AEEC (SurfaceWithoutSwimMod) to prevent velocity increases via swim mod which push Link into the air + // #region SOH [Enhancement] + if (CVarGetInteger("gEnableWalkModify", 0)) { + SurfaceWithoutSwimMod(this, &this->actor.velocity.y, arg2, this->currentYaw); + // #endregion + } else { + func_8084AEEC(this, &this->actor.velocity.y, arg2, this->currentYaw); + } } void func_8084DC48(Player* this, PlayState* play) { diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c index 28723d3d0..34dc47aa9 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c @@ -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/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index b70197cc2..5f8da1b70 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -1204,8 +1204,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 }, @@ -1374,10 +1372,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); @@ -1889,11 +1883,17 @@ void KaleidoScope_DrawInfoPanel(PlayState* play) { gSPMatrix(POLY_KAL_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); - gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 90, 100, 130, 255); + const Color_RGBA8 namePanelColor = CVarGetColor("gCosmetics.Kal_NamePanel.Value", (Color_RGBA8){90,100,130,255}); + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, namePanelColor.r, namePanelColor.g, namePanelColor.b, namePanelColor.a); gSPVertex(POLY_KAL_DISP++, &pauseCtx->infoPanelVtx[0], 16, 0); gSPDisplayList(POLY_KAL_DISP++, gItemNamePanelDL); + if (CVarGetInteger("gUniformLR", 0) == 0) { // Restore the misplace gDPSetPrimColor + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 90, 100, 130, 255); + } + if ((pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) && (pauseCtx->unk_1E4 == 0)) { gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, D_808321A0, D_808321A2, D_808321A4, D_808321A6); } else { @@ -1901,6 +1901,7 @@ void KaleidoScope_DrawInfoPanel(PlayState* play) { gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 180, 210, 255, 255); } } + gSPDisplayList(POLY_KAL_DISP++, gLButtonIconDL); @@ -2311,15 +2312,15 @@ void KaleidoScope_SetView(PauseContext* pauseCtx, f32 x, f32 y, f32 z) { func_800AAA50(&pauseCtx->view, 127); } -static u8 D_8082AE48[][4] = { +static u8 sPageVtxColorR[][4] = { { 10, 70, 70, 10 }, { 10, 90, 90, 10 }, { 80, 140, 140, 80 }, { 80, 120, 120, 80 }, { 80, 140, 140, 80 }, { 50, 110, 110, 50 }, }; -static u8 D_8082AE60[][4] = { +static u8 sPageVtxColorG[][4] = { { 50, 100, 100, 50 }, { 50, 100, 100, 50 }, { 40, 60, 60, 40 }, { 80, 120, 120, 80 }, { 40, 60, 60, 40 }, { 50, 110, 110, 50 }, }; -static u8 D_8082AE78[][4] = { +static u8 sPageVtxColorB[][4] = { { 80, 130, 130, 80 }, { 40, 60, 60, 40 }, { 30, 60, 60, 30 }, { 50, 70, 70, 50 }, { 30, 60, 60, 30 }, { 50, 110, 110, 50 }, }; @@ -2447,10 +2448,87 @@ static s16 D_8082B0E4[] = { 0x0019, 0x000D, 0x0001, 0x0001, 0x000D, 0x0015, 0x000F, 0x000D, 0x000C, 0x0001, 0x0000, }; -s16 func_80823A0C(PlayState* play, Vtx* vtx, s16 arg2, s16 arg3) { +static const char* gPageVtxColorCvars[][4] = { + { + "gCosmetics.Kal_ItemSelA.Value", + "gCosmetics.Kal_ItemSelB.Value", + "gCosmetics.Kal_ItemSelC.Value", + "gCosmetics.Kal_ItemSelD.Value", + }, + { + "gCosmetics.Kal_EquipSelA.Value", + "gCosmetics.Kal_EquipSelB.Value", + "gCosmetics.Kal_EquipSelC.Value", + "gCosmetics.Kal_EquipSelD.Value", + }, + { + "gCosmetics.Kal_MapSelDunA.Value", + "gCosmetics.Kal_MapSelDunB.Value", + "gCosmetics.Kal_MapSelDunC.Value", + "gCosmetics.Kal_MapSelDunD.Value", + }, + { + "gCosmetics.Kal_QuestStatusA.Value", + "gCosmetics.Kal_QuestStatusB.Value", + "gCosmetics.Kal_QuestStatusC.Value", + "gCosmetics.Kal_QuestStatusD.Value", + }, + { + "gCosmetics.Kal_MapSelectA.Value", + "gCosmetics.Kal_MapSelectB.Value", + "gCosmetics.Kal_MapSelectC.Value", + "gCosmetics.Kal_MapSelectD.Value", + }, + { + "gCosmetics.Kal_SaveA.Value", + "gCosmetics.Kal_SaveB.Value", + "gCosmetics.Kal_SaveC.Value", + "gCosmetics.Kal_SaveD.Value", + }, +}; + +s16 func_80823A0C(PlayState* play, Vtx* vtx, s16 pageIndex, s16 arg3) { static s16 D_8082B110 = 0; static s16 D_8082B114 = 1; static s16 D_8082B118 = 0; + static const Color_RGBA8 pageColors[][4] = { + { + { 10, 50, 80, 255 }, + { 70, 100, 130, 255 }, + { 70, 100, 130, 255 }, + { 10, 50, 80, 255 }, + }, + { + { 10, 50, 40, 255 }, + { 90, 100, 60, 255 }, + { 90, 100, 60, 255 }, + { 10, 50, 40, 255 }, + }, + { + { 80,40,30, 255}, + { 140,60,60,255 }, + { 140,60,60,255 }, + { 80, 40, 30, 255 }, + }, + { + { 80,80,50,255 }, + { 120,120,70,255 }, + { 120,120,70,255 }, + { 80, 80, 50, 255 }, + }, + { + { 80, 40, 30, 255 }, + { 140,60,60,255 }, + { 140,60,60,255 }, + { 80, 40, 30, 255 }, + }, + { + { 50,50,50,255 }, + { 110,110,110,255 }, + { 110,110,110,255 }, + { 50,50,50,255 }, + }, + }; PauseContext* pauseCtx = &play->pauseCtx; s16* ptr1; s16* ptr2; @@ -2460,109 +2538,114 @@ s16 func_80823A0C(PlayState* play, Vtx* vtx, s16 arg2, s16 arg3) { s16 phi_t0; s16 phi_a1; s16 phi_a2; - s16 phi_t3; - s16 phi_t1; + s16 colorIndex; // Also used for other things. + s16 vtxIndex; phi_t0 = -200; - for (phi_t1 = 0, phi_t3 = 0; phi_t3 < 3; phi_t3++) { + for (vtxIndex = 0, colorIndex = 0; colorIndex < 3; colorIndex++) { phi_t0 += 80; - for (phi_a1 = 80, phi_a2 = 0; phi_a2 < 5; phi_a2++, phi_t1 += 4, phi_a1 -= 32) { - vtx[phi_t1 + 0].v.ob[0] = vtx[phi_t1 + 2].v.ob[0] = phi_t0; + for (phi_a1 = 80, phi_a2 = 0; phi_a2 < 5; phi_a2++, vtxIndex += 4, phi_a1 -= 32) { + vtx[vtxIndex + 0].v.ob[0] = vtx[vtxIndex + 2].v.ob[0] = phi_t0; - vtx[phi_t1 + 1].v.ob[0] = vtx[phi_t1 + 3].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] + 80; + vtx[vtxIndex + 1].v.ob[0] = vtx[vtxIndex + 3].v.ob[0] = vtx[vtxIndex + 0].v.ob[0] + 80; - vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = phi_a1 + pauseCtx->offsetY; + vtx[vtxIndex + 0].v.ob[1] = vtx[vtxIndex + 1].v.ob[1] = phi_a1 + pauseCtx->offsetY; - vtx[phi_t1 + 2].v.ob[1] = vtx[phi_t1 + 3].v.ob[1] = vtx[phi_t1 + 0].v.ob[1] - 32; + vtx[vtxIndex + 2].v.ob[1] = vtx[vtxIndex + 3].v.ob[1] = vtx[vtxIndex + 0].v.ob[1] - 32; - vtx[phi_t1 + 0].v.ob[2] = vtx[phi_t1 + 1].v.ob[2] = vtx[phi_t1 + 2].v.ob[2] = vtx[phi_t1 + 3].v.ob[2] = 0; + vtx[vtxIndex + 0].v.ob[2] = vtx[vtxIndex + 1].v.ob[2] = vtx[vtxIndex + 2].v.ob[2] = vtx[vtxIndex + 3].v.ob[2] = 0; - vtx[phi_t1 + 0].v.flag = 0; - vtx[phi_t1 + 1].v.flag = 0; - vtx[phi_t1 + 2].v.flag = 0; - vtx[phi_t1 + 3].v.flag = 0; + vtx[vtxIndex + 0].v.flag = 0; + vtx[vtxIndex + 1].v.flag = 0; + vtx[vtxIndex + 2].v.flag = 0; + vtx[vtxIndex + 3].v.flag = 0; - vtx[phi_t1 + 0].v.tc[0] = vtx[phi_t1 + 0].v.tc[1] = vtx[phi_t1 + 1].v.tc[1] = vtx[phi_t1 + 2].v.tc[0] = 0; + vtx[vtxIndex + 0].v.tc[0] = vtx[vtxIndex + 0].v.tc[1] = vtx[vtxIndex + 1].v.tc[1] = vtx[vtxIndex + 2].v.tc[0] = 0; - vtx[phi_t1 + 1].v.tc[0] = vtx[phi_t1 + 3].v.tc[0] = 0xA00; + vtx[vtxIndex + 1].v.tc[0] = vtx[vtxIndex + 3].v.tc[0] = 0xA00; - vtx[phi_t1 + 2].v.tc[1] = vtx[phi_t1 + 3].v.tc[1] = 0x400; + vtx[vtxIndex + 2].v.tc[1] = vtx[vtxIndex + 3].v.tc[1] = 0x400; - vtx[phi_t1 + 0].v.cn[0] = vtx[phi_t1 + 2].v.cn[0] = D_8082AE48[arg2][phi_t3 + 0]; + //Color in the pages. Pages are drawn in groups. Each group is faded to the next. There are 4 total colors, 1/4 and 2/3 are the same creating a mirrored color set. + // TODO, go from 0,1,2,3 to 0,1,1,0 to only use two colors instead of 4. + Color_RGBA8 color = CVarGetColor(gPageVtxColorCvars[pageIndex][colorIndex], pageColors[pageIndex][colorIndex]); + Color_RGBA8 colorb = + CVarGetColor(gPageVtxColorCvars[pageIndex][colorIndex + 1], pageColors[pageIndex][colorIndex+1]); + vtx[vtxIndex + 0].v.cn[0] = vtx[vtxIndex + 2].v.cn[0] = color.r; // sPageVtxColorR[pageIndex][colorIndex + 0]; - vtx[phi_t1 + 0].v.cn[1] = vtx[phi_t1 + 2].v.cn[1] = D_8082AE60[arg2][phi_t3 + 0]; + vtx[vtxIndex + 0].v.cn[1] = vtx[vtxIndex + 2].v.cn[1] = color.g;// sPageVtxColorG[pageIndex][colorIndex + 0]; - vtx[phi_t1 + 0].v.cn[2] = vtx[phi_t1 + 2].v.cn[2] = D_8082AE78[arg2][phi_t3 + 0]; + vtx[vtxIndex + 0].v.cn[2] = vtx[vtxIndex + 2].v.cn[2] = color.b; // sPageVtxColorB[pageIndex][colorIndex + 0]; - vtx[phi_t1 + 1].v.cn[0] = vtx[phi_t1 + 3].v.cn[0] = D_8082AE48[arg2][phi_t3 + 1]; + vtx[vtxIndex + 1].v.cn[0] = vtx[vtxIndex + 3].v.cn[0] = colorb.r;//sPageVtxColorR[pageIndex][colorIndex + 1]; - vtx[phi_t1 + 1].v.cn[1] = vtx[phi_t1 + 3].v.cn[1] = D_8082AE60[arg2][phi_t3 + 1]; + vtx[vtxIndex + 1].v.cn[1] = vtx[vtxIndex + 3].v.cn[1] = colorb.g; // sPageVtxColorG[pageIndex][colorIndex + 1]; - vtx[phi_t1 + 1].v.cn[2] = vtx[phi_t1 + 3].v.cn[2] = D_8082AE78[arg2][phi_t3 + 1]; + vtx[vtxIndex + 1].v.cn[2] = vtx[vtxIndex + 3].v.cn[2] = colorb.b; // sPageVtxColorB[pageIndex][colorIndex + 1]; - vtx[phi_t1 + 0].v.cn[3] = vtx[phi_t1 + 2].v.cn[3] = vtx[phi_t1 + 1].v.cn[3] = vtx[phi_t1 + 3].v.cn[3] = + vtx[vtxIndex + 0].v.cn[3] = vtx[vtxIndex + 2].v.cn[3] = vtx[vtxIndex + 1].v.cn[3] = vtx[vtxIndex + 3].v.cn[3] = pauseCtx->alpha; } } - phi_s2 = phi_t1; + phi_s2 = vtxIndex; if (arg3 != 0) { - ptr1 = D_8082B000[arg2]; - ptr2 = D_8082B018[arg2]; - ptr3 = D_8082B030[arg2]; - ptr4 = D_8082B048[arg2]; + ptr1 = D_8082B000[pageIndex]; + ptr2 = D_8082B018[pageIndex]; + ptr3 = D_8082B030[pageIndex]; + ptr4 = D_8082B048[pageIndex]; - for (phi_t3 = 0; phi_t3 < arg3; phi_t3++, phi_t1 += 4) { - vtx[phi_t1 + 2].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] = ptr1[phi_t3]; + for (colorIndex = 0; colorIndex < arg3; colorIndex++, vtxIndex += 4) { + vtx[vtxIndex + 2].v.ob[0] = vtx[vtxIndex + 0].v.ob[0] = ptr1[colorIndex]; - vtx[phi_t1 + 1].v.ob[0] = vtx[phi_t1 + 3].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] + ptr2[phi_t3]; + vtx[vtxIndex + 1].v.ob[0] = vtx[vtxIndex + 3].v.ob[0] = vtx[vtxIndex + 0].v.ob[0] + ptr2[colorIndex]; if (!((pauseCtx->state >= 8) && (pauseCtx->state <= 0x11))) { - vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = ptr3[phi_t3] + pauseCtx->offsetY; + vtx[vtxIndex + 0].v.ob[1] = vtx[vtxIndex + 1].v.ob[1] = ptr3[colorIndex] + pauseCtx->offsetY; } else { - vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = YREG(60 + phi_t3) + pauseCtx->offsetY; + vtx[vtxIndex + 0].v.ob[1] = vtx[vtxIndex + 1].v.ob[1] = YREG(60 + colorIndex) + pauseCtx->offsetY; } - vtx[phi_t1 + 2].v.ob[1] = vtx[phi_t1 + 3].v.ob[1] = vtx[phi_t1 + 0].v.ob[1] - ptr4[phi_t3]; + vtx[vtxIndex + 2].v.ob[1] = vtx[vtxIndex + 3].v.ob[1] = vtx[vtxIndex + 0].v.ob[1] - ptr4[colorIndex]; - vtx[phi_t1 + 0].v.ob[2] = vtx[phi_t1 + 1].v.ob[2] = vtx[phi_t1 + 2].v.ob[2] = vtx[phi_t1 + 3].v.ob[2] = 0; + vtx[vtxIndex + 0].v.ob[2] = vtx[vtxIndex + 1].v.ob[2] = vtx[vtxIndex + 2].v.ob[2] = vtx[vtxIndex + 3].v.ob[2] = 0; - vtx[phi_t1 + 0].v.flag = vtx[phi_t1 + 1].v.flag = vtx[phi_t1 + 2].v.flag = vtx[phi_t1 + 3].v.flag = 0; + vtx[vtxIndex + 0].v.flag = vtx[vtxIndex + 1].v.flag = vtx[vtxIndex + 2].v.flag = vtx[vtxIndex + 3].v.flag = 0; - vtx[phi_t1 + 0].v.tc[0] = vtx[phi_t1 + 0].v.tc[1] = vtx[phi_t1 + 1].v.tc[1] = vtx[phi_t1 + 2].v.tc[0] = 0; + vtx[vtxIndex + 0].v.tc[0] = vtx[vtxIndex + 0].v.tc[1] = vtx[vtxIndex + 1].v.tc[1] = vtx[vtxIndex + 2].v.tc[0] = 0; - vtx[phi_t1 + 1].v.tc[0] = vtx[phi_t1 + 3].v.tc[0] = ptr2[phi_t3] << 5; + vtx[vtxIndex + 1].v.tc[0] = vtx[vtxIndex + 3].v.tc[0] = ptr2[colorIndex] << 5; - vtx[phi_t1 + 2].v.tc[1] = vtx[phi_t1 + 3].v.tc[1] = ptr4[phi_t3] << 5; + vtx[vtxIndex + 2].v.tc[1] = vtx[vtxIndex + 3].v.tc[1] = ptr4[colorIndex] << 5; - vtx[phi_t1 + 0].v.cn[0] = vtx[phi_t1 + 2].v.cn[0] = vtx[phi_t1 + 0].v.cn[1] = vtx[phi_t1 + 2].v.cn[1] = - vtx[phi_t1 + 0].v.cn[2] = vtx[phi_t1 + 2].v.cn[2] = vtx[phi_t1 + 1].v.cn[0] = vtx[phi_t1 + 3].v.cn[0] = - vtx[phi_t1 + 1].v.cn[1] = vtx[phi_t1 + 3].v.cn[1] = vtx[phi_t1 + 1].v.cn[2] = - vtx[phi_t1 + 3].v.cn[2] = 255; + vtx[vtxIndex + 0].v.cn[0] = vtx[vtxIndex + 2].v.cn[0] = vtx[vtxIndex + 0].v.cn[1] = vtx[vtxIndex + 2].v.cn[1] = + vtx[vtxIndex + 0].v.cn[2] = vtx[vtxIndex + 2].v.cn[2] = vtx[vtxIndex + 1].v.cn[0] = vtx[vtxIndex + 3].v.cn[0] = + vtx[vtxIndex + 1].v.cn[1] = vtx[vtxIndex + 3].v.cn[1] = vtx[vtxIndex + 1].v.cn[2] = + vtx[vtxIndex + 3].v.cn[2] = 255; - vtx[phi_t1 + 0].v.cn[3] = vtx[phi_t1 + 2].v.cn[3] = vtx[phi_t1 + 1].v.cn[3] = vtx[phi_t1 + 3].v.cn[3] = + vtx[vtxIndex + 0].v.cn[3] = vtx[vtxIndex + 2].v.cn[3] = vtx[vtxIndex + 1].v.cn[3] = vtx[vtxIndex + 3].v.cn[3] = pauseCtx->alpha; } - if (arg2 == 4) { - phi_t1 -= 12; + if (pageIndex == 4) { + vtxIndex -= 12; - phi_t3 = gSaveContext.worldMapArea; + colorIndex = gSaveContext.worldMapArea; - vtx[phi_t1 + 0].v.ob[0] = vtx[phi_t1 + 2].v.ob[0] = D_8082B060[phi_t3]; + vtx[vtxIndex + 0].v.ob[0] = vtx[vtxIndex + 2].v.ob[0] = D_8082B060[colorIndex]; - if (phi_t3) {} + if (colorIndex) {} - vtx[phi_t1 + 1].v.ob[0] = vtx[phi_t1 + 3].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] + D_8082B08C[phi_t3]; + vtx[vtxIndex + 1].v.ob[0] = vtx[vtxIndex + 3].v.ob[0] = vtx[vtxIndex + 0].v.ob[0] + D_8082B08C[colorIndex]; - vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = D_8082B0B8[phi_t3] + pauseCtx->offsetY; + vtx[vtxIndex + 0].v.ob[1] = vtx[vtxIndex + 1].v.ob[1] = D_8082B0B8[colorIndex] + pauseCtx->offsetY; - vtx[phi_t1 + 2].v.ob[1] = vtx[phi_t1 + 3].v.ob[1] = vtx[phi_t1 + 0].v.ob[1] - D_8082B0E4[phi_t3]; + vtx[vtxIndex + 2].v.ob[1] = vtx[vtxIndex + 3].v.ob[1] = vtx[vtxIndex + 0].v.ob[1] - D_8082B0E4[colorIndex]; - phi_t1 += 12; + vtxIndex += 12; if (pauseCtx->tradeQuestLocation != 0xFF) { if (D_8082B114 == 0) { @@ -2582,14 +2665,14 @@ s16 func_80823A0C(PlayState* play, Vtx* vtx, s16 arg2, s16 arg3) { D_8082B114--; } - phi_t3 = phi_s2 + (pauseCtx->tradeQuestLocation * 4) + 64; + colorIndex = phi_s2 + (pauseCtx->tradeQuestLocation * 4) + 64; phi_a2 = phi_s2 + 116; - vtx[phi_a2 + 0].v.ob[0] = vtx[phi_a2 + 2].v.ob[0] = vtx[phi_t3 + 0].v.ob[0]; + vtx[phi_a2 + 0].v.ob[0] = vtx[phi_a2 + 2].v.ob[0] = vtx[colorIndex + 0].v.ob[0]; vtx[phi_a2 + 1].v.ob[0] = vtx[phi_a2 + 3].v.ob[0] = vtx[phi_a2 + 0].v.ob[0] + 8; - vtx[phi_a2 + 0].v.ob[1] = vtx[phi_a2 + 1].v.ob[1] = vtx[phi_t3 + 0].v.ob[1] - D_8082B110 + 10; + vtx[phi_a2 + 0].v.ob[1] = vtx[phi_a2 + 1].v.ob[1] = vtx[colorIndex + 0].v.ob[1] - D_8082B110 + 10; vtx[phi_a2 + 0].v.ob[2] = vtx[phi_a2 + 1].v.ob[2] = vtx[phi_a2 + 2].v.ob[2] = vtx[phi_a2 + 3].v.ob[2] = 0; @@ -2598,7 +2681,7 @@ s16 func_80823A0C(PlayState* play, Vtx* vtx, s16 arg2, s16 arg3) { vtx[phi_a2 + 0].v.flag = vtx[phi_a2 + 1].v.flag = vtx[phi_a2 + 2].v.flag = vtx[phi_a2 + 3].v.flag = 0; - vtx[phi_t1].v.tc[0] = vtx[phi_t1].v.tc[1] = vtx[phi_a2 + 1].v.tc[1] = vtx[phi_a2 + 2].v.tc[0] = 0; + vtx[vtxIndex].v.tc[0] = vtx[vtxIndex].v.tc[1] = vtx[phi_a2 + 1].v.tc[1] = vtx[phi_a2 + 2].v.tc[0] = 0; vtx[phi_a2 + 1].v.tc[0] = vtx[phi_a2 + 3].v.tc[0] = 0x100; @@ -2615,7 +2698,7 @@ s16 func_80823A0C(PlayState* play, Vtx* vtx, s16 arg2, s16 arg3) { } } - return phi_t1; + return vtxIndex; } static s16 D_8082B11C[] = { 0, 4, 8, 12, 24, 32, 56 }; @@ -3325,6 +3408,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 @@ -3356,19 +3440,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; } @@ -3403,6 +3484,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; @@ -3443,6 +3529,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])) {