diff --git a/.github/workflows/generate-builds.yml b/.github/workflows/generate-builds.yml index 64c908a37..45cab21d9 100644 --- a/.github/workflows/generate-builds.yml +++ b/.github/workflows/generate-builds.yml @@ -6,42 +6,44 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: + generate-soh-otr: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - name: Git Checkout + uses: actions/checkout@v4 with: submodules: true - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.14 + - name: Configure ccache + uses: hendrikmuhs/ccache-action@v1.2 with: + save: ${{ github.ref_name == github.event.repository.default_branch }} key: ${{ runner.os }}-otr-ccache-${{ github.ref }}-${{ github.sha }} restore-keys: | ${{ runner.os }}-otr-ccache-${{ github.ref }} - ${{ runner.os }}-otr-ccache- + ${{ runner.os }}-otr-ccache - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y $(cat .github/workflows/apt-deps.txt) libzip-dev zipcmp zipmerge ziptool - - name: Cache build folders - uses: actions/cache@v4 + - name: Restore Cached deps folder + uses: actions/cache/restore@v4 with: - key: ${{ runner.os }}-otr-build-${{ github.ref }}-${{ github.sha }} + key: ${{ runner.os }}-deps-${{ github.ref }}-${{ github.sha }} restore-keys: | - ${{ runner.os }}-otr-build-${{ github.ref }} - ${{ runner.os }}-otr-build- - path: | - build-cmake - SDL2-2.30.3 - tinyxml2-10.0.0 + ${{ runner.os }}-deps-${{ github.ref }}- + ${{ runner.os }}-deps- + path: deps + - name: Create deps folder + run: mkdir -p deps - name: Install latest SDL run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - if [ ! -d "SDL2-2.30.3" ]; then - wget https://www.libsdl.org/release/SDL2-2.30.3.tar.gz - tar -xzf SDL2-2.30.3.tar.gz + if [ ! -d "deps/SDL2-2.30.3" ]; then + wget https://github.com/libsdl-org/SDL/releases/download/release-2.30.3/SDL2-2.30.3.tar.gz + tar -xzf SDL2-2.30.3.tar.gz -C deps fi - cd SDL2-2.30.3 + cd deps/SDL2-2.30.3 ./configure --enable-hidapi-libusb make -j 10 sudo make install @@ -50,11 +52,11 @@ jobs: run: | sudo apt-get remove libtinyxml2-dev export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - if [ ! -d "tinyxml2-10.0.0" ]; then + if [ ! -d "deps/tinyxml2-10.0.0" ]; then wget https://github.com/leethomason/tinyxml2/archive/refs/tags/10.0.0.tar.gz - tar -xzf 10.0.0.tar.gz + tar -xzf 10.0.0.tar.gz -C deps fi - cd tinyxml2-10.0.0 + cd deps/tinyxml2-10.0.0 mkdir -p build cd build cmake .. @@ -64,49 +66,57 @@ jobs: run: | 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@v4 + cmake --build build-cmake --config Release --target GenerateSohOtr -j3 + - name: Upload soh.otr + uses: actions/upload-artifact@v4 with: name: soh.otr path: soh.otr - retention-days: 1 + retention-days: 3 + build-macos: needs: generate-soh-otr runs-on: macos-14 steps: - - uses: actions/checkout@v4 + - name: Git Checkout + uses: actions/checkout@v4 with: submodules: true - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.14 + - name: Configure ccache + uses: hendrikmuhs/ccache-action@v1.2 with: create-symlink: true + save: ${{ github.ref_name == github.event.repository.default_branch }} key: ${{ runner.os }}-14-ccache-${{ github.ref }}-${{ github.sha }} restore-keys: | ${{ runner.os }}-14-ccache-${{ github.ref }} - ${{ runner.os }}-14-ccache- + ${{ runner.os }}-14-ccache + # Needed to apply sudo for macports cache restore - name: Install gtar wrapper run: | sudo mv /opt/homebrew/bin/gtar /opt/homebrew/bin/gtar.orig - sudo cp .github/workflows//gtar /opt/homebrew/bin/gtar + sudo cp .github/workflows/gtar /opt/homebrew/bin/gtar sudo chmod +x /opt/homebrew/bin/gtar - - name: Cache MacPorts - id: cache-macports - uses: actions/cache@v4 + - name: Restore Cached MacPorts + id: restore-cache-macports + uses: actions/cache/restore@v4 with: - path: /opt/local/ - key: ${{ runner.os }}-14-macports-${{ hashFiles('.github/workflows/macports-deps.txt') }} + key: ${{ runner.os }}-14-macports-${{ hashFiles('.github/workflows/macports-deps.txt') }}-${{ github.sha }} restore-keys: | + ${{ runner.os }}-14-macports-${{ hashFiles('.github/workflows/macports-deps.txt') }}- ${{ runner.os }}-14-macports- + path: /opt/local/ + # Updated PATH applies to the next step and onwards - name: Install MacPorts (if necessary) run: | - if [ -d /opt/local/ ]; then + if command -v /opt/local/bin/port 2>&1 >/dev/null; then echo "MacPorts already installed" else + echo "Installing MacPorts" wget https://github.com/macports/macports-base/releases/download/v2.9.3/MacPorts-2.9.3-14-Sonoma.pkg sudo installer -pkg ./MacPorts-2.9.3-14-Sonoma.pkg -target / fi - echo "/opt/local/bin:/opt/local/sbin" >> $GITHUB_PATH + echo "/opt/local/bin:/opt/local/sbin" >> "$GITHUB_PATH" - name: Install dependencies run: | brew uninstall --ignore-dependencies libpng @@ -116,11 +126,12 @@ jobs: uses: actions/download-artifact@v4 with: name: soh.otr + path: build-cmake/soh - name: Build SoH run: | + export PATH="/usr/lib/ccache:/opt/homebrew/opt/ccache/libexec:/usr/local/opt/ccache/libexec:$PATH" cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DBUILD_REMOTE_CONTROL=1 cmake --build build-cmake --config Release --parallel 10 - mv soh.otr build-cmake/soh (cd build-cmake && cpack) mv _packages/*.dmg SoH.dmg @@ -132,44 +143,52 @@ jobs: path: | SoH.dmg readme.txt + - name: Save Cache MacPorts + if: ${{ github.ref_name == github.event.repository.default_branch }} + uses: actions/cache/save@v4 + with: + key: ${{ steps.restore-cache-macports.outputs.cache-primary-key }} + path: /opt/local/ + build-linux: needs: generate-soh-otr runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - name: Git Checkout + uses: actions/checkout@v4 with: submodules: true - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y $(cat .github/workflows/apt-deps.txt) - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.14 + - name: Configure ccache + uses: hendrikmuhs/ccache-action@v1.2 with: - key: linux-ccache-${{ github.ref }}-${{ github.sha }} + save: ${{ github.ref_name == github.event.repository.default_branch }} + key: ${{ runner.os }}-ccache-${{ github.ref }}-${{ github.sha }} restore-keys: | - linux-ccache-${{ github.ref }} - linux-ccache- - - name: Cache build folders - uses: actions/cache@v4 + ${{ runner.os }}-ccache-${{ github.ref }} + ${{ runner.os }}-ccache + - name: Restore Cached deps folder + id: restore-cache-deps + uses: actions/cache/restore@v4 with: - key: linux-build-${{ github.ref }}-${{ github.sha }} + key: ${{ runner.os }}-deps-${{ github.ref }}-${{ github.sha }} restore-keys: | - linux-build-${{ github.ref }} - linux-build- - path: | - SDL2-2.30.3 - SDL2_net-2.2.0 - tinyxml2-10.0.0 - libzip-1.10.1 + ${{ runner.os }}-deps-${{ github.ref }}- + ${{ runner.os }}-deps- + path: deps + - name: Create deps folder + run: mkdir -p deps - name: Install latest SDL run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - if [ ! -d "SDL2-2.30.3" ]; then - wget https://www.libsdl.org/release/SDL2-2.30.3.tar.gz - tar -xzf SDL2-2.30.3.tar.gz + if [ ! -d "deps/SDL2-2.30.3" ]; then + wget https://github.com/libsdl-org/SDL/releases/download/release-2.30.3/SDL2-2.30.3.tar.gz + tar -xzf SDL2-2.30.3.tar.gz -C deps fi - cd SDL2-2.30.3 + cd deps/SDL2-2.30.3 ./configure --enable-hidapi-libusb make -j 10 sudo make install @@ -177,11 +196,11 @@ jobs: - name: Install latest SDL_net run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - if [ ! -d "SDL2_net-2.2.0" ]; then + if [ ! -d "deps/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 + tar -xzf SDL2_net-2.2.0.tar.gz -C deps fi - cd SDL2_net-2.2.0 + cd deps/SDL2_net-2.2.0 ./configure make -j 10 sudo make install @@ -190,11 +209,11 @@ jobs: run: | sudo apt-get remove libtinyxml2-dev export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - if [ ! -d "tinyxml2-10.0.0" ]; then + if [ ! -d "deps/tinyxml2-10.0.0" ]; then wget https://github.com/leethomason/tinyxml2/archive/refs/tags/10.0.0.tar.gz - tar -xzf 10.0.0.tar.gz + tar -xzf 10.0.0.tar.gz -C deps fi - cd tinyxml2-10.0.0 + cd deps/tinyxml2-10.0.0 mkdir -p build cd build cmake .. @@ -203,11 +222,11 @@ jobs: - name: Install libzip without crypto run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - if [ ! -d "libzip-1.10.1" ]; then + if [ ! -d "deps/libzip-1.10.1" ]; then wget https://github.com/nih-at/libzip/releases/download/v1.10.1/libzip-1.10.1.tar.gz - tar -xzf libzip-1.10.1.tar.gz + tar -xzf libzip-1.10.1.tar.gz -C deps fi - cd libzip-1.10.1 + cd deps/libzip-1.10.1 mkdir -p build cd build cmake .. -DENABLE_COMMONCRYPTO=OFF -DENABLE_GNUTLS=OFF -DENABLE_MBEDTLS=OFF -DENABLE_OPENSSL=OFF @@ -218,6 +237,7 @@ jobs: uses: actions/download-artifact@v4 with: name: soh.otr + path: build-cmake/soh - name: Build SoH run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" @@ -237,63 +257,73 @@ jobs: path: | soh.appimage readme.txt + - name: Save Cache deps folder + if: ${{ github.ref_name == github.event.repository.default_branch }} + uses: actions/cache/save@v4 + with: + key: ${{ steps.restore-cache-deps.outputs.cache-primary-key }} + path: deps + build-windows: needs: generate-soh-otr - runs-on: ${{ (vars.WINDOWS_RUNNER && fromJSON(vars.WINDOWS_RUNNER)) || 'windows-latest' }} + runs-on: windows-latest steps: - name: Install dependencies - if: ${{ !vars.WINDOWS_RUNNER }} run: | - choco install ninja + choco install ninja -y Remove-Item -Path "C:\ProgramData\Chocolatey\bin\ccache.exe" -Force -ErrorAction SilentlyContinue - - uses: actions/checkout@v4 + - name: Git Checkout + uses: actions/checkout@v4 with: submodules: true - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.14 + - name: Configure sccache + uses: hendrikmuhs/ccache-action@v1.2 with: variant: sccache - max-size: "1G" + max-size: "2G" + evict-old-files: job + save: ${{ github.ref_name == github.event.repository.default_branch }} 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 + ${{ runner.os }}-ccache + - name: Restore Cached VCPKG folder + id: restore-cache-vcpkg + uses: actions/cache/restore@v4 with: - key: ${{ runner.os }}-build-${{ github.ref }}-${{ github.sha }} + key: ${{ runner.os }}-vcpkg-${{ github.ref }}-${{ github.sha }} restore-keys: | - ${{ runner.os }}-build-${{ github.ref }} - ${{ runner.os }}-build- - path: | - build-windows - vcpkg + ${{ runner.os }}-vcpkg-${{ github.ref }}- + ${{ runner.os }}-vcpkg- + path: vcpkg - name: Configure Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1 + - name: Download soh.otr + uses: actions/download-artifact@v4 + with: + name: soh.otr + path: build-windows/soh - name: Build SoH - env: + env: 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=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DBUILD_REMOTE_CONTROL=1 cmake --build build-windows --config Release --parallel 10 - mkdir soh-windows - mv ./x64/Release/soh.exe ./soh-windows/soh.exe - mkdir soh-windows/debug - mkdir soh-windows/mods - New-Item soh-windows/mods/custom_otr_files_go_here.txt -type file - mv ./x64/Release/soh.pdb ./soh-windows/debug/soh.pdb - mv ./README.md ./soh-windows/readme.txt - mv ./build-windows/gamecontrollerdb.txt ./soh-windows/gamecontrollerdb.txt - mv ./x64/Release/assets ./soh-windows - - name: Download soh.otr - uses: actions/download-artifact@v4 - with: - name: soh.otr - path: soh-windows + (cd build-windows && cpack) + cd .. + mv _packages/*.zip _packages/soh-windows.zip + - name: Unzip package + run: Expand-Archive -Path _packages/soh-windows.zip -DestinationPath soh-windows - name: Upload build uses: actions/upload-artifact@v4 with: name: soh-windows path: soh-windows + - name: Save Cache VCPKG folder + if: ${{ github.ref_name == github.event.repository.default_branch }} + uses: actions/cache/save@v4 + with: + key: ${{ steps.restore-cache-vcpkg.outputs.cache-primary-key }} + path: vcpkg diff --git a/CMake/Packaging-2.cmake b/CMake/Packaging-2.cmake index a29635eac..dc5da1b7e 100644 --- a/CMake/Packaging-2.cmake +++ b/CMake/Packaging-2.cmake @@ -1,9 +1,9 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY 0) -set(CPACK_COMPONENTS_ALL "ship" "extractor" "appimage") +set(CPACK_COMPONENTS_ALL "ship") -if (NOT CPACK_GENERATOR STREQUAL "External") - list(REMOVE_ITEM CPACK_COMPONENTS_ALL "appimage") +if (CPACK_GENERATOR STREQUAL "External") + list(APPEND CPACK_COMPONENTS_ALL "extractor" "appimage") endif() if (CPACK_GENERATOR MATCHES "DEB|RPM") diff --git a/CMakeLists.txt b/CMakeLists.txt index 3437ef3fd..7aa873c62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,10 @@ execute_process( OUTPUT_STRIP_TRAILING_WHITESPACE ) -set(CMAKE_PROJECT_GIT_COMMIT_HASH "${GIT_COMMIT_HASH}" CACHE STRING "Git commit hash" FORCE) +# Get only the first 7 characters of the hash +string(SUBSTRING "${GIT_COMMIT_HASH}" 0 7 SHORT_COMMIT_HASH) + +set(CMAKE_PROJECT_GIT_COMMIT_HASH "${SHORT_COMMIT_HASH}" CACHE STRING "Git commit hash" FORCE) execute_process( COMMAND git describe --tags --abbrev=0 --exact-match HEAD @@ -171,7 +174,7 @@ set_property(TARGET soh PROPERTY APPIMAGE_ICON_FILE "${CMAKE_BINARY_DIR}/sohIcon if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") install(PROGRAMS "${CMAKE_BINARY_DIR}/linux/soh.sh" DESTINATION . COMPONENT appimage) -install(FILES "${CMAKE_SOURCE_DIR}/soh.otr" DESTINATION . COMPONENT ship) +install(FILES "${CMAKE_BINARY_DIR}/soh/soh.otr" DESTINATION . COMPONENT ship) install(TARGETS ZAPD DESTINATION ./assets/extractor COMPONENT extractor) install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/extractor/" DESTINATION ./assets/extractor COMPONENT extractor) install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/xml/" DESTINATION ./assets/extractor/xmls COMPONENT extractor) diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index b4ee6ed7f..7277b2dfb 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -440,6 +440,7 @@ if(MSVC) /Gy; /W3 > + /bigobj; /sdl-; /permissive-; /MP; diff --git a/soh/assets/custom/fonts/Inconsolata-Regular.ttf b/soh/assets/custom/fonts/Inconsolata-Regular.ttf new file mode 100644 index 000000000..d1241516b Binary files /dev/null and b/soh/assets/custom/fonts/Inconsolata-Regular.ttf differ diff --git a/soh/assets/custom/fonts/Montserrat-Regular.ttf b/soh/assets/custom/fonts/Montserrat-Regular.ttf new file mode 100644 index 000000000..f4a266dd3 Binary files /dev/null and b/soh/assets/custom/fonts/Montserrat-Regular.ttf differ diff --git a/soh/assets/custom/objects/object_keyring/Hilite_new.rgba16 b/soh/assets/custom/objects/object_keyring/Hilite_new.rgba16 new file mode 100644 index 000000000..5438695fa Binary files /dev/null and b/soh/assets/custom/objects/object_keyring/Hilite_new.rgba16 differ diff --git a/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL index 3552eef1b..7cb17d889 100644 --- a/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL +++ b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL @@ -1,11 +1,10 @@ + + + + - - - - - diff --git a/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_tri_0 b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_tri_0 index 791defa81..c7b29e9c7 100644 --- a/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_tri_0 +++ b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_tri_0 @@ -1,213 +1,110 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_vtx_0 b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_vtx_0 index 1dd0cbb61..5ab0f76ec 100644 --- a/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_vtx_0 +++ b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_vtx_0 @@ -1,233 +1,210 @@ - + - - - - - - - + + + + + + + - + - + - - - + - - - + + + - - - + + + - - - + - - - + + + + + - - - - + + + + - - - + - - - + - - - + + + - - + + - - - - + + - - - - - - + + + + + + + + - - - + + + + - - - - - - - + + + + + - - - - + + + + - - - + + + - - - + - + - - - + + + + + - + + - - - - + + - - - - + + + - - - - - - - + + + + - + - + - - - + + + - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - - - - - - + + + - - - + - + - + + - - - - - - + + + + - - - - + + + + + - - - - - - - - - - + + - + - + - + - + - + + + - + - + - + - - + + - + diff --git a/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_vtx_cull b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_vtx_cull new file mode 100644 index 000000000..6ef0139b8 --- /dev/null +++ b/soh/assets/custom/objects/object_keyring/gKeyringIconGerudoFortressDL_vtx_cull @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_keyring/mat_gKeyringIconGerudoFortressDL_f3dlite_IconMetal_GerudoFortress b/soh/assets/custom/objects/object_keyring/mat_gKeyringIconGerudoFortressDL_f3dlite_IconMetal_GerudoFortress index b5755b4a8..f926de784 100644 --- a/soh/assets/custom/objects/object_keyring/mat_gKeyringIconGerudoFortressDL_f3dlite_IconMetal_GerudoFortress +++ b/soh/assets/custom/objects/object_keyring/mat_gKeyringIconGerudoFortressDL_f3dlite_IconMetal_GerudoFortress @@ -1,21 +1,15 @@ + - - - + - - - - - + + - - diff --git a/soh/assets/custom/objects/object_keyring/model.xml b/soh/assets/custom/objects/object_keyring/model.xml new file mode 100644 index 000000000..6ef0139b8 --- /dev/null +++ b/soh/assets/custom/objects/object_keyring/model.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/include/variables.h b/soh/include/variables.h index e3408eef6..27ad4584d 100644 --- a/soh/include/variables.h +++ b/soh/include/variables.h @@ -45,12 +45,12 @@ extern "C" extern OSViContext* __osViNext; extern OSViMode osViModeFpalLan1; extern u32 __additional_scanline; - extern u8 gBuildVersion[]; + extern const char gBuildVersion[]; extern u16 gBuildVersionMajor; extern u16 gBuildVersionMinor; extern u16 gBuildVersionPatch; - extern u8 gGitBranch[]; - extern u8 gGitCommitHash[]; + extern const char gGitBranch[]; + extern const char gGitCommitHash[]; extern u8 gGitCommitTag[]; extern u8 gBuildTeam[]; extern u8 gBuildDate[]; diff --git a/soh/soh/AboutWindow.cpp b/soh/soh/AboutWindow.cpp deleted file mode 100644 index e5d51300c..000000000 --- a/soh/soh/AboutWindow.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "AboutWindow.h" -#include -#include -#include "soh/ResourceManagerHelpers.h" - -extern "C" { -#include "variables.h" -} - -AboutWindow::~AboutWindow() { - SPDLOG_TRACE("destruct about window"); -} - -void AboutWindow::InitElement() { - mIsTaggedVersion = gGitCommitTag[0] != 0; - - strncpy(mGitCommitHashTruncated, (char*)gGitCommitHash, 7); - mGitCommitHashTruncated[7] = 0; -} - -void AboutWindow::Draw() { - if (!IsVisible()) { - return; - } - - ImGuiWindowFlags windowFlags = ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoDocking | - ImGuiWindowFlags_NoScrollWithMouse | - ImGuiWindowFlags_NoScrollbar; - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(16 * ImGui::GetIO().FontGlobalScale, 8 * ImGui::GetIO().FontGlobalScale)); - - if (!ImGui::Begin(GetName().c_str(), &mIsVisible, windowFlags)) { - ImGui::End(); - } else { - DrawElement(); - ImGui::End(); - } - - ImGui::PopStyleVar(); - - // Sync up the IsVisible flag if it was changed by ImGui - SyncVisibilityConsoleVariable(); -} - -const char* AboutWindow::GetGameVersionString(uint32_t index) { - uint32_t gameVersion = ResourceMgr_GetGameVersion(index); - switch (gameVersion) { - case OOT_NTSC_US_10: - return "NTSC-U 1.0"; - case OOT_NTSC_US_11: - return "NTSC-U 1.1"; - case OOT_NTSC_US_12: - return "NTSC-U 1.2"; - case OOT_PAL_10: - return "PAL 1.0"; - case OOT_PAL_11: - return "PAL 1.1"; - case OOT_PAL_GC: - return "PAL GC"; - case OOT_PAL_MQ: - return "PAL MQ"; - case OOT_PAL_GC_DBG1: - case OOT_PAL_GC_DBG2: - return "PAL GC-D"; - case OOT_PAL_GC_MQ_DBG: - return "PAL MQ-D"; - case OOT_IQUE_CN: - return "IQUE CN"; - case OOT_IQUE_TW: - return "IQUE TW"; - default: - return "UNKNOWN"; - } -} - -void AboutWindow::DrawElement() { - // The icon is already padded - adjust for that - ImVec2 cursorPos = ImGui::GetCursorScreenPos(); - cursorPos.x -= 16 * ImGui::GetIO().FontGlobalScale; - ImGui::SetCursorScreenPos(cursorPos); - - ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Game_Icon"), ImVec2(64.0f * ImGui::GetIO().FontGlobalScale, 64.0f * ImGui::GetIO().FontGlobalScale)); - - ImGui::SameLine(); - - ImGui::BeginGroup(); - ImGui::Text("Ship of Harkinian"); - if (mIsTaggedVersion) { - ImGui::Text("%s", gBuildVersion); - } else { - ImGui::Text("%s", gGitBranch); - ImGui::Text("%s", mGitCommitHashTruncated); - } - ImGui::EndGroup(); - - ImGui::Dummy(ImVec2(0, 2 * ImGui::GetIO().FontGlobalScale)); - ImGui::Text("Game Archives:"); - for (uint32_t i = 0; i < ResourceMgr_GetNumGameVersions(); i++) { - ImGui::BulletText(GetGameVersionString(i)); - } -} diff --git a/soh/soh/AboutWindow.h b/soh/soh/AboutWindow.h deleted file mode 100644 index 5a731fdc1..000000000 --- a/soh/soh/AboutWindow.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -class AboutWindow : public Ship::GuiWindow { - public: - using GuiWindow::GuiWindow; - ~AboutWindow(); - - private: - void InitElement() override; - void Draw() override; - void DrawElement() override; - void UpdateElement() override {}; - - const char* GetGameVersionString(uint32_t index); - - bool mIsTaggedVersion; - char mGitCommitHashTruncated[8]; -}; diff --git a/soh/soh/Enhancements/RemoveSpinAttackDarkness.cpp b/soh/soh/Enhancements/RemoveSpinAttackDarkness.cpp index 20beb193a..37fed1b7d 100644 --- a/soh/soh/Enhancements/RemoveSpinAttackDarkness.cpp +++ b/soh/soh/Enhancements/RemoveSpinAttackDarkness.cpp @@ -10,6 +10,12 @@ void Custom_EnMThunder_Update(Actor* thisx, PlayState* play) { f32 blueRadius; s32 redGreen; + // If thunder effect doesn't exist (aka player doesn't have magic), + // don't do anything. + if (enMThunder->actionFunc == nullptr) { + return; + } + enMThunder->actionFunc(enMThunder, play); // don't call this part, it's what makes the spin attack darkness happen // func_80A9F314(play, this->unk_1BC); diff --git a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h index 090ac2901..671577631 100644 --- a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h +++ b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h @@ -13,20 +13,20 @@ class TimeDisplayWindow : public Ship::GuiWindow { void TimeDisplayUpdateDisplayOptions(); void TimeDisplayInitSettings(); -typedef enum { +typedef enum TimerDisplay { DISPLAY_IN_GAME_TIMER, DISPLAY_TIME_OF_DAY, DISPLAY_CONDITIONAL_TIMER, DISPLAY_NAVI_TIMER -}; +} TimerDisplay; -typedef enum { +typedef enum NaviTimerValues { NAVI_PREPARE = 600, NAVI_ACTIVE = 3000, NAVI_COOLDOWN = 25800, DAY_BEGINS = 17759, NIGHT_BEGINS = 49155 -}; +} NaviTimerValues; typedef struct { uint32_t timeID; diff --git a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp index c9a999cb1..ae4236218 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp @@ -20,6 +20,11 @@ static bool sEnteredBlueWarp = false; * should also account for the difference between your first and following visits to the blue warp. */ void SkipBlueWarp_ShouldPlayTransitionCS(GIVanillaBehavior _, bool* should, va_list originalArgs) { + // Do nothing when in a boss rush + if (IS_BOSS_RUSH) { + return; + } + bool overrideBlueWarpDestinations = IS_RANDO && (RAND_GET_OPTION(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF || RAND_GET_OPTION(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF); diff --git a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp index 7d676e6f8..2894cb914 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp @@ -65,7 +65,12 @@ void SkipChildRutoInteractions_Register() { enRu1->action = 42; Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildWait2Anim, 1.0f, 0, Animation_GetLastFrame((void*)&gRutoChildWait2Anim), ANIMMODE_LOOP, -8.0f); - enRu1->unk_28C->cameraSetting = 1; + // If we aren't skipping one point cutscenes and BgBdan objects has set the camera setting + // to CAM_SET_NORMAL1 (2), don't reset the camera setting to 1. This prevents the One Point + // Cutscene of Ruto getting lifted up from getting queued up twice. + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO) || enRu1->unk_28C->cameraSetting != 2) { + enRu1->unk_28C->cameraSetting = 1; + } Actor* sapphire = func_80AEB124(gPlayState); if (sapphire != NULL) { Actor_Kill(sapphire); diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index d15bc5f25..caa40c66f 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -12,9 +12,15 @@ #include "soh/cvar_prefixes.h" #include #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "AudioCollection.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" +extern "C" { + #include "z64save.h" + extern SaveContext gSaveContext; +} + Vec3f pos = { 0.0f, 0.0f, 0.0f }; f32 freqScale = 1.0f; s8 reverbAdd = 0; @@ -165,13 +171,20 @@ void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequence const std::string previewButton = ICON_FA_PLAY + hiddenKey; if (CVarGetInteger(CVAR_AUDIO("Playing"), 0) == sequenceId) { - if (ImGui::Button(stopButton.c_str())) { + if (UIWidgets::Button(stopButton.c_str(), UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Padding(ImVec2(10.0f, 6.0f)) + .Tooltip("Stop Preview") + .Color(THEME_COLOR))) { func_800F5C2C(); CVarSetInteger(CVAR_AUDIO("Playing"), 0); } - UIWidgets::Tooltip("Stop Preview"); } else { - if (ImGui::Button(previewButton.c_str())) { + if (UIWidgets::Button(previewButton.c_str(), UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Padding(ImVec2(10.0f, 6.0f)) + .Tooltip("Play Preview") + .Color(THEME_COLOR))) { if (CVarGetInteger(CVAR_AUDIO("Playing"), 0) != 0) { func_800F5C2C(); CVarSetInteger(CVAR_AUDIO("Playing"), 0); @@ -188,11 +201,10 @@ void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequence } } } - UIWidgets::Tooltip("Play Preview"); } } -void Draw_SfxTab(const std::string& tabId, SeqType type) { +void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabName) { const std::map& map = AudioCollection::Instance->GetAllSequences(); const std::string hiddenTabId = "##" + tabId; @@ -200,7 +212,10 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { 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())) { + + ImGui::SeparatorText(tabName.c_str()); + if (UIWidgets::Button(resetAllButton.c_str(), + UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) { auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); ResetGroup(map, type); @@ -211,7 +226,8 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { } } ImGui::SameLine(); - if (ImGui::Button(randomizeAllButton.c_str())) { + if (UIWidgets::Button(randomizeAllButton.c_str(), + UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) { auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); RandomizeGroup(type); @@ -222,7 +238,8 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { } } ImGui::SameLine(); - if (ImGui::Button(lockAllButton.c_str())) { + if (UIWidgets::Button(lockAllButton.c_str(), + UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) { auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); LockGroup(map, type); @@ -233,7 +250,8 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { } } ImGui::SameLine(); - if (ImGui::Button(unlockAllButton.c_str())) { + if (UIWidgets::Button(unlockAllButton.c_str(), + UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) { auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); UnlockGroup(map, type); @@ -244,10 +262,12 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { } } + // Longest text in Audio Editor + ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)"); ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, columnSize.x + 30); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, columnSize.x + 30); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 160.0f); for (const auto& [defaultValue, seqData] : map) { if (~(seqData.category) & type) { continue; @@ -273,6 +293,7 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { ImGui::TableNextColumn(); ImGui::PushItemWidth(-FLT_MIN); const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; + UIWidgets::PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo(hiddenKey.c_str(), map.at(initialValue).label.c_str())) { for (const auto& [value, seqData] : map) { // If excluded as a replacement sequence, don't show in other dropdowns except the effect's own dropdown. @@ -293,22 +314,30 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { ImGui::EndCombo(); } + UIWidgets::PopStyleCombobox(); ImGui::TableNextColumn(); ImGui::PushItemWidth(-FLT_MIN); DrawPreviewButton((type == SEQ_SFX || type == SEQ_VOICE || type == SEQ_INSTRUMENT) ? defaultValue : currentValue, seqData.sfxKey, type); auto locked = CVarGetInteger(cvarLockKey.c_str(), 0) == 1; ImGui::SameLine(); ImGui::PushItemWidth(-FLT_MIN); - if (ImGui::Button(resetButton.c_str())) { + if (UIWidgets::Button(resetButton.c_str(), UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Padding(ImVec2(10.0f, 6.0f)) + .Tooltip("Reset to default") + .Color(THEME_COLOR))) { CVarClear(cvarKey.c_str()); CVarClear(cvarLockKey.c_str()); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); UpdateCurrentBGM(defaultValue, seqData.category); } - UIWidgets::Tooltip("Reset to default"); ImGui::SameLine(); ImGui::PushItemWidth(-FLT_MIN); - if (ImGui::Button(randomizeButton.c_str())) { + if (UIWidgets::Button(randomizeButton.c_str(), UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Padding(ImVec2(10.0f, 6.0f)) + .Tooltip("Randomize this sound") + .Color(THEME_COLOR))) { std::vector validSequences = {}; for (const auto seqInfo : AudioCollection::Instance->GetIncludedSequences()) { if (seqInfo->category & type) { @@ -327,10 +356,14 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { UpdateCurrentBGM(defaultValue, type); } } - UIWidgets::Tooltip("Randomize this sound"); ImGui::SameLine(); ImGui::PushItemWidth(-FLT_MIN); - if (ImGui::Button(locked ? lockedButton.c_str() : unlockedButton.c_str())) { + if (UIWidgets::Button(locked ? lockedButton.c_str() : unlockedButton.c_str(), + UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Padding(ImVec2(10.0f, 6.0f)) + .Tooltip(locked ? "Sound locked" : "Sound unlocked") + .Color(THEME_COLOR))) { if (locked) { CVarClear(cvarLockKey.c_str()); } else { @@ -338,7 +371,6 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { } Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - UIWidgets::Tooltip(locked ? "Sound locked" : "Sound unlocked"); } ImGui::EndTable(); } @@ -401,10 +433,11 @@ ImVec4 GetSequenceTypeColor(SeqType type) { } } -void DrawTypeChip(SeqType type) { +void DrawTypeChip(SeqType type, std::string sequenceName) { ImGui::BeginDisabled(); ImGui::PushStyleColor(ImGuiCol_Button, GetSequenceTypeColor(type)); - ImGui::SmallButton(GetSequenceTypeName(type).c_str()); + std::string buttonLabel = GetSequenceTypeName(type) + "##" + sequenceName; + ImGui::Button(buttonLabel.c_str()); ImGui::PopStyleColor(); ImGui::EndDisabled(); } @@ -412,7 +445,7 @@ void DrawTypeChip(SeqType type) { void AudioEditorRegisterOnSceneInitHook() { GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { - if (CVarGetInteger(CVAR_AUDIO("RandomizeAllOnNewScene"), 0)) { + if (gSaveContext.gameMode != GAMEMODE_END_CREDITS && CVarGetInteger(CVAR_AUDIO("RandomizeAllOnNewScene"), 0)) { AudioEditor_RandomizeAll(); } }); @@ -425,114 +458,113 @@ void AudioEditor::InitElement() { void AudioEditor::DrawElement() { AudioCollection::Instance->InitializeShufflePool(); - float buttonSegments = ImGui::GetContentRegionAvail().x / 4; - if (ImGui::Button("Randomize All Groups", ImVec2(buttonSegments, 30.0f))) { + UIWidgets::Separator(); + if (UIWidgets::Button("Randomize All Groups", + UIWidgets::ButtonOptions() + .Size(ImVec2(230.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("Randomizes all unlocked music and sound effects across tab groups"))) { 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))) { + if (UIWidgets::Button("Reset All Groups", + UIWidgets::ButtonOptions() + .Size(ImVec2(230.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("Resets all unlocked music and sound effects across tab groups"))) { 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))) { + if (UIWidgets::Button("Lock All Groups", UIWidgets::ButtonOptions() + .Size(ImVec2(230.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("Locks all music and sound effects across tab groups"))) { 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))) { + if (UIWidgets::Button("Unlock All Groups", + UIWidgets::ButtonOptions() + .Size(ImVec2(230.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("Unlocks all music and sound effects across tab groups"))) { AudioEditor_UnlockAll(); } - UIWidgets::Tooltip("Unlocks all music and sound effects across tab groups"); - + UIWidgets::Separator(); + UIWidgets::PushStyleTabs(THEME_COLOR); if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { - if (ImGui::BeginTabItem("Background Music")) { - Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Fanfares")) { - Draw_SfxTab("fanfares", SEQ_FANFARE); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Events")) { - Draw_SfxTab("event", SEQ_BGM_EVENT); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Battle Music")) { - Draw_SfxTab("battleMusic", SEQ_BGM_BATTLE); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Ocarina")) { - Draw_SfxTab("instrument", SEQ_INSTRUMENT); - Draw_SfxTab("ocarina", SEQ_OCARINA); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Sound Effects")) { - Draw_SfxTab("sfx", SEQ_SFX); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Voices")) { - Draw_SfxTab("voice", SEQ_VOICE); - ImGui::EndTabItem(); - } - + static ImVec2 cellPadding(8.0f, 8.0f); - if (ImGui::BeginTabItem("Options")) { + if (ImGui::BeginTabItem("Audio Options")) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); - ImGui::BeginTable("Options", 1, ImGuiTableFlags_SizingStretchSame); + ImGui::BeginTable("Audio Options", 1, ImGuiTableFlags_SizingStretchSame); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); ImGui::TableNextColumn(); if (ImGui::BeginChild("SfxOptions", ImVec2(0, -8))) { - ImGui::PushItemWidth(-FLT_MIN); - UIWidgets::EnhancementCheckbox("Disable Enemy Proximity Music", CVAR_AUDIO("EnemyBGMDisable")); - UIWidgets::InsertHelpHoverText( - "Disables the music change when getting close to enemies. Useful for hearing " - "your custom music for each scene more often."); - UIWidgets::EnhancementCheckbox("Disable Leading Music in Lost Woods", CVAR_AUDIO("LostWoodsConsistentVolume")); - UIWidgets::InsertHelpHoverText( - "Disables the volume shifting in the Lost Woods. Useful for hearing " - "your custom music in the Lost Woods if you don't need the navigation assitance " - "the volume changing provides. If toggling this while in the Lost Woods, reload " - "the area for the effect to kick in." - ); - UIWidgets::EnhancementCheckbox("Display Sequence Name on Overlay", CVAR_AUDIO("SeqNameOverlay")); - UIWidgets::InsertHelpHoverText( - "Displays the name of the current sequence in the corner of the screen whenever a new sequence " - "is loaded to the main sequence player (does not apply to fanfares or enemy BGM)." - ); + UIWidgets::CVarCheckbox( + "Mute Low HP Alarm", CVAR_AUDIO("LowHpAlarm"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR).Tooltip("Disable the low HP beeping sound.")); + UIWidgets::CVarCheckbox("Disable Navi Call Audio", CVAR_AUDIO("DisableNaviCallAudio"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Disables the voice audio when Navi calls you.")); + UIWidgets::CVarCheckbox( + "Disable Enemy Proximity Music", CVAR_AUDIO("EnemyBGMDisable"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Disables the music change when getting close to enemies. Useful for hearing " + "your custom music for each scene more often.")); + UIWidgets::CVarCheckbox( + "Disable Leading Music in Lost Woods", CVAR_AUDIO("LostWoodsConsistentVolume"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Disables the volume shifting in the Lost Woods. Useful for hearing " + "your custom music in the Lost Woods if you don't need the navigation assitance " + "the volume changing provides. If toggling this while in the Lost Woods, reload " + "the area for the effect to kick in.")); + UIWidgets::CVarCheckbox( + "Display Sequence Name on Overlay", CVAR_AUDIO("SeqNameOverlay"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Displays the name of the current sequence in the corner of the screen whenever a new " + "sequence " + "is loaded to the main sequence player (does not apply to fanfares or enemy BGM).")); + UIWidgets::CVarSliderInt("Overlay Duration: %d seconds", CVAR_AUDIO("SeqNameOverlayDuration"), + UIWidgets::IntSliderOptions() + .Min(1) + .Max(10) + .DefaultValue(5) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); + UIWidgets::CVarSliderFloat("Link's voice pitch multiplier", CVAR_AUDIO("LinkVoiceFreqMultiplier"), + UIWidgets::FloatSliderOptions() + .IsPercentage() + .Min(0.4f) + .Max(2.5f) + .DefaultValue(1.0f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); ImGui::SameLine(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - UIWidgets::EnhancementSliderInt("Overlay Duration: %d seconds", "##SeqNameOverlayDuration", - CVAR_AUDIO("SeqNameOverlayDuration"), 1, 10, "", 5); - ImGui::PopItemWidth(); - ImGui::NewLine(); - ImGui::PopItemWidth(); - UIWidgets::EnhancementSliderFloat("Link's voice pitch multiplier: %.1f %%", "##linkVoiceFreqMultiplier", - CVAR_AUDIO("LinkVoiceFreqMultiplier"), 0.4, 2.5, "", 1.0, true, true); - ImGui::SameLine(); - const std::string resetButton = "Reset##linkVoiceFreqMultiplier"; - if (ImGui::Button(resetButton.c_str())) { + ImGui::SetCursorPosY(ImGui::GetCursorPos().y + 40.f); + if (UIWidgets::Button("Reset##linkVoiceFreqMultiplier", + UIWidgets::ButtonOptions().Size(ImVec2(80, 36)).Padding(ImVec2(5.0f, 0.0f)))) { CVarSetFloat(CVAR_AUDIO("LinkVoiceFreqMultiplier"), 1.0f); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - - ImGui::NewLine(); - UIWidgets::EnhancementCheckbox("Randomize All Music and Sound Effects on New Scene", CVAR_AUDIO("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(); - UIWidgets::PaddedText("The following options are experimental and may cause music\nto sound odd or have other undesireable effects."); - UIWidgets::EnhancementCheckbox("Lower Octaves of Unplayable High Notes", CVAR_AUDIO("ExperimentalOctaveDrop")); - UIWidgets::InsertHelpHoverText("Some custom sequences may have notes that are too high for the game's audio " - "engine to play. Enabling this checkbox will cause these notes to drop a " - "couple of octaves so they can still harmonize with the other notes of the " - "sequence."); - ImGui::PopItemWidth(); + UIWidgets::CVarCheckbox( + "Randomize All Music and Sound Effects on New Scene", CVAR_AUDIO("RandomizeAllOnNewScene"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip( + "Enables randomizing all unlocked music and sound effects when you enter a new scene.")); + UIWidgets::CVarCheckbox( + "Lower Octaves of Unplayable High Notes", CVAR_AUDIO("ExperimentalOctaveDrop"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Some custom sequences may have notes that are too high for the game's audio " + "engine to play. Enabling this checkbox will cause these notes to drop a " + "couple of octaves so they can still harmonize with the other notes of the " + "sequence.")); } ImGui::EndChild(); ImGui::EndTable(); @@ -540,6 +572,36 @@ void AudioEditor::DrawElement() { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Background Music")) { + Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD, "Background Music"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Fanfares")) { + Draw_SfxTab("fanfares", SEQ_FANFARE, "Fanfares"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Events")) { + Draw_SfxTab("event", SEQ_BGM_EVENT, "Events"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Battle Music")) { + Draw_SfxTab("battleMusic", SEQ_BGM_BATTLE, "Battle Music"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Ocarina")) { + Draw_SfxTab("instrument", SEQ_INSTRUMENT, "Instruments"); + Draw_SfxTab("ocarina", SEQ_OCARINA, "Ocarina"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Sound Effects")) { + Draw_SfxTab("sfx", SEQ_SFX, "Sound Effects"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Voices")) { + Draw_SfxTab("voice", SEQ_VOICE, "Voices"); + ImGui::EndTabItem(); + } + static bool excludeTabOpen = false; if (ImGui::BeginTabItem("Audio Shuffle Pool Management")) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); @@ -564,9 +626,12 @@ void AudioEditor::DrawElement() { std::set seqsToExclude = {}; static ImGuiTextFilter sequenceSearch; + UIWidgets::PushStyleInput(THEME_COLOR); sequenceSearch.Draw("Filter (inc,-exc)", 490.0f); + UIWidgets::PopStyleInput(); ImGui::SameLine(); - if (ImGui::Button("Exclude All")) { + if (UIWidgets::Button("Exclude All", + UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) { for (auto seqInfo : AudioCollection::Instance->GetIncludedSequences()) { if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) { seqsToExclude.insert(seqInfo); @@ -574,7 +639,8 @@ void AudioEditor::DrawElement() { } } ImGui::SameLine(); - if (ImGui::Button("Include All")) { + if (UIWidgets::Button("Include All", + UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) { for (auto seqInfo : AudioCollection::Instance->GetExcludedSequences()) { if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) { seqsToInclude.insert(seqInfo); @@ -643,13 +709,17 @@ void AudioEditor::DrawElement() { ImGui::BeginChild("ChildIncludedSequences", ImVec2(0, -8)); for (auto seqInfo : AudioCollection::Instance->GetIncludedSequences()) { if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) { - if (ImGui::Button(std::string(ICON_FA_TIMES "##" + seqInfo->sfxKey).c_str())) { + if (UIWidgets::Button(std::string(ICON_FA_TIMES "##" + seqInfo->sfxKey).c_str(), + UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Padding(ImVec2(9.0f, 6.0f)) + .Color(THEME_COLOR))) { seqsToExclude.insert(seqInfo); } ImGui::SameLine(); DrawPreviewButton(seqInfo->sequenceId, seqInfo->sfxKey, seqInfo->category); ImGui::SameLine(); - DrawTypeChip(seqInfo->category); + DrawTypeChip(seqInfo->category, seqInfo->label); ImGui::SameLine(); ImGui::Text("%s", seqInfo->label.c_str()); } @@ -667,13 +737,17 @@ void AudioEditor::DrawElement() { ImGui::BeginChild("ChildExcludedSequences", ImVec2(0, -8)); for (auto seqInfo : AudioCollection::Instance->GetExcludedSequences()) { if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) { - if (ImGui::Button(std::string(ICON_FA_PLUS "##" + seqInfo->sfxKey).c_str())) { + if (UIWidgets::Button(std::string(ICON_FA_PLUS "##" + seqInfo->sfxKey).c_str(), + UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Padding(ImVec2(9.0f, 6.0f)) + .Color(THEME_COLOR))) { seqsToInclude.insert(seqInfo); } ImGui::SameLine(); DrawPreviewButton(seqInfo->sequenceId, seqInfo->sfxKey, seqInfo->category); ImGui::SameLine(); - DrawTypeChip(seqInfo->category); + DrawTypeChip(seqInfo->category, seqInfo->sfxKey); ImGui::SameLine(); ImGui::Text("%s", seqInfo->label.c_str()); } @@ -695,6 +769,7 @@ void AudioEditor::DrawElement() { ImGui::EndTabBar(); } + UIWidgets::PopStyleTabs(); } std::vector allTypes = { SEQ_BGM_WORLD, SEQ_BGM_EVENT, SEQ_BGM_BATTLE, SEQ_OCARINA, SEQ_FANFARE, SEQ_INSTRUMENT, SEQ_SFX, SEQ_VOICE }; diff --git a/soh/soh/Enhancements/controls/InputViewer.cpp b/soh/soh/Enhancements/controls/InputViewer.cpp index 5f650a25f..d26de6719 100644 --- a/soh/soh/Enhancements/controls/InputViewer.cpp +++ b/soh/soh/Enhancements/controls/InputViewer.cpp @@ -10,31 +10,25 @@ #include #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" + +using namespace UIWidgets; // Text colors -static ImVec4 textColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); -static ImVec4 range1Color = ImVec4(1.0f, 0.7f, 0, 1.0f); -static ImVec4 range2Color = ImVec4(0, 1.0f, 0, 1.0f); +static Color_RGBA8 textColorDefault = { 255, 255, 255, 255 }; +static Color_RGBA8 range1ColorDefault = { 255, 178, 0, 255 }; +static Color_RGBA8 range2ColorDefault = { 0, 255, 0, 255 }; -static const char* buttonOutlineOptions[4] = { "Always Shown", "Shown Only While Not Pressed", - "Shown Only While Pressed", "Always Hidden" }; -static const char* buttonOutlineOptionsVerbose[4] = { "Outline Always Shown", "Outline Shown Only While Not Pressed", - "Outline Shown Only While Pressed", "Outline Always Hidden" }; +static std::unordered_map buttonOutlineOptions = + {{ BUTTON_OUTLINE_ALWAYS_SHOWN, "Always Shown" }, { BUTTON_OUTLINE_NOT_PRESSED, "Shown Only While Not Pressed" }, + { BUTTON_OUTLINE_PRESSED, "Shown Only While Pressed" }, { BUTTON_OUTLINE_ALWAYS_HIDDEN, "Always Hidden" }}; +static std::unordered_map buttonOutlineOptionsVerbose = + {{ BUTTON_OUTLINE_ALWAYS_SHOWN, "Outline Always Shown" }, { BUTTON_OUTLINE_NOT_PRESSED, "Outline Shown Only While Not Pressed" }, + { BUTTON_OUTLINE_PRESSED, "Outline Shown Only While Pressed" }, { BUTTON_OUTLINE_ALWAYS_HIDDEN, "Outline Always Hidden" }}; -static const char* stickModeOptions[3] = { "Always", "While In Use", "Never" }; - -static Color_RGBA8 vec2Color(ImVec4 vec) { - Color_RGBA8 color; - color.r = vec.x * 255.0; - color.g = vec.y * 255.0; - color.b = vec.z * 255.0; - color.a = vec.w * 255.0; - return color; -} - -static ImVec4 color2Vec(Color_RGBA8 color) { - return ImVec4(color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0); -} +static std::unordered_map stickModeOptions = + {{ STICK_MODE_ALWAYS_SHOWN, "Always" }, { STICK_MODE_HIDDEN_IN_DEADZONE, "While In Use" }, { STICK_MODE_ALWAYS_HIDDEN, "Never" }}; InputViewer::~InputViewer() { SPDLOG_TRACE("destruct input viewer"); @@ -399,17 +393,17 @@ void InputViewer::DrawElement() { (rSquared >= (range1Min * range1Min)) && (rSquared < (range1Max * range1Max))) { ImGui::PushStyleColor( ImGuiCol_Text, - color2Vec(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Color"), vec2Color(range1Color)))); + VecFromRGBA8(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Color.Value"), range1ColorDefault))); } else if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Enabled"), 0) && (rSquared >= (range2Min * range2Min)) && (rSquared < (range2Max * range2Max))) { ImGui::PushStyleColor( ImGuiCol_Text, - color2Vec(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Color"), vec2Color(range2Color)))); + VecFromRGBA8(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Color.Value"), range2ColorDefault))); } else { - ImGui::PushStyleColor(ImGuiCol_Text, color2Vec(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.TextColor"), - vec2Color(textColor)))); + ImGui::PushStyleColor(ImGuiCol_Text, VecFromRGBA8(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.TextColor.Value"), + textColorDefault))); } // Render text @@ -434,158 +428,139 @@ InputViewerSettingsWindow::~InputViewerSettingsWindow() { } void InputViewerSettingsWindow::DrawElement() { - // gInputViewer.Scale - UIWidgets::EnhancementSliderFloat("Input Viewer Scale: %.2f", "##Input", CVAR_INPUT_VIEWER("Scale"), 0.1f, 5.0f, "", - 1.0f, false, true); - UIWidgets::Tooltip("Sets the on screen size of the input viewer"); + // gInputViewer.Scale + CVarSliderFloat("Input Viewer Scale: %.2f", CVAR_INPUT_VIEWER("Scale"), + FloatSliderOptions().Color(THEME_COLOR).DefaultValue(1.0f).Min(0.1f).Max(5.0f).ShowButtons(true).Tooltip("Sets the on screen size of the input viewer")); // gInputViewer.EnableDragging - UIWidgets::EnhancementCheckbox("Enable Dragging", CVAR_INPUT_VIEWER("EnableDragging"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Enable Dragging", CVAR_INPUT_VIEWER("EnableDragging"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); UIWidgets::PaddedSeparator(true, true); // gInputViewer.ShowBackground - UIWidgets::EnhancementCheckbox("Show Background Layer", CVAR_INPUT_VIEWER("ShowBackground"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show Background Layer", CVAR_INPUT_VIEWER("ShowBackground"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); UIWidgets::PaddedSeparator(true, true); + PushStyleHeader(THEME_COLOR); if (ImGui::CollapsingHeader("Buttons")) { // gInputViewer.ButtonOutlineMode - UIWidgets::PaddedText("Button Outlines/Backgrounds", true, false); - UIWidgets::EnhancementCombobox( - CVAR_INPUT_VIEWER("ButtonOutlineMode"), buttonOutlineOptions, BUTTON_OUTLINE_NOT_PRESSED, - !CVarGetInteger(CVAR_INPUT_VIEWER("UseGlobalButtonOutlineMode"), 1), "", - CVarGetInteger(CVAR_INPUT_VIEWER("ButtonOutlineMode"), BUTTON_OUTLINE_NOT_PRESSED)); - UIWidgets::Tooltip( - "Sets the desired visibility behavior for the button outline/background layers. Useful for " - "custom input viewers."); + CVarCombobox("Button Outlines/Backgrounds", CVAR_INPUT_VIEWER("ButtonOutlineMode"), buttonOutlineOptions, + ComboboxOptions({{ .disabled = !CVarGetInteger(CVAR_INPUT_VIEWER("UseGlobalButtonOutlineMode"), 1), .disabledTooltip = "Disabled because Global Button Outline is off" }}) + .Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED) + .Tooltip("Sets the desired visibility behavior for the button outline/background layers. Useful for " + "custom input viewers.")); // gInputViewer.UseGlobalButtonOutlineMode - UIWidgets::EnhancementCheckbox("Use for all buttons", CVAR_INPUT_VIEWER("UseGlobalButtonOutlineMode"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Use for all buttons", CVAR_INPUT_VIEWER("UseGlobalButtonOutlineMode"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); UIWidgets::PaddedSeparator(); bool useIndividualOutlines = !CVarGetInteger(CVAR_INPUT_VIEWER("UseGlobalButtonOutlineMode"), 1); // gInputViewer.ABtn - UIWidgets::EnhancementCheckbox("Show A-Button Layers", CVAR_INPUT_VIEWER("ABtn"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show A-Button Layers", CVAR_INPUT_VIEWER("ABtn"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("ABtn"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("ABtnOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##ABtnOutline", CVAR_INPUT_VIEWER("ABtnOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.BBtn - UIWidgets::EnhancementCheckbox("Show B-Button Layers", CVAR_INPUT_VIEWER("BBtn"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show B-Button Layers", CVAR_INPUT_VIEWER("BBtn"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("BBtn"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("BBtnOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##BBtnOutline", CVAR_INPUT_VIEWER("BBtnOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.CUp - UIWidgets::EnhancementCheckbox("Show C-Up Layers", CVAR_INPUT_VIEWER("CUp"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show C-Up Layers", CVAR_INPUT_VIEWER("CUp"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("CUp"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("CUpOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##CUpOutline", CVAR_INPUT_VIEWER("CUpOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.CRight - UIWidgets::EnhancementCheckbox("Show C-Right Layers", CVAR_INPUT_VIEWER("CRight"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show C-Right Layers", CVAR_INPUT_VIEWER("CRight"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("CRight"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("CRightOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##CRightOutline", CVAR_INPUT_VIEWER("CRightOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.CDown - UIWidgets::EnhancementCheckbox("Show C-Down Layers", CVAR_INPUT_VIEWER("CDown"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show C-Down Layers", CVAR_INPUT_VIEWER("CDown"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("CDown"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("CDownOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##CDownOutline", CVAR_INPUT_VIEWER("CDownOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.CLeft - UIWidgets::EnhancementCheckbox("Show C-Left Layers", CVAR_INPUT_VIEWER("CLeft"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show C-Left Layers", CVAR_INPUT_VIEWER("CLeft"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("CLeft"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("CLeftOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##CLeftOutline", CVAR_INPUT_VIEWER("CLeftOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.LBtn - UIWidgets::EnhancementCheckbox("Show L-Button Layers", CVAR_INPUT_VIEWER("LBtn"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show L-Button Layers", CVAR_INPUT_VIEWER("LBtn"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("LBtn"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("LBtnOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##LBtnOutline", CVAR_INPUT_VIEWER("LBtnOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.RBtn - UIWidgets::EnhancementCheckbox("Show R-Button Layers", CVAR_INPUT_VIEWER("RBtn"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show R-Button Layers", CVAR_INPUT_VIEWER("RBtn"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("RBtn"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("RBtnOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##RBtnOutline", CVAR_INPUT_VIEWER("RBtnOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.ZBtn - UIWidgets::EnhancementCheckbox("Show Z-Button Layers", CVAR_INPUT_VIEWER("ZBtn"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show Z-Button Layers", CVAR_INPUT_VIEWER("ZBtn"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("ZBtn"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("ZBtnOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##ZBtnOutline", CVAR_INPUT_VIEWER("ZBtnOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.StartBtn - UIWidgets::EnhancementCheckbox("Show Start Button Layers", CVAR_INPUT_VIEWER("StartBtn"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, true); + CVarCheckbox("Show Start Button Layers", CVAR_INPUT_VIEWER("StartBtn"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("StartBtn"), 1)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("StartBtnOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##StartBtnOutline", CVAR_INPUT_VIEWER("StartBtnOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.Dpad - UIWidgets::EnhancementCheckbox("Show D-Pad Layers", CVAR_INPUT_VIEWER("Dpad"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, false); + CVarCheckbox("Show D-Pad Layers", CVAR_INPUT_VIEWER("Dpad"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Dpad"), 0)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("DpadOutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##DpadOutline", CVAR_INPUT_VIEWER("DpadOutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.Mod1 - UIWidgets::EnhancementCheckbox("Show Modifier Button 1 Layers", CVAR_INPUT_VIEWER("Mod1"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, false); + CVarCheckbox("Show Modifier Button 1 Layers", CVAR_INPUT_VIEWER("Mod1"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Mod1"), 0)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("Mod1OutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##Mmod1Outline", CVAR_INPUT_VIEWER("Mod1OutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } // gInputViewer.Mod2 - UIWidgets::EnhancementCheckbox("Show Modifier Button 2 Layers", CVAR_INPUT_VIEWER("Mod2"), false, "", - UIWidgets::CheckboxGraphics::Checkmark, false); + CVarCheckbox("Show Modifier Button 2 Layers", CVAR_INPUT_VIEWER("Mod2"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Mod2"), 0)) { ImGui::Indent(); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("Mod2OutlineMode"), buttonOutlineOptionsVerbose, - BUTTON_OUTLINE_NOT_PRESSED); + CVarCombobox("##Mod2Outline", CVAR_INPUT_VIEWER("Mod2OutlineMode"), buttonOutlineOptionsVerbose, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(BUTTON_OUTLINE_NOT_PRESSED)); ImGui::Unindent(); } @@ -594,90 +569,74 @@ void InputViewerSettingsWindow::DrawElement() { if (ImGui::CollapsingHeader("Analog Stick")) { // gInputViewer.AnalogStick.VisibilityMode - UIWidgets::PaddedText("Analog Stick Visibility", true, false); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("AnalogStick.VisibilityMode"), stickModeOptions, - STICK_MODE_ALWAYS_SHOWN); - UIWidgets::Tooltip( - "Determines the conditions under which the moving layer of the analog stick texture is visible."); + CVarCombobox("Analog Stick Visibility", CVAR_INPUT_VIEWER("AnalogStick.VisibilityMode"), stickModeOptions, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .Tooltip("Determines the conditions under which the moving layer of the analog stick texture is visible.")); // gInputViewer.AnalogStick.OutlineMode - UIWidgets::PaddedText("Analog Stick Outline/Background Visibility", true, false); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("AnalogStick.OutlineMode"), stickModeOptions, - STICK_MODE_ALWAYS_SHOWN); - UIWidgets::Tooltip( - "Determines the conditions under which the analog stick outline/background texture is visible."); + CVarCombobox("Analog Stick Outline/Background Visibility", CVAR_INPUT_VIEWER("AnalogStick.OutlineMode"), stickModeOptions, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .Tooltip("Determines the conditions under which the analog stick outline/background texture is visible.")); // gInputViewer.AnalogStick.Movement - UIWidgets::EnhancementSliderInt("Analog Stick Movement: %dpx", "##AnalogMovement", - CVAR_INPUT_VIEWER("AnalogStick.Movement"), 0, 200, "", 12, true); - UIWidgets::Tooltip( - "Sets the distance to move the analog stick in the input viewer. Useful for custom input viewers."); + CVarSliderInt("Analog Stick Movement: %dpx", CVAR_INPUT_VIEWER("AnalogStick.Movement"), IntSliderOptions().Color(THEME_COLOR).Min(0).Max(200).DefaultValue(12).ShowButtons(true) + .Tooltip("Sets the distance to move the analog stick in the input viewer. Useful for custom input viewers.")); UIWidgets::PaddedSeparator(true, true); } if (ImGui::CollapsingHeader("Additional (\"Right\") Stick")) { // gInputViewer.RightStick.VisibilityMode - UIWidgets::PaddedText("Right Stick Visibility", true, false); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("RightStick.VisibilityMode"), stickModeOptions, - STICK_MODE_HIDDEN_IN_DEADZONE); - UIWidgets::Tooltip( - "Determines the conditions under which the moving layer of the right stick texture is visible."); + CVarCombobox("Right Stick Visibility", CVAR_INPUT_VIEWER("RightStick.VisibilityMode"), stickModeOptions, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .Tooltip("Determines the conditions under which the moving layer of the right stick texture is visible.")); // gInputViewer.RightStick.OutlineMode - UIWidgets::PaddedText("Right Stick Outline/Background Visibility", true, false); - UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("RightStick.OutlineMode"), stickModeOptions, - STICK_MODE_HIDDEN_IN_DEADZONE); - UIWidgets::Tooltip( - "Determines the conditions under which the right stick outline/background texture is visible."); + CVarCombobox("Right Stick Outline/Background Visibility", CVAR_INPUT_VIEWER("RightStick.OutlineMode"), stickModeOptions, + ComboboxOptions().Color(THEME_COLOR).DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .Tooltip("Determines the conditions under which the right stick outline/background texture is visible.")); // gInputViewer.RightStick.Movement - UIWidgets::EnhancementSliderInt("Right Stick Movement: %dpx", "##RightMovement", - CVAR_INPUT_VIEWER("RightStick.Movement"), 0, 200, "", 7, true); - UIWidgets::Tooltip( - "Sets the distance to move the right stick in the input viewer. Useful for custom input viewers."); + CVarSliderInt("Right Stick Movement: %dpx", CVAR_INPUT_VIEWER("RightStick.Movement"), IntSliderOptions().Color(THEME_COLOR).Min(0).Max(200).DefaultValue(7).ShowButtons(true) + .Tooltip("Sets the distance to move the right stick in the input viewer. Useful for custom input viewers.")); UIWidgets::PaddedSeparator(true, true); } if (ImGui::CollapsingHeader("Analog Angle Values")) { // gAnalogAngles - UIWidgets::EnhancementCheckbox("Show Analog Stick Angle Values", CVAR_INPUT_VIEWER("AnalogAngles.Enabled")); - UIWidgets::Tooltip("Displays analog stick angle values in the input viewer"); + CVarCheckbox("Show Analog Stick Angle Values", CVAR_INPUT_VIEWER("AnalogAngles.Enabled"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Displays analog stick angle values in the input viewer")); if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Enabled"), 0)) { // gInputViewer.AnalogAngles.TextColor - if (ImGui::ColorEdit4("Text Color", (float*)&textColor)) { - CVarSetColor(CVAR_INPUT_VIEWER("AnalogAngles.TextColor"), vec2Color(textColor)); - } + CVarColorPicker("Text Color", CVAR_INPUT_VIEWER("AnalogAngles.TextColor"), textColorDefault, + true, ColorPickerRandomButton | ColorPickerResetButton); // gAnalogAngleScale - UIWidgets::EnhancementSliderFloat("Angle Text Scale: %.2f%%", "##AnalogAngleScale", - CVAR_INPUT_VIEWER("AnalogAngles.Scale"), 0.1f, 5.0f, "", 1.0f, true, true); + CVarSliderFloat("Angle Text Scale: %.2f%%", CVAR_INPUT_VIEWER("AnalogAngles.Scale"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.1f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); // gInputViewer.AnalogAngles.Offset - UIWidgets::EnhancementSliderInt("Angle Text Offset: %dpx", "##AnalogAngleOffset", - CVAR_INPUT_VIEWER("AnalogAngles.Offset"), 0, 400, "", 0, true); + CVarSliderInt("Angle Text Offset: %dpx", CVAR_INPUT_VIEWER("AnalogAngles.Offset"), IntSliderOptions().Color(THEME_COLOR).Min(0).Max(400).DefaultValue(0).ShowButtons(true) + .Tooltip("Sets the distance to move the right stick in the input viewer. Useful for custom input viewers.")); UIWidgets::PaddedSeparator(true, true); // gInputViewer.AnalogAngles.Range1.Enabled - UIWidgets::EnhancementCheckbox("Highlight ESS Position", CVAR_INPUT_VIEWER("AnalogAngles.Range1.Enabled")); - UIWidgets::Tooltip( - "Highlights the angle value text when the analog stick is in ESS position (on flat ground)"); + CVarCheckbox("Highlight ESS Position", CVAR_INPUT_VIEWER("AnalogAngles.Range1.Enabled"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Highlights the angle value text when the analog stick is in ESS position (on flat ground)")); if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Enabled"), 0)) { // gInputViewer.AnalogAngles.Range1.Color - if (ImGui::ColorEdit4("ESS Color", (float*)&range1Color)) { - CVarSetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Color"), vec2Color(range1Color)); - } + CVarColorPicker("ESS Color", CVAR_INPUT_VIEWER("AnalogAngles.Range1.Color"), range1ColorDefault, + true, ColorPickerRandomButton | ColorPickerResetButton); } UIWidgets::PaddedSeparator(true, true); // gInputViewer.AnalogAngles.Range2.Enabled - UIWidgets::EnhancementCheckbox("Highlight Walking Speed Angles", - CVAR_INPUT_VIEWER("AnalogAngles.Range2.Enabled")); - UIWidgets::Tooltip("Highlights the angle value text when the analog stick is at an angle that would " + CVarCheckbox("Highlight Walking Speed Angles", CVAR_INPUT_VIEWER("AnalogAngles.Range2.Enabled"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Highlights the angle value text when the analog stick is at an angle that would " "produce a walking speed (on flat ground)\n\n" - "Useful for 1.0 Empty Jumpslash Quick Put Away"); + "Useful for 1.0 Empty Jumpslash Quick Put Away")); if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Enabled"), 0)) { // gInputViewer.AnalogAngles.Range2.Color - if (ImGui::ColorEdit4("Walking Speed Color", (float*)&range2Color)) { - CVarSetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Color"), vec2Color(range2Color)); - } + CVarColorPicker("Walking Speed Color", CVAR_INPUT_VIEWER("AnalogAngles.Range2.Color"), range2ColorDefault, + true, ColorPickerRandomButton | ColorPickerResetButton); } } } + PopStyleHeader(); } diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 7e4918799..7dbc74a75 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -2,6 +2,7 @@ #include #include "soh/OTRGlobals.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "z64.h" #include "soh/cvar_prefixes.h" #ifndef __WIIU__ @@ -10,6 +11,8 @@ #define SCALE_IMGUI_SIZE(value) ((value / 13.0f) * ImGui::GetFontSize()) +using namespace UIWidgets; + SohInputEditorWindow::~SohInputEditorWindow() { } @@ -1062,17 +1065,16 @@ void SohInputEditorWindow::DrawLEDSection(uint8_t port) { } // todo: clean this up, probably just hardcode to LED_COLOR_SOURCE_GAME and use SoH options only here if (mapping->GetColorSource() == LED_COLOR_SOURCE_GAME) { - static const char* ledSources[] = { + static std::vector ledSources = { "Original Tunic Colors", "Cosmetics Tunic Colors", "Health Colors", "Original Navi Targeting Colors", "Cosmetics Navi Targeting Colors", "Custom" }; - UIWidgets::PaddedText("Source"); - UIWidgets::EnhancementCombobox(CVAR_SETTING("LEDColorSource"), ledSources, LED_SOURCE_TUNIC_ORIGINAL); - UIWidgets::Tooltip("Health\n- Red when health critical (13-20% depending on max health)\n- Yellow when " + CVarCombobox("Source", CVAR_SETTING("LEDColorSource"), ledSources, UIWidgets::ComboboxOptions().Color(THEME_COLOR).DefaultIndex(LED_SOURCE_TUNIC_ORIGINAL) + .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"); + "Custom: single, solid color")); if (CVarGetInteger(CVAR_SETTING("LEDColorSource"), 1) == LED_SOURCE_CUSTOM) { UIWidgets::Spacer(3); auto port1Color = CVarGetColor24(CVAR_SETTING("LEDPort1Color"), { 255, 255, 255 }); @@ -1090,14 +1092,13 @@ void SohInputEditorWindow::DrawLEDSection(uint8_t port) { ImGui::SameLine(); ImGui::Text("Custom Color"); } - UIWidgets::PaddedEnhancementSliderFloat("Brightness: %.1f %%", "##LED_Brightness", CVAR_SETTING("LEDBrightness"), 0.0f, - 1.0f, "", 1.0f, true, true); - UIWidgets::Tooltip("Sets the brightness of controller LEDs. 0% brightness = LEDs off."); - UIWidgets::PaddedEnhancementCheckbox( - "Critical Health Override", CVAR_SETTING("LEDCriticalOverride"), true, true, - CVarGetInteger(CVAR_SETTING("LEDColorSource"), LED_SOURCE_TUNIC_ORIGINAL) == LED_SOURCE_HEALTH, - "Override redundant for health source.", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip("Shows red color when health is critical, otherwise displays according to color source."); + CVarSliderFloat("Brightness: %.1f %%", CVAR_SETTING("LEDBrightness"), + FloatSliderOptions().IsPercentage().Min(0.0f).Max(1.0f).DefaultValue(1.0f).ShowButtons(true) + .Tooltip("Sets the brightness of controller LEDs. 0% brightness = LEDs off.")); + CVarCheckbox("Critical Health Override", CVAR_SETTING("LEDCriticalOverride"), + CheckboxOptions({{ .disabled = CVarGetInteger(CVAR_SETTING("LEDColorSource"), LED_SOURCE_TUNIC_ORIGINAL) == LED_SOURCE_HEALTH, + .disabledTooltip = "Override redundant for health source."}}).DefaultValue(true) + .Tooltip("Shows red color when health is critical, otherwise displays according to color source.")); } ImGui::TreePop(); } @@ -1281,7 +1282,6 @@ void SohInputEditorWindow::DrawMapping(CustomButtonMap& mapping, float labelWidt preview = "Unknown"; } - UIWidgets::Spacer(0); ImVec2 cursorPos = ImGui::GetCursorPos(); ImVec2 textSize = ImGui::CalcTextSize(mapping.label); ImGui::SetCursorPosY(cursorPos.y + textSize.y / 4); @@ -1303,16 +1303,16 @@ void SohInputEditorWindow::DrawMapping(CustomButtonMap& mapping, float labelWidt } ImGui::EndCombo(); } - UIWidgets::Spacer(0); } void SohInputEditorWindow::DrawOcarinaControlPanel() { ImVec2 cursor = ImGui::GetCursorPos(); ImGui::SetCursorPos(ImVec2(cursor.x, cursor.y + 5)); - UIWidgets::EnhancementCheckbox("Dpad Ocarina Playback", CVAR_SETTING("CustomOcarina.Dpad")); - UIWidgets::EnhancementCheckbox("Right Stick Ocarina Playback", CVAR_SETTING("CustomOcarina.RightStick")); - UIWidgets::EnhancementCheckbox("Customize Ocarina Controls", CVAR_SETTING("CustomOcarina.Enabled")); + CheckboxOptions checkOpt = CheckboxOptions().Color(THEME_COLOR); + CVarCheckbox("Dpad Ocarina Playback", CVAR_SETTING("CustomOcarina.Dpad"), checkOpt); + CVarCheckbox("Right Stick Ocarina Playback", CVAR_SETTING("CustomOcarina.RightStick"), checkOpt); + CVarCheckbox("Customize Ocarina Controls", CVAR_SETTING("CustomOcarina.Enabled"), checkOpt); if (!CVarGetInteger(CVAR_SETTING("CustomOcarina.Enabled"), 0)) { ImGui::BeginDisabled(); @@ -1344,60 +1344,56 @@ void SohInputEditorWindow::DrawCameraControlPanel() { ImVec2 cursor = ImGui::GetCursorPos(); ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); Ship::GuiWindow::BeginGroupPanel("Aiming/First-Person Camera", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("Right Stick Aiming", CVAR_SETTING("Controls.RightStickAim")); - UIWidgets::Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming"); + CVarCheckbox("Right Stick Aiming", CVAR_SETTING("Controls.RightStickAim"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming")); if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { - UIWidgets::PaddedEnhancementCheckbox("Allow moving while in first person mode", CVAR_SETTING("MoveInFirstPerson")); - UIWidgets::Tooltip("Changes the left stick to move the player while in first person mode"); + CVarCheckbox("Allow moving while in first person mode", CVAR_SETTING("MoveInFirstPerson"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Changes the left stick to move the player while in first person mode")); } - UIWidgets::PaddedEnhancementCheckbox("Invert Aiming X Axis", CVAR_SETTING("Controls.InvertAimingXAxis")); - UIWidgets::Tooltip("Inverts the Camera X Axis in:\n-First-Person/C-Up view\n-Weapon Aiming"); - UIWidgets::PaddedEnhancementCheckbox("Invert Aiming Y Axis", CVAR_SETTING("Controls.InvertAimingYAxis"), 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 X Axis", CVAR_SETTING("Controls.InvertShieldAimingXAxis"), true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip("Inverts the Shield Aiming X Axis"); - UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming Y Axis", CVAR_SETTING("Controls.InvertShieldAimingYAxis")); - UIWidgets::Tooltip("Inverts the Shield Aiming Y Axis"); - UIWidgets::PaddedEnhancementCheckbox("Invert Z-Weapon Aiming Y Axis", CVAR_SETTING("Controls.InvertZAimingYAxis"), 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", CVAR_SETTING("DisableFirstPersonAutoCenterView")); - UIWidgets::Tooltip("Prevents the C-Up view from auto-centering, allowing for Gyro Aiming"); - if (UIWidgets::PaddedEnhancementCheckbox("Enable Custom Aiming/First-Person sensitivity", CVAR_SETTING("FirstPersonCameraSensitivity.Enabled"), true, false)) { + CVarCheckbox("Invert Aiming X Axis", CVAR_SETTING("Controls.InvertAimingXAxis"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Inverts the Camera X Axis in:\n-First-Person/C-Up view\n-Weapon Aiming")); + CVarCheckbox("Invert Aiming Y Axis", CVAR_SETTING("Controls.InvertAimingYAxis"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true) + .Tooltip("Inverts the Camera Y Axis in:\n-First-Person/C-Up view\n-Weapon Aiming")); + CVarCheckbox("Invert Shield Aiming X Axis", CVAR_SETTING("Controls.InvertShieldAimingXAxis"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true) + .Tooltip("Inverts the Shield Aiming X Axis")); + CVarCheckbox("Invert Shield Aiming Y Axis", CVAR_SETTING("Controls.InvertShieldAimingYAxis"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Inverts the Shield Aiming Y Axis")); + CVarCheckbox("Invert Z-Weapon Aiming Y Axis", CVAR_SETTING("Controls.InvertZAimingYAxis"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true) + .Tooltip("Inverts the Camera Y Axis in:\n-Z-Weapon Aiming")); + CVarCheckbox("Disable Auto-Centering in First-Person View", CVAR_SETTING("DisableFirstPersonAutoCenterView"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Prevents the C-Up view from auto-centering, allowing for Gyro Aiming")); + if (CVarCheckbox("Enable Custom Aiming/First-Person sensitivity", CVAR_SETTING("FirstPersonCameraSensitivity.Enabled"), CheckboxOptions().Color(THEME_COLOR))) { if (!CVarGetInteger(CVAR_SETTING("FirstPersonCameraSensitivity.Enabled"), 0)) { CVarClear(CVAR_SETTING("FirstPersonCameraSensitivity.X")); CVarClear(CVAR_SETTING("FirstPersonCameraSensitivity.Y")); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } } if (CVarGetInteger(CVAR_SETTING("FirstPersonCameraSensitivity.Enabled"), 0)) { - UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %.0f %%", "##FirstPersonSensitivity Horizontal", - CVAR_SETTING("FirstPersonCameraSensitivity.X"), 0.01f, 5.0f, "", 1.0f, true); - UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %.0f %%", "##FirstPersonSensitivity Vertical", - CVAR_SETTING("FirstPersonCameraSensitivity.Y"), 0.01f, 5.0f, "", 1.0f, true); + CVarSliderFloat("Aiming/First-Person Horizontal Sensitivity: %.0f %%", CVAR_SETTING("FirstPersonCameraSensitivity.X"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.01f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); + CVarSliderFloat("Aiming/First-Person Vertical Sensitivity: %.0f %%", CVAR_SETTING("FirstPersonCameraSensitivity.Y"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.01f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); } - UIWidgets::Spacer(0); Ship::GuiWindow::EndGroupPanel(0); - UIWidgets::Spacer(0); cursor = ImGui::GetCursorPos(); ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); Ship::GuiWindow::BeginGroupPanel("Third-Person Camera", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("Free Look", CVAR_SETTING("FreeLook.Enabled")); - UIWidgets::Tooltip("Enables free look camera control\nNote: You must remap C buttons off of the right stick in the " - "controller config menu, and map the camera stick to the right stick."); - UIWidgets::PaddedEnhancementCheckbox("Invert Camera X Axis", CVAR_SETTING("FreeLook.InvertXAxis")); - UIWidgets::Tooltip("Inverts the Camera X Axis in:\n-Free look"); - UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", CVAR_SETTING("FreeLook.InvertYAxis"), true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip("Inverts the Camera Y Axis in:\n-Free look"); - UIWidgets::Spacer(0); - UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %.0f %%", "##ThirdPersonSensitivity Horizontal", - CVAR_SETTING("FreeLook.CameraSensitivity.X"), 0.01f, 5.0f, "", 1.0f, true, true, false, true); - UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %.0f %%", "##ThirdPersonSensitivity Vertical", - CVAR_SETTING("FreeLook.CameraSensitivity.Y"), 0.01f, 5.0f, "", 1.0f, true, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist", - CVAR_SETTING("FreeLook.MaxCameraDistance"), 100, 900, "", 185, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed", - CVAR_SETTING("FreeLook.TransitionSpeed"), 0, 900, "", 25, true, false, true); + CVarCheckbox("Free Look", CVAR_SETTING("FreeLook.Enabled"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Enables free look camera control\nNote: You must remap C buttons off of the right stick in the " + "controller config menu, and map the camera stick to the right stick.")); + CVarCheckbox("Invert Camera X Axis", CVAR_SETTING("FreeLook.InvertXAxis"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Inverts the Camera X Axis in:\n-Free look")); + CVarCheckbox("Invert Camera Y Axis", CVAR_SETTING("FreeLook.InvertYAxis"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true) + .Tooltip("Inverts the Camera Y Axis in:\n-Free look")); + CVarSliderFloat("Third-Person Horizontal Sensitivity: %.0f %%", CVAR_SETTING("FreeLook.CameraSensitivity.X"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.01f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); + CVarSliderFloat("Third-Person Vertical Sensitivity: %.0f %%", CVAR_SETTING("FreeLook.CameraSensitivity.Y"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.01f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); + CVarSliderInt("Camera Distance: %d", CVAR_SETTING("FreeLook.MaxCameraDistance"), IntSliderOptions().Color(THEME_COLOR).Min(100).Max(900).DefaultValue(185).ShowButtons(true)); + CVarSliderInt("Camera Transition Speed: %d", CVAR_SETTING("FreeLook.TransitionSpeed"), IntSliderOptions().Color(THEME_COLOR).Min(0).Max(900).DefaultValue(25).ShowButtons(true)); Ship::GuiWindow::EndGroupPanel(0); } @@ -1405,17 +1401,17 @@ void SohInputEditorWindow::DrawDpadControlPanel() { ImVec2 cursor = ImGui::GetCursorPos(); ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); Ship::GuiWindow::BeginGroupPanel("D-Pad Options", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("D-pad Support on Pause Screen", CVAR_SETTING("DPadOnPause")); - 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"); - UIWidgets::PaddedEnhancementCheckbox("D-pad Support in Text Boxes", CVAR_SETTING("DpadInText")); - UIWidgets::Tooltip("Navigate choices in text boxes, shop item selection, and the file select / name entry screens with the D-pad"); + CVarCheckbox("D-pad Support on Pause Screen", CVAR_SETTING("DPadOnPause"), CheckboxOptions().Color(THEME_COLOR) + .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")); + CVarCheckbox("D-pad Support in Text Boxes", CVAR_SETTING("DpadInText"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Navigate choices in text boxes, shop item selection, and the file select / name entry screens with the D-pad")); if (!CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CVarGetInteger(CVAR_SETTING("DpadInText"), 0)) { ImGui::BeginDisabled(); } - UIWidgets::PaddedEnhancementCheckbox("D-pad hold change", CVAR_SETTING("DpadHoldChange"), true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip("The cursor will only move a single space no matter how long a D-pad direction is held"); + CVarCheckbox("D-pad hold change", CVAR_SETTING("DpadHoldChange"), CheckboxOptions().Color(THEME_COLOR).DefaultValue(true) + .Tooltip("The cursor will only move a single space no matter how long a D-pad direction is held")); if (!CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CVarGetInteger(CVAR_SETTING("DpadInText"), 0)) { ImGui::EndDisabled(); @@ -1505,10 +1501,10 @@ void SohInputEditorWindow::DrawLinkTab() { } if (ImGui::CollapsingHeader("D-Pad", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_UP).c_str(), portIndex, BTN_DUP); - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_DOWN).c_str(), portIndex, BTN_DDOWN); - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_LEFT).c_str(), portIndex, BTN_DLEFT); - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_RIGHT).c_str(), portIndex, BTN_DRIGHT); + DrawButtonLine(StringHelper::Sprintf("D %s", ICON_FA_ARROW_UP).c_str(), portIndex, BTN_DUP); + DrawButtonLine(StringHelper::Sprintf("D %s", ICON_FA_ARROW_DOWN).c_str(), portIndex, BTN_DDOWN); + DrawButtonLine(StringHelper::Sprintf("D %s", ICON_FA_ARROW_LEFT).c_str(), portIndex, BTN_DLEFT); + DrawButtonLine(StringHelper::Sprintf("D %s", ICON_FA_ARROW_RIGHT).c_str(), portIndex, BTN_DRIGHT); } if (ImGui::CollapsingHeader("Analog Stick", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { @@ -1536,30 +1532,24 @@ void SohInputEditorWindow::DrawLinkTab() { DrawButtonLine("M2", portIndex, BTN_CUSTOM_MODIFIER2); ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); - UIWidgets::PaddedEnhancementCheckbox("Enable speed modifiers", CVAR_SETTING("WalkModifier.Enabled"), true, false); - UIWidgets::Tooltip("Hold the assigned button to change the maximum walking or swimming speed"); + CVarCheckbox("Enable speed modifiers", CVAR_SETTING("WalkModifier.Enabled"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Hold the assigned button to change the maximum walking or swimming speed")); if (CVarGetInteger(CVAR_SETTING("WalkModifier.Enabled"), 0)) { UIWidgets::Spacer(5); Ship::GuiWindow::BeginGroupPanel("Speed Modifier", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", - CVAR_SETTING("WalkModifier.SpeedToggle"), true, false); + CVarCheckbox("Toggle modifier instead of holding", CVAR_SETTING("WalkModifier.SpeedToggle"), CheckboxOptions().Color(THEME_COLOR)); Ship::GuiWindow::BeginGroupPanel("Walk Modifier", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementCheckbox("Don't affect jump distance/velocity", - CVAR_SETTING("WalkModifier.DoesntChangeJump"), true, false); - UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 1: %.0f %%", "##WalkMod1", - CVAR_SETTING("WalkModifier.Mapping1"), 0.0f, 5.0f, "", 1.0f, - true, true, false, true); - UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 2: %.0f %%", "##WalkMod2", - CVAR_SETTING("WalkModifier.Mapping2"), 0.0f, 5.0f, "", 1.0f, - true, true, false, true); + CVarCheckbox("Don't affect jump distance/velocity", CVAR_SETTING("WalkModifier.DoesntChangeJump"), CheckboxOptions().Color(THEME_COLOR)); + CVarSliderFloat("Walk Modifier 1: %.0f %%", CVAR_SETTING("WalkModifier.Mapping1"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.0f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); + CVarSliderFloat("Walk Modifier 2: %.0f %%", CVAR_SETTING("WalkModifier.Mapping2"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.0f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); Ship::GuiWindow::EndGroupPanel(0); Ship::GuiWindow::BeginGroupPanel("Swim Modifier", ImGui::GetContentRegionAvail()); - UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 1: %.0f %%", "##SwimMod1", - CVAR_SETTING("WalkModifier.SwimMapping1"), 0.0f, 5.0f, "", 1.0f, - true, true, false, true); - UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 2: %.0f %%", "##SwimMod2", - CVAR_SETTING("WalkModifier.SwimMapping2"), 0.0f, 5.0f, "", 1.0f, - true, true, false, true); + CVarSliderFloat("Swim Modifier 1: %.0f %%", CVAR_SETTING("WalkModifier.SwimMapping1"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.0f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); + CVarSliderFloat("Swim Modifier 2: %.0f %%", CVAR_SETTING("WalkModifier.SwimMapping2"), + FloatSliderOptions().Color(THEME_COLOR).IsPercentage().Min(0.0f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true)); Ship::GuiWindow::EndGroupPanel(0); Ship::GuiWindow::EndGroupPanel(0); } @@ -1659,9 +1649,7 @@ void SohInputEditorWindow::DrawDebugPortTab(uint8_t portIndex, std::string custo UpdateBitmaskToMappingIds(portIndex); UpdateStickDirectionToMappingIds(portIndex); - 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)); + PushStyleHeader(THEME_COLOR); if (ImGui::CollapsingHeader("Buttons", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { DrawButtonLine("A", portIndex, BTN_A, CHIP_COLOR_N64_BLUE); @@ -1690,19 +1678,20 @@ void SohInputEditorWindow::DrawDebugPortTab(uint8_t portIndex, std::string custo DrawStickSection(portIndex, Ship::LEFT, 0); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + PopStyleHeader(); ImGui::EndTabItem(); } } void SohInputEditorWindow::DrawClearAllButton(uint8_t portIndex) { + PushStyleButton(THEME_COLOR); if (ImGui::Button("Clear All", ImGui::CalcTextSize("Clear All") * 2)) { ImGui::OpenPopup("Clear All##clearAllPopup"); } + PopStyleButton(); if (ImGui::BeginPopupModal("Clear All##clearAllPopup", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("This will clear all mappings for port %d.\n\nContinue?", portIndex + 1); + PushStyleButton(THEME_COLOR); if (ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup(); } @@ -1710,6 +1699,7 @@ void SohInputEditorWindow::DrawClearAllButton(uint8_t portIndex) { Ship::Context::GetInstance()->GetControlDeck()->GetControllerByPort(portIndex)->ClearAllMappings(); ImGui::CloseCurrentPopup(); } + PopStyleButton(); ImGui::EndPopup(); } } @@ -1717,22 +1707,23 @@ void SohInputEditorWindow::DrawClearAllButton(uint8_t portIndex) { void SohInputEditorWindow::DrawSetDefaultsButton(uint8_t portIndex) { ImGui::SameLine(); auto popupId = StringHelper::Sprintf("setDefaultsPopup##%d", portIndex); + PushStyleButton(THEME_COLOR); if (ImGui::Button(StringHelper::Sprintf("Set Defaults##%d", portIndex).c_str(), ImVec2(ImGui::CalcTextSize("Set Defaults") * 2))) { ImGui::OpenPopup(popupId.c_str()); } + PopStyleButton(); if (ImGui::BeginPopup(popupId.c_str())) { bool shouldClose = false; - ImGui::PushStyleColor(ImGuiCol_Button, BUTTON_COLOR_KEYBOARD_BEIGE); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, BUTTON_COLOR_KEYBOARD_BEIGE_HOVERED); + PushStyleButton(BUTTON_COLOR_KEYBOARD_BEIGE); if (ImGui::Button(StringHelper::Sprintf("%s Keyboard", ICON_FA_KEYBOARD_O).c_str())) { ImGui::OpenPopup("Set Defaults for Keyboard"); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + PopStyleButton(); if (ImGui::BeginPopupModal("Set Defaults for Keyboard", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("This will clear all existing mappings for\nKeyboard on port %d.\n\nContinue?", portIndex + 1); + PushStyleButton(THEME_COLOR); if (ImGui::Button("Cancel")) { shouldClose = true; ImGui::CloseCurrentPopup(); @@ -1747,21 +1738,21 @@ void SohInputEditorWindow::DrawSetDefaultsButton(uint8_t portIndex) { shouldClose = true; ImGui::CloseCurrentPopup(); } + PopStyleButton(); ImGui::EndPopup(); } auto buttonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); auto buttonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); GetButtonColorsForDeviceType(Ship::PhysicalDeviceType::SDLGamepad, buttonColor, buttonHoveredColor); - ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + PushStyleButton(buttonColor); if (ImGui::Button(StringHelper::Sprintf("%s %s", ICON_FA_GAMEPAD, "Gamepad (SDL)").c_str())) { ImGui::OpenPopup("Set Defaults for Gamepad (SDL)"); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + PopStyleButton(); if (ImGui::BeginPopupModal("Set Defaults for Gamepad (SDL)", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("This will clear all existing mappings for\nGamepad (SDL) on port %d.\n\nContinue?", portIndex + 1); + PushStyleButton(THEME_COLOR); if (ImGui::Button("Cancel")) { shouldClose = true; ImGui::CloseCurrentPopup(); @@ -1776,18 +1767,26 @@ void SohInputEditorWindow::DrawSetDefaultsButton(uint8_t portIndex) { shouldClose = true; ImGui::CloseCurrentPopup(); } + PopStyleButton(); ImGui::EndPopup(); } + PushStyleButton(THEME_COLOR); if (ImGui::Button("Cancel") || shouldClose) { ImGui::CloseCurrentPopup(); } + PopStyleButton(); ImGui::EndPopup(); } } void SohInputEditorWindow::DrawElement() { + ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); + ImVec4 themeColor = ColorValues.at(THEME_COLOR); + ImGui::PushStyleColor(ImGuiCol_Tab, ImVec4(themeColor.x, themeColor.y, themeColor.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_TabHovered, ImVec4(themeColor.x, themeColor.y, themeColor.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_TabActive, ImVec4(themeColor.x, themeColor.y, themeColor.z, 0.6f)); ImGui::BeginTabBar("##ControllerConfigPortTabs"); DrawLinkTab(); DrawIvanTab(); @@ -1796,4 +1795,6 @@ void SohInputEditorWindow::DrawElement() { DrawDebugPortTab(3); } ImGui::EndTabBar(); + ImGui::PopStyleColor(3); + ImGui::PopFont(); } diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index 16fb2bed4..ddd36fa56 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -11,6 +11,7 @@ #include #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" @@ -92,6 +93,7 @@ std::map groupLabels = { typedef struct { const char* cvar; + const char* valuesCvar; const char* rainbowCvar; const char* lockedCvar; const char* changedCvar; @@ -111,7 +113,8 @@ Color_RGBA8 ColorRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { #define COSMETIC_OPTION(id, label, group, defaultColor, supportsAlpha, supportsRainbow, advancedOption) \ { id, { \ - CVAR_COSMETIC(id ".Value"), CVAR_COSMETIC(id ".Rainbow"), CVAR_COSMETIC(id ".Locked"), CVAR_COSMETIC(id ".Changed"), label, group, \ + CVAR_COSMETIC(id), CVAR_COSMETIC(id ".Value"), CVAR_COSMETIC(id ".Rainbow"), CVAR_COSMETIC(id ".Locked"), \ + CVAR_COSMETIC(id ".Changed"), label, group, \ ImVec4(defaultColor.r / 255.0f, defaultColor.g / 255.0f, defaultColor.b / 255.0f, defaultColor.a / 255.0f), defaultColor, \ supportsAlpha, supportsRainbow, advancedOption \ } } @@ -280,13 +283,13 @@ static std::map cosmeticOptions = { COSMETIC_OPTION("Key.GanonsBossGem", "Ganons Boss Key Gem", COSMETICS_GROUP_BOSS_KEYS, ColorRGBA8(255, 0, 0, 255), false, true, false), COSMETIC_OPTION("Key.WellSmallBody", "Well Small Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), - COSMETIC_OPTION("Key.WellSmallEmblem", "Well Small Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(227, 110, 255, 255), false, true, true), + COSMETIC_OPTION("Key.WellSmallEmblem", "Well Small Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(227, 110, 255, 255), false, true, false), COSMETIC_OPTION("Key.FortSmallBody", "Fortress Small Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), - COSMETIC_OPTION("Key.FortSmallEmblem", "Fortress Small Key Emblem",COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, true), + COSMETIC_OPTION("Key.FortSmallEmblem", "Fortress Small Key Emblem",COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), COSMETIC_OPTION("Key.GTGSmallBody", "GTG Small Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), - COSMETIC_OPTION("Key.GTGSmallEmblem", "GTG Small Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(221, 212, 60, 255), false, true, true), + COSMETIC_OPTION("Key.GTGSmallEmblem", "GTG Small Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(221, 212, 60, 255), false, true, false), //COSMETIC_OPTION("Key.ChestGameSmallBody", "Chest Game Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), - //COSMETIC_OPTION("Key.ChestGameEmblem", "Chest Game Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 0, 0, 255), false, true, true), + //COSMETIC_OPTION("Key.ChestGameEmblem", "Chest Game Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 0, 0, 255), false, true, false), COSMETIC_OPTION("Key.Skeleton", "Skeleton Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 170, 255), false, true, false), COSMETIC_OPTION("HUD.AButton", "A Button", COSMETICS_GROUP_HUD, ColorRGBA8( 90, 90, 255, 255), false, true, false), @@ -313,7 +316,7 @@ static std::map cosmeticOptions = { #define MESSAGE_COSMETIC_OPTION(id, label, r, g, b) COSMETIC_OPTION("Message." id, label, COSMETICS_GROUP_MESSAGE, ColorRGBA8(r, g, b, 255), false, true, true) MESSAGE_COSMETIC_OPTION("Default.Normal", "Message Default Color", 255, 255, 255), - MESSAGE_COSMETIC_OPTION("Default.NoneNoShadow", "Message Default (None No Shadow) Color", 0, 0, 0), + MESSAGE_COSMETIC_OPTION("Default.NoneNoShadow", "Message Default (None No Shadow)", 0, 0, 0), MESSAGE_COSMETIC_OPTION("Red.Normal", "Message Red Color", 255, 60, 60), MESSAGE_COSMETIC_OPTION("Red.Wooden", "Message Red (Wooden) Color", 255, 120, 0), MESSAGE_COSMETIC_OPTION("Adjustable.Normal", "Message Adjustable Color", 70, 255, 80), @@ -322,7 +325,7 @@ static std::map cosmeticOptions = { MESSAGE_COSMETIC_OPTION("Blue.Wooden", "Message Blue (Wooden) Color", 80, 110, 255), MESSAGE_COSMETIC_OPTION("LightBlue.Normal", "Message Light Blue Color", 100, 180, 255), MESSAGE_COSMETIC_OPTION("LightBlue.Wooden", "Message Light Blue (Wooden) Color", 90, 180, 255), - MESSAGE_COSMETIC_OPTION("LightBlue.LightBlue.NoneNoShadow", "Message Light Blue (None No Shadow) Color", 80, 150, 180), + MESSAGE_COSMETIC_OPTION("LightBlue.LightBlue.NoneNoShadow", "Message Light Blue (None No Shadow)", 80, 150, 180), MESSAGE_COSMETIC_OPTION("Purple.Normal", "Message Purple Color", 255, 150, 180), MESSAGE_COSMETIC_OPTION("Purple.Wooden", "Message Purple (Wooden) Color", 210, 100, 255), MESSAGE_COSMETIC_OPTION("Yellow.Normal", "Message Yellow Color", 255, 255, 50), @@ -469,41 +472,38 @@ static const char* MarginCvarNonAnchor[] { CVAR_COSMETIC("HUD.TitleCard.Boss") }; -ImVec4 GetRandomValue() { -#if !defined(__SWITCH__) && !defined(__WIIU__) - std::random_device rd; - std::mt19937 rng(rd()); -#else - size_t seed = std::hash{}(std::to_string(rand())); - std::mt19937_64 rng(seed); -#endif - std::uniform_int_distribution dist(0, 255 - 1); - - ImVec4 NewColor; - NewColor.x = (float)(dist(rng)) / 255.0f; - NewColor.y = (float)(dist(rng)) / 255.0f; - NewColor.z = (float)(dist(rng)) / 255.0f; - return NewColor; -} - -void SetMarginAll(const char* ButtonName, bool SetActivated) { - if (ImGui::Button(ButtonName)) { - //MarginCvarNonAnchor is an array that list every element that has No anchor by default, because if that the case this function will not touch it with pose type 0. +void SetMarginAll(const char* ButtonName, bool SetActivated, const char* tooltip) { + if (UIWidgets::Button(ButtonName, UIWidgets::ButtonOptions() + .Size(ImVec2(200.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip(tooltip))) { + // MarginCvarNonAnchor is an array that list every element that has No anchor by default, because if that the + // case this function will not touch it with pose type 0. u8 arrayLengthNonMargin = sizeof(MarginCvarNonAnchor) / sizeof(*MarginCvarNonAnchor); for (auto cvarName : MarginCvarList) { std::string cvarPosType = std::string(cvarName).append(".PosType"); std::string cvarNameMargins = std::string(cvarName).append(".UseMargins"); - if (CVarGetInteger(cvarPosType.c_str(),0) <= ANCHOR_RIGHT && SetActivated) { //Our element is not Hidden or Non anchor + if (CVarGetInteger(cvarPosType.c_str(), 0) <= ANCHOR_RIGHT && + SetActivated) { // Our element is not Hidden or Non anchor for (int i = 0; i < arrayLengthNonMargin; i++) { - if ((strcmp(cvarName, MarginCvarNonAnchor[i]) == 0) && (CVarGetInteger(cvarPosType.c_str(), 0) == ORIGINAL_LOCATION)) { //Our element is both in original position and do not have anchor by default so we skip it. - CVarSetInteger(cvarNameMargins.c_str(), false); //force set off - } else if ((strcmp(cvarName, MarginCvarNonAnchor[i]) == 0) && (CVarGetInteger(cvarPosType.c_str(), 0) != ORIGINAL_LOCATION)) { //Our element is not in original position regarless it has no anchor by default since player made it anchored we can toggle margins + if ((strcmp(cvarName, MarginCvarNonAnchor[i]) == 0) && + (CVarGetInteger(cvarPosType.c_str(), 0) == + ORIGINAL_LOCATION)) { // Our element is both in original position and do not have anchor by + // default so we skip it. + CVarSetInteger(cvarNameMargins.c_str(), false); // force set off + } else if ((strcmp(cvarName, MarginCvarNonAnchor[i]) == 0) && + (CVarGetInteger(cvarPosType.c_str(), 0) != + ORIGINAL_LOCATION)) { // Our element is not in original position regarless it has no + // anchor by default since player made it anchored we can toggle + // margins CVarSetInteger(cvarNameMargins.c_str(), SetActivated); - } else if (strcmp(cvarName, MarginCvarNonAnchor[i]) != 0) { //Our elements has an anchor by default so regarless of it's position right now that okay to toggle margins. + } else if (strcmp(cvarName, MarginCvarNonAnchor[i]) != + 0) { // Our elements has an anchor by default so regarless of it's position right now + // that okay to toggle margins. CVarSetInteger(cvarNameMargins.c_str(), SetActivated); } } - } else { //Since the user requested to turn all margin off no need to do any check there. + } else { // Since the user requested to turn all margin off no need to do any check there. CVarSetInteger(cvarNameMargins.c_str(), SetActivated); } } @@ -511,12 +511,16 @@ void SetMarginAll(const char* ButtonName, bool SetActivated) { } void ResetPositionAll() { - if (ImGui::Button("Reset all positions")) { + if (UIWidgets::Button("Reset all positions", + UIWidgets::ButtonOptions() + .Size(ImVec2(200.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("Revert every element to use their original position and no margins"))) { for (auto cvarName : MarginCvarList) { std::string cvarPosType = std::string(cvarName).append(".PosType"); std::string cvarNameMargins = std::string(cvarName).append(".UseMargins"); CVarSetInteger(cvarPosType.c_str(), 0); - CVarSetInteger(cvarNameMargins.c_str(), false); //Turn margin off to everythings as that original position. + CVarSetInteger(cvarNameMargins.c_str(), false); // Turn margin off to everythings as that original position. } } } @@ -545,7 +549,7 @@ void CosmeticsUpdateTick() { cosmeticOption.currentColor.z = newColor.b / 255.0f; cosmeticOption.currentColor.w = newColor.a / 255.0f; - CVarSetColor(cosmeticOption.cvar, newColor); + CVarSetColor(cosmeticOption.valuesCvar, newColor); } // If we don't want the rainbow color on items to be synced, offset the index for each item in the loop. // Technically this would work if you replaced "60" with 1 but the hue would be so close it's @@ -573,21 +577,21 @@ void CosmeticsUpdateTick() { void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& magicFaroresPrimary = cosmeticOptions.at("Magic.FaroresPrimary"); if (manualChange || CVarGetInteger(magicFaroresPrimary.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(magicFaroresPrimary.cvar, magicFaroresPrimary.defaultColor); + Color_RGBA8 color = CVarGetColor(magicFaroresPrimary.valuesCvar, magicFaroresPrimary.defaultColor); PATCH_GFX(sInnerCylinderDL, "Magic_FaroresPrimary1", magicFaroresPrimary.changedCvar, 24, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(sOuterCylinderDL, "Magic_FaroresPrimary2", magicFaroresPrimary.changedCvar, 24, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); } static CosmeticOption& magicFaroresSecondary = cosmeticOptions.at("Magic.FaroresSecondary"); if (manualChange || CVarGetInteger(magicFaroresSecondary.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(magicFaroresSecondary.cvar, magicFaroresSecondary.defaultColor); + Color_RGBA8 color = CVarGetColor(magicFaroresSecondary.valuesCvar, magicFaroresSecondary.defaultColor); PATCH_GFX(sInnerCylinderDL, "Magic_FaroresSecondary1", magicFaroresSecondary.changedCvar, 25, gsDPSetEnvColor(color.r, color.g, color.b, 255)); PATCH_GFX(sOuterCylinderDL, "Magic_FaroresSecondary2", magicFaroresSecondary.changedCvar, 25, gsDPSetEnvColor(color.r, color.g, color.b, 255)); } static CosmeticOption& linkGoronTunic = cosmeticOptions.at("Link.GoronTunic"); if (manualChange || CVarGetInteger(linkGoronTunic.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(linkGoronTunic.cvar, linkGoronTunic.defaultColor); + Color_RGBA8 color = CVarGetColor(linkGoronTunic.valuesCvar, linkGoronTunic.defaultColor); PATCH_GFX(gGiGoronTunicColorDL, "Link_GoronTunic1", linkGoronTunic.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiGoronCollarColorDL, "Link_GoronTunic2", linkGoronTunic.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r / 2, color.g / 2, color.b / 2, 255)); PATCH_GFX(gGiGoronTunicColorDL, "Link_GoronTunic3", linkGoronTunic.changedCvar, 4, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255)); @@ -596,7 +600,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& linkZoraTunic = cosmeticOptions.at("Link.ZoraTunic"); if (manualChange || CVarGetInteger(linkZoraTunic.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(linkZoraTunic.cvar, linkZoraTunic.defaultColor); + Color_RGBA8 color = CVarGetColor(linkZoraTunic.valuesCvar, linkZoraTunic.defaultColor); PATCH_GFX(gGiZoraTunicColorDL, "Link_ZoraTunic1", linkZoraTunic.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiZoraCollarColorDL, "Link_ZoraTunic2", linkZoraTunic.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r / 2, color.g / 2, color.b / 2, 255)); PATCH_GFX(gGiZoraTunicColorDL, "Link_ZoraTunic3", linkZoraTunic.changedCvar, 4, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255)); @@ -605,7 +609,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& linkHair = cosmeticOptions.at("Link.Hair"); if (manualChange || CVarGetInteger(linkHair.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(linkHair.cvar, linkHair.defaultColor); + Color_RGBA8 color = CVarGetColor(linkHair.valuesCvar, linkHair.defaultColor); PATCH_GFX(gLinkChildHeadNearDL, "Link_Hair1", linkHair.changedCvar, 10, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkChildHeadFarDL, "Link_Hair2", linkHair.changedCvar, 10, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultHeadNearDL, "Link_Hair3", linkHair.changedCvar, 10, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); @@ -627,7 +631,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& linkLinen = cosmeticOptions.at("Link.Linen"); if (manualChange || CVarGetInteger(linkLinen.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(linkLinen.cvar, linkLinen.defaultColor); + Color_RGBA8 color = CVarGetColor(linkLinen.valuesCvar, linkLinen.defaultColor); PATCH_GFX(gLinkAdultLeftArmNearDL, "Link_Linen1", linkLinen.changedCvar, 30, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultLeftArmNearDL, "Link_Linen2", linkLinen.changedCvar, 83, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultLeftArmOutNearDL, "Link_Linen3", linkLinen.changedCvar, 25, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -668,7 +672,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& linkBoots = cosmeticOptions.at("Link.Boots"); if (manualChange || CVarGetInteger(linkBoots.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(linkBoots.cvar, linkBoots.defaultColor); + Color_RGBA8 color = CVarGetColor(linkBoots.valuesCvar, linkBoots.defaultColor); PATCH_GFX(gLinkChildRightShinNearDL, "Link_Boots1", linkBoots.changedCvar, 10, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkChildRightShinFarDL, "Link_Boots2", linkBoots.changedCvar, 10, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultRightLegNearDL, "Link_Boots3", linkBoots.changedCvar, 10, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); @@ -704,7 +708,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& mirrorShieldBody = cosmeticOptions.at("MirrorShield.Body"); if (manualChange || CVarGetInteger(mirrorShieldBody.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(mirrorShieldBody.cvar, mirrorShieldBody.defaultColor); + Color_RGBA8 color = CVarGetColor(mirrorShieldBody.valuesCvar, mirrorShieldBody.defaultColor); PATCH_GFX(gGiMirrorShieldDL, "MirrorShield_Body1", mirrorShieldBody.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiMirrorShieldDL, "MirrorShield_Body2", mirrorShieldBody.changedCvar, 6, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultMirrorShieldSwordAndSheathNearDL, "MirrorShield_Body3", mirrorShieldBody.changedCvar, 28, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -716,7 +720,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& mirrorShieldMirror = cosmeticOptions.at("MirrorShield.Mirror"); if (manualChange || CVarGetInteger(mirrorShieldMirror.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(mirrorShieldMirror.cvar, mirrorShieldMirror.defaultColor); + Color_RGBA8 color = CVarGetColor(mirrorShieldMirror.valuesCvar, mirrorShieldMirror.defaultColor); PATCH_GFX(gGiMirrorShieldDL, "MirrorShield_Mirror1", mirrorShieldMirror.changedCvar, 47, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiMirrorShieldDL, "MirrorShield_Mirror2", mirrorShieldMirror.changedCvar, 48, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultMirrorShieldSwordAndSheathNearDL, "MirrorShield_Mirror3", mirrorShieldMirror.changedCvar, 17, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -728,7 +732,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& mirrorShieldEmblem = cosmeticOptions.at("MirrorShield.Emblem"); if (manualChange || CVarGetInteger(mirrorShieldEmblem.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(mirrorShieldEmblem.cvar, mirrorShieldEmblem.defaultColor); + Color_RGBA8 color = CVarGetColor(mirrorShieldEmblem.valuesCvar, mirrorShieldEmblem.defaultColor); PATCH_GFX(gGiMirrorShieldSymbolDL, "MirrorShield_Emblem1", mirrorShieldEmblem.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 140)); PATCH_GFX(gGiMirrorShieldSymbolDL, "MirrorShield_Emblem2", mirrorShieldEmblem.changedCvar, 6, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultMirrorShieldSwordAndSheathNearDL, "MirrorShield_Emblem3", mirrorShieldEmblem.changedCvar, 165, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -741,7 +745,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& swordsKokiriBlade = cosmeticOptions.at("Swords.KokiriBlade"); if (manualChange || CVarGetInteger(swordsKokiriBlade.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(swordsKokiriBlade.cvar, swordsKokiriBlade.defaultColor); + Color_RGBA8 color = CVarGetColor(swordsKokiriBlade.valuesCvar, swordsKokiriBlade.defaultColor); PATCH_GFX(gLinkChildLeftFistAndKokiriSwordNearDL, "Swords_KokiriBlade1", swordsKokiriBlade.changedCvar, 79, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gLinkChildLeftFistAndKokiriSwordFarDL, "Swords_KokiriBlade2", swordsKokiriBlade.changedCvar, 75, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiKokiriSwordDL, "Swords_KokiriBlade3", swordsKokiriBlade.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -750,7 +754,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { /* static CosmeticOption& swordsKokiriHilt = cosmeticOptions.at("Swords.KokiriHilt"); if (manualChange || CVarGetInteger(swordsKokiriHilt.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(swordsKokiriHilt.cvar, swordsKokiriHilt.defaultColor); + Color_RGBA8 color = CVarGetColor(swordsKokiriHilt.valuesCvar, swordsKokiriHilt.defaultColor); PATCH_GFX(gLinkChildLeftFistAndKokiriSwordNearDL, "Swords_KokiriHilt1", swordsKokiriHilt.changedCvar, 4, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkChildLeftFistAndKokiriSwordFarDL, "Swords_KokiriHilt2", swordsKokiriHilt.changedCvar, 4, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkChildSwordAndSheathNearDL, "Swords_KokiriHilt3", swordsKokiriHilt.changedCvar, 4, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); @@ -791,7 +795,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { */ static CosmeticOption& swordsMasterBlade = cosmeticOptions.at("Swords.MasterBlade"); if (manualChange || CVarGetInteger(swordsMasterBlade.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(swordsMasterBlade.cvar, swordsMasterBlade.defaultColor); + Color_RGBA8 color = CVarGetColor(swordsMasterBlade.valuesCvar, swordsMasterBlade.defaultColor); PATCH_GFX(gLinkAdultLeftHandHoldingMasterSwordFarDL, "Swords_MasterBlade1", swordsMasterBlade.changedCvar, 60, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultLeftHandHoldingMasterSwordNearDL, "Swords_MasterBlade2", swordsMasterBlade.changedCvar, 17, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(object_toki_objects_DL_001BD0, "Swords_MasterBlade3", swordsMasterBlade.changedCvar, 13, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -802,7 +806,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { /* static CosmeticOption& swordsMasterHilt = cosmeticOptions.at("Swords.MasterHilt"); if (manualChange || CVarGetInteger(swordsMasterHilt.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(swordsMasterHilt.cvar, swordsMasterHilt.defaultColor); + Color_RGBA8 color = CVarGetColor(swordsMasterHilt.valuesCvar, swordsMasterHilt.defaultColor); PATCH_GFX(gLinkAdultLeftHandHoldingMasterSwordNearDL, "Swords_MasterHilt1", swordsMasterHilt.changedCvar, 20, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultLeftHandHoldingMasterSwordFarDL, "Swords_MasterHilt2", swordsMasterHilt.changedCvar, 20, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(object_toki_objects_DL_001BD0, "Swords_MasterHilt3", swordsMasterHilt.changedCvar, 16, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); @@ -849,7 +853,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { */ static CosmeticOption& swordsBiggoronBlade = cosmeticOptions.at("Swords.BiggoronBlade"); if (manualChange || CVarGetInteger(swordsBiggoronBlade.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(swordsBiggoronBlade.cvar, swordsBiggoronBlade.defaultColor); + Color_RGBA8 color = CVarGetColor(swordsBiggoronBlade.valuesCvar, swordsBiggoronBlade.defaultColor); PATCH_GFX(gLinkAdultLeftHandHoldingBgsFarDL, "Swords_BiggoronBlade1", swordsBiggoronBlade.changedCvar, 108, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultLeftHandHoldingBgsNearDL, "Swords_BiggoronBlade2", swordsBiggoronBlade.changedCvar, 63, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBiggoronSwordDL, "Swords_BiggoronBlade3", swordsBiggoronBlade.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -858,7 +862,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { /* static CosmeticOption& swordsBiggoronHilt = cosmeticOptions.at("Swords.BiggoronHilt"); if (manualChange || CVarGetInteger(swordsBiggoronHilt.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(swordsBiggoronHilt.cvar, swordsBiggoronHilt.defaultColor); + Color_RGBA8 color = CVarGetColor(swordsBiggoronHilt.valuesCvar, swordsBiggoronHilt.defaultColor); PATCH_GFX(gLinkAdultLeftHandHoldingBgsNearDL, "Swords_BiggoronHilt1", swordsBiggoronHilt.changedCvar, 20, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gLinkAdultLeftHandHoldingBgsFarDL, "Swords_BiggoronHilt2", swordsBiggoronHilt.changedCvar, 20, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gGiBiggoronSwordDL, "Swords_BiggoronHilt3", swordsBiggoronHilt.changedCvar, 74, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -877,7 +881,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { */ static CosmeticOption& glovesGoronBracelet = cosmeticOptions.at("Gloves.GoronBracelet"); if (manualChange || CVarGetInteger(glovesGoronBracelet.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(glovesGoronBracelet.cvar, glovesGoronBracelet.defaultColor); + Color_RGBA8 color = CVarGetColor(glovesGoronBracelet.valuesCvar, glovesGoronBracelet.defaultColor); PATCH_GFX(gGiGoronBraceletDL, "Gloves_GoronBracelet1", glovesGoronBracelet.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiGoronBraceletDL, "Gloves_GoronBracelet2", glovesGoronBracelet.changedCvar, 6, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkChildGoronBraceletDL, "Gloves_GoronBracelet3", glovesGoronBracelet.changedCvar, 3, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); @@ -889,19 +893,19 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& glovesSilverGauntlets = cosmeticOptions.at("Gloves.SilverGauntlets"); if (manualChange || CVarGetInteger(glovesSilverGauntlets.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(glovesSilverGauntlets.cvar, glovesSilverGauntlets.defaultColor); + Color_RGBA8 color = CVarGetColor(glovesSilverGauntlets.valuesCvar, glovesSilverGauntlets.defaultColor); PATCH_GFX(gGiSilverGauntletsColorDL, "Gloves_SilverGauntlets1", glovesSilverGauntlets.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiSilverGauntletsColorDL, "Gloves_SilverGauntlets2", glovesSilverGauntlets.changedCvar, 4, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); } static CosmeticOption& glovesGoldenGauntlets = cosmeticOptions.at("Gloves.GoldenGauntlets"); if (manualChange || CVarGetInteger(glovesGoldenGauntlets.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(glovesGoldenGauntlets.cvar, glovesGoldenGauntlets.defaultColor); + Color_RGBA8 color = CVarGetColor(glovesGoldenGauntlets.valuesCvar, glovesGoldenGauntlets.defaultColor); PATCH_GFX(gGiGoldenGauntletsColorDL, "Gloves_GoldenGauntlets1", glovesGoldenGauntlets.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiGoldenGauntletsColorDL, "Gloves_GoldenGauntlets2", glovesGoldenGauntlets.changedCvar, 4, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); } static CosmeticOption& glovesGauntletsGem = cosmeticOptions.at("Gloves.GauntletsGem"); if (manualChange || CVarGetInteger(glovesGauntletsGem.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(glovesGauntletsGem.cvar, glovesGauntletsGem.defaultColor); + Color_RGBA8 color = CVarGetColor(glovesGauntletsGem.valuesCvar, glovesGauntletsGem.defaultColor); PATCH_GFX(gGiGauntletsDL, "Gloves_GauntletsGem1", glovesGauntletsGem.changedCvar, 84, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiGauntletsDL, "Gloves_GauntletsGem2", glovesGauntletsGem.changedCvar, 85, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultLeftGauntletPlate2DL, "Gloves_GauntletsGem3", glovesGauntletsGem.changedCvar, 42, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -912,7 +916,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& equipmentBoomerangBody = cosmeticOptions.at("Equipment.BoomerangBody"); if (manualChange || CVarGetInteger(equipmentBoomerangBody.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentBoomerangBody.cvar, equipmentBoomerangBody.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentBoomerangBody.valuesCvar, equipmentBoomerangBody.defaultColor); PATCH_GFX(gGiBoomerangDL, "Equipment_BoomerangBody1", equipmentBoomerangBody.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBoomerangDL, "Equipment_BoomerangBody2", equipmentBoomerangBody.changedCvar, 6, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkChildLeftFistAndBoomerangNearDL, "Equipment_BoomerangBody3", equipmentBoomerangBody.changedCvar, 34, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -921,7 +925,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& equipmentBoomerangGem = cosmeticOptions.at("Equipment.BoomerangGem"); if (manualChange || CVarGetInteger(equipmentBoomerangGem.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentBoomerangGem.cvar, equipmentBoomerangGem.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentBoomerangGem.valuesCvar, equipmentBoomerangGem.defaultColor); PATCH_GFX(gGiBoomerangDL, "Equipment_BoomerangGem1", equipmentBoomerangGem.changedCvar, 84, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBoomerangDL, "Equipment_BoomerangGem2", equipmentBoomerangGem.changedCvar, 85, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkChildLeftFistAndBoomerangNearDL, "Equipment_BoomerangGem3", equipmentBoomerangGem.changedCvar, 16, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -932,7 +936,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { /* static CosmeticOption& equipmentSlingshotBody = cosmeticOptions.at("Equipment.SlingshotBody"); if (manualChange || CVarGetInteger(equipmentSlingshotBody.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentSlingshotBody.cvar, equipmentSlingshotBody.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentSlingshotBody.valuesCvar, equipmentSlingshotBody.defaultColor); PATCH_GFX(gGiSlingshotDL, "Equipment_SlingshotBody1", equipmentSlingshotBody.changedCvar, 10, gsDPSetPrimColor(0, 0, MAX(color.r - 100, 0), MAX(color.g - 100, 0), MAX(color.b - 100, 0), 255)); PATCH_GFX(gGiSlingshotDL, "Equipment_SlingshotBody2", equipmentSlingshotBody.changedCvar, 12, gsDPSetEnvColor(MAX(color.r - 100, 0) / 3, MAX(color.g - 100, 0) / 3, MAX(color.b - 100, 0) / 3, 255)); PATCH_GFX(gGiSlingshotDL, "Equipment_SlingshotBody3", equipmentSlingshotBody.changedCvar, 74, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -956,7 +960,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { */ static CosmeticOption& equipmentSlingshotString = cosmeticOptions.at("Equipment.SlingshotString"); if (manualChange || CVarGetInteger(equipmentSlingshotString.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentSlingshotString.cvar, equipmentSlingshotString.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentSlingshotString.valuesCvar, equipmentSlingshotString.defaultColor); PATCH_GFX(gGiSlingshotDL, "Equipment_SlingshotString1",equipmentSlingshotString.changedCvar, 75, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiSlingshotDL, "Equipment_SlingshotString2",equipmentSlingshotString.changedCvar, 76, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255)); PATCH_GFX(gLinkChildSlingshotStringDL, "Equipment_SlingshotString3",equipmentSlingshotString.changedCvar, 9, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -964,7 +968,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& equipmentBowTips = cosmeticOptions.at("Equipment.BowTips"); if (manualChange || CVarGetInteger(equipmentBowTips.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentBowTips.cvar, equipmentBowTips.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentBowTips.valuesCvar, equipmentBowTips.defaultColor); PATCH_GFX(gGiBowDL, "Equipment_BowTips1", equipmentBowTips.changedCvar, 86, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBowDL, "Equipment_BowTips2", equipmentBowTips.changedCvar, 87, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultRightHandHoldingBowFirstPersonDL, "Equipment_BowTips3", equipmentBowTips.changedCvar, 34, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -973,14 +977,14 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& equipmentBowString = cosmeticOptions.at("Equipment.BowString"); if (manualChange || CVarGetInteger(equipmentBowString.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentBowString.cvar, equipmentBowString.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentBowString.valuesCvar, equipmentBowString.defaultColor); PATCH_GFX(gGiBowDL, "Equipment_BowString1", equipmentBowString.changedCvar, 105, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBowDL, "Equipment_BowString2", equipmentBowString.changedCvar, 106, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultBowStringDL, "Equipment_BowString3", equipmentBowString.changedCvar, 9, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); } static CosmeticOption& equipmentBowBody = cosmeticOptions.at("Equipment.BowBody"); if (manualChange || CVarGetInteger(equipmentBowBody.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentBowBody.cvar, equipmentBowBody.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentBowBody.valuesCvar, equipmentBowBody.defaultColor); PATCH_GFX(gGiBowDL, "Equipment_BowBody1", equipmentBowBody.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBowDL, "Equipment_BowBody2", equipmentBowBody.changedCvar, 6, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultRightHandHoldingBowFirstPersonDL, "Equipment_BowBody3", equipmentBowBody.changedCvar, 42, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -989,7 +993,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& equipmentBowHandle = cosmeticOptions.at("Equipment.BowHandle"); if (manualChange || CVarGetInteger(equipmentBowHandle.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentBowHandle.cvar, equipmentBowHandle.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentBowHandle.valuesCvar, equipmentBowHandle.defaultColor); PATCH_GFX(gGiBowDL, "Equipment_BowHandle1", equipmentBowHandle.changedCvar, 51, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBowDL, "Equipment_BowHandle2", equipmentBowHandle.changedCvar, 52, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gLinkAdultRightHandHoldingBowFirstPersonDL, "Equipment_BowHandle3", equipmentBowHandle.changedCvar, 18, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -999,7 +1003,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& equipmentHammerHead = cosmeticOptions.at("Equipment.HammerHead"); if (manualChange || CVarGetInteger(equipmentHammerHead.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentHammerHead.cvar, equipmentHammerHead.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentHammerHead.valuesCvar, equipmentHammerHead.defaultColor); PATCH_GFX(gGiHammerDL, "Equipment_HammerHead1", equipmentHammerHead.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiHammerDL, "Equipment_HammerHead2", equipmentHammerHead.changedCvar, 6, gsDPSetEnvColor(color.r / 5, color.g / 5, color.b / 5, 255)); PATCH_GFX(gGiHammerDL, "Equipment_HammerHead3", equipmentHammerHead.changedCvar, 68, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -1009,7 +1013,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& equipmentHammerHandle = cosmeticOptions.at("Equipment.HammerHandle"); if (manualChange || CVarGetInteger(equipmentHammerHandle.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentHammerHandle.cvar, equipmentHammerHandle.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentHammerHandle.valuesCvar, equipmentHammerHandle.defaultColor); PATCH_GFX(gGiHammerDL, "Equipment_HammerHandle1", equipmentHammerHandle.changedCvar, 84, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiHammerDL, "Equipment_HammerHandle2", equipmentHammerHandle.changedCvar, 85, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255)); PATCH_GFX(gLinkAdultLeftHandHoldingHammerNearDL, "Equipment_HammerHandle5", equipmentHammerHandle.changedCvar, 18, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -1018,13 +1022,13 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& equipmentHookshotChain = cosmeticOptions.at("Equipment.HookshotChain"); if (manualChange || CVarGetInteger(equipmentHookshotChain.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentHookshotChain.cvar, equipmentHookshotChain.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentHookshotChain.valuesCvar, equipmentHookshotChain.defaultColor); PATCH_GFX(gLinkAdultHookshotChainDL, "Equipment_HookshotChain1", equipmentHookshotChain.changedCvar, 17, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); } static CosmeticOption& equipmentChuFace = cosmeticOptions.at("Equipment.ChuFace"); if (manualChange || CVarGetInteger(equipmentChuFace.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentChuFace.cvar, equipmentChuFace.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentChuFace.valuesCvar, equipmentChuFace.defaultColor); PATCH_GFX(gGiBombchuDL, "Equipment_ChuFace1", equipmentChuFace.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBombchuDL, "Equipment_ChuFace2", equipmentChuFace.changedCvar, 6, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gBombchuDL, "Equipment_ChuFace3", equipmentChuFace.changedCvar, 2, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); @@ -1036,7 +1040,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& equipmentChuBody = cosmeticOptions.at("Equipment.ChuBody"); if (manualChange || CVarGetInteger(equipmentChuBody.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentChuBody.cvar, equipmentChuBody.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentChuBody.valuesCvar, equipmentChuBody.defaultColor); PATCH_GFX(gGiBombchuDL, "Equipment_ChuBody1", equipmentChuBody.changedCvar, 39, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBombchuDL, "Equipment_ChuBody2", equipmentChuBody.changedCvar, 40, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gGiBombchuDL, "Equipment_ChuBody3", equipmentChuBody.changedCvar, 60, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -1046,7 +1050,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& equipmentBunnyHood = cosmeticOptions.at("Equipment.BunnyHood"); if (manualChange || CVarGetInteger(equipmentBunnyHood.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(equipmentBunnyHood.cvar, equipmentBunnyHood.defaultColor); + Color_RGBA8 color = CVarGetColor(equipmentBunnyHood.valuesCvar, equipmentBunnyHood.defaultColor); PATCH_GFX(gGiBunnyHoodDL, "Equipment_BunnyHood1", equipmentBunnyHood.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBunnyHoodDL, "Equipment_BunnyHood2", equipmentBunnyHood.changedCvar, 6, gsDPSetEnvColor(color.r / 3, color.g / 3, color.b / 3, 255)); PATCH_GFX(gGiBunnyHoodDL, "Equipment_BunnyHood3", equipmentBunnyHood.changedCvar, 83, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -1065,7 +1069,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& consumableGreenRupee = cosmeticOptions.at("Consumable.GreenRupee"); if (manualChange || CVarGetInteger(consumableGreenRupee.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(consumableGreenRupee.cvar, consumableGreenRupee.defaultColor); + Color_RGBA8 color = CVarGetColor(consumableGreenRupee.valuesCvar, consumableGreenRupee.defaultColor); PATCH_GFX(gGiGreenRupeeInnerColorDL, "Consumable_GreenRupee1", consumableGreenRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiGreenRupeeInnerColorDL, "Consumable_GreenRupee2", consumableGreenRupee.changedCvar, 4, gsDPSetEnvColor(color.r / 5, color.g / 5, color.b / 5, 255)); PATCH_GFX(gGiGreenRupeeOuterColorDL, "Consumable_GreenRupee3", consumableGreenRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, MIN(color.r + 100, 255), MIN(color.g + 100, 255), MIN(color.b + 100, 255), 255)); @@ -1084,7 +1088,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& consumableBlueRupee = cosmeticOptions.at("Consumable.BlueRupee"); if (manualChange || CVarGetInteger(consumableBlueRupee.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(consumableBlueRupee.cvar, consumableBlueRupee.defaultColor); + Color_RGBA8 color = CVarGetColor(consumableBlueRupee.valuesCvar, consumableBlueRupee.defaultColor); PATCH_GFX(gGiBlueRupeeInnerColorDL, "Consumable_BlueRupee1", consumableBlueRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiBlueRupeeInnerColorDL, "Consumable_BlueRupee2", consumableBlueRupee.changedCvar, 4, gsDPSetEnvColor(color.r / 5, color.g / 5, color.b / 5, 255)); PATCH_GFX(gGiBlueRupeeOuterColorDL, "Consumable_BlueRupee3", consumableBlueRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, MIN(color.r + 100, 255), MIN(color.g + 100, 255), MIN(color.b + 100, 255), 255)); @@ -1092,7 +1096,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& consumableRedRupee = cosmeticOptions.at("Consumable.RedRupee"); if (manualChange || CVarGetInteger(consumableRedRupee.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(consumableRedRupee.cvar, consumableRedRupee.defaultColor); + Color_RGBA8 color = CVarGetColor(consumableRedRupee.valuesCvar, consumableRedRupee.defaultColor); PATCH_GFX(gGiRedRupeeInnerColorDL, "Consumable_RedRupee1", consumableRedRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiRedRupeeInnerColorDL, "Consumable_RedRupee2", consumableRedRupee.changedCvar, 4, gsDPSetEnvColor(color.r / 5, color.g / 5, color.b / 5, 255)); PATCH_GFX(gGiRedRupeeOuterColorDL, "Consumable_RedRupee3", consumableRedRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, MIN(color.r + 100, 255), MIN(color.g + 100, 255), MIN(color.b + 100, 255), 255)); @@ -1100,7 +1104,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& consumablePurpleRupee = cosmeticOptions.at("Consumable.PurpleRupee"); if (manualChange || CVarGetInteger(consumablePurpleRupee.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(consumablePurpleRupee.cvar, consumablePurpleRupee.defaultColor); + Color_RGBA8 color = CVarGetColor(consumablePurpleRupee.valuesCvar, consumablePurpleRupee.defaultColor); PATCH_GFX(gGiPurpleRupeeInnerColorDL, "Consumable_PurpleRupee1", consumablePurpleRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiPurpleRupeeInnerColorDL, "Consumable_PurpleRupee2", consumablePurpleRupee.changedCvar, 4, gsDPSetEnvColor(color.r / 5, color.g / 5, color.b / 5, 255)); PATCH_GFX(gGiPurpleRupeeOuterColorDL, "Consumable_PurpleRupee3", consumablePurpleRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, MIN(color.r + 100, 255), MIN(color.g + 100, 255), MIN(color.b + 100, 255), 255)); @@ -1108,7 +1112,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& consumableGoldRupee = cosmeticOptions.at("Consumable.GoldRupee"); if (manualChange || CVarGetInteger(consumableGoldRupee.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(consumableGoldRupee.cvar, consumableGoldRupee.defaultColor); + Color_RGBA8 color = CVarGetColor(consumableGoldRupee.valuesCvar, consumableGoldRupee.defaultColor); PATCH_GFX(gGiGoldRupeeInnerColorDL, "Consumable_GoldRupee1", consumableGoldRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiGoldRupeeInnerColorDL, "Consumable_GoldRupee2", consumableGoldRupee.changedCvar, 4, gsDPSetEnvColor(color.r / 5, color.g / 5, color.b / 5, 255)); PATCH_GFX(gGiGoldRupeeOuterColorDL, "Consumable_GoldRupee3", consumableGoldRupee.changedCvar, 3, gsDPSetPrimColor(0, 0, MIN(color.r + 100, 255), MIN(color.g + 100, 255), MIN(color.b + 100, 255), 255)); @@ -1117,7 +1121,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& consumableHearts = cosmeticOptions.at("Consumable.Hearts"); if (manualChange || CVarGetInteger(consumableHearts.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(consumableHearts.cvar, consumableHearts.defaultColor); + Color_RGBA8 color = CVarGetColor(consumableHearts.valuesCvar, consumableHearts.defaultColor); /* PATCH_GFX(gGiRecoveryHeartDL, "Consumable_Hearts1", consumableHearts.changedCvar, 4, gsDPSetGrayscaleColor(color.r, color.g, color.b, 255)); PATCH_GFX(gGiRecoveryHeartDL, "Consumable_Hearts2", consumableHearts.changedCvar, 26, gsSPGrayscale(true)); @@ -1133,7 +1137,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { } static CosmeticOption& consumableMagic = cosmeticOptions.at("Consumable.Magic"); if (manualChange || CVarGetInteger(consumableMagic.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(consumableMagic.cvar, consumableMagic.defaultColor); + Color_RGBA8 color = CVarGetColor(consumableMagic.valuesCvar, consumableMagic.defaultColor); PATCH_GFX(gGiMagicJarSmallDL, "Consumable_Magic1", consumableMagic.changedCvar, 31, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gGiMagicJarSmallDL, "Consumable_Magic2", consumableMagic.changedCvar, 32, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255)); PATCH_GFX(gGiMagicJarLargeDL, "Consumable_Magic3", consumableMagic.changedCvar, 31, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -1144,7 +1148,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& npcGoldenSkulltula = cosmeticOptions.at("NPC.GoldenSkulltula"); if (manualChange || CVarGetInteger(npcGoldenSkulltula.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(npcGoldenSkulltula.cvar, npcGoldenSkulltula.defaultColor); + Color_RGBA8 color = CVarGetColor(npcGoldenSkulltula.valuesCvar, npcGoldenSkulltula.defaultColor); PATCH_GFX(gSkulltulaTokenDL, "NPC_GoldenSkulltula1", npcGoldenSkulltula.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); PATCH_GFX(gSkulltulaTokenDL, "NPC_GoldenSkulltula2", npcGoldenSkulltula.changedCvar, 6, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255)); PATCH_GFX(gSkulltulaTokenFlameDL, "NPC_GoldenSkulltula3", npcGoldenSkulltula.changedCvar, 32, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); @@ -1159,7 +1163,7 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& npcGerudo = cosmeticOptions.at("NPC.Gerudo"); if (manualChange || CVarGetInteger(npcGerudo.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(npcGerudo.cvar, npcGerudo.defaultColor); + Color_RGBA8 color = CVarGetColor(npcGerudo.valuesCvar, npcGerudo.defaultColor); PATCH_GFX(gGerudoPurpleTorsoDL, "NPC_Gerudo1", npcGerudo.changedCvar, 139, gsDPSetEnvColor( color.r, color.g, color.b, 255)); PATCH_GFX(gGerudoPurpleRightThighDL, "NPC_Gerudo2", npcGerudo.changedCvar, 11, gsDPSetEnvColor(color.r, color.g, color.b, 255)); PATCH_GFX(gGerudoPurpleLeftThighDL, "NPC_Gerudo3", npcGerudo.changedCvar, 11, gsDPSetEnvColor(color.r, color.g, color.b, 255)); @@ -1172,39 +1176,39 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& npcMetalTrap = cosmeticOptions.at("NPC.MetalTrap"); if (manualChange || CVarGetInteger(npcMetalTrap.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(npcMetalTrap.cvar, npcMetalTrap.defaultColor); + Color_RGBA8 color = CVarGetColor(npcMetalTrap.valuesCvar, npcMetalTrap.defaultColor); PATCH_GFX(gSlidingBladeTrapDL, "NPC_MetalTrap1", npcMetalTrap.changedCvar, 59, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); } static CosmeticOption& n64LogoRed = cosmeticOptions.at("Title.N64LogoRed"); if (manualChange || CVarGetInteger(n64LogoRed.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(n64LogoRed.cvar, n64LogoRed.defaultColor); + Color_RGBA8 color = CVarGetColor(n64LogoRed.valuesCvar, n64LogoRed.defaultColor); PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoRed1", n64LogoRed.changedCvar, 17, gsDPSetPrimColor(0, 0, 255, 255, 255, 255)) PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoRed2", n64LogoRed.changedCvar, 18, gsDPSetEnvColor(color.r, color.g, color.b, 128)); } static CosmeticOption& n64LogoBlue = cosmeticOptions.at("Title.N64LogoBlue"); if (manualChange || CVarGetInteger(n64LogoBlue.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(n64LogoBlue.cvar, n64LogoBlue.defaultColor); + Color_RGBA8 color = CVarGetColor(n64LogoBlue.valuesCvar, n64LogoBlue.defaultColor); PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoBlue1", n64LogoBlue.changedCvar, 29, gsDPSetPrimColor(0, 0, 255, 255, 255, 255)) PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoBlue2", n64LogoBlue.changedCvar, 30, gsDPSetEnvColor(color.r, color.g, color.b, 128)); } static CosmeticOption& n64LogoGreen = cosmeticOptions.at("Title.N64LogoGreen"); if (manualChange || CVarGetInteger(n64LogoGreen.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(n64LogoGreen.cvar, n64LogoGreen.defaultColor); + Color_RGBA8 color = CVarGetColor(n64LogoGreen.valuesCvar, n64LogoGreen.defaultColor); PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoGreen1", n64LogoGreen.changedCvar, 56, gsDPSetPrimColor(0, 0, 255, 255, 255, 255)) PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoGreen2", n64LogoGreen.changedCvar, 57, gsDPSetEnvColor(color.r, color.g, color.b, 128)); } static CosmeticOption& n64LogoYellow = cosmeticOptions.at("Title.N64LogoYellow"); if (manualChange || CVarGetInteger(n64LogoYellow.rainbowCvar, 0)) { - Color_RGBA8 color = CVarGetColor(n64LogoYellow.cvar, n64LogoYellow.defaultColor); + Color_RGBA8 color = CVarGetColor(n64LogoYellow.valuesCvar, n64LogoYellow.defaultColor); PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoYellow1", n64LogoYellow.changedCvar, 81, gsDPSetPrimColor(0, 0, 255, 255, 255, 255)) PATCH_GFX(gNintendo64LogoDL, "Title_N64LogoYellow2", n64LogoYellow.changedCvar, 82, gsDPSetEnvColor(color.r, color.g, color.b, 128)); } if (gPlayState != nullptr) { - if (CVarGetInteger(CVAR_COSMETIC("Link.BodyScale.Changed"), 0)) { + if (CVarGetInteger(CVAR_COSMETIC("Link.BodySize.Changed"), 0)) { static Player* player = GET_PLAYER(gPlayState); - float scale = CVarGetFloat(CVAR_COSMETIC("Link.BodyScale.Value"), 0.01f); + float scale = CVarGetFloat(CVAR_COSMETIC("Link.BodySize.Value"), 0.01f); player->actor.scale.x = scale; player->actor.scale.y = scale; player->actor.scale.z = scale; @@ -1235,24 +1239,36 @@ void Table_InitHeader(bool has_header = true) { void DrawUseMarginsSlider(const std::string ElementName, const std::string CvarName){ std::string CvarLabel = CvarName + ".UseMargins"; std::string Label = ElementName + " use margins"; - UIWidgets::EnhancementCheckbox(Label.c_str(), CvarLabel.c_str()); - UIWidgets::Tooltip("Using this allow you move the element with General margins sliders"); + UIWidgets::CVarCheckbox(Label.c_str(), CvarLabel.c_str(), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Using this allow you move the element with General margins sliders")); } -void DrawPositionsRadioBoxes(const std::string CvarName, bool NoAnchorEnabled = true){ +void DrawPositionsRadioBoxes(const std::string CvarName, bool NoAnchorEnabled = true) { std::string CvarLabel = CvarName + ".PosType"; - UIWidgets::EnhancementRadioButton("Original position", CvarLabel.c_str(), 0); - UIWidgets::Tooltip("This will use original intended elements position"); - UIWidgets::EnhancementRadioButton("Anchor to the left", CvarLabel.c_str(), 1); - UIWidgets::Tooltip("This will make your elements follow the left side of your game window"); - UIWidgets::EnhancementRadioButton("Anchor to the right", CvarLabel.c_str(), 2); - UIWidgets::Tooltip("This will make your elements follow the right side of your game window"); + UIWidgets::CVarRadioButton("Original position", CvarLabel.c_str(), 0, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will use original intended elements position")); + UIWidgets::CVarRadioButton("Anchor to the left", CvarLabel.c_str(), 1, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will make your elements follow the left side of your game window")); + UIWidgets::CVarRadioButton("Anchor to the right", CvarLabel.c_str(), 2, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will make your elements follow the right side of your game window")); if (NoAnchorEnabled) { - UIWidgets::EnhancementRadioButton("No anchors", CvarLabel.c_str(), 3); - UIWidgets::Tooltip("This will make your elements to not follow any side\nBetter used for center elements"); + UIWidgets::CVarRadioButton( + "No anchors", CvarLabel.c_str(), 3, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will make your elements to not follow any side\nBetter used for center elements")); } - UIWidgets::EnhancementRadioButton("Hidden", CvarLabel.c_str(), 4); - UIWidgets::Tooltip("This will make your elements hidden"); + UIWidgets::CVarRadioButton( + "Hidden", CvarLabel.c_str(), 4, + UIWidgets::RadioButtonsOptions().Color(THEME_COLOR).Tooltip("This will make your elements hidden")); } void DrawPositionSlider(const std::string CvarName, int MinY, int MaxY, int MinX, int MaxX){ @@ -1260,10 +1276,22 @@ void DrawPositionSlider(const std::string CvarName, int MinY, int MaxY, int MinX std::string PosYCvar = CvarName + ".PosY"; std::string InvisibleLabelX = "##" + PosXCvar; std::string InvisibleLabelY = "##" + PosYCvar; - UIWidgets::EnhancementSliderInt("Up <-> Down : %d", InvisibleLabelY.c_str(), PosYCvar.c_str(), MinY, MaxY, "", 0); - UIWidgets::Tooltip("This slider is used to move Up and Down your elements."); - UIWidgets::EnhancementSliderInt("Left <-> Right : %d", InvisibleLabelX.c_str(), PosXCvar.c_str(), MinX, MaxX, "", 0); - UIWidgets::Tooltip("This slider is used to move Left and Right your elements."); + UIWidgets::CVarSliderInt("Up <-> Down : %d", PosYCvar.c_str(), + UIWidgets::IntSliderOptions() + .Min(MinY) + .Max(MaxY) + .DefaultValue(0) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("This slider is used to move Up and Down your elements.")); + UIWidgets::CVarSliderInt("Left <-> Right : %d", PosXCvar.c_str(), + UIWidgets::IntSliderOptions() + .Min(MinX) + .Max(MaxX) + .DefaultValue(0) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("This slider is used to move Left and Right your elements.")); } void DrawScaleSlider(const std::string CvarName, float DefaultValue){ @@ -1274,6 +1302,7 @@ void DrawScaleSlider(const std::string CvarName, float DefaultValue){ } void Draw_Table_Dropdown(const char* Header_Title, const char* Table_ID, const char* Column_Title, const char* Slider_Title, const char* Slider_ID, int MinY, int MaxY, int MinX, int MaxX, float Default_Value) { + UIWidgets::PushStyleHeader(THEME_COLOR); if (ImGui::CollapsingHeader(Header_Title)) { if (ImGui::BeginTable(Table_ID, 1, FlagsTable)) { ImGui::TableSetupColumn(Column_Title, FlagsCell, TablesCellsWidth); @@ -1282,13 +1311,14 @@ void Draw_Table_Dropdown(const char* Header_Title, const char* Table_ID, const c DrawPositionsRadioBoxes(Slider_ID); DrawPositionSlider(Slider_ID, MinY, MaxY, MinX, MaxX); DrawScaleSlider(Slider_ID, Default_Value); - ImGui::NewLine(); ImGui::EndTable(); } } + UIWidgets::PopStyleHeader(); } void C_Button_Dropdown(const char* Header_Title, const char* Table_ID, const char* Column_Title, const char* Slider_Title, const char* Slider_ID, const char* Int_Type, float Slider_Scale_Value) { + UIWidgets::PushStyleHeader(THEME_COLOR); if (ImGui::CollapsingHeader(Header_Title)) { if (ImGui::BeginTable(Table_ID, 1, FlagsTable)) { ImGui::TableSetupColumn(Column_Title, FlagsCell, TablesCellsWidth); @@ -1306,7 +1336,6 @@ void C_Button_Dropdown(const char* Header_Title, const char* Table_ID, const cha } DrawPositionSlider(Slider_ID, 0, static_cast(ImGui::GetWindowViewport()->Size.y / 2), Min_X_CU, Max_X_CU); DrawScaleSlider(Slider_ID, Slider_Scale_Value); - ImGui::NewLine(); ImGui::EndTable(); } std::shared_ptr controller = Ship::Context::GetInstance()->GetControlDeck()->GetControllerByPort(0); @@ -1323,27 +1352,48 @@ void C_Button_Dropdown(const char* Header_Title, const char* Table_ID, const cha controller->GetButton(BTN_CUSTOM_OCARINA_NOTE_D5)->AddButtonMapping(mapping); } } + UIWidgets::PopStyleHeader(); } void Draw_Placements(){ - if (ImGui::BeginTable("tableMargins", 1, FlagsTable)) { - ImGui::TableSetupColumn("General margins settings", FlagsCell, TablesCellsWidth); - Table_InitHeader(); - UIWidgets::EnhancementSliderInt("Top : %dx", "##UIMARGINT", CVAR_COSMETIC("HUD.Margin.T"), static_cast(ImGui::GetWindowViewport()->Size.y / 2) * -1, 25, "", 0); - UIWidgets::EnhancementSliderInt("Left: %dx", "##UIMARGINL", CVAR_COSMETIC("HUD.Margin.L"), -25, static_cast(ImGui::GetWindowViewport()->Size.x), "", 0); - UIWidgets::EnhancementSliderInt("Right: %dx", "##UIMARGINR", CVAR_COSMETIC("HUD.Margin.R"), static_cast(ImGui::GetWindowViewport()->Size.x) * -1, 25, "", 0); - UIWidgets::EnhancementSliderInt("Bottom: %dx", "##UIMARGINB", CVAR_COSMETIC("HUD.Margin.B"), static_cast(ImGui::GetWindowViewport()->Size.y / 2) * -1, 25, "", 0); - SetMarginAll("All margins on",true); - UIWidgets::Tooltip("Set most of the elements to use margins\nSome elements with default position will not be affected\nElements without Anchor or Hidden will not be turned on"); - ImGui::SameLine(); - SetMarginAll("All margins off",false); - UIWidgets::Tooltip("Set all of the elements to not use margins"); - ImGui::SameLine(); - ResetPositionAll(); - UIWidgets::Tooltip("Revert every element to use their original position and no margins"); - ImGui::NewLine(); - ImGui::EndTable(); - } + UIWidgets::PushStyleHeader(THEME_COLOR); + ImGui::SeparatorText("General Margins Settings"); + UIWidgets::CVarSliderInt("Top: %dpx", CVAR_COSMETIC("HUD.Margin.T"), + UIWidgets::IntSliderOptions() + .Min(static_cast(ImGui::GetWindowViewport()->Size.y / 2) * -1) + .Max(25) + .DefaultValue(0) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); + UIWidgets::CVarSliderInt("Left: %dpx", CVAR_COSMETIC("HUD.Margin.L"), + UIWidgets::IntSliderOptions() + .Min(-25) + .Max(static_cast(ImGui::GetWindowViewport()->Size.x)) + .DefaultValue(0) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); + UIWidgets::CVarSliderInt("Right: %dpx", CVAR_COSMETIC("HUD.Margin.R"), + UIWidgets::IntSliderOptions() + .Min(static_cast(ImGui::GetWindowViewport()->Size.x) * -1) + .Max(25) + .DefaultValue(0) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); + UIWidgets::CVarSliderInt("Bottom: %dpx", CVAR_COSMETIC("HUD.Margin.B"), + UIWidgets::IntSliderOptions() + .Min(static_cast(ImGui::GetWindowViewport()->Size.y / 2) * -1) + .Max(25) + .DefaultValue(0) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); + SetMarginAll("All margins on", true, + "Set most of the elements to use margins\nSome elements with default position will not be " + "affected\nElements without Anchor or Hidden will not be turned on"); + ImGui::SameLine(); + SetMarginAll("All margins off", false, "Set all of the elements to not use margins"); + ImGui::SameLine(); + ResetPositionAll(); + UIWidgets::Separator(true, true, 2.0f, 2.0f); if (ImGui::CollapsingHeader("Hearts count position")) { if (ImGui::BeginTable("tableHeartsCounts", 1, FlagsTable)) { ImGui::TableSetupColumn("Hearts counts settings", FlagsCell, TablesCellsWidth); @@ -1352,9 +1402,14 @@ void Draw_Placements(){ DrawPositionsRadioBoxes(CVAR_COSMETIC("HUD.HeartsCount")); DrawPositionSlider(CVAR_COSMETIC("HUD.HeartsCount"), -22, static_cast(ImGui::GetWindowViewport()->Size.y), -125, static_cast(ImGui::GetWindowViewport()->Size.x)); DrawScaleSlider(CVAR_COSMETIC("HUD.HeartsCount"), 0.7f); - UIWidgets::EnhancementSliderInt("Heart line length : %d", "##HeartLineLength", CVAR_COSMETIC("HUD.Hearts.LineLength"), 0, 20, "", 10); - UIWidgets::Tooltip("This will set the length of a row of hearts. Set to 0 for unlimited length."); - ImGui::NewLine(); + UIWidgets::CVarSliderInt("Heart line length : %d", CVAR_COSMETIC("HUD.Hearts.LineLength"), + UIWidgets::IntSliderOptions() + .Min(0) + .Max(20) + .DefaultValue(0) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("This will set the length of a row of hearts. Set to 0 for unlimited length.")); ImGui::EndTable(); } } @@ -1364,11 +1419,13 @@ void Draw_Placements(){ Table_InitHeader(false); DrawUseMarginsSlider("Magic meter", CVAR_COSMETIC("HUD.MagicBar")); DrawPositionsRadioBoxes(CVAR_COSMETIC("HUD.MagicBar")); - UIWidgets::EnhancementRadioButton("Anchor to life bar", CVAR_COSMETIC("HUD.MagicBar.PosType"), 5); - UIWidgets::Tooltip("This will make your elements follow the bottom of the life meter"); + UIWidgets::CVarRadioButton( + "Anchor to life bar", CVAR_COSMETIC("HUD.MagicBar.PosType"), 5, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will make your elements follow the bottom of the life meter")); DrawPositionSlider(CVAR_COSMETIC("HUD.MagicBar"), 0, static_cast(ImGui::GetWindowViewport()->Size.y / 2), -5, static_cast(ImGui::GetWindowViewport()->Size.x / 2)); DrawScaleSlider(CVAR_COSMETIC("HUD.MagicBar"), 1.0f); - ImGui::NewLine(); ImGui::EndTable(); } } @@ -1387,7 +1444,6 @@ void Draw_Placements(){ } DrawPositionSlider(CVAR_COSMETIC("HUD.VisualSoA"), 0, static_cast(ImGui::GetWindowViewport()->Size.y / 2), Min_X_VSOA, Max_X_VSOA); DrawScaleSlider(CVAR_COSMETIC("HUD.VisualSoA"), 1.0f); - ImGui::NewLine(); ImGui::EndTable(); } } @@ -1413,7 +1469,6 @@ void Draw_Placements(){ } DrawPositionSlider(CVAR_COSMETIC("HUD.Dpad"), 0, static_cast(ImGui::GetWindowViewport()->Size.y / 2), Min_X_Dpad, Max_X_Dpad); DrawScaleSlider(CVAR_COSMETIC("HUD.Dpad"), 1.0f); - ImGui::NewLine(); ImGui::EndTable(); } } @@ -1440,66 +1495,94 @@ void Draw_Placements(){ ImGui::TableSetupColumn("Enemy Health Bar settings", FlagsCell, TablesCellsWidth); Table_InitHeader(false); std::string posTypeCVar = CVAR_COSMETIC("HUD.EnemyHealthBar.PosType"); - UIWidgets::EnhancementRadioButton("Anchor to Enemy", posTypeCVar.c_str(), ENEMYHEALTH_ANCHOR_ACTOR); - UIWidgets::Tooltip("This will use enemy on screen position"); - UIWidgets::EnhancementRadioButton("Anchor to the top", posTypeCVar.c_str(), ENEMYHEALTH_ANCHOR_TOP); - UIWidgets::Tooltip("This will make your elements follow the top edge of your game window"); - UIWidgets::EnhancementRadioButton("Anchor to the bottom", posTypeCVar.c_str(), ENEMYHEALTH_ANCHOR_BOTTOM); - UIWidgets::Tooltip("This will make your elements follow the bottom edge of your game window"); + UIWidgets::CVarRadioButton("Anchor to Enemy", CVAR_COSMETIC("HUD.EnemyHealthBar.PosType"), + ENEMYHEALTH_ANCHOR_ACTOR, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will use enemy on screen position")); + UIWidgets::CVarRadioButton( + "Anchor to the top", CVAR_COSMETIC("HUD.EnemyHealthBar.PosType"), ENEMYHEALTH_ANCHOR_TOP, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will make your elements follow the top edge of your game window")); + UIWidgets::CVarRadioButton( + "Anchor to the bottom", CVAR_COSMETIC("HUD.EnemyHealthBar.PosType"), ENEMYHEALTH_ANCHOR_BOTTOM, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR) + .Tooltip("This will make your elements follow the bottom edge of your game window")); DrawPositionSlider(CVAR_COSMETIC("HUD.EnemyHealthBar."), -SCREEN_HEIGHT, SCREEN_HEIGHT, -static_cast(ImGui::GetWindowViewport()->Size.x / 2), static_cast(ImGui::GetWindowViewport()->Size.x / 2)); - if (UIWidgets::EnhancementSliderInt("Health Bar Width: %d", "##EnemyHealthBarWidth", CVAR_COSMETIC("HUD.EnemyHealthBar.Width.Value"), 32, 128, "", 64)) { + if (UIWidgets::CVarSliderInt( + "Health Bar Width: %d", CVAR_COSMETIC("HUD.EnemyHealthBar.Width.Value"), + UIWidgets::IntSliderOptions() + .Min(32) + .Max(128) + .DefaultValue(64) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR) + .Tooltip("This will change the width of the health bar"))) { CVarSetInteger(CVAR_COSMETIC("HUD.EnemyHealthBar.Width.Changed"), 1); } - UIWidgets::Tooltip("This will change the width of the health bar"); ImGui::SameLine(); - if (ImGui::Button("Reset##EnemyHealthBarWidth")) { + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (ImGui::CalcTextSize("g").y * 2)); + if (UIWidgets::Button("Reset##EnemyHealthBarWidth", + UIWidgets::ButtonOptions().Size(ImVec2(80, 36)).Padding(ImVec2(5.0f, 0.0f)))) { CVarClear(CVAR_COSMETIC("HUD.EnemyHealthBar.Width.Value")); CVarClear(CVAR_COSMETIC("HUD.EnemyHealthBar.Width.Changed")); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - ImGui::NewLine(); ImGui::EndTable(); } } + UIWidgets::PopStyleHeader(); } void Reset_Option_Single(const char* Button_Title, const char* name) { ImGui::SameLine(); - if (ImGui::Button(Button_Title)) { + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (ImGui::CalcTextSize("g").y * 2)); + if (UIWidgets::Button(Button_Title, + UIWidgets::ButtonOptions().Size(ImVec2(80, 36)).Padding(ImVec2(5.0f, 0.0f)))) { CVarClear(name); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } } void Reset_Option_Double(const char* Button_Title, const char* name) { ImGui::SameLine(); - if (ImGui::Button(Button_Title)) { + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (ImGui::CalcTextSize("g").y * 2)); + if (UIWidgets::Button(Button_Title, + UIWidgets::ButtonOptions().Size(ImVec2(80, 36)).Padding(ImVec2(5.0f, 0.0f)))) { CVarClear((std::string(name) + ".Value").c_str()); CVarClear((std::string(name) + ".Changed").c_str()); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } } void DrawSillyTab() { ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - if (UIWidgets::EnhancementCheckbox("Let It Snow", CVAR_GENERAL("LetItSnow"))) { - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - UIWidgets::Tooltip("Makes snow fall, changes chest texture colors to red and green, etc, for December holidays.\nWill reset on restart outside of December 23-25."); + UIWidgets::CVarCheckbox("Let It Snow", CVAR_GENERAL("LetItSnow"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Makes snow fall, changes chest texture colors to red and green, etc, for December holidays.\nWill reset on restart outside of December 23-25.")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - if (UIWidgets::EnhancementSliderFloat("Link Body Scale: %.3fx", "##Link_BodyScale", CVAR_COSMETIC("Link.BodyScale.Value"), 0.001f, 0.025f, "", 0.01f, true)) { - CVarSetInteger(CVAR_COSMETIC("Link.BodyScale.Changed"), 1); + if (UIWidgets::CVarSliderFloat("Link Body Size", CVAR_COSMETIC("Link.BodySize.Value"), + UIWidgets::FloatSliderOptions() + .Format("%.3f") + .Min(0.001f) + .Max(0.05f) + .DefaultValue(0.01f) + .Step(0.001f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR))) { + CVarSetInteger(CVAR_COSMETIC("Link.BodySize.Changed"), 1); } ImGui::SameLine(); - if (ImGui::Button("Reset##Link_BodyScale")) { - CVarClear(CVAR_COSMETIC("Link.BodyScale.Value")); - CVarClear(CVAR_COSMETIC("Link.BodyScale.Changed")); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (ImGui::CalcTextSize("g").y * 2)); + if (UIWidgets::Button("Reset##Link_BodySize", + UIWidgets::ButtonOptions().Size(ImVec2(80, 36)).Padding(ImVec2(5.0f, 0.0f)))) { + CVarClear(CVAR_COSMETIC("Link.BodySize.Value")); + CVarClear(CVAR_COSMETIC("Link.BodySize.Changed")); if (gPlayState != nullptr) { static Player* player = GET_PLAYER(gPlayState); player->actor.scale.x = 0.01f; @@ -1508,62 +1591,135 @@ void DrawSillyTab() { } } - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); - - if (UIWidgets::EnhancementSliderFloat("Link Head Scale: %.2fx", "##Link_HeadScale", CVAR_COSMETIC("Link.HeadScale.Value"), 0.4f, 4.0f, "", 1.0f, false)) { + UIWidgets::Separator(true, true, 2.0f, 2.0f); + if (UIWidgets::CVarSliderFloat("Link Head Scale", CVAR_COSMETIC("Link.HeadScale.Value"), + UIWidgets::FloatSliderOptions() + .Format("%.1fx") + .Min(0.1f) + .Max(5.0f) + .DefaultValue(1.0f) + .Step(0.1f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR))) { CVarSetInteger(CVAR_COSMETIC("Link.HeadScale.Changed"), 1); } Reset_Option_Double("Reset##Link_HeadScale", CVAR_COSMETIC("Link.HeadScale")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - if (UIWidgets::EnhancementSliderFloat("Link Sword Scale: %f", "##Link_SwordScale", CVAR_COSMETIC("Link.SwordScale.Value"), 1.0f, 2.5f, "", 1.0f, false)) { + if (UIWidgets::CVarSliderFloat("Link Sword Scale", CVAR_COSMETIC("Link.SwordScale.Value"), + UIWidgets::FloatSliderOptions() + .Format("%.1fx") + .Min(0.1f) + .Max(5.0f) + .DefaultValue(1.0f) + .Step(0.1f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR))) { CVarSetInteger(CVAR_COSMETIC("Link.SwordScale.Changed"), 1); } Reset_Option_Double("Reset##Link_SwordScale", CVAR_COSMETIC("Link.SwordScale")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::EnhancementSliderFloat("Bunny Hood Length: %f", "##BunnyHood_EarLength", CVAR_COSMETIC("BunnyHood.EarLength"), -300.0f, 1000.0f, "", 0.0f, false); + UIWidgets::CVarSliderFloat("Bunny Hood Length", CVAR_COSMETIC("BunnyHood.EarLength"), + UIWidgets::FloatSliderOptions() + .Format("%.0f") + .Min(-300.0f) + .Max(1000.0f) + .DefaultValue(0.0f) + .Step(10.0f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); Reset_Option_Single("Reset##BunnyHood_EarLength", CVAR_COSMETIC("BunnyHood.EarLength")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::EnhancementSliderFloat("Bunny Hood Spread: %f", "##BunnyHood_EarSpread", CVAR_COSMETIC("BunnyHood.EarSpread"), -300.0f, 500.0f, "", 0.0f, false); + UIWidgets::CVarSliderFloat("Bunny Hood Spread", CVAR_COSMETIC("BunnyHood.EarSpread"), + UIWidgets::FloatSliderOptions() + .Format("%.0f") + .Min(-300.0f) + .Max(500.0f) + .DefaultValue(0.0f) + .Step(10.0f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); Reset_Option_Single("Reset##BunnyHood_EarSpread", CVAR_COSMETIC("BunnyHood.EarSpread")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::EnhancementSliderFloat("Goron Neck Length: %f", "##Goron_NeckLength", CVAR_COSMETIC("Goron.NeckLength"), 0.0f, 5000.0f, "", 0.0f, false); + UIWidgets::CVarSliderFloat("Goron Neck Length", CVAR_COSMETIC("Goron.NeckLength"), + UIWidgets::FloatSliderOptions() + .Format("%.0f") + .Min(0.0f) + .Max(5000.0f) + .DefaultValue(0.0f) + .Step(10.0f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); Reset_Option_Single("Reset##Goron_NeckLength", CVAR_COSMETIC("Goron.NeckLength")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::EnhancementCheckbox("Unfix Goron Spin", CVAR_COSMETIC("UnfixGoronSpin")); + UIWidgets::CVarCheckbox("Unfix Goron Spin", CVAR_COSMETIC("UnfixGoronSpin"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR)); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::EnhancementSliderFloat("Fairies Size: %f", "##Fairies_Size", CVAR_COSMETIC("Fairies.Size"), 0.25f, 5.0f, "", 1.0f, false); + UIWidgets::CVarSliderFloat("Fairies Size", CVAR_COSMETIC("Fairies.Size"), + UIWidgets::FloatSliderOptions() + .Format("%.1fx") + .Min(0.1f) + .Max(5.0f) + .DefaultValue(1.0f) + .Step(0.1f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); Reset_Option_Single("Reset##Fairies_Size", CVAR_COSMETIC("Fairies.Size")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::EnhancementSliderFloat("N64 Logo Spin Speed: %f", "##N64Logo_SpinSpeed", CVAR_COSMETIC("N64Logo.SpinSpeed"), 0.25f, 5.0f, "", 1.0f, false); + UIWidgets::CVarSliderFloat("N64 Logo Spin Speed", CVAR_COSMETIC("N64Logo.SpinSpeed"), + UIWidgets::FloatSliderOptions() + .Format("%.1fx") + .Min(0.1f) + .Max(5.0f) + .DefaultValue(1.0f) + .Step(0.1f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); Reset_Option_Single("Reset##N64Logo_SpinSpeed", CVAR_COSMETIC("N64Logo.SpinSpeed")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::EnhancementSliderFloat("Moon Size: %f", "##Moon_Size", CVAR_COSMETIC("Moon.Size"), 0.5f, 2.0f, "", 1.0f, false); + UIWidgets::CVarSliderFloat("Moon Size", CVAR_COSMETIC("Moon.Size"), + UIWidgets::FloatSliderOptions() + .Format("%.1fx") + .Min(0.1f) + .Max(5.0f) + .DefaultValue(1.0f) + .Step(0.1f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); Reset_Option_Single("Reset##Moon_Size", CVAR_COSMETIC("Moon.Size")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - if (UIWidgets::EnhancementSliderFloat("Kak Windmill Speed: %f", "##Kak_Windmill_Speed", CVAR_COSMETIC("Kak.Windmill_Speed.Value"), 100.0f, 6000.0f, "", 100.0f, false)) { + if (UIWidgets::CVarSliderFloat("Kak Windmill Speed", CVAR_COSMETIC("Kak.Windmill_Speed.Value"), + UIWidgets::FloatSliderOptions() + .Format("%.0f") + .Min(100.0f) + .Max(6000.0f) + .DefaultValue(100.0f) + .Step(10.0f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR))) { CVarSetInteger(CVAR_COSMETIC("Kak.Windmill_Speed.Changed"), 1); } Reset_Option_Double("Reset##Kak_Windmill_Speed", CVAR_COSMETIC("Kak.Windmill_Speed")); - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); ImGui::EndDisabled(); } @@ -1582,7 +1738,7 @@ void CopyMultipliedColor(CosmeticOption& cosmeticOptionSrc, CosmeticOption& cosm cosmeticOptionTarget.currentColor.z = newColor.b / 255.0f; cosmeticOptionTarget.currentColor.w = newColor.a / 255.0f; - CVarSetColor(cosmeticOptionTarget.cvar, newColor); + CVarSetColor(cosmeticOptionTarget.valuesCvar, newColor); CVarSetInteger((cosmeticOptionTarget.rainbowCvar), 0); CVarSetInteger((cosmeticOptionTarget.changedCvar), 1); } @@ -1642,7 +1798,7 @@ void RandomizeColor(CosmeticOption& cosmeticOption) { cosmeticOption.currentColor.z = newColor.b / 255.0f; cosmeticOption.currentColor.w = newColor.a / 255.0f; - CVarSetColor(cosmeticOption.cvar, newColor); + CVarSetColor(cosmeticOption.valuesCvar, newColor); CVarSetInteger(cosmeticOption.rainbowCvar, 0); CVarSetInteger(cosmeticOption.changedCvar, 1); ApplySideEffects(cosmeticOption); @@ -1658,12 +1814,12 @@ void ResetColor(CosmeticOption& cosmeticOption) { CVarClear(cosmeticOption.changedCvar); CVarClear(cosmeticOption.rainbowCvar); CVarClear(cosmeticOption.lockedCvar); - CVarClear(cosmeticOption.cvar); - CVarClear((std::string(cosmeticOption.cvar) + ".R").c_str()); - CVarClear((std::string(cosmeticOption.cvar) + ".G").c_str()); - CVarClear((std::string(cosmeticOption.cvar) + ".B").c_str()); - CVarClear((std::string(cosmeticOption.cvar) + ".A").c_str()); - CVarClear((std::string(cosmeticOption.cvar) + ".Type").c_str()); + CVarClear(cosmeticOption.valuesCvar); + CVarClear((std::string(cosmeticOption.valuesCvar) + ".R").c_str()); + CVarClear((std::string(cosmeticOption.valuesCvar) + ".G").c_str()); + CVarClear((std::string(cosmeticOption.valuesCvar) + ".B").c_str()); + CVarClear((std::string(cosmeticOption.valuesCvar) + ".A").c_str()); + CVarClear((std::string(cosmeticOption.valuesCvar) + ".Type").c_str()); // This portion should match 1:1 the multiplied colors in `ApplySideEffect()` if (cosmeticOption.label == "Bow Body") { @@ -1709,40 +1865,28 @@ void ResetColor(CosmeticOption& cosmeticOption) { } void DrawCosmeticRow(CosmeticOption& cosmeticOption) { - bool colorChanged; - if (cosmeticOption.supportsAlpha) { - colorChanged = ImGui::ColorEdit4(cosmeticOption.label.c_str(), (float*)&cosmeticOption.currentColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel); - } else { - colorChanged = ImGui::ColorEdit3(cosmeticOption.label.c_str(), (float*)&cosmeticOption.currentColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel); - } - if (colorChanged) { - Color_RGBA8 color; - color.r = static_cast(cosmeticOption.currentColor.x * 255.0f); - color.g = static_cast(cosmeticOption.currentColor.y * 255.0f); - color.b = static_cast(cosmeticOption.currentColor.z * 255.0f); - color.a = static_cast(cosmeticOption.currentColor.w * 255.0f); - - CVarSetColor(cosmeticOption.cvar, color); + if (UIWidgets::CVarColorPicker(cosmeticOption.label.c_str(), cosmeticOption.cvar, + cosmeticOption.defaultColor, + cosmeticOption.supportsAlpha, 0, THEME_COLOR)) { CVarSetInteger((cosmeticOption.rainbowCvar), 0); CVarSetInteger((cosmeticOption.changedCvar), 1); ApplySideEffects(cosmeticOption); ApplyOrResetCustomGfxPatches(); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - ImGui::SameLine(); - ImGui::Text("%s", cosmeticOption.label.c_str()); //the longest option name - ImGui::SameLine((ImGui::CalcTextSize("Message Light Blue (None No Shadow) Color").x * 1.0f) + 60.0f); - if (ImGui::Button(("Random##" + cosmeticOption.label).c_str())) { + ImGui::SameLine((ImGui::CalcTextSize("Message Light Blue (None No Shadow)").x * 1.0f) + 60.0f); + if (UIWidgets::Button( + ("Random##" + cosmeticOption.label).c_str(), + UIWidgets::ButtonOptions().Size(ImVec2(80, 31)).Padding(ImVec2(2.0f, 0.0f)).Color(THEME_COLOR))) { RandomizeColor(cosmeticOption); ApplyOrResetCustomGfxPatches(); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } if (cosmeticOption.supportsRainbow) { ImGui::SameLine(); - bool isRainbow = (bool)CVarGetInteger((cosmeticOption.rainbowCvar), 0); - if (ImGui::Checkbox(("Rainbow##" + cosmeticOption.label).c_str(), &isRainbow)) { - CVarSetInteger((cosmeticOption.rainbowCvar), isRainbow); + if (UIWidgets::CVarCheckbox(("Rainbow##" + cosmeticOption.label).c_str(), cosmeticOption.rainbowCvar, + UIWidgets::CheckboxOptions().Color(THEME_COLOR))) { CVarSetInteger((cosmeticOption.changedCvar), 1); ApplySideEffects(cosmeticOption); ApplyOrResetCustomGfxPatches(); @@ -1750,14 +1894,14 @@ void DrawCosmeticRow(CosmeticOption& cosmeticOption) { } } ImGui::SameLine(); - bool isLocked = (bool)CVarGetInteger((cosmeticOption.lockedCvar), 0); - if (ImGui::Checkbox(("Locked##" + cosmeticOption.label).c_str(), &isLocked)) { - CVarSetInteger((cosmeticOption.lockedCvar), isLocked); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } + + UIWidgets::CVarCheckbox(("Locked##" + cosmeticOption.label).c_str(), cosmeticOption.lockedCvar, + UIWidgets::CheckboxOptions().Color(THEME_COLOR)); + if (CVarGetInteger((cosmeticOption.changedCvar), 0)) { ImGui::SameLine(); - if (ImGui::Button(("Reset##" + cosmeticOption.label).c_str())) { + if (UIWidgets::Button(("Reset##" + cosmeticOption.label).c_str(), + UIWidgets::ButtonOptions().Size(ImVec2(80, 31)).Padding(ImVec2(2.0f, 0.0f)))) { ResetColor(cosmeticOption); ApplyOrResetCustomGfxPatches(); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); @@ -1769,32 +1913,35 @@ void DrawCosmeticGroup(CosmeticGroup cosmeticGroup) { std::string label = groupLabels.at(cosmeticGroup); ImGui::Text("%s", label.c_str()); // the longest option name - ImGui::SameLine((ImGui::CalcTextSize("Message Light Blue (None No Shadow) Color").x * 1.0f) + 60.0f); - if (ImGui::Button(("Random##" + label).c_str())) { + ImGui::SameLine((ImGui::CalcTextSize("Message Light Blue (None No Shadow)").x * 1.0f) + 60.0f); + if (UIWidgets::Button(("Random##" + label).c_str(), + UIWidgets::ButtonOptions().Size(ImVec2(80, 31)).Padding(ImVec2(2.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { - if (cosmeticOption.group == cosmeticGroup && (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0)) && !CVarGetInteger(cosmeticOption.lockedCvar, 0)) { + if (cosmeticOption.group == cosmeticGroup && + (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0)) && + !CVarGetInteger(cosmeticOption.lockedCvar, 0)) { RandomizeColor(cosmeticOption); } } ApplyOrResetCustomGfxPatches(); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } ImGui::SameLine(); - if (ImGui::Button(("Reset##" + label).c_str())) { + if (UIWidgets::Button(("Reset##" + label).c_str(), + UIWidgets::ButtonOptions().Size(ImVec2(80, 31)).Padding(ImVec2(2.0f, 0.0f)))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (cosmeticOption.group == cosmeticGroup && !CVarGetInteger(cosmeticOption.lockedCvar, 0)) { ResetColor(cosmeticOption); } } ApplyOrResetCustomGfxPatches(); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } + UIWidgets::Spacer(); for (auto& [id, cosmeticOption] : cosmeticOptions) { if (cosmeticOption.group == cosmeticGroup && (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0))) { DrawCosmeticRow(cosmeticOption); } } - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); } static const char* colorSchemes[2] = { @@ -1807,193 +1954,199 @@ void CosmeticsEditorWindow::ApplyDungeonKeyColors() { ResetColor(cosmeticOptions.at("Key.KeyringRing")); // Forest Temple - CVarSetColor(cosmeticOptions["Key.ForestSmallBody"].cvar, { 4, 195, 46, 255 }); + CVarSetColor(cosmeticOptions["Key.ForestSmallBody"].valuesCvar, { 4, 195, 46, 255 }); CVarSetInteger(cosmeticOptions["Key.ForestSmallBody"].changedCvar, 1); cosmeticOptions["Key.ForestSmallBody"].currentColor = { 4 / 255.0f, 195 / 255.0f, 46 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.ForestSmallEmblem")); ResetColor(cosmeticOptions.at("Key.ForestBossBody")); - CVarSetColor(cosmeticOptions["Key.ForestBossGem"].cvar, { 0, 255, 0, 255 }); + CVarSetColor(cosmeticOptions["Key.ForestBossGem"].valuesCvar, { 0, 255, 0, 255 }); CVarSetInteger(cosmeticOptions["Key.ForestBossGem"].changedCvar, 1); cosmeticOptions["Key.ForestBossGem"].currentColor = { 0, 255 / 255.0f, 0, 255 / 255.0f }; // Fire Temple - CVarSetColor(cosmeticOptions["Key.FireSmallBody"].cvar, { 237, 95, 95, 255 }); + CVarSetColor(cosmeticOptions["Key.FireSmallBody"].valuesCvar, { 237, 95, 95, 255 }); CVarSetInteger(cosmeticOptions["Key.FireSmallBody"].changedCvar, 1); cosmeticOptions["Key.FireSmallBody"].currentColor = { 237 / 255.0f, 95 / 255.0f, 95 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.FireSmallEmblem")); ResetColor(cosmeticOptions.at("Key.FireBossBody")); - CVarSetColor(cosmeticOptions["Key.FireBossGem"].cvar, { 255, 30, 0, 255 }); + CVarSetColor(cosmeticOptions["Key.FireBossGem"].valuesCvar, { 255, 30, 0, 255 }); CVarSetInteger(cosmeticOptions["Key.FireBossGem"].changedCvar, 1); cosmeticOptions["Key.FireBossGem"].currentColor = { 255 / 255.0f, 30 / 255.0f, 0, 255 / 255.0f }; // Water Temple - CVarSetColor(cosmeticOptions["Key.WaterSmallBody"].cvar, { 85, 180, 223, 255 }); + CVarSetColor(cosmeticOptions["Key.WaterSmallBody"].valuesCvar, { 85, 180, 223, 255 }); CVarSetInteger(cosmeticOptions["Key.WaterSmallBody"].changedCvar, 1); cosmeticOptions["Key.WaterSmallBody"].currentColor = { 85 / 255.0f, 180 / 255.0f, 223 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.WaterSmallEmblem")); ResetColor(cosmeticOptions.at("Key.WaterBossBody")); - CVarSetColor(cosmeticOptions["Key.WaterBossGem"].cvar, { 0, 137, 255, 255 }); + CVarSetColor(cosmeticOptions["Key.WaterBossGem"].valuesCvar, { 0, 137, 255, 255 }); CVarSetInteger(cosmeticOptions["Key.WaterBossGem"].changedCvar, 1); cosmeticOptions["Key.WaterBossGem"].currentColor = { 0, 137 / 255.0f, 255 / 255.0f, 255 / 255.0f }; // Spirit Temple - CVarSetColor(cosmeticOptions["Key.SpiritSmallBody"].cvar, { 222, 158, 47, 255 }); + CVarSetColor(cosmeticOptions["Key.SpiritSmallBody"].valuesCvar, { 222, 158, 47, 255 }); CVarSetInteger(cosmeticOptions["Key.SpiritSmallBody"].changedCvar, 1); cosmeticOptions["Key.SpiritSmallBody"].currentColor = { 222 / 255.0f, 158 / 255.0f, 47 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.SpiritSmallEmblem")); ResetColor(cosmeticOptions.at("Key.SpiritBossBody")); - CVarSetColor(cosmeticOptions["Key.SpiritBossGem"].cvar, { 255, 85, 0, 255 }); + CVarSetColor(cosmeticOptions["Key.SpiritBossGem"].valuesCvar, { 255, 85, 0, 255 }); CVarSetInteger(cosmeticOptions["Key.SpiritBossGem"].changedCvar, 1); cosmeticOptions["Key.SpiritBossGem"].currentColor = { 255 / 255.0f, 85 / 255.0f, 0, 255 / 255.0f }; // Shadow Temple - CVarSetColor(cosmeticOptions["Key.ShadowSmallBody"].cvar, { 126, 16, 177, 255 }); + CVarSetColor(cosmeticOptions["Key.ShadowSmallBody"].valuesCvar, { 126, 16, 177, 255 }); CVarSetInteger(cosmeticOptions["Key.ShadowSmallBody"].changedCvar, 1); cosmeticOptions["Key.ShadowSmallBody"].currentColor = { 126 / 255.0f, 16 / 255.0f, 177 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.ShadowSmallEmblem")); ResetColor(cosmeticOptions.at("Key.ShadowBossBody")); - CVarSetColor(cosmeticOptions["Key.ShadowBossGem"].cvar, { 153, 0, 255, 255 }); + CVarSetColor(cosmeticOptions["Key.ShadowBossGem"].valuesCvar, { 153, 0, 255, 255 }); CVarSetInteger(cosmeticOptions["Key.ShadowBossGem"].changedCvar, 1); cosmeticOptions["Key.ShadowBossGem"].currentColor = { 153 / 255.0f, 0, 255 / 255.0f, 255 / 255.0f }; // Ganon's Tower - CVarSetColor(cosmeticOptions["Key.GanonsSmallBody"].cvar, { 80, 80, 80, 255 }); + CVarSetColor(cosmeticOptions["Key.GanonsSmallBody"].valuesCvar, { 80, 80, 80, 255 }); CVarSetInteger(cosmeticOptions["Key.GanonsSmallBody"].changedCvar, 1); cosmeticOptions["Key.GanonsSmallBody"].currentColor = { 80 / 255.0f, 80 / 255.0f, 80 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.GanonsSmallEmblem")); - CVarSetColor(cosmeticOptions["Key.GanonsBossBody"].cvar, { 80, 80, 80, 255 }); + CVarSetColor(cosmeticOptions["Key.GanonsBossBody"].valuesCvar, { 80, 80, 80, 255 }); CVarSetInteger(cosmeticOptions["Key.GanonsBossBody"].changedCvar, 1); cosmeticOptions["Key.GanonsBossBody"].currentColor = { 80 / 255.0f, 80 / 255.0f, 80 / 255.0f, 255 / 255.0f }; - CVarSetColor(cosmeticOptions["Key.GanonsBossGem"].cvar, { 255, 0, 0, 255 }); + CVarSetColor(cosmeticOptions["Key.GanonsBossGem"].valuesCvar, { 255, 0, 0, 255 }); CVarSetInteger(cosmeticOptions["Key.GanonsBossGem"].changedCvar, 1); cosmeticOptions["Key.GanonsBossGem"].currentColor = { 255 / 255.0f, 0, 0, 255 / 255.0f }; // Bottom of the Well - CVarSetColor(cosmeticOptions["Key.WellSmallBody"].cvar, { 227, 110, 255, 255 }); + CVarSetColor(cosmeticOptions["Key.WellSmallBody"].valuesCvar, { 227, 110, 255, 255 }); CVarSetInteger(cosmeticOptions["Key.WellSmallBody"].changedCvar, 1); cosmeticOptions["Key.WellSmallBody"].currentColor = { 227 / 255.0f, 110 / 255.0f, 255 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.WellSmallEmblem")); // Gerudo Training Ground - CVarSetColor(cosmeticOptions["Key.GTGSmallBody"].cvar, { 221, 212, 60, 255 }); + CVarSetColor(cosmeticOptions["Key.GTGSmallBody"].valuesCvar, { 221, 212, 60, 255 }); CVarSetInteger(cosmeticOptions["Key.GTGSmallBody"].changedCvar, 1); cosmeticOptions["Key.GTGSmallBody"].currentColor = { 221 / 255.0f, 212 / 255.0f, 60 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.GTGSmallEmblem")); // Gerudo Fortress - CVarSetColor(cosmeticOptions["Key.FortSmallBody"].cvar, { 255, 255, 255, 255 }); + CVarSetColor(cosmeticOptions["Key.FortSmallBody"].valuesCvar, { 255, 255, 255, 255 }); CVarSetInteger(cosmeticOptions["Key.FortSmallBody"].changedCvar, 1); cosmeticOptions["Key.FortSmallBody"].currentColor = { 255 / 255.0f, 255 / 255.0f, 255 / 255.0f, 255 / 255.0f }; ResetColor(cosmeticOptions.at("Key.FortSmallEmblem")); } void CosmeticsEditorWindow::DrawElement() { - ImGui::Text("Color Scheme"); - ImGui::SameLine(); - UIWidgets::EnhancementCombobox(CVAR_COSMETIC("DefaultColorScheme"), colorSchemes, COLORSCHEME_N64); - UIWidgets::EnhancementCheckbox("Advanced Mode", CVAR_COSMETIC("AdvancedMode")); - UIWidgets::InsertHelpHoverText( - "Some cosmetic options may not apply if you have any mods that provide custom models for the cosmetic option.\n\n" - "For example, if you have custom Link model, then the Link's Hair color option will most likely not apply." - ); - + UIWidgets::CVarCombobox("Color Scheme", CVAR_COSMETIC("DefaultColorScheme"), colorSchemes, + UIWidgets::ComboboxOptions() + .DefaultIndex(COLORSCHEME_N64) + .Color(THEME_COLOR) + .LabelPosition(UIWidgets::LabelPositions::Near) + .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); + UIWidgets::CVarCheckbox("Sync Rainbow colors", CVAR_COSMETIC("RainbowSync"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR)); + UIWidgets::CVarSliderFloat("Rainbow Speed", CVAR_COSMETIC("RainbowSpeed"), + UIWidgets::FloatSliderOptions() + .Format("%.2f") + .Min(0.01f) + .Max(1.0f) + .DefaultValue(0.6f) + .Step(0.01f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Randomize All on New Scene", CVAR_COSMETIC("RandomizeAllOnNewScene"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip("Enables randomizing all unlocked cosmetics when you enter a new scene.")); + UIWidgets::CVarCheckbox( + "Advanced Mode", CVAR_COSMETIC("AdvancedMode"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .Tooltip( + "Some cosmetic options may not apply if you have any mods that provide custom models for the cosmetic " + "option.\n\n" + "For example, if you have custom Link model, then the Link's Hair color option will most likely not " + "apply.")); if (CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0)) { - if (ImGui::Button("Lock All Advanced", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) { + if (UIWidgets::Button("Lock All Advanced", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (cosmeticOption.advancedOption) { CVarSetInteger(cosmeticOption.lockedCvar, 1); } } - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } ImGui::SameLine(); - if (ImGui::Button("Unlock All Advanced", ImVec2(ImGui::GetContentRegionAvail().x, 30.0f))) { + if (UIWidgets::Button("Unlock All Advanced", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (cosmeticOption.advancedOption) { CVarSetInteger(cosmeticOption.lockedCvar, 0); } } - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } } - UIWidgets::EnhancementCheckbox("Sync Rainbow colors", CVAR_COSMETIC("RainbowSync")); - UIWidgets::EnhancementSliderFloat("Rainbow Speed: %.3f", "##rainbowSpeed", CVAR_COSMETIC("RainbowSpeed"), 0.03f, 1.0f, "", 0.6f, false, true); - UIWidgets::EnhancementCheckbox("Randomize All on New Scene", CVAR_COSMETIC("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))) { + if (UIWidgets::Button("Randomize All", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { CosmeticsEditor_RandomizeAll(); } ImGui::SameLine(); - if (ImGui::Button("Reset All", ImVec2(ImGui::GetContentRegionAvail().x, 30.0f))) { - for (auto& [id, cosmeticOption] : cosmeticOptions) { - if (!CVarGetInteger(cosmeticOption.lockedCvar, 0)) { - ResetColor(cosmeticOption); - } - } + if (UIWidgets::Button("Reset All", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { + CVarClearBlock("gCosmetics"); ApplyOrResetCustomGfxPatches(); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - - if (ImGui::Button("Lock All", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) { + if (UIWidgets::Button("Lock All", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0)) { CVarSetInteger(cosmeticOption.lockedCvar, 1); } } - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } ImGui::SameLine(); - if (ImGui::Button("Unlock All", ImVec2(ImGui::GetContentRegionAvail().x, 30.0f))) { + if (UIWidgets::Button("Unlock All", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0)) { CVarSetInteger(cosmeticOption.lockedCvar, 0); } } - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - if (ImGui::Button("Rainbow All", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) { + if (UIWidgets::Button("Rainbow All", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { - if ( - !CVarGetInteger(cosmeticOption.lockedCvar, 0) && - ( - !cosmeticOption.advancedOption || - CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0) - ) - ) { + if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && + (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0))) { CVarSetInteger(cosmeticOption.rainbowCvar, 1); CVarSetInteger(cosmeticOption.changedCvar, 1); } } - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } ImGui::SameLine(); - if (ImGui::Button("Un-Rainbow All", ImVec2(ImGui::GetContentRegionAvail().x, 30.0f))) { + if (UIWidgets::Button("Un-Rainbow All", + UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { - if ( - !CVarGetInteger(cosmeticOption.lockedCvar, 0) && - ( - !cosmeticOption.advancedOption || - CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0) - ) - ) { + if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && + (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0))) { CVarSetInteger(cosmeticOption.rainbowCvar, 0); } } - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } + UIWidgets::Spacer(3.0f); + + UIWidgets::PushStyleTabs(THEME_COLOR); if (ImGui::BeginTabBar("CosmeticsContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("Link & Items")) { - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); DrawCosmeticGroup(COSMETICS_GROUP_LINK); DrawCosmeticGroup(COSMETICS_GROUP_GLOVES); @@ -2006,14 +2159,15 @@ void CosmeticsEditorWindow::DrawElement() { if (ImGui::BeginTabItem("Keys")) { - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); - if (ImGui::Button("Give all keys dungeon-specific colors", ImVec2(300.0f, 30.0f))) { + if (UIWidgets::Button( + "Give all keys dungeon-specific colors", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { ApplyDungeonKeyColors(); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); DrawCosmeticGroup(COSMETICS_GROUP_KEYRING); DrawCosmeticGroup(COSMETICS_GROUP_SMALL_KEYS); @@ -2024,29 +2178,37 @@ void CosmeticsEditorWindow::DrawElement() { if (ImGui::BeginTabItem("Effects")) { - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); DrawCosmeticGroup(COSMETICS_GROUP_MAGIC); DrawCosmeticGroup(COSMETICS_GROUP_ARROWS); DrawCosmeticGroup(COSMETICS_GROUP_SPIN_ATTACK); DrawCosmeticGroup(COSMETICS_GROUP_TRAILS); - if (UIWidgets::EnhancementSliderInt("Trails Duration: %d", "##Trails_Duration", CVAR_COSMETIC("Trails.Duration.Value"), 2, 20, "", 4)) { + if (UIWidgets::CVarSliderInt("Trails Duration: %d", CVAR_COSMETIC("Trails.Duration.Value"), + UIWidgets::IntSliderOptions() + .Min(2) + .Max(20) + .DefaultValue(4) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR))) { CVarSetInteger(CVAR_COSMETIC("Trails.Duration.Changed"), 1); } ImGui::SameLine(); - if (ImGui::Button("Reset##Trails_Duration")) { + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (ImGui::CalcTextSize("g").y * 2)); + if (UIWidgets::Button("Reset##Trails_Duration", UIWidgets::ButtonOptions() + .Size(ImVec2(80, 36)) + .Padding(ImVec2(5.0f, 0.0f)))) { CVarClear(CVAR_COSMETIC("Trails.Duration.Value")); CVarClear(CVAR_COSMETIC("Trails.Duration.Changed")); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("World & NPCs")) { - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); DrawCosmeticGroup(COSMETICS_GROUP_WORLD); DrawCosmeticGroup(COSMETICS_GROUP_NAVI); @@ -2060,7 +2222,7 @@ void CosmeticsEditorWindow::DrawElement() { } if (ImGui::BeginTabItem("HUD")) { - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); + UIWidgets::Separator(true, true, 2.0f, 2.0f); DrawCosmeticGroup(COSMETICS_GROUP_HUD); DrawCosmeticGroup(COSMETICS_GROUP_TITLE); @@ -2074,6 +2236,7 @@ void CosmeticsEditorWindow::DrawElement() { if (CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0)) { if (ImGui::BeginTabItem("Pause Menu")) { + UIWidgets::Separator(true, true, 2.0f, 2.0f); DrawCosmeticGroup(COSMETICS_GROUP_KALEIDO); ImGui::EndTabItem(); } @@ -2081,12 +2244,14 @@ void CosmeticsEditorWindow::DrawElement() { if (CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0)) { if (ImGui::BeginTabItem("Message")) { + UIWidgets::Separator(true, true, 2.0f, 2.0f); DrawCosmeticGroup(COSMETICS_GROUP_MESSAGE); ImGui::EndTabItem(); } } ImGui::EndTabBar(); } + UIWidgets::PopStyleTabs(); } void RegisterOnLoadGameHook() { @@ -2113,7 +2278,7 @@ void CosmeticsEditorWindow::InitElement() { // Convert the `current color` into the format that the ImGui color picker expects for (auto& [id, cosmeticOption] : cosmeticOptions) { Color_RGBA8 defaultColor = {cosmeticOption.defaultColor.r, cosmeticOption.defaultColor.g, cosmeticOption.defaultColor.b, cosmeticOption.defaultColor.a}; - Color_RGBA8 cvarColor = CVarGetColor(cosmeticOption.cvar, defaultColor); + Color_RGBA8 cvarColor = CVarGetColor(cosmeticOption.valuesCvar, defaultColor); cosmeticOption.currentColor.x = cvarColor.r / 255.0f; cosmeticOption.currentColor.y = cvarColor.g / 255.0f; diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h index c9a28ef29..9c3dd76ba 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h @@ -52,7 +52,6 @@ static float TablesCellsWidth = 300.0f; static ImGuiTableColumnFlags FlagsTable = ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV; static ImGuiTableColumnFlags FlagsCell = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_NoSort; -ImVec4 GetRandomValue(); void CosmeticsEditor_RandomizeAll(); void CosmeticsEditor_RandomizeGroup(CosmeticGroup group); void CosmeticsEditor_ResetAll(); diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index 67547437d..33eed43f9 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -79,6 +79,7 @@ static bool ActorSpawnHandler(std::shared_ptr Console, const std: if (args[8][0] != ',') { spawnPoint.rot.z = std::stoi(args[8]); } + [[fallthrough]]; case 6: if (args[3][0] != ',') { spawnPoint.pos.x = std::stoi(args[3]); diff --git a/soh/soh/Enhancements/debugger/MessageViewer.cpp b/soh/soh/Enhancements/debugger/MessageViewer.cpp index 589dbaf4a..aabe9ad61 100644 --- a/soh/soh/Enhancements/debugger/MessageViewer.cpp +++ b/soh/soh/Enhancements/debugger/MessageViewer.cpp @@ -1,6 +1,9 @@ #include "MessageViewer.h" -#include +#include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" +#include "soh/OTRGlobals.h" + #include #include "../custom-message/CustomMessageManager.h" @@ -13,6 +16,8 @@ extern "C" u8 sMessageHasSetSfx; +using namespace UIWidgets; + void MessageViewer::InitElement() { CustomMessageManager::Instance->AddCustomMessageTable(TABLE_ID); mTableIdBuf = static_cast(calloc(MAX_STRING_SIZE, sizeof(char))); @@ -23,6 +28,7 @@ void MessageViewer::InitElement() { void MessageViewer::DrawElement() { ImGui::Text("Table ID"); ImGui::SameLine(); + PushStyleInput(THEME_COLOR); ImGui::InputText("##TableID", mTableIdBuf, MAX_STRING_SIZE, ImGuiInputTextFlags_CallbackCharFilter, UIWidgets::TextFilters::FilterAlphaNum); UIWidgets::InsertHelpHoverText("Leave blank for vanilla table"); ImGui::Text("Text ID"); @@ -38,6 +44,8 @@ void MessageViewer::DrawElement() { UIWidgets::InsertHelpHoverText("Hexadecimal Text ID of the message to load. Hexadecimal digits only (0-9/A-F)."); break; } + PopStyleInput(); + PushStyleCheckbox(THEME_COLOR); if (ImGui::RadioButton("Hexadecimal", &mTextIdBase, HEXADECIMAL)) { memset(mTextIdBuf, 0, sizeof(char) * MAX_STRING_SIZE); } @@ -45,8 +53,10 @@ void MessageViewer::DrawElement() { if (ImGui::RadioButton("Decimal", &mTextIdBase, DECIMAL)) { memset(mTextIdBuf, 0, sizeof(char) * MAX_STRING_SIZE); } + PopStyleCheckbox(); ImGui::Text("Language"); ImGui::SameLine(); + PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("##Language", mLanguages[mLanguage])) { // ReSharper disable CppDFAUnreachableCode for (size_t i = 0; i < mLanguages.size(); i++) { @@ -58,7 +68,9 @@ void MessageViewer::DrawElement() { } ImGui::EndCombo(); } + PopStyleCombobox(); UIWidgets::InsertHelpHoverText("Which language to load from the selected text ID"); + PushStyleButton(THEME_COLOR); if (ImGui::Button("Display Message##ExistingMessage")) { mDisplayExistingMessageClicked = true; } @@ -66,11 +78,13 @@ void MessageViewer::DrawElement() { UIWidgets::InsertHelpHoverText("Enter a string using Custom Message Syntax to preview it in-game. " "Any newline (\\n) characters inserted by the Enter key will be stripped " "from the output."); + PushStyleInput(THEME_COLOR); ImGui::InputTextMultiline("##CustomMessage", mCustomMessageBuf, MAX_STRING_SIZE); + PopStyleInput(); if (ImGui::Button("Display Message##CustomMessage")) { mDisplayCustomMessageClicked = true; } - // ReSharper restore CppDFAUnreachableCode + PopStyleButton(); } void MessageViewer::UpdateElement() { diff --git a/soh/soh/Enhancements/debugger/actorViewer.cpp b/soh/soh/Enhancements/debugger/actorViewer.cpp index 8aac60954..767ff2723 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -1,6 +1,7 @@ #include "actorViewer.h" #include "../../util.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "soh/ActorDB.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/nametag.h" @@ -59,6 +60,8 @@ std::array acMapping = { "Chest" }; +using namespace UIWidgets; + typedef enum { ACTORVIEWER_NAMETAGS_NONE, ACTORVIEWER_NAMETAGS_DESC, @@ -70,29 +73,18 @@ const std::string GetActorDescription(u16 id) { return ActorDB::Instance->RetrieveEntry(id).entry.valid ? ActorDB::Instance->RetrieveEntry(id).entry.desc : "???"; } -template void DrawGroupWithBorder(T&& drawFunc) { +template void DrawGroupWithBorder(T&& drawFunc, std::string section) { // First group encapsulates the inner portion and border - ImGui::BeginGroup(); - - ImVec2 padding = ImGui::GetStyle().FramePadding; - ImVec2 p0 = ImGui::GetCursorScreenPos(); - ImGui::SetCursorScreenPos(ImVec2(p0.x + padding.x, p0.y + padding.y)); + ImGui::BeginChild(std::string("##" + section).c_str(), ImVec2(0, 0), + ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY); // Second group encapsulates just the inner portion ImGui::BeginGroup(); - + ImGui::AlignTextToFramePadding(); drawFunc(); - - ImGui::Dummy(padding); ImGui::EndGroup(); - ImVec2 p1 = ImGui::GetItemRectMax(); - p1.x += padding.x; - ImVec4 borderCol = ImGui::GetStyle().Colors[ImGuiCol_Border]; - ImGui::GetWindowDrawList()->AddRect( - p0, p1, IM_COL32(borderCol.x * 255, borderCol.y * 255, borderCol.z * 255, borderCol.w * 255)); - - ImGui::EndGroup(); + ImGui::EndChild(); } void PopulateActorDropdown(int i, std::vector& data) { @@ -938,7 +930,7 @@ void ActorViewerWindow::DrawElement() { static std::string filler = "Please select"; static std::vector list; static u16 lastSceneId = 0; - static char searchString[64] = ""; + static std::string searchString = ""; static s16 currentSelectedInDropdown; static std::vector actors; @@ -951,13 +943,13 @@ void ActorViewerWindow::DrawElement() { filler = "Please Select"; list.clear(); needs_reset = false; - for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) { - searchString[i] = 0; - } + searchString = ""; currentSelectedInDropdown = -1; actors.clear(); } lastSceneId = gPlayState->sceneNum; + + PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("Actor Type", acMapping[category])) { for (int i = 0; i < acMapping.size(); i++) { if (ImGui::Selectable(acMapping[i])) { @@ -990,7 +982,9 @@ void ActorViewerWindow::DrawElement() { } ImGui::EndCombo(); } + PopStyleCombobox(); + PushStyleHeader(THEME_COLOR); if (ImGui::TreeNode("Selected Actor")) { DrawGroupWithBorder([&]() { ImGui::Text("Name: %s", ActorDB::Instance->RetrieveEntry(display->id).name.c_str()); @@ -998,46 +992,52 @@ void ActorViewerWindow::DrawElement() { ImGui::Text("Category: %s", acMapping[display->category]); ImGui::Text("ID: %d", display->id); ImGui::Text("Parameters: %d", display->params); - }); - + }, "Selected Actor"); + ImGui::SameLine(); ImGui::PushItemWidth(ImGui::GetFontSize() * 6); DrawGroupWithBorder([&]() { + ImGui::PushItemWidth(ImGui::GetFontSize() * 6); + PushStyleInput(THEME_COLOR); ImGui::Text("Actor Position"); - ImGui::InputScalar("x pos", ImGuiDataType_Float, &display->world.pos.x); - ImGui::SameLine(); - ImGui::InputScalar("y pos", ImGuiDataType_Float, &display->world.pos.y); - ImGui::SameLine(); - ImGui::InputScalar("z pos", ImGuiDataType_Float, &display->world.pos.z); - }); - + ImGui::InputScalar("X##CurPos", ImGuiDataType_Float, &display->world.pos.x); + ImGui::InputScalar("Y##CurPos", ImGuiDataType_Float, &display->world.pos.y); + ImGui::InputScalar("Z##CurPos", ImGuiDataType_Float, &display->world.pos.z); + ImGui::PopItemWidth(); + PopStyleInput(); + }, "Actor Position"); + ImGui::SameLine(); DrawGroupWithBorder([&]() { + PushStyleInput(THEME_COLOR); + ImGui::PushItemWidth(ImGui::GetFontSize() * 6); ImGui::Text("Actor Rotation"); - ImGui::InputScalar("x rot", ImGuiDataType_S16, &display->world.rot.x); - ImGui::SameLine(); - ImGui::InputScalar("y rot", ImGuiDataType_S16, &display->world.rot.y); - ImGui::SameLine(); - ImGui::InputScalar("z rot", ImGuiDataType_S16, &display->world.rot.z); - }); + ImGui::InputScalar("X##CurRot", ImGuiDataType_S16, &display->world.rot.x); + ImGui::InputScalar("Y##CurRot", ImGuiDataType_S16, &display->world.rot.y); + ImGui::InputScalar("Z##CurRot", ImGuiDataType_S16, &display->world.rot.z); + ImGui::PopItemWidth(); + PopStyleInput(); + }, "Actor Rotation"); if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health); + PopStyleInput(); UIWidgets::InsertHelpHoverText("Some actors might not use this!"); } DrawGroupWithBorder([&]() { ImGui::Text("flags"); UIWidgets::DrawFlagArray32("flags", display->flags); - }); + }, "flags"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("bgCheckFlags"); UIWidgets::DrawFlagArray16("bgCheckFlags", display->bgCheckFlags); - }); + }, "bgCheckFlags"); - if (ImGui::Button("Refresh")) { + if (Button("Refresh", ButtonOptions().Color(THEME_COLOR))) { PopulateActorDropdown(category, list); switch (rm) { case INTERACT: @@ -1053,13 +1053,13 @@ void ActorViewerWindow::DrawElement() { } } - if (ImGui::Button("Go to Actor")) { + if (Button("Go to Actor", ButtonOptions().Color(THEME_COLOR))) { Player* player = GET_PLAYER(gPlayState); Math_Vec3f_Copy(&player->actor.world.pos, &display->world.pos); Math_Vec3f_Copy(&player->actor.home.pos, &player->actor.world.pos); } - if (ImGui::Button("Fetch from Target")) { + if (Button("Fetch from Target", ButtonOptions().Color(THEME_COLOR).Tooltip("Grabs actor with target arrow above it. You might need C-Up for enemies"))) { Player* player = GET_PLAYER(gPlayState); fetch = player->talkActor; if (fetch != NULL) { @@ -1069,8 +1069,7 @@ void ActorViewerWindow::DrawElement() { rm = TARGET; } } - UIWidgets::InsertHelpHoverText("Grabs actor with target arrow above it. You might need C-Up for enemies"); - if (ImGui::Button("Fetch from Held")) { + if (Button("Fetch from Held", ButtonOptions().Color(THEME_COLOR).Tooltip("Grabs actor that Link is holding"))) { Player* player = GET_PLAYER(gPlayState); fetch = player->heldActor; if (fetch != NULL) { @@ -1080,8 +1079,7 @@ void ActorViewerWindow::DrawElement() { rm = HELD; } } - UIWidgets::InsertHelpHoverText("Grabs actor that Link is holding"); - if (ImGui::Button("Fetch from Interaction")) { + if (Button("Fetch from Interaction", ButtonOptions().Color(THEME_COLOR).Tooltip("Grabs actor from \"interaction range\""))) { Player* player = GET_PLAYER(gPlayState); fetch = player->interactRangeActor; if (fetch != NULL) { @@ -1091,21 +1089,21 @@ void ActorViewerWindow::DrawElement() { rm = INTERACT; } } - UIWidgets::InsertHelpHoverText("Grabs actor from \"interaction range\""); ImGui::TreePop(); } if (ImGui::TreeNode("New...")) { - ImGui::PushItemWidth(ImGui::GetFontSize() * 10); + //ImGui::PushItemWidth(ImGui::GetFontSize() * 10); - if (ImGui::InputText("Search Actor", searchString, ARRAY_COUNT(searchString))) { - actors = GetActorsWithDescriptionContainingString(std::string(searchString)); + if (InputString("Search Actor", &searchString, InputOptions().Color(THEME_COLOR))) { + actors = GetActorsWithDescriptionContainingString(searchString); currentSelectedInDropdown = -1; } - if (searchString[0] != 0 && !actors.empty()) { + if (!SohUtils::IsStringEmpty(searchString) && !actors.empty()) { std::string preview = currentSelectedInDropdown == -1 ? "Please Select" : ActorDB::Instance->RetrieveEntry(actors[currentSelectedInDropdown]).desc; + PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("Results", preview.c_str())) { for (u8 i = 0; i < actors.size(); i++) { if (ImGui::Selectable( @@ -1118,6 +1116,7 @@ void ActorViewerWindow::DrawElement() { } ImGui::EndCombo(); } + PopStyleCombobox(); } ImGui::Text("%s", GetActorDescription(newActor.id).c_str()); @@ -1125,44 +1124,51 @@ void ActorViewerWindow::DrawElement() { newActor.params = 0; } - UIWidgets::EnhancementCheckbox("Advanced mode", CVAR_DEVELOPER_TOOLS("ActorViewer.AdvancedParams")); - UIWidgets::InsertHelpHoverText("Changes the actor specific param menus with a direct input"); + CVarCheckbox("Advanced mode", CVAR_DEVELOPER_TOOLS("ActorViewer.AdvancedParams"), CheckboxOptions().Tooltip("Changes the actor specific param menus with a direct input")); if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("ActorViewer.AdvancedParams"), 0)) { + PushStyleInput(THEME_COLOR); ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one); + PopStyleInput(); } else if (std::find(noParamsActors.begin(), noParamsActors.end(), newActor.id) == noParamsActors.end()) { CreateActorSpecificData(); if (actorSpecificData.find(newActor.id) == actorSpecificData.end()) { + PushStyleInput(THEME_COLOR); ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one); + PopStyleInput(); } else { DrawGroupWithBorder([&]() { ImGui::Text("Actor Specific Data"); newActor.params = actorSpecificData[newActor.id](newActor.params); - }); + }, "Actor Specific Data"); } } ImGui::PushItemWidth(ImGui::GetFontSize() * 6); DrawGroupWithBorder([&]() { + PushStyleInput(THEME_COLOR); ImGui::Text("New Actor Position"); - ImGui::InputScalar("posX", ImGuiDataType_Float, &newActor.pos.x); - ImGui::SameLine(); - ImGui::InputScalar("posY", ImGuiDataType_Float, &newActor.pos.y); - ImGui::SameLine(); - ImGui::InputScalar("posZ", ImGuiDataType_Float, &newActor.pos.z); - }); - + ImGui::PushItemWidth(ImGui::GetFontSize() * 6); + ImGui::InputScalar("X##NewPos", ImGuiDataType_Float, &newActor.pos.x); + ImGui::InputScalar("Y##NewPos", ImGuiDataType_Float, &newActor.pos.y); + ImGui::InputScalar("Z##NewPos", ImGuiDataType_Float, &newActor.pos.z); + ImGui::PopItemWidth(); + PopStyleInput(); + }, "New Actor Position"); + ImGui::SameLine(); DrawGroupWithBorder([&]() { + PushStyleInput(THEME_COLOR); ImGui::Text("New Actor Rotation"); - ImGui::InputScalar("rotX", ImGuiDataType_S16, &newActor.rot.x); - ImGui::SameLine(); - ImGui::InputScalar("rotY", ImGuiDataType_S16, &newActor.rot.y); - ImGui::SameLine(); - ImGui::InputScalar("rotZ", ImGuiDataType_S16, &newActor.rot.z); - }); + ImGui::PushItemWidth(ImGui::GetFontSize() * 6); + ImGui::InputScalar("X##NewRot", ImGuiDataType_S16, &newActor.rot.x); + ImGui::InputScalar("Y##NewRot", ImGuiDataType_S16, &newActor.rot.y); + ImGui::InputScalar("Z##NewRot", ImGuiDataType_S16, &newActor.rot.z); + ImGui::PopItemWidth(); + PopStyleInput(); + }, "New Actor Rotation"); - if (ImGui::Button("Fetch from Link")) { + if (Button("Fetch from Link", ButtonOptions().Color(THEME_COLOR))) { Player* player = GET_PLAYER(gPlayState); Vec3f newPos = player->actor.world.pos; Vec3s newRot = player->actor.world.rot; @@ -1170,7 +1176,7 @@ void ActorViewerWindow::DrawElement() { newActor.rot = newRot; } - if (ImGui::Button("Spawn")) { + if (Button("Spawn", ButtonOptions().Color(THEME_COLOR))) { if (ActorDB::Instance->RetrieveEntry(newActor.id).entry.valid) { Actor_Spawn(&gPlayState->actorCtx, gPlayState, newActor.id, newActor.pos.x, newActor.pos.y, newActor.pos.z, newActor.rot.x, newActor.rot.y, newActor.rot.z, newActor.params, 0); @@ -1179,7 +1185,7 @@ void ActorViewerWindow::DrawElement() { } } - if (ImGui::Button("Spawn as Child")) { + if (Button("Spawn as Child", ButtonOptions().Color(THEME_COLOR))) { Actor* parent = display; if (parent != NULL) { if (newActor.id >= 0 && newActor.id < ACTOR_ID_MAX && @@ -1193,28 +1199,26 @@ void ActorViewerWindow::DrawElement() { } } - if (ImGui::Button("Reset")) { + if (Button("Reset", ButtonOptions().Color(THEME_COLOR))) { newActor = { 0, 0, { 0, 0, 0 }, { 0, 0, 0 } }; } ImGui::TreePop(); } + PopStyleHeader(); - static const char* nameTagOptions[] = { - "None", - "Short Description", - "Actor ID", - "Both" + static std::unordered_map nameTagOptions = { + { 0, "None" }, + { 1, "Short Description" }, + { 2, "Actor ID" }, + { 3, "Both" }, }; - UIWidgets::Spacer(0); - - ImGui::Text("Actor Name Tags"); - if (UIWidgets::EnhancementCombobox(CVAR_DEVELOPER_TOOLS("ActorViewer.NameTags"), nameTagOptions, ACTORVIEWER_NAMETAGS_NONE)) { + if (CVarCombobox("Actor Name Tags", CVAR_DEVELOPER_TOOLS("ActorViewer.NameTags"), nameTagOptions, + ComboboxOptions().Color(THEME_COLOR).Tooltip("Adds \"name tags\" above actors for identification"))) { NameTag_RemoveAllByTag(DEBUG_ACTOR_NAMETAG_TAG); ActorViewer_AddTagForAllActors(); } - UIWidgets::Tooltip("Adds \"name tags\" above actors for identification"); } else { ImGui::Text("Global Context needed for actor info!"); if (needs_reset) { @@ -1223,9 +1227,7 @@ void ActorViewerWindow::DrawElement() { filler = "Please Select"; list.clear(); needs_reset = false; - for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) { - searchString[i] = 0; - } + searchString = ""; currentSelectedInDropdown = -1; actors.clear(); } diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp index 493ffdfdc..de2272598 100644 --- a/soh/soh/Enhancements/debugger/colViewer.cpp +++ b/soh/soh/Enhancements/debugger/colViewer.cpp @@ -1,6 +1,7 @@ #include "colViewer.h" #include "../../frame_interpolation.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include #include @@ -19,12 +20,12 @@ extern "C" { extern PlayState* gPlayState; } -enum class ColRenderSetting { Disabled, Solid, Transparent }; +typedef enum ColRenderSetting { ColRenderDisabled, ColRenderSolid, ColRenderTransparent } ColRenderSetting ; -static const char* ColRenderSettingNames[] = { - "Disabled", - "Solid", - "Transparent", +static std::unordered_map ColRenderSettingNames = { + { ColRenderDisabled, "Disabled" }, + { ColRenderSolid, "Solid" }, + { ColRenderTransparent, "Transparent" }, }; ImVec4 scene_col; @@ -53,45 +54,69 @@ static std::vector cylinderVtx; static std::vector sphereGfx; static std::vector sphereVtx; +using namespace UIWidgets; + // Draws the ImGui window for the collision viewer void ColViewerWindow::DrawElement() { - UIWidgets::EnhancementCheckbox("Enabled", CVAR_DEVELOPER_TOOLS("ColViewer.Enabled")); + CheckboxOptions checkOpt = CheckboxOptions().Color(THEME_COLOR); + ComboboxOptions comboOpt = ComboboxOptions().Color(THEME_COLOR); + CVarCheckbox("Enabled", CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), checkOpt); - UIWidgets::LabeledRightAlignedEnhancementCombobox("Scene", CVAR_DEVELOPER_TOOLS("ColViewer.Scene"), ColRenderSettingNames, COLVIEW_DISABLED); - UIWidgets::LabeledRightAlignedEnhancementCombobox("Bg Actors", CVAR_DEVELOPER_TOOLS("ColViewer.BGActors"), ColRenderSettingNames, COLVIEW_DISABLED); - UIWidgets::LabeledRightAlignedEnhancementCombobox("Col Check", CVAR_DEVELOPER_TOOLS("ColViewer.ColCheck"), ColRenderSettingNames, COLVIEW_DISABLED); - UIWidgets::LabeledRightAlignedEnhancementCombobox("Waterbox", CVAR_DEVELOPER_TOOLS("ColViewer.Waterbox"), ColRenderSettingNames, COLVIEW_DISABLED); + CVarCombobox("Scene", CVAR_DEVELOPER_TOOLS("ColViewer.Scene"), ColRenderSettingNames, comboOpt); + CVarCombobox("Bg Actors", CVAR_DEVELOPER_TOOLS("ColViewer.BGActors"), ColRenderSettingNames, comboOpt); + CVarCombobox("Col Check", CVAR_DEVELOPER_TOOLS("ColViewer.ColCheck"), ColRenderSettingNames, comboOpt); + CVarCombobox("Waterbox", CVAR_DEVELOPER_TOOLS("ColViewer.Waterbox"), ColRenderSettingNames, comboOpt); - UIWidgets::EnhancementCheckbox("Apply as decal", CVAR_DEVELOPER_TOOLS("ColViewer.Decal"), false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::InsertHelpHoverText("Applies the collision as a decal display. This can be useful if there is z-fighting occuring " - "with the scene geometry, but can cause other artifacts."); - UIWidgets::EnhancementCheckbox("Shaded", CVAR_DEVELOPER_TOOLS("ColViewer.Shaded")); - UIWidgets::InsertHelpHoverText("Applies the scene's shading to the collision display."); + CVarCheckbox("Apply as decal", CVAR_DEVELOPER_TOOLS("ColViewer.Decal"), + checkOpt.DefaultValue(true).Tooltip("Applies the collision as a decal display. This can be useful if there is z-fighting occuring " + "with the scene geometry, but can cause other artifacts.")); + CVarCheckbox("Shaded", CVAR_DEVELOPER_TOOLS("ColViewer.Shaded"), checkOpt.DefaultValue(false).Tooltip("Applies the scene's shading to the collision display.")); // This has to be duplicated in both code paths due to the nature of ImGui::IsItemHovered() const std::string colorHelpText = "View and change the colors used for collision display."; + PushStyleHeader(THEME_COLOR); if (ImGui::TreeNode("Colors")) { - UIWidgets::InsertHelpHoverText(colorHelpText); + UIWidgets::Tooltip(colorHelpText.c_str()); - UIWidgets::EnhancementColor("Normal", CVAR_DEVELOPER_TOOLS("ColViewer.ColorNormal"), scene_col, ImVec4(255, 255, 255, 255), false); - UIWidgets::EnhancementColor("Hookshot", CVAR_DEVELOPER_TOOLS("ColViewer.ColorHookshot"), hookshot_col, ImVec4(128, 128, 255, 255), - false); - UIWidgets::EnhancementColor("Entrance", CVAR_DEVELOPER_TOOLS("ColViewer.ColorEntrance"), entrance_col, ImVec4(0, 255, 0, 255), false); - UIWidgets::EnhancementColor("Special Surface (Grass/Sand/Etc)", CVAR_DEVELOPER_TOOLS("ColViewer.ColorSpecialSurface"), - specialSurface_col, ImVec4(192, 255, 192, 255), false); - UIWidgets::EnhancementColor("Interactable (Vines/Crawlspace/Etc)", CVAR_DEVELOPER_TOOLS("ColViewer.ColorInteractable"), - interactable_col, ImVec4(192, 0, 192, 255), false); - UIWidgets::EnhancementColor("Slope", CVAR_DEVELOPER_TOOLS("ColViewer.ColorSlope"), slope_col, ImVec4(255, 255, 128, 255), false); - UIWidgets::EnhancementColor("Void", CVAR_DEVELOPER_TOOLS("ColViewer.ColorVoid"), void_col, ImVec4(255, 0, 0, 255), false); - UIWidgets::EnhancementColor("OC", CVAR_DEVELOPER_TOOLS("ColViewer.ColorOC"), oc_col, ImVec4(255, 255, 255, 255), false); - UIWidgets::EnhancementColor("AC", CVAR_DEVELOPER_TOOLS("ColViewer.ColorAC"), ac_col, ImVec4(0, 0, 255, 255), false); - UIWidgets::EnhancementColor("AT", CVAR_DEVELOPER_TOOLS("ColViewer.ColorAT"), at_col, ImVec4(255, 0, 0, 255), false); - UIWidgets::EnhancementColor("Waterbox", CVAR_DEVELOPER_TOOLS("ColViewer.ColorWaterbox"), waterbox_col, ImVec4(0, 0, 255, 255), false); + if (CVarColorPicker("Normal", CVAR_DEVELOPER_TOOLS("ColViewer.ColorNormal"), { 255, 255, 255, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + scene_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorNormal"), { 255, 255, 255, 255 })); + } + if (CVarColorPicker("Hookshot", CVAR_DEVELOPER_TOOLS("ColViewer.ColorHookshot"), { 128, 128, 255, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + hookshot_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorHookshot"), { 128, 128, 255, 255 })); + } + if (CVarColorPicker("Entrance", CVAR_DEVELOPER_TOOLS("ColViewer.ColorEntrance"), { 0, 255, 0, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + entrance_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorEntrance"), { 0, 255, 0, 255 })); + } + if (CVarColorPicker("Special Surface (Grass/Sand/Etc)", CVAR_DEVELOPER_TOOLS("ColViewer.ColorSpecialSurface"), { 192, 255, 192, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + specialSurface_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSpecialSurface"), { 192, 255, 192, 255 })); + } + if (CVarColorPicker("Interactable (Vines/Crawlspace/Etc)", CVAR_DEVELOPER_TOOLS("ColViewer.ColorInteractable"), { 192, 0, 192, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + interactable_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorInteractable"), { 192, 0, 192, 255 })); + } + if (CVarColorPicker("Slope", CVAR_DEVELOPER_TOOLS("ColViewer.ColorSlope"), { 255, 255, 128, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + slope_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSlope"), { 255, 255, 128, 255 })); + } + if (CVarColorPicker("Void", CVAR_DEVELOPER_TOOLS("ColViewer.ColorVoid"), { 255, 0, 0, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + void_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorVoid"), { 255, 0, 0, 255 })); + } + if (CVarColorPicker("OC", CVAR_DEVELOPER_TOOLS("ColViewer.ColorOC"), { 255, 255, 255, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + oc_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorOC"), { 255, 255, 255, 255 })); + } + if (CVarColorPicker("AC", CVAR_DEVELOPER_TOOLS("ColViewer.ColorAC"), { 0, 0, 255, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + ac_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAC"), { 0, 0, 255, 255 })); + } + if (CVarColorPicker("AT", CVAR_DEVELOPER_TOOLS("ColViewer.ColorAT"), { 255, 0, 0, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + at_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAT"), { 255, 0, 0, 255 })); + } + if (CVarColorPicker("Waterbox", CVAR_DEVELOPER_TOOLS("ColViewer.ColorWaterbox"), { 0, 0, 255, 255 }, false, ColorPickerResetButton | ColorPickerRandomButton, THEME_COLOR)) { + waterbox_col = VecFromRGBA8(CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorWaterbox"), { 0, 0, 255, 255 })); + } ImGui::TreePop(); } else { - UIWidgets::InsertHelpHoverText(colorHelpText); + UIWidgets::Tooltip(colorHelpText.c_str()); } + PopStyleHeader(); } // Calculates the normal for a triangle at the 3 specified points @@ -287,7 +312,7 @@ void InitGfx(std::vector& gfx, ColRenderSetting setting) { uint64_t cm; uint32_t gm; - if (setting == ColRenderSetting::Transparent) { + if (setting == ColRenderTransparent) { rm = Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL; blc1 = GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA); blc2 = GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA); @@ -301,7 +326,7 @@ void InitGfx(std::vector& gfx, ColRenderSetting setting) { if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Decal"), 1) != 0) { rm |= ZMODE_DEC; - } else if (setting == ColRenderSetting::Transparent) { + } else if (setting == ColRenderTransparent) { rm |= ZMODE_XLU; } else { rm |= ZMODE_OPA; @@ -340,21 +365,21 @@ void DrawDynapoly(std::vector& dl, CollisionHeader* col, int32_t bgId) { CollisionPoly* poly = &col->polyList[i]; if (SurfaceType_IsHookshotSurface(&gPlayState->colCtx, poly, bgId)) { - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorHookshot"), { 128, 128, 255, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorHookshot.Value"), { 128, 128, 255, 255 }); } else if (func_80041D94(&gPlayState->colCtx, poly, bgId) > 0x01) { - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorInteractable"), {192, 0, 192, 255}); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorInteractable.Value"), {192, 0, 192, 255}); } else if (func_80041E80(&gPlayState->colCtx, poly, bgId) == 0x0C) { - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorVoid"), { 255, 0, 0, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorVoid.Value"), { 255, 0, 0, 255 }); } else if (SurfaceType_GetSceneExitIndex(&gPlayState->colCtx, poly, bgId) || func_80041E80(&gPlayState->colCtx, poly, bgId) == 0x05) { - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorEntrance"), { 0, 255, 0, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorEntrance.Value"), { 0, 255, 0, 255 }); } else if (func_80041D4C(&gPlayState->colCtx, poly, bgId) != 0 || SurfaceType_IsWallDamage(&gPlayState->colCtx, poly, bgId)) { - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSpecialSurface"), { 192, 255, 192, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSpecialSurface.Value"), { 192, 255, 192, 255 }); } else if (SurfaceType_GetSlope(&gPlayState->colCtx, poly, bgId) == 0x01) { - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSlope"), { 255, 255, 128, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSlope.Value"), { 255, 255, 128, 255 }); } else { - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorNormal"), { 255, 255, 255, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorNormal.Value"), { 255, 255, 255, 255 }); } if (color.r != lastColorR || color.g != lastColorG || color.b != lastColorB) { @@ -404,11 +429,11 @@ void DrawDynapoly(std::vector& dl, CollisionHeader* col, int32_t bgId) { void DrawSceneCollision() { ColRenderSetting showSceneColSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Scene"), COLVIEW_DISABLED); - if (showSceneColSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { + if (showSceneColSetting == ColRenderDisabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { return; } - std::vector& dl = (showSceneColSetting == ColRenderSetting::Transparent) ? xluDl : opaDl; + std::vector& dl = (showSceneColSetting == ColRenderTransparent) ? xluDl : opaDl; InitGfx(dl, showSceneColSetting); dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH)); @@ -418,11 +443,11 @@ void DrawSceneCollision() { // Draws all Bg Actors void DrawBgActorCollision() { ColRenderSetting showBgActorSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.BGActors"), COLVIEW_DISABLED); - if (showBgActorSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { + if (showBgActorSetting == ColRenderDisabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { return; } - std::vector& dl = (showBgActorSetting == ColRenderSetting::Transparent) ? xluDl : opaDl; + std::vector& dl = (showBgActorSetting == ColRenderTransparent) ? xluDl : opaDl; InitGfx(dl, showBgActorSetting); dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH)); @@ -543,22 +568,22 @@ void DrawColCheckList(std::vector& dl, Collider** objects, int32_t count) { // Draws all Col Check objects void DrawColCheckCollision() { ColRenderSetting showColCheckSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.ColCheck"), COLVIEW_DISABLED); - if (showColCheckSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { + if (showColCheckSetting == ColRenderDisabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { return; } - std::vector& dl = (showColCheckSetting == ColRenderSetting::Transparent) ? xluDl : opaDl; + std::vector& dl = (showColCheckSetting == ColRenderTransparent) ? xluDl : opaDl; InitGfx(dl, showColCheckSetting); dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH)); CollisionCheckContext& col = gPlayState->colChkCtx; - Color_RGBA8 color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorOC"), { 255, 255, 255, 255 }); + Color_RGBA8 color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorOC.Value"), { 255, 255, 255, 255 }); dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); DrawColCheckList(dl, col.colOC, col.colOCCount); - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAC"), { 0, 0, 255, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAC.Value"), { 0, 0, 255, 255 }); dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); DrawColCheckList(dl, col.colAC, col.colACCount); - color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAT"), { 0, 0, 255, 255 }); + color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAT.Value"), { 0, 0, 255, 255 }); dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); DrawColCheckList(dl, col.colAT, col.colATCount); @@ -595,15 +620,15 @@ extern "C" f32 zdWaterBoxMinY; // Draws all waterboxes void DrawWaterboxList() { ColRenderSetting showWaterboxSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Waterbox"), COLVIEW_DISABLED); - if (showWaterboxSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { + if (showWaterboxSetting == ColRenderDisabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) { return; } - std::vector& dl = (showWaterboxSetting == ColRenderSetting::Transparent) ? xluDl : opaDl; + std::vector& dl = (showWaterboxSetting == ColRenderTransparent) ? xluDl : opaDl; InitGfx(dl, showWaterboxSetting); dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH)); - Color_RGBA8 color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorWaterbox"), { 0, 0, 255, 255 }); + Color_RGBA8 color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorWaterbox.Value"), { 0, 0, 255, 255 }); dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255)); diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index b9858aff4..96163f656 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -3,6 +3,7 @@ #include "soh/SohGui/ImGuiUtils.h" #include "soh/OTRGlobals.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include #include @@ -27,7 +28,7 @@ extern PlayState* gPlayState; } // Maps entries in the GS flag array to the area name it represents -std::vector gsMapping = { +std::vector gsMapping = { "Deku Tree", "Dodongo's Cavern", "Inside Jabu-Jabu's Belly", @@ -56,6 +57,15 @@ extern "C" u8 gAreaGsFlags[]; extern "C" u8 gAmmoItems[]; +#define IMAGE_SIZE 48.0f + +using namespace UIWidgets; + +IntSliderOptions intSliderOptionsBase; +ButtonOptions buttonOptionsBase; +CheckboxOptions checkboxOptionsBase; +ComboboxOptions comboboxOptionsBase; + // Modification of gAmmoItems that replaces ITEM_NONE with the item in inventory slot it represents u8 gAllAmmoItems[] = { ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_BOW, ITEM_ARROW_FIRE, ITEM_DINS_FIRE, @@ -65,28 +75,17 @@ u8 gAllAmmoItems[] = { // Encapsulates what is drawn by the passed-in function within a border template -void DrawGroupWithBorder(T&& drawFunc) { +void DrawGroupWithBorder(T&& drawFunc, std::string section) { // First group encapsulates the inner portion and border + ImGui::BeginChild(std::string("##" + section).c_str(), ImVec2(0, 0), + ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY); + ImGui::BeginGroup(); - - ImVec2 padding = ImGui::GetStyle().FramePadding; - ImVec2 p0 = ImGui::GetCursorScreenPos(); - ImGui::SetCursorScreenPos(ImVec2(p0.x + padding.x, p0.y + padding.y)); - - // Second group encapsulates just the inner portion - ImGui::BeginGroup(); - + ImGui::AlignTextToFramePadding(); drawFunc(); - - ImGui::Dummy(padding); ImGui::EndGroup(); - ImVec2 p1 = ImGui::GetItemRectMax(); - p1.x += padding.x; - ImVec4 borderCol = ImGui::GetStyle().Colors[ImGuiCol_Border]; - ImGui::GetWindowDrawList()->AddRect(p0, p1, IM_COL32(borderCol.x * 255, borderCol.y * 255, borderCol.z * 255, borderCol.w * 255)); - - ImGui::EndGroup(); + ImGui::EndChild(); } char z2ASCII(int code) { @@ -108,6 +107,42 @@ char z2ASCII(int code) { } +typedef enum MagicLevel { + MAGIC_LEVEL_NONE, + MAGIC_LEVEL_SINGLE, + MAGIC_LEVEL_DOUBLE +}; + +std::unordered_map magicLevelMap = { + { MAGIC_LEVEL_NONE, "None" }, + { MAGIC_LEVEL_SINGLE, "Single" }, + { MAGIC_LEVEL_DOUBLE, "Double" }, +}; + +typedef enum AudioOutput { + AUDIO_STEREO, + AUDIO_MONO, + AUDIO_HEADSET, + AUDIO_SURROUND, +}; + +std::unordered_map audioMap = { + { AUDIO_STEREO, "Stereo" }, + { AUDIO_MONO, "Mono" }, + { AUDIO_HEADSET, "Headset" }, + { AUDIO_SURROUND, "Surround" }, +}; + +typedef enum ZTarget { + Z_TARGET_SWITCH, + Z_TARGET_HOLD, +}; + +std::unordered_map zTargetMap = { + { Z_TARGET_SWITCH, "Switch" }, + { Z_TARGET_HOLD, "Hold" }, +}; + void DrawInfoTab() { // TODO Needs a better method for name changing but for now this will work. std::string name; @@ -121,200 +156,147 @@ void DrawInfoTab() { ImGui::PushItemWidth(ImGui::GetFontSize() * 6); ImGui::Text("Name: %s", name.c_str()); - UIWidgets::InsertHelpHoverText("Player Name"); + Tooltip("Player Name"); std::string nameID; for (int i = 0; i < 8; i++) { nameID = z2ASCII(i); if (i % 4 != 0) { ImGui::SameLine(); } + PushStyleInput(THEME_COLOR); ImGui::InputScalar(nameID.c_str(), ImGuiDataType_U8, &gSaveContext.playerName[i], &one, NULL); + PopStyleInput(); } // Use an intermediary to keep the health from updating (and potentially killing the player) // until it is done being edited int16_t healthIntermediary = gSaveContext.healthCapacity; + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Max Health", ImGuiDataType_S16, &healthIntermediary); + PopStyleInput(); if (ImGui::IsItemDeactivated()) { gSaveContext.healthCapacity = healthIntermediary; } - UIWidgets::InsertHelpHoverText("Maximum health. 16 units per full heart"); + Tooltip("Maximum health. 16 units per full heart"); if (gSaveContext.health > gSaveContext.healthCapacity) { gSaveContext.health = gSaveContext.healthCapacity; // Clamp health to new max } - - const uint16_t healthMin = 0; - const uint16_t healthMax = gSaveContext.healthCapacity; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderScalar("Health", ImGuiDataType_S16, &gSaveContext.health, &healthMin, &healthMax); - UIWidgets::InsertHelpHoverText("Current health. 16 units per full heart"); + int32_t health = (int32_t)gSaveContext.health; + if (SliderInt("Health", &health, intSliderOptionsBase.Tooltip("Current health. 16 units per full heart") + .Min(0).Max(gSaveContext.healthCapacity))) { + gSaveContext.health = (int16_t)health; + } bool isDoubleDefenseAcquired = gSaveContext.isDoubleDefenseAcquired != 0; - if (ImGui::Checkbox("Double Defense", &isDoubleDefenseAcquired)) { + if (Checkbox("Double Defense", &isDoubleDefenseAcquired, checkboxOptionsBase.Tooltip("Is double defense unlocked?"))) { gSaveContext.isDoubleDefenseAcquired = isDoubleDefenseAcquired; gSaveContext.inventory.defenseHearts = gSaveContext.isDoubleDefenseAcquired ? 20 : 0; // Set to get the border drawn in the UI } - UIWidgets::InsertHelpHoverText("Is double defense unlocked?"); - - std::string magicName; - if (gSaveContext.magicLevel == 2) { - magicName = "Double"; - } else if (gSaveContext.magicLevel == 1) { - magicName = "Single"; - } else { - magicName = "None"; + if (Combobox("Magic Level", &gSaveContext.magicLevel, magicLevelMap, comboboxOptionsBase.Tooltip("Current magic level"))) { + gSaveContext.isMagicAcquired = gSaveContext.magicLevel > 0; + gSaveContext.isDoubleMagicAcquired = gSaveContext.magicLevel == 2; } - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6); - if (ImGui::BeginCombo("Magic Level", magicName.c_str())) { - if (ImGui::Selectable("Double")) { - gSaveContext.magicLevel = 2; - gSaveContext.isMagicAcquired = true; - gSaveContext.isDoubleMagicAcquired = true; - } - if (ImGui::Selectable("Single")) { - gSaveContext.magicLevel = 1; - gSaveContext.isMagicAcquired = true; - gSaveContext.isDoubleMagicAcquired = false; - } - if (ImGui::Selectable("None")) { - gSaveContext.magicLevel = 0; - gSaveContext.isMagicAcquired = false; - gSaveContext.isDoubleMagicAcquired = false; - } - - ImGui::EndCombo(); - } - UIWidgets::InsertHelpHoverText("Current magic level"); gSaveContext.magicCapacity = gSaveContext.magicLevel * 0x30; // Set to get the bar drawn in the UI if (gSaveContext.magic > gSaveContext.magicCapacity) { gSaveContext.magic = gSaveContext.magicCapacity; // Clamp magic to new max } - const uint8_t magicMin = 0; - const uint8_t magicMax = gSaveContext.magicCapacity; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderScalar("Magic", ImGuiDataType_S8, &gSaveContext.magic, &magicMin, &magicMax); - UIWidgets::InsertHelpHoverText("Current magic. 48 units per magic level"); + int32_t magic = (int32_t)gSaveContext.magic; + if (SliderInt("Magic", &magic, intSliderOptionsBase.Min(0).Max(gSaveContext.magicCapacity).Tooltip("Current magic. 48 units per magic level"))) { + gSaveContext.magic = (int8_t)magic; + } + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Rupees", ImGuiDataType_S16, &gSaveContext.rupees); - UIWidgets::InsertHelpHoverText("Current rupees"); + Tooltip("Current rupees"); + PopStyleInput(); - const uint16_t dayTimeMin = 0; - const uint16_t dayTimeMax = 0xFFFF; - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); - ImGui::SliderScalar("Time", ImGuiDataType_U16, &gSaveContext.dayTime, &dayTimeMin, &dayTimeMax); - UIWidgets::InsertHelpHoverText("Time of day"); - if (ImGui::Button("Dawn")) { + SliderInt("Time", (int32_t*) &gSaveContext.dayTime, intSliderOptionsBase.Min(0).Max(0xFFFF).Tooltip("Time of day")); + if (Button("Dawn", buttonOptionsBase)) { gSaveContext.dayTime = 0x4000; } ImGui::SameLine(); - if (ImGui::Button("Noon")) { + if (Button("Noon", buttonOptionsBase)) { gSaveContext.dayTime = 0x8000; } ImGui::SameLine(); - if (ImGui::Button("Sunset")) { + if (Button("Sunset", buttonOptionsBase)) { gSaveContext.dayTime = 0xC001; } ImGui::SameLine(); - if (ImGui::Button("Midnight")) { + if (Button("Midnight", buttonOptionsBase)) { gSaveContext.dayTime = 0; } + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Total Days", ImGuiDataType_S32, &gSaveContext.totalDays); - UIWidgets::InsertHelpHoverText("Total number of days elapsed since the start of the game"); + Tooltip("Total number of days elapsed since the start of the game"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Deaths", ImGuiDataType_U16, &gSaveContext.deaths); - UIWidgets::InsertHelpHoverText("Total number of deaths"); + Tooltip("Total number of deaths"); + PopStyleInput(); - bool bgsFlag = gSaveContext.bgsFlag != 0; - if (ImGui::Checkbox("Has BGS", &bgsFlag)) { - gSaveContext.bgsFlag = bgsFlag; - } - UIWidgets::InsertHelpHoverText("Is Biggoron sword unlocked? Replaces Giant's knife"); + Checkbox("Has BGS", (bool*) &gSaveContext.bgsFlag, checkboxOptionsBase.Tooltip("Is Biggoron sword unlocked? Replaces Giant's knife")); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Sword Health", ImGuiDataType_U16, &gSaveContext.swordHealth); - UIWidgets::InsertHelpHoverText("Giant's knife health. Default is 8. Must be >0 for Biggoron sword to work"); + Tooltip("Giant's knife health. Default is 8. Must be >0 for Biggoron sword to work"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Bgs Day Count", ImGuiDataType_S32, &gSaveContext.bgsDayCount); - UIWidgets::InsertHelpHoverText("Total number of days elapsed since giving Biggoron the claim check"); + Tooltip("Total number of days elapsed since giving Biggoron the claim check"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Entrance Index", ImGuiDataType_S32, &gSaveContext.entranceIndex); - UIWidgets::InsertHelpHoverText("From which entrance did Link arrive?"); + Tooltip("From which entrance did Link arrive?"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Cutscene Index", ImGuiDataType_S32, &gSaveContext.cutsceneIndex); - UIWidgets::InsertHelpHoverText("Which cutscene is this?"); + Tooltip("Which cutscene is this?"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Navi Timer", ImGuiDataType_U16, &gSaveContext.naviTimer); - UIWidgets::InsertHelpHoverText("Navi wants to talk at 600 units, decides not to at 3000."); + Tooltip("Navi wants to talk at 600 units, decides not to at 3000."); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Timer 1 State", ImGuiDataType_S16, &gSaveContext.timer1State); - UIWidgets::InsertHelpHoverText("Heat timer, race timer, etc. Has white font"); + Tooltip("Heat timer, race timer, etc. Has white font"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Timer 1 Value", ImGuiDataType_S16, &gSaveContext.timer1Value, &one, NULL); - UIWidgets::InsertHelpHoverText("Time, in seconds"); + Tooltip("Time, in seconds"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Timer 2 State", ImGuiDataType_S16, &gSaveContext.timer2State); - UIWidgets::InsertHelpHoverText("Trade timer, Ganon collapse timer, etc. Has yellow font"); + Tooltip("Trade timer, Ganon collapse timer, etc. Has yellow font"); + PopStyleInput(); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Timer 2 Value", ImGuiDataType_S16, &gSaveContext.timer2Value, &one, NULL); - UIWidgets::InsertHelpHoverText("Time, in seconds"); - - const char* audioName; - switch (gSaveContext.audioSetting) { - case 0: - audioName = "Stereo"; - break; - case 1: - audioName = "Mono"; - break; - case 2: - audioName = "Headset"; - break; - case 3: - audioName = "Surround"; - break; - default: - audioName = "?"; - } - if (ImGui::BeginCombo("Audio", audioName)) { - if (ImGui::Selectable("Stereo")) { - gSaveContext.audioSetting = 0; - } - if (ImGui::Selectable("Mono")) { - gSaveContext.audioSetting = 1; - } - if (ImGui::Selectable("Headset")) { - gSaveContext.audioSetting = 2; - } - if (ImGui::Selectable("Surround")) { - gSaveContext.audioSetting = 3; - } + Tooltip("Time, in seconds"); + PopStyleInput(); - ImGui::EndCombo(); - } - UIWidgets::InsertHelpHoverText("Sound setting"); + Combobox("Audio", &gSaveContext.audioSetting, audioMap, comboboxOptionsBase.Tooltip("Sound setting")); - bool n64DDFlag = gSaveContext.n64ddFlag != 0; - if (ImGui::Checkbox("64 DD file?", &n64DDFlag)) { - gSaveContext.n64ddFlag = n64DDFlag; - } - UIWidgets::InsertHelpHoverText("WARNING! If you save, your file may be locked! Use caution!"); + Checkbox("64 DD file?", (bool*) &gSaveContext.n64ddFlag, checkboxOptionsBase.Tooltip("WARNING! If you save, your file may be locked! Use caution!")); - if (ImGui::BeginCombo("Z Target Mode", gSaveContext.zTargetSetting ? "Hold" : "Switch")) { - if (ImGui::Selectable("Switch")) { - gSaveContext.zTargetSetting = 0; - } - if (ImGui::Selectable("Hold")) { - gSaveContext.zTargetSetting = 1; - } - ImGui::EndCombo(); - } - UIWidgets::InsertHelpHoverText("Z-Targeting behavior"); + Combobox("Z Target Mode", &gSaveContext.zTargetSetting, zTargetMap, comboboxOptionsBase.Tooltip("Z-Targeting behavior")); if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT)) { + PushStyleInput(THEME_COLOR); ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U8, &gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); - UIWidgets::InsertHelpHoverText("Currently obtained Triforce Pieces. For Triforce Hunt."); + Tooltip("Currently obtained Triforce Pieces. For Triforce Hunt."); + PopStyleInput(); } ImGui::PushItemWidth(ImGui::GetFontSize() * 10); @@ -330,78 +312,79 @@ void DrawInfoTab() { for (int i = 0; i < 7; i++) { if(i == 2 && ImGui::TreeNode("Fishing") ){ //fishing has a few more flags to it u8 fishSize = gSaveContext.highScores[i] & 0x7F; + PushStyleInput(THEME_COLOR); if(ImGui::InputScalar("Child Size Record",ImGuiDataType_U8,&fishSize)){ gSaveContext.highScores[i]&=~0x7F; gSaveContext.highScores[i]|=fishSize & 0x7F; } char fishMsg[64]; std::sprintf(fishMsg,"Weight: %2.0f lbs",((SQ(fishSize)*.0036)+.5)); - UIWidgets::InsertHelpHoverText(fishMsg); + Tooltip(fishMsg); + PopStyleInput(); bool FishBool = gSaveContext.highScores[i]&0x80; - if (ImGui::Checkbox("Cheated as Child", &FishBool)) { + if (Checkbox("Cheated as Child", &FishBool, checkboxOptionsBase.Tooltip("Used the Sinking lure to catch it."))) { gSaveContext.highScores[i] &= ~0x80; gSaveContext.highScores[i] |= (0x80 * FishBool); } - UIWidgets::InsertHelpHoverText("Used the Sinking lure to catch it."); fishSize=(gSaveContext.highScores[i] & 0x7F000000)>>0x18; + PushStyleInput(THEME_COLOR); if(ImGui::InputScalar("Adult Size Record",ImGuiDataType_U8,&fishSize)){ gSaveContext.highScores[i]&=~0x7F000000; gSaveContext.highScores[i]|=(fishSize & 0x7F) << 0x18; } std::sprintf(fishMsg,"Weight: %2.0f lbs",((SQ(fishSize)*.0036)+.5)); - UIWidgets::InsertHelpHoverText(fishMsg); + Tooltip(fishMsg); + PopStyleInput(); FishBool = gSaveContext.highScores[i] & 0x80000000; - if (ImGui::Checkbox("Cheated as Adult", &FishBool)) { + if (Checkbox("Cheated as Adult", &FishBool, checkboxOptionsBase.Tooltip("Used the Sinking lure to catch it."))) { gSaveContext.highScores[i] &= ~0x80000000; gSaveContext.highScores[i] |= (0x80000000 * FishBool); } - UIWidgets::InsertHelpHoverText("Used the Sinking lure to catch it."); FishBool = gSaveContext.highScores[i]&0x100; - if (ImGui::Checkbox("Played as Child", &FishBool)) { + if (Checkbox("Played as Child", &FishBool, checkboxOptionsBase.Tooltip("Played at least one game as a child"))) { gSaveContext.highScores[i] &= ~0x100; gSaveContext.highScores[i] |= (0x100 * FishBool); } - UIWidgets::InsertHelpHoverText("Played at least one game as a child"); FishBool = gSaveContext.highScores[i]&0x200; - if (ImGui::Checkbox("Played as Adult", &FishBool)) { + if (Checkbox("Played as Adult", &FishBool, checkboxOptionsBase.Tooltip("Played at least one game as an adult"))) { gSaveContext.highScores[i] &= ~0x200; gSaveContext.highScores[i] |= (0x200 * FishBool); } - UIWidgets::InsertHelpHoverText("Played at least one game as an adult"); FishBool = gSaveContext.highScores[i]&0x400; - if (ImGui::Checkbox("Got Prize as Child", &FishBool)) { + if (Checkbox("Got Prize as Child", &FishBool, checkboxOptionsBase.Tooltip("Got the prize item (Heart Piece, unless rando.)\nunlocks Sinking Lure for Child Link."))) { gSaveContext.highScores[i] &= ~0x400; gSaveContext.highScores[i] |= (0x400 * FishBool); } - UIWidgets::InsertHelpHoverText("Got the prize item (Heart Piece, unless rando.)\nunlocks Sinking Lure for Child Link."); FishBool = gSaveContext.highScores[i]&0x800; - if (ImGui::Checkbox("Got Prize as Adult", &FishBool)) { + if (Checkbox("Got Prize as Adult", &FishBool, checkboxOptionsBase.Tooltip("Got the prize item (Golden Scale, unless rando.)\nUnlocks Sinking Lure for Adult Link."))) { gSaveContext.highScores[i] &= ~0x800; gSaveContext.highScores[i] |= (0x800 * FishBool); } - UIWidgets::InsertHelpHoverText("Got the prize item (Golden Scale, unless rando.)\nUnlocks Sinking Lure for Adult Link."); FishBool = gSaveContext.highScores[i] & 0x1000; - if (ImGui::Checkbox("Stole Owner's Hat", &FishBool)) { + if (Checkbox("Stole Owner's Hat", &FishBool, checkboxOptionsBase.Tooltip("The owner's now visibly bald when Adult Link."))) { gSaveContext.highScores[i] &= ~0x1000; gSaveContext.highScores[i] |= (0x1000 * FishBool); } - UIWidgets::InsertHelpHoverText("The owner's now visibly bald when Adult Link."); fishSize=(gSaveContext.highScores[i] & 0xFF0000)>>16; + PushStyleInput(THEME_COLOR); if(ImGui::InputScalar("Times Played",ImGuiDataType_U8,&fishSize)){ gSaveContext.highScores[i]&=~0xFF0000; gSaveContext.highScores[i]|=(fishSize) << 16; } - UIWidgets::InsertHelpHoverText("Determines weather and school size during dawn/dusk."); + Tooltip("Determines weather and school size during dawn/dusk."); + PopStyleInput(); ImGui::TreePop(); continue; } - if (i == 5) { //HS_UNK_05 is unused + if (i == 5 || i == 2) { //HS_UNK_05 is unused continue; } std::string minigameLbl = minigameHS[i]; + PushStyleInput(THEME_COLOR); ImGui::InputScalar(minigameLbl.c_str(), ImGuiDataType_S32, &gSaveContext.highScores[i], &one, NULL); + PopStyleInput(); } ImGui::TreePop(); @@ -412,14 +395,14 @@ void DrawInfoTab() { void DrawBGSItemFlag(uint8_t itemID) { const ItemMapEntry& slotEntry = itemMapping[itemID]; - ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(slotEntry.name), ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); + ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(slotEntry.name), + ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); } void DrawInventoryTab() { static bool restrictToValid = true; - ImGui::Checkbox("Restrict to valid items", &restrictToValid); - UIWidgets::InsertHelpHoverText("Restricts items and ammo to only what is possible to legally acquire in-game"); + Checkbox("Restrict to valid items", &restrictToValid, checkboxOptionsBase.Tooltip("Restricts items and ammo to only what is possible to legally acquire in-game")); for (int32_t y = 0; y < 4; y++) { for (int32_t x = 0; x < 6; x++) { @@ -433,35 +416,33 @@ void DrawInventoryTab() { ImGui::SameLine(); } - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, 1, 1, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); uint8_t item = gSaveContext.inventory.items[index]; + PushStyleButton(Colors::DarkGray); if (item != ITEM_NONE) { const ItemMapEntry& slotEntry = itemMapping.find(item)->second; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); auto ret = ImGui::ImageButton(slotEntry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(slotEntry.name), - ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); + ImVec2(48.0f, 48.0f), ImVec2(0, 0), ImVec2(1, 1)); if (ret) { selectedIndex = index; ImGui::OpenPopup(itemPopupPicker); } } else { - if (ImGui::Button("##itemNone", ImVec2(32.0f, 32.0f))) { + if (ImGui::Button("##itemNone", ImVec2(IMAGE_SIZE, IMAGE_SIZE) + ImGui::GetStyle().FramePadding * 2)) { selectedIndex = index; ImGui::OpenPopup(itemPopupPicker); } } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); + PopStyleButton(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); if (ImGui::BeginPopup(itemPopupPicker)) { - if (ImGui::Button("##itemNonePicker", ImVec2(32.0f, 32.0f))) { + PushStyleButton(Colors::DarkGray); + if (ImGui::Button("##itemNonePicker", ImVec2(IMAGE_SIZE, IMAGE_SIZE) + ImGui::GetStyle().FramePadding * 2)) { gSaveContext.inventory.items[selectedIndex] = ITEM_NONE; ImGui::CloseCurrentPopup(); } - UIWidgets::SetLastItemHoverText("None"); + PopStyleButton(); + UIWidgets::Tooltip("None"); std::vector possibleItems; if (restrictToValid) { @@ -486,15 +467,15 @@ void DrawInventoryTab() { ImGui::SameLine(); } const ItemMapEntry& slotEntry = possibleItems[pickerIndex]; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + PushStyleButton(Colors::DarkGray); auto ret = ImGui::ImageButton(slotEntry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(slotEntry.name), - ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); + ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1)); + PopStyleButton(); if (ret) { gSaveContext.inventory.items[selectedIndex] = slotEntry.id; ImGui::CloseCurrentPopup(); } - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(slotEntry.id)); + UIWidgets::Tooltip(SohUtils::GetItemName(slotEntry.id).c_str()); } ImGui::EndPopup(); @@ -517,11 +498,13 @@ void DrawInventoryTab() { drawnAmmoItems++; ImGui::PushID(ammoIndex); - ImGui::PushItemWidth(32.0f); + ImGui::PushItemWidth(IMAGE_SIZE); ImGui::BeginGroup(); - ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(itemMapping[item].name), ImVec2(32.0f, 32.0f)); + ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(itemMapping[item].name), ImVec2(IMAGE_SIZE, IMAGE_SIZE)); + PushStyleInput(THEME_COLOR); ImGui::InputScalar("##ammoInput", ImGuiDataType_S8, &AMMO(item)); + PopStyleInput(); ImGui::EndGroup(); ImGui::PopItemWidth(); @@ -548,7 +531,10 @@ void DrawFlagTableArray16(const FlagTable& flagTable, uint16_t row, uint16_t& fl ImGui::PushID(flagIndex); bool hasDescription = !!flagTable.flagDescriptions.contains(row * 16 + flagIndex); uint32_t bitMask = 1 << flagIndex; - ImGui::PushStyleColor(ImGuiCol_FrameBg, hasDescription ? ImVec4(0.16f, 0.29f, 0.48f, 0.54f) : ImVec4(0.16f, 0.29f, 0.48f, 0.24f)); + ImVec4 themeColor = ColorValues.at(THEME_COLOR); + ImVec4 colorDark = { themeColor.x * 0.4f, themeColor.y * 0.4f, themeColor.z * 0.4f , themeColor.z }; + PushStyleCheckbox(hasDescription ? themeColor : colorDark); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)); bool flag = (flags & bitMask) != 0; if (ImGui::Checkbox("##check", &flag)) { if (flag) { @@ -557,7 +543,8 @@ void DrawFlagTableArray16(const FlagTable& flagTable, uint16_t row, uint16_t& fl flags &= ~bitMask; } } - ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + PopStyleCheckbox(); if (ImGui::IsItemHovered() && hasDescription) { ImGui::BeginTooltip(); ImGui::Text("%s", UIWidgets::WrappedText(flagTable.flagDescriptions.at(row * 16 + flagIndex), 60).c_str()); @@ -575,165 +562,154 @@ void DrawFlagsTab() { DrawGroupWithBorder([&]() { ImGui::Text("stateFlags1"); - UIWidgets::DrawFlagArray32("stateFlags1", player->stateFlags1); - }); + DrawFlagArray32("stateFlags1", player->stateFlags1, THEME_COLOR); + }, "stateFlags1"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("stateFlags2"); - UIWidgets::DrawFlagArray32("stateFlags2", player->stateFlags2); - }); + DrawFlagArray32("stateFlags2", player->stateFlags2, THEME_COLOR); + }, "stateFlags2"); DrawGroupWithBorder([&]() { ImGui::Text("stateFlags3"); - UIWidgets::DrawFlagArray8("stateFlags3", player->stateFlags3); - }); + DrawFlagArray8("stateFlags3", player->stateFlags3, THEME_COLOR); + }, "stateFlags3"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("unk_6AE_rotFlags"); - UIWidgets::DrawFlagArray16("unk_6AE_rotFlags", player->unk_6AE_rotFlags); - }); + DrawFlagArray16("unk_6AE_rotFlags", player->unk_6AE_rotFlags, THEME_COLOR); + }, "unk_6AE_rotFlags"); } ImGui::TreePop(); } if (ImGui::TreeNode("Current Scene")) { if (gPlayState != nullptr) { ActorContext* act = &gPlayState->actorCtx; - DrawGroupWithBorder([&]() { ImGui::Text("Switch"); - UIWidgets::InsertHelpHoverText("Permanently-saved switch flags"); - ImGui::SameLine(); - if (ImGui::Button("Set All##Switch")) { + InsertHelpHoverText("Permanently-saved switch flags"); + if (Button("Set All##Switch", buttonOptionsBase.Tooltip(""))) { act->flags.swch = UINT32_MAX; } ImGui::SameLine(); - if (ImGui::Button("Clear All##Switch")) { + if (Button("Clear All##Switch", buttonOptionsBase.Tooltip(""))) { act->flags.swch = 0; } - UIWidgets::DrawFlagArray32("Switch", act->flags.swch); - }); + DrawFlagArray32("Switch", act->flags.swch, THEME_COLOR); + }, "Switch"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Temp Switch"); - UIWidgets::InsertHelpHoverText("Temporary switch flags. Unset on scene transitions"); - ImGui::SameLine(); - if (ImGui::Button("Set All##Temp Switch")) { + InsertHelpHoverText("Temporary switch flags. Unset on scene transitions"); + if (Button("Set All##Temp Switch", buttonOptionsBase.Tooltip(""))) { act->flags.tempSwch = UINT32_MAX; } ImGui::SameLine(); - if (ImGui::Button("Clear All##Temp Switch")) { + if (Button("Clear All##Temp Switch", buttonOptionsBase.Tooltip(""))) { act->flags.tempSwch = 0; } - UIWidgets::DrawFlagArray32("Temp Switch", act->flags.tempSwch); - }); + DrawFlagArray32("Temp Switch", act->flags.tempSwch, THEME_COLOR); + }, "Temp Switch"); DrawGroupWithBorder([&]() { ImGui::Text("Clear"); - UIWidgets::InsertHelpHoverText("Permanently-saved room-clear flags"); - ImGui::SameLine(); - if (ImGui::Button("Set All##Clear")) { + InsertHelpHoverText("Permanently-saved room-clear flags"); + if (Button("Set All##Clear", buttonOptionsBase.Tooltip(""))) { act->flags.clear = UINT32_MAX; } ImGui::SameLine(); - if (ImGui::Button("Clear All##Clear")) { + if (Button("Clear All##Clear", buttonOptionsBase.Tooltip(""))) { act->flags.clear = 0; } - UIWidgets::DrawFlagArray32("Clear", act->flags.clear); - }); + DrawFlagArray32("Clear", act->flags.clear, THEME_COLOR); + }, "Clear"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Temp Clear"); - UIWidgets::InsertHelpHoverText("Temporary room-clear flags. Unset on scene transitions"); - ImGui::SameLine(); - if (ImGui::Button("Set All##Temp Clear")) { + InsertHelpHoverText("Temporary room-clear flags. Unset on scene transitions"); + if (Button("Set All##Temp Clear", buttonOptionsBase.Tooltip(""))) { act->flags.tempClear = UINT32_MAX; } ImGui::SameLine(); - if (ImGui::Button("Clear All##Temp Clear")) { + if (Button("Clear All##Temp Clear", buttonOptionsBase.Tooltip(""))) { act->flags.tempClear = 0; } - UIWidgets::DrawFlagArray32("Temp Clear", act->flags.tempClear); - }); + DrawFlagArray32("Temp Clear", act->flags.tempClear, THEME_COLOR); + }, "Temp Clear"); DrawGroupWithBorder([&]() { ImGui::Text("Collect"); - UIWidgets::InsertHelpHoverText("Permanently-saved collect flags"); - ImGui::SameLine(); - if (ImGui::Button("Set All##Collect")) { + InsertHelpHoverText("Permanently-saved collect flags"); + if (Button("Set All##Collect", buttonOptionsBase.Tooltip(""))) { act->flags.collect = UINT32_MAX; } ImGui::SameLine(); - if (ImGui::Button("Clear All##Collect")) { + if (Button("Clear All##Collect", buttonOptionsBase.Tooltip(""))) { act->flags.collect = 0; } - UIWidgets::DrawFlagArray32("Collect", act->flags.collect); - }); + DrawFlagArray32("Collect", act->flags.collect, THEME_COLOR); + }, "Collect"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Temp Collect"); - UIWidgets::InsertHelpHoverText("Temporary collect flags. Unset on scene transitions"); - ImGui::SameLine(); - if (ImGui::Button("Set All##Temp Collect")) { + InsertHelpHoverText("Temporary collect flags. Unset on scene transitions"); + if (Button("Set All##Temp Collect", buttonOptionsBase.Tooltip(""))) { act->flags.tempCollect = UINT32_MAX; } ImGui::SameLine(); - if (ImGui::Button("Clear All##Temp Collect")) { + if (Button("Clear All##Temp Collect", buttonOptionsBase.Tooltip(""))) { act->flags.tempCollect = 0; } - UIWidgets::DrawFlagArray32("Temp Collect", act->flags.tempCollect); - }); + DrawFlagArray32("Temp Collect", act->flags.tempCollect, THEME_COLOR); + }, "Temp Collect"); DrawGroupWithBorder([&]() { ImGui::Text("Chest"); - UIWidgets::InsertHelpHoverText("Permanently-saved chest flags"); - ImGui::SameLine(); - if (ImGui::Button("Set All##Chest")) { + InsertHelpHoverText("Permanently-saved chest flags"); + if (Button("Set All##Chest", buttonOptionsBase.Tooltip(""))) { act->flags.chest = UINT32_MAX; } ImGui::SameLine(); - if (ImGui::Button("Clear All##Chest")) { + if (Button("Clear All##Chest", buttonOptionsBase.Tooltip(""))) { act->flags.chest = 0; } - UIWidgets::DrawFlagArray32("Chest", act->flags.chest); - }); + DrawFlagArray32("Chest", act->flags.chest, THEME_COLOR); + }, "Chest"); ImGui::SameLine(); ImGui::BeginGroup(); - if (ImGui::Button("Reload Flags")) { + if (Button("Reload Flags", buttonOptionsBase.Tooltip("Load flags from saved scene flags. Normally happens on scene load"))) { act->flags.swch = gSaveContext.sceneFlags[gPlayState->sceneNum].swch; act->flags.clear = gSaveContext.sceneFlags[gPlayState->sceneNum].clear; act->flags.collect = gSaveContext.sceneFlags[gPlayState->sceneNum].collect; act->flags.chest = gSaveContext.sceneFlags[gPlayState->sceneNum].chest; } - UIWidgets::SetLastItemHoverText("Load flags from saved scene flags. Normally happens on scene load"); - if (ImGui::Button("Save Flags")) { + if (Button("Save Flags", buttonOptionsBase.Tooltip("Save current scene flags. Normally happens on scene exit"))) { gSaveContext.sceneFlags[gPlayState->sceneNum].swch = act->flags.swch; gSaveContext.sceneFlags[gPlayState->sceneNum].clear = act->flags.clear; gSaveContext.sceneFlags[gPlayState->sceneNum].collect = act->flags.collect; gSaveContext.sceneFlags[gPlayState->sceneNum].chest = act->flags.chest; } - UIWidgets::SetLastItemHoverText("Save current scene flags. Normally happens on scene exit"); - if (ImGui::Button("Clear Flags")) { + if (Button("Clear Flags", buttonOptionsBase.Tooltip("Clear current scene flags. Reload scene to see changes"))) { act->flags.swch = 0; act->flags.clear = 0; act->flags.collect = 0; act->flags.chest = 0; } - UIWidgets::SetLastItemHoverText("Clear current scene flags. Reload scene to see changes"); ImGui::EndGroup(); @@ -746,8 +722,10 @@ void DrawFlagsTab() { if (ImGui::TreeNode("Saved Scene Flags")) { static uint32_t selectedSceneFlagMap = 0; + ImGui::AlignTextToFramePadding(); ImGui::Text("Map"); ImGui::SameLine(); + PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("##Map", SohUtils::GetSceneName(selectedSceneFlagMap).c_str())) { for (int32_t sceneIndex = 0; sceneIndex < SCENE_ID_MAX; sceneIndex++) { if (ImGui::Selectable(SohUtils::GetSceneName(sceneIndex).c_str())) { @@ -757,77 +735,68 @@ void DrawFlagsTab() { ImGui::EndCombo(); } + PopStyleCombobox(); // Don't show current scene button if there is no current scene if (gPlayState != nullptr) { ImGui::SameLine(); - if (ImGui::Button("Current")) { + if (Button("Current", buttonOptionsBase.Tooltip("Open flags for current scene"))) { selectedSceneFlagMap = gPlayState->sceneNum; } - UIWidgets::SetLastItemHoverText("Open flags for current scene"); } DrawGroupWithBorder([&]() { ImGui::Text("Switch"); - UIWidgets::InsertHelpHoverText("Switch flags"); - UIWidgets::DrawFlagArray32("Switch", gSaveContext.sceneFlags[selectedSceneFlagMap].swch); - }); + InsertHelpHoverText("Switch flags"); + DrawFlagArray32("Switch", gSaveContext.sceneFlags[selectedSceneFlagMap].swch, THEME_COLOR); + }, "Saved Switch"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Clear"); - UIWidgets::InsertHelpHoverText("Room-clear flags"); - UIWidgets::DrawFlagArray32("Clear", gSaveContext.sceneFlags[selectedSceneFlagMap].clear); - }); + InsertHelpHoverText("Room-clear flags"); + DrawFlagArray32("Clear", gSaveContext.sceneFlags[selectedSceneFlagMap].clear, THEME_COLOR); + }, "Saved Clear"); DrawGroupWithBorder([&]() { ImGui::Text("Collect"); - UIWidgets::InsertHelpHoverText("Collect flags"); - UIWidgets::DrawFlagArray32("Collect", gSaveContext.sceneFlags[selectedSceneFlagMap].collect); - }); + InsertHelpHoverText("Collect flags"); + DrawFlagArray32("Collect", gSaveContext.sceneFlags[selectedSceneFlagMap].collect, THEME_COLOR); + }, "Saved Collect"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Chest"); - UIWidgets::InsertHelpHoverText("Chest flags"); - UIWidgets::DrawFlagArray32("Chest", gSaveContext.sceneFlags[selectedSceneFlagMap].chest); - }); + InsertHelpHoverText("Chest flags"); + DrawFlagArray32("Chest", gSaveContext.sceneFlags[selectedSceneFlagMap].chest, THEME_COLOR); + }, "Saved Chest"); DrawGroupWithBorder([&]() { ImGui::Text("Rooms"); - UIWidgets::InsertHelpHoverText("Flags for visted rooms"); - UIWidgets::DrawFlagArray32("Rooms", gSaveContext.sceneFlags[selectedSceneFlagMap].rooms); - }); + InsertHelpHoverText("Flags for visted rooms"); + DrawFlagArray32("Rooms", gSaveContext.sceneFlags[selectedSceneFlagMap].rooms, THEME_COLOR); + }, "Saved Rooms"); ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Floors"); - UIWidgets::InsertHelpHoverText("Flags for visted floors"); - UIWidgets::DrawFlagArray32("Floors", gSaveContext.sceneFlags[selectedSceneFlagMap].floors); - }); + InsertHelpHoverText("Flags for visted floors"); + DrawFlagArray32("Floors", gSaveContext.sceneFlags[selectedSceneFlagMap].floors, THEME_COLOR); + }, "Saved Floors"); ImGui::TreePop(); } DrawGroupWithBorder([&]() { - static uint32_t selectedGsMap = 0; + size_t selectedGsMap = 0; ImGui::Text("Gold Skulltulas"); - ImGui::Text("Map"); - ImGui::SameLine(); - if (ImGui::BeginCombo("##Gold Skulltula Map", gsMapping[selectedGsMap].c_str())) { - for (int32_t gsIndex = 0; gsIndex < gsMapping.size(); gsIndex++) { - if (ImGui::Selectable(gsMapping[gsIndex].c_str())) { - selectedGsMap = gsIndex; - } - } - - ImGui::EndCombo(); - } + Combobox("Map##Gold Skulltulas", &selectedGsMap, gsMapping, comboboxOptionsBase.Tooltip("")); // TODO We should write out descriptions for each one... ugh + ImGui::AlignTextToFramePadding(); ImGui::Text("Flags"); uint32_t currentFlags = GET_GS_FLAGS(selectedGsMap); uint32_t allFlags = gAreaGsFlags[selectedGsMap]; @@ -838,6 +807,8 @@ void DrawFlagsTab() { ImGui::SameLine(); ImGui::PushID(allFlags); + PushStyleCheckbox(THEME_COLOR); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)); if (ImGui::Checkbox("##gs", &isThisSet)) { if (isThisSet) { SET_GS_FLAGS(selectedGsMap, setMask); @@ -848,6 +819,8 @@ void DrawFlagsTab() { SET_GS_FLAGS(selectedGsMap, currentFlagsBase & ~setMask); } } + ImGui::PopStyleVar(); + PopStyleCheckbox(); ImGui::PopID(); @@ -860,8 +833,8 @@ void DrawFlagsTab() { // GS Token Count updated, since Gold Skulltulas killed will not correlate to GS Tokens Collected. if (!(IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF)) { static bool keepGsCountUpdated = true; - ImGui::Checkbox("Keep GS Count Updated", &keepGsCountUpdated); - UIWidgets::InsertHelpHoverText("Automatically adjust the number of gold skulltula tokens acquired based on set flags."); + Checkbox("Keep GS Count Updated", &keepGsCountUpdated, + checkboxOptionsBase.Tooltip("Automatically adjust the number of gold skulltula tokens acquired based on set flags.")); int32_t gsCount = 0; if (keepGsCountUpdated) { for (int32_t gsFlagIndex = 0; gsFlagIndex < 6; gsFlagIndex++) { @@ -870,7 +843,7 @@ void DrawFlagsTab() { gSaveContext.inventory.gsTokens = gsCount; } } - }); + }, "Gold Skulltulas"); for (int i = 0; i < flagTables.size(); i++) { const FlagTable& flagTable = flagTables[i]; @@ -899,7 +872,7 @@ void DrawFlagsTab() { DrawFlagTableArray16(flagTable, j, gSaveContext.ship.randomizerInf[j]); break; } - }); + }, flagTable.name); } // make some buttons to help with fishsanity debugging @@ -941,6 +914,8 @@ void DrawUpgrade(const std::string& categoryName, int32_t categoryId, const std: ImGui::Text("%s", categoryName.c_str()); ImGui::SameLine(); ImGui::PushID(categoryName.c_str()); + PushStyleCombobox(THEME_COLOR); + ImGui::AlignTextToFramePadding(); if (ImGui::BeginCombo("##upgrade", names[CUR_UPG_VALUE(categoryId)].c_str())) { for (int32_t i = 0; i < names.size(); i++) { if (ImGui::Selectable(names[i].c_str())) { @@ -950,8 +925,9 @@ void DrawUpgrade(const std::string& categoryName, int32_t categoryId, const std: ImGui::EndCombo(); } + PopStyleCombobox(); ImGui::PopID(); - UIWidgets::SetLastItemHoverText(categoryName.c_str()); + UIWidgets::Tooltip(categoryName.c_str()); } // Draws a combo that lets you choose and upgrade value from a popup grid of icons @@ -959,59 +935,50 @@ void DrawUpgradeIcon(const std::string& categoryName, int32_t categoryId, const static const char* upgradePopupPicker = "upgradePopupPicker"; ImGui::PushID(categoryName.c_str()); - - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, 1, 1, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + + PushStyleButton(Colors::DarkGray); uint8_t item = items[CUR_UPG_VALUE(categoryId)]; if (item != ITEM_NONE) { const ItemMapEntry& slotEntry = itemMapping[item]; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - auto ret = ImGui::ImageButton(slotEntry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(slotEntry.name), - ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); - if (ret) { + if (ImGui::ImageButton(slotEntry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(slotEntry.name), + ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1))) { ImGui::OpenPopup(upgradePopupPicker); } } else { - if (ImGui::Button("##itemNone", ImVec2(32.0f, 32.0f))) { + if (ImGui::Button("##itemNone", ImVec2(IMAGE_SIZE, IMAGE_SIZE) + ImGui::GetStyle().FramePadding * 2)) { ImGui::OpenPopup(upgradePopupPicker); } } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - UIWidgets::SetLastItemHoverText(categoryName.c_str()); + PopStyleButton(); + Tooltip(categoryName.c_str()); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); if (ImGui::BeginPopup(upgradePopupPicker)) { for (int32_t pickerIndex = 0; pickerIndex < items.size(); pickerIndex++) { if ((pickerIndex % 8) != 0) { ImGui::SameLine(); } - + + PushStyleButton(Colors::DarkGray); if (items[pickerIndex] == ITEM_NONE) { - if (ImGui::Button("##upgradePopupPicker", ImVec2(32.0f, 32.0f))) { + if (ImGui::Button("##upgradePopupPicker", ImVec2(IMAGE_SIZE, IMAGE_SIZE) + ImGui::GetStyle().FramePadding * 2)) { Inventory_ChangeUpgrade(categoryId, pickerIndex); ImGui::CloseCurrentPopup(); } - UIWidgets::SetLastItemHoverText("None"); + Tooltip("None"); } else { const ItemMapEntry& slotEntry = itemMapping[items[pickerIndex]]; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); auto ret = ImGui::ImageButton(slotEntry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(slotEntry.name), - ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); + ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1)); if (ret) { Inventory_ChangeUpgrade(categoryId, pickerIndex); ImGui::CloseCurrentPopup(); } - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(slotEntry.id)); + Tooltip(SohUtils::GetItemName(slotEntry.id).c_str()); } + PopStyleButton(); } - ImGui::EndPopup(); } - ImGui::PopStyleVar(); - ImGui::PopID(); } @@ -1035,11 +1002,9 @@ void DrawEquipmentTab() { uint32_t bitMask = 1 << i; bool hasEquip = (bitMask & gSaveContext.inventory.equipment) != 0; const ItemMapEntry& entry = itemMapping[equipmentValues[i]]; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + PushStyleButton(Colors::DarkGray); auto ret = ImGui::ImageButton(entry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasEquip ? entry.name : entry.nameFaded), - ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); + ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1)); if (ret) { if (hasEquip) { gSaveContext.inventory.equipment &= ~bitMask; @@ -1047,9 +1012,9 @@ void DrawEquipmentTab() { gSaveContext.inventory.equipment |= bitMask; } } - ImGui::PopStyleColor(); + PopStyleButton(); + Tooltip(SohUtils::GetItemName(entry.id).c_str()); ImGui::PopID(); - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(entry.id)); } const std::vector bulletBagValues = { @@ -1137,11 +1102,9 @@ void DrawQuestItemButton(uint32_t item) { const QuestMapEntry& entry = questMapping[item]; uint32_t bitMask = 1 << entry.id; bool hasQuestItem = (bitMask & gSaveContext.inventory.questItems) != 0; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + PushStyleButton(Colors::DarkGray); auto ret = ImGui::ImageButton(entry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasQuestItem ? entry.name : entry.nameFaded), - ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); + ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1)); if (ret) { if (hasQuestItem) { gSaveContext.inventory.questItems &= ~bitMask; @@ -1149,8 +1112,8 @@ void DrawQuestItemButton(uint32_t item) { gSaveContext.inventory.questItems |= bitMask; } } - ImGui::PopStyleColor(); - UIWidgets::SetLastItemHoverText(SohUtils::GetQuestItemName(entry.id)); + PopStyleButton(); + Tooltip(SohUtils::GetQuestItemName(entry.id).c_str()); } // Draws a toggleable icon for a dungeon item that is faded when disabled @@ -1158,11 +1121,9 @@ void DrawDungeonItemButton(uint32_t item, uint32_t scene) { const ItemMapEntry& entry = itemMapping[item]; uint32_t bitMask = 1 << (entry.id - ITEM_KEY_BOSS); // Bitset starts at ITEM_KEY_BOSS == 0. the rest are sequential bool hasItem = (bitMask & gSaveContext.inventory.dungeonItems[scene]) != 0; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + PushStyleButton(Colors::DarkGray); auto ret = ImGui::ImageButton(entry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasItem ? entry.name : entry.nameFaded), - ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); + ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1)); if (ret) { if (hasItem) { gSaveContext.inventory.dungeonItems[scene] &= ~bitMask; @@ -1170,12 +1131,11 @@ void DrawDungeonItemButton(uint32_t item, uint32_t scene) { gSaveContext.inventory.dungeonItems[scene] |= bitMask; } } - ImGui::PopStyleColor(); - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(entry.id)); + PopStyleButton(); + Tooltip(SohUtils::GetItemName(entry.id).c_str()); } void DrawQuestStatusTab() { - ImGui::PushItemWidth(ImGui::GetFontSize() * 6); for (int32_t i = QUEST_MEDALLION_FOREST; i < QUEST_MEDALLION_LIGHT + 1; i++) { if (i != QUEST_MEDALLION_FOREST) { @@ -1193,14 +1153,13 @@ void DrawQuestStatusTab() { // Put Stone of Agony and Gerudo Card on the same line with a little space between them ImGui::SameLine(); - ImGui::Dummy(ImVec2(20, 0)); + ImGui::Dummy(ImVec2(IMAGE_SIZE, IMAGE_SIZE) + ImGui::GetStyle().FramePadding * 2); ImGui::SameLine(); DrawQuestItemButton(QUEST_STONE_OF_AGONY); ImGui::SameLine(); DrawQuestItemButton(QUEST_GERUDO_CARD); - for (const auto& [quest, entry] : songMapping) { if ((entry.id != QUEST_SONG_MINUET) && (entry.id != QUEST_SONG_LULLABY)) { ImGui::SameLine(); @@ -1208,11 +1167,9 @@ void DrawQuestStatusTab() { uint32_t bitMask = 1 << entry.id; bool hasQuestItem = (bitMask & gSaveContext.inventory.questItems) != 0; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + PushStyleButton(Colors::DarkGray); auto ret = ImGui::ImageButton(entry.name.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasQuestItem ? entry.name : entry.nameFaded), - ImVec2(16.0f, 24.0f), ImVec2(0, 0), ImVec2(1, 1)); - ImGui::PopStyleVar(); + ImVec2(32.0f, 48.0f), ImVec2(0, 0), ImVec2(1, 1)); if (ret) { if (hasQuestItem) { gSaveContext.inventory.questItems &= ~bitMask; @@ -1220,25 +1177,28 @@ void DrawQuestStatusTab() { gSaveContext.inventory.questItems |= bitMask; } } - ImGui::PopStyleColor(); - UIWidgets::SetLastItemHoverText(SohUtils::GetQuestItemName(entry.id)); + PopStyleButton(); + Tooltip(SohUtils::GetQuestItemName(entry.id).c_str()); } + PushStyleInput(THEME_COLOR); ImGui::InputScalar("GS Count", ImGuiDataType_S16, &gSaveContext.inventory.gsTokens); - UIWidgets::InsertHelpHoverText("Number of gold skulltula tokens aquired"); + PopStyleInput(); + InsertHelpHoverText("Number of gold skulltula tokens aquired"); uint32_t bitMask = 1 << QUEST_SKULL_TOKEN; bool gsUnlocked = (bitMask & gSaveContext.inventory.questItems) != 0; - if (ImGui::Checkbox("GS unlocked", &gsUnlocked)) { + if (Checkbox("GS unlocked", &gsUnlocked, CheckboxOptions().Color(THEME_COLOR))) { if (gsUnlocked) { gSaveContext.inventory.questItems |= bitMask; } else { gSaveContext.inventory.questItems &= ~bitMask; } } - UIWidgets::InsertHelpHoverText("If unlocked, enables showing the gold skulltula count in the quest status menu"); + InsertHelpHoverText("If unlocked, enables showing the gold skulltula count in the quest status menu"); int32_t pohCount = (gSaveContext.inventory.questItems & 0xF0000000) >> 28; + PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("PoH count", std::to_string(pohCount).c_str())) { for (int32_t i = 0; i < 4; i++) { if (ImGui::Selectable(std::to_string(i).c_str(), pohCount == i)) { @@ -1248,13 +1208,14 @@ void DrawQuestStatusTab() { } ImGui::EndCombo(); } - UIWidgets::InsertHelpHoverText("The number of pieces of heart acquired towards the next heart container"); + InsertHelpHoverText("The number of pieces of heart acquired towards the next heart container"); + PopStyleCombobox(); DrawGroupWithBorder([&]() { ImGui::Text("Dungeon Items"); static int32_t dungeonItemsScene = SCENE_DEKU_TREE; - ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); + PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("##DungeonSelect", SohUtils::GetSceneName(dungeonItemsScene).c_str())) { for (int32_t dungeonIndex = SCENE_DEKU_TREE; dungeonIndex < SCENE_JABU_JABU_BOSS + 1; dungeonIndex++) { if (ImGui::Selectable(SohUtils::GetSceneName(dungeonIndex).c_str(), @@ -1265,7 +1226,7 @@ void DrawQuestStatusTab() { ImGui::EndCombo(); } - ImGui::PopItemWidth(); + PopStyleCombobox(); DrawDungeonItemButton(ITEM_KEY_BOSS, dungeonItemsScene); ImGui::SameLine(); @@ -1277,16 +1238,17 @@ void DrawQuestStatusTab() { float lineHeight = ImGui::GetTextLineHeightWithSpacing(); ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(itemMapping[ITEM_KEY_SMALL].name), ImVec2(lineHeight, lineHeight)); ImGui::SameLine(); + PushStyleInput(THEME_COLOR); if (ImGui::InputScalar("##Keys", ImGuiDataType_S8, gSaveContext.inventory.dungeonKeys + dungeonItemsScene)) { gSaveContext.ship.stats.dungeonKeys[dungeonItemsScene] = gSaveContext.inventory.dungeonKeys[dungeonItemsScene]; }; + PopStyleInput(); } else { // dungeonItems is size 20 but dungeonKeys is size 19, so there are no keys for the last scene (Barinade's Lair) ImGui::Text("Barinade's Lair does not have small keys"); } - }); + }, "Dungeon Items"); - ImGui::PopItemWidth(); } void DrawPlayerTab() { @@ -1364,50 +1326,53 @@ void DrawPlayerTab() { } ImGui::PushItemWidth(ImGui::GetFontSize() * 6); + PushStyleInput(THEME_COLOR); DrawGroupWithBorder([&]() { ImGui::Text("Link's Position"); - ImGui::InputScalar("X Pos", ImGuiDataType_Float, &player->actor.world.pos.x); - ImGui::SameLine(); - ImGui::InputScalar("Y Pos", ImGuiDataType_Float, &player->actor.world.pos.y); - ImGui::SameLine(); - ImGui::InputScalar("Z Pos", ImGuiDataType_Float, &player->actor.world.pos.z); - }); - + ImGui::PushItemWidth(ImGui::GetFontSize() * 12); + ImGui::InputScalar("X##Pos", ImGuiDataType_Float, &player->actor.world.pos.x); + ImGui::InputScalar("Y##Pos", ImGuiDataType_Float, &player->actor.world.pos.y); + ImGui::InputScalar("Z##Pos", ImGuiDataType_Float, &player->actor.world.pos.z); + ImGui::PopItemWidth(); + }, "Link's Position"); + ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Link's Rotation"); - UIWidgets::InsertHelpHoverText("For Link's rotation in relation to the world"); - ImGui::InputScalar("X Rot", ImGuiDataType_S16, &player->actor.world.rot.x); - ImGui::SameLine(); - ImGui::InputScalar("Y Rot", ImGuiDataType_S16, &player->actor.world.rot.y); - ImGui::SameLine(); - ImGui::InputScalar("Z Rot", ImGuiDataType_S16, &player->actor.world.rot.z); - }); - + InsertHelpHoverText("For Link's rotation in relation to the world"); + ImGui::PushItemWidth(ImGui::GetFontSize() * 12); + ImGui::InputScalar("X##Rot", ImGuiDataType_S16, &player->actor.world.rot.x); + ImGui::InputScalar("Y##Rot", ImGuiDataType_S16, &player->actor.world.rot.y); + ImGui::InputScalar("Z##Rot", ImGuiDataType_S16, &player->actor.world.rot.z); + ImGui::PopItemWidth(); + }, "Link's Rotation"); + ImGui::SameLine(); DrawGroupWithBorder([&]() { ImGui::Text("Link's Model Rotation"); - UIWidgets::InsertHelpHoverText("For Link's actual model"); - ImGui::InputScalar("X ModRot", ImGuiDataType_S16, &player->actor.shape.rot.x); - ImGui::SameLine(); - ImGui::InputScalar("Y ModRot", ImGuiDataType_S16, &player->actor.shape.rot.y); - ImGui::SameLine(); - ImGui::InputScalar("Z ModRot", ImGuiDataType_S16, &player->actor.shape.rot.z); - }); + InsertHelpHoverText("For Link's actual model"); + ImGui::PushItemWidth(ImGui::GetFontSize() * 12); + ImGui::InputScalar("X##ModRot", ImGuiDataType_S16, &player->actor.shape.rot.x); + ImGui::InputScalar("Y##ModRot", ImGuiDataType_S16, &player->actor.shape.rot.y); + ImGui::InputScalar("Z##ModRot", ImGuiDataType_S16, &player->actor.shape.rot.z); + ImGui::PopItemWidth(); + }, "Link's Model Rotation"); ImGui::InputScalar("Linear Velocity", ImGuiDataType_Float, &player->linearVelocity); - UIWidgets::InsertHelpHoverText("Link's speed along the XZ plane"); + InsertHelpHoverText("Link's speed along the XZ plane"); ImGui::InputScalar("Y Velocity", ImGuiDataType_Float, &player->actor.velocity.y); - UIWidgets::InsertHelpHoverText("Link's speed along the Y plane. Caps at -20"); + InsertHelpHoverText("Link's speed along the Y plane. Caps at -20"); ImGui::InputScalar("Wall Height", ImGuiDataType_Float, &player->yDistToLedge); - UIWidgets::InsertHelpHoverText("Height used to determine whether Link can climb or grab a ledge at the top"); + InsertHelpHoverText("Height used to determine whether Link can climb or grab a ledge at the top"); ImGui::InputScalar("Invincibility Timer", ImGuiDataType_S8, &player->invincibilityTimer); - UIWidgets::InsertHelpHoverText("Can't take damage while this is nonzero"); + InsertHelpHoverText("Can't take damage while this is nonzero"); ImGui::InputScalar("Gravity", ImGuiDataType_Float, &player->actor.gravity); - UIWidgets::InsertHelpHoverText("Rate at which Link falls. Default -4.0f"); + InsertHelpHoverText("Rate at which Link falls. Default -4.0f"); + PopStyleInput(); + PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("Link Age on Load", gPlayState->linkAgeOnLoad == 0 ? "Adult" : "Child")) { if (ImGui::Selectable("Adult")) { gPlayState->linkAgeOnLoad = 0; @@ -1417,132 +1382,140 @@ void DrawPlayerTab() { } ImGui::EndCombo(); } - - UIWidgets::InsertHelpHoverText("This will change Link's age when you load a map"); - + InsertHelpHoverText("This will change Link's age when you load a map"); + PopStyleCombobox(); ImGui::Separator(); - ImGui::Text("Link's Current Equipment"); - ImGui::PushItemWidth(ImGui::GetFontSize() * 15); - if (ImGui::BeginCombo("Sword", curSword)) { - if (ImGui::Selectable("None")) { - player->currentSwordItemId = ITEM_NONE; - gSaveContext.equips.buttonItems[0] = ITEM_NONE; - Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_NONE); - } - if (ImGui::Selectable("Kokiri Sword")) { - player->currentSwordItemId = ITEM_SWORD_KOKIRI; - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KOKIRI; - Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI); - } - if (ImGui::Selectable("Master Sword")) { - player->currentSwordItemId = ITEM_SWORD_MASTER; - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; - Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_MASTER); - } - if (ImGui::Selectable("Biggoron's Sword")) { - if (gSaveContext.bgsFlag) { - if (gSaveContext.swordHealth < 8) { - gSaveContext.swordHealth = 8; - } - player->currentSwordItemId = ITEM_SWORD_BGS; - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; - } else { - if (gSaveContext.swordHealth < 8) { - gSaveContext.swordHealth = 8; - } - player->currentSwordItemId = ITEM_SWORD_BGS; - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KNIFE; + DrawGroupWithBorder([&]() { + PushStyleCombobox(THEME_COLOR); + ImGui::Text("Link's Current Equipment"); + ImGui::PushItemWidth(ImGui::GetFontSize() * 12); + if (ImGui::BeginCombo("Sword", curSword)) { + if (ImGui::Selectable("None")) { + player->currentSwordItemId = ITEM_NONE; + gSaveContext.equips.buttonItems[0] = ITEM_NONE; + Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_NONE); } - - Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_BIGGORON); - } - if (ImGui::Selectable("Fishing Pole")) { - player->currentSwordItemId = ITEM_FISHING_POLE; - gSaveContext.equips.buttonItems[0] = ITEM_FISHING_POLE; - Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_MASTER); - } - ImGui::EndCombo(); + if (ImGui::Selectable("Kokiri Sword")) { + player->currentSwordItemId = ITEM_SWORD_KOKIRI; + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KOKIRI; + Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI); + } + if (ImGui::Selectable("Master Sword")) { + player->currentSwordItemId = ITEM_SWORD_MASTER; + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; + Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_MASTER); + } + if (ImGui::Selectable("Biggoron's Sword")) { + if (gSaveContext.bgsFlag) { + if (gSaveContext.swordHealth < 8) { + gSaveContext.swordHealth = 8; + } + player->currentSwordItemId = ITEM_SWORD_BGS; + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + } + else { + if (gSaveContext.swordHealth < 8) { + gSaveContext.swordHealth = 8; + } + player->currentSwordItemId = ITEM_SWORD_BGS; + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KNIFE; + } - } - if (ImGui::BeginCombo("Shield", curShield)) { - if (ImGui::Selectable("None")) { - player->currentShield = PLAYER_SHIELD_NONE; - Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_NONE); - } - if (ImGui::Selectable("Deku Shield")) { - player->currentShield = PLAYER_SHIELD_DEKU; - Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_DEKU); - } - if (ImGui::Selectable("Hylian Shield")) { - player->currentShield = PLAYER_SHIELD_HYLIAN; - Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_HYLIAN); - } - if (ImGui::Selectable("Mirror Shield")) { - player->currentShield = PLAYER_SHIELD_MIRROR; - Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); - } - ImGui::EndCombo(); - } + Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_BIGGORON); + } + if (ImGui::Selectable("Fishing Pole")) { + player->currentSwordItemId = ITEM_FISHING_POLE; + gSaveContext.equips.buttonItems[0] = ITEM_FISHING_POLE; + Inventory_ChangeEquipment(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_MASTER); + } + ImGui::EndCombo(); - if (ImGui::BeginCombo("Tunic", curTunic)) { - if (ImGui::Selectable("Kokiri Tunic")) { - player->currentTunic = PLAYER_TUNIC_KOKIRI; - Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_KOKIRI); } - if (ImGui::Selectable("Goron Tunic")) { - player->currentTunic = PLAYER_TUNIC_GORON; - Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_GORON); + if (ImGui::BeginCombo("Shield", curShield)) { + if (ImGui::Selectable("None")) { + player->currentShield = PLAYER_SHIELD_NONE; + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_NONE); + } + if (ImGui::Selectable("Deku Shield")) { + player->currentShield = PLAYER_SHIELD_DEKU; + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_DEKU); + } + if (ImGui::Selectable("Hylian Shield")) { + player->currentShield = PLAYER_SHIELD_HYLIAN; + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_HYLIAN); + } + if (ImGui::Selectable("Mirror Shield")) { + player->currentShield = PLAYER_SHIELD_MIRROR; + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); + } + ImGui::EndCombo(); } - if (ImGui::Selectable("Zora Tunic")) { - player->currentTunic = PLAYER_TUNIC_ZORA; - Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_ZORA); - } - ImGui::EndCombo(); - } - if (ImGui::BeginCombo("Boots", curBoots)) { - if (ImGui::Selectable("Kokiri Boots")) { - player->currentBoots = PLAYER_BOOTS_KOKIRI; - Inventory_ChangeEquipment(EQUIP_TYPE_BOOTS, EQUIP_VALUE_BOOTS_KOKIRI); + if (ImGui::BeginCombo("Tunic", curTunic)) { + if (ImGui::Selectable("Kokiri Tunic")) { + player->currentTunic = PLAYER_TUNIC_KOKIRI; + Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_KOKIRI); + } + if (ImGui::Selectable("Goron Tunic")) { + player->currentTunic = PLAYER_TUNIC_GORON; + Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_GORON); + } + if (ImGui::Selectable("Zora Tunic")) { + player->currentTunic = PLAYER_TUNIC_ZORA; + Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_ZORA); + } + ImGui::EndCombo(); } - if (ImGui::Selectable("Iron Boots")) { - player->currentBoots = PLAYER_BOOTS_IRON; - Inventory_ChangeEquipment(EQUIP_TYPE_BOOTS, EQUIP_VALUE_BOOTS_IRON); + + if (ImGui::BeginCombo("Boots", curBoots)) { + if (ImGui::Selectable("Kokiri Boots")) { + player->currentBoots = PLAYER_BOOTS_KOKIRI; + Inventory_ChangeEquipment(EQUIP_TYPE_BOOTS, EQUIP_VALUE_BOOTS_KOKIRI); + } + if (ImGui::Selectable("Iron Boots")) { + player->currentBoots = PLAYER_BOOTS_IRON; + Inventory_ChangeEquipment(EQUIP_TYPE_BOOTS, EQUIP_VALUE_BOOTS_IRON); + } + if (ImGui::Selectable("Hover Boots")) { + player->currentBoots = PLAYER_BOOTS_HOVER; + Inventory_ChangeEquipment(EQUIP_TYPE_BOOTS, EQUIP_VALUE_BOOTS_HOVER); + } + ImGui::EndCombo(); } - if (ImGui::Selectable("Hover Boots")) { - player->currentBoots = PLAYER_BOOTS_HOVER; - Inventory_ChangeEquipment(EQUIP_TYPE_BOOTS, EQUIP_VALUE_BOOTS_HOVER); - } - ImGui::EndCombo(); - } + ImGui::PopItemWidth(); + PopStyleCombobox(); + }, "Current Equipment"); + ImGui::SameLine(); ImU16 one = 1; - ImGui::PushItemWidth(ImGui::GetFontSize() * 6); DrawGroupWithBorder([&]() { - ImGui::Text("Current B Item"); + ImGui::PushItemWidth(ImGui::GetFontSize() * 6); + PushStyleInput(THEME_COLOR); + ImGui::Text("Current Items"); ImGui::InputScalar("B Button", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[0], &one, NULL); - ImGui::NewLine(); - - ImGui::Text("Current C Equips"); ImGui::InputScalar("C Left", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[1], &one, NULL); - ImGui::SameLine(); ImGui::InputScalar("C Down", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[2], &one, NULL); - ImGui::SameLine(); ImGui::InputScalar("C Right", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[3], &one, NULL); - - if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0)) { - ImGui::NewLine(); - ImGui::Text("Current D-pad Equips"); - ImGui::InputScalar("D-pad Up ", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[4], &one, NULL); // Two spaces at the end for aligning, not elegant but it's working - ImGui::SameLine(); + PopStyleInput(); + ImGui::PopItemWidth(); + }, "Current Items"); + + if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0)) { + ImGui::SameLine(); + DrawGroupWithBorder([&]() { + ImGui::PushItemWidth(ImGui::GetFontSize() * 6); + PushStyleInput(THEME_COLOR); + ImGui::Text("Current D-pad Items"); + // Two spaces at the end for aligning, not elegant but it's working + ImGui::InputScalar("D-pad Up ", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[4], &one, NULL); ImGui::InputScalar("D-pad Down", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[5], &one, NULL); - // Intentionnal to not put everything on the same line, else it's taking too much for lower resolution. ImGui::InputScalar("D-pad Left", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[6], &one, NULL); - ImGui::SameLine(); ImGui::InputScalar("D-pad Right", ImGuiDataType_U8, &gSaveContext.equips.buttonItems[7], &one, NULL); - } - }); + PopStyleInput(); + ImGui::PopItemWidth(); + }, "Current D-pad Items"); + } ImGui::Text("Player State"); uint8_t bit[32] = {}; @@ -1550,8 +1523,9 @@ void DrawPlayerTab() { std::vector> flag_strs = { state1, state2, state3 }; for (int j = 0; j <= 2; j++) { + std::string label = fmt::format("State Flags {}", j + 1); DrawGroupWithBorder([&]() { - ImGui::Text("State Flags %d", j + 1); + ImGui::Text("%s", label.c_str()); std::vector state = flag_strs[j]; for (int i = 0; i <= 31; i++) { bit[i] = ((flags[j] >> i) & 1); @@ -1559,46 +1533,62 @@ void DrawPlayerTab() { ImGui::Text("%s", state[i].c_str()); } } - }); + }, label.c_str()); ImGui::SameLine(); } DrawGroupWithBorder([&]() { ImGui::Text("Sword"); ImGui::Text(" %d", player->meleeWeaponState); - }); + }, "Sword"); } else { ImGui::Text("Global Context needed for player info!"); } } +void ResetBaseOptions() { + intSliderOptionsBase.Color(THEME_COLOR).Size({320.0f, 0.0f}).Tooltip(""); + buttonOptionsBase.Color(THEME_COLOR).Size(Sizes::Inline).Tooltip(""); + checkboxOptionsBase.Color(THEME_COLOR).Tooltip(""); + comboboxOptionsBase.Color(THEME_COLOR).ComponentAlignment(ComponentAlignments::Left).LabelPosition(LabelPositions::Near).Tooltip(""); +} + void SaveEditorWindow::DrawElement() { + PushStyleTabs(THEME_COLOR); + ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); + if (ImGui::BeginTabBar("SaveContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { + ResetBaseOptions(); if (ImGui::BeginTabItem("Info")) { DrawInfoTab(); ImGui::EndTabItem(); } + ResetBaseOptions(); if (ImGui::BeginTabItem("Inventory")) { DrawInventoryTab(); ImGui::EndTabItem(); } + ResetBaseOptions(); if (ImGui::BeginTabItem("Flags")) { DrawFlagsTab(); ImGui::EndTabItem(); } + ResetBaseOptions(); if (ImGui::BeginTabItem("Equipment")) { DrawEquipmentTab(); ImGui::EndTabItem(); } + ResetBaseOptions(); if (ImGui::BeginTabItem("Quest Status")) { DrawQuestStatusTab(); ImGui::EndTabItem(); } + ResetBaseOptions(); if (ImGui::BeginTabItem("Player")) { DrawPlayerTab(); ImGui::EndTabItem(); @@ -1606,6 +1596,9 @@ void SaveEditorWindow::DrawElement() { ImGui::EndTabBar(); } + + ImGui::PopFont(); + PopStyleTabs(); } void SaveEditorWindow::InitElement() {} diff --git a/soh/soh/Enhancements/debugger/dlViewer.cpp b/soh/soh/Enhancements/debugger/dlViewer.cpp index 4f4d13bff..27833058a 100644 --- a/soh/soh/Enhancements/debugger/dlViewer.cpp +++ b/soh/soh/Enhancements/debugger/dlViewer.cpp @@ -1,6 +1,7 @@ #include "actorViewer.h" #include "soh/util.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "ResourceManager.h" #include "DisplayList.h" #include "soh/OTRGlobals.h" @@ -91,10 +92,14 @@ void PerformDisplayListSearch() { void DLViewerWindow::DrawElement() { // Debounce the search field as listing otr files is expensive + UIWidgets::PushStyleInput(THEME_COLOR); + ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); + if (ImGui::InputText("Search Display Lists", searchString, ARRAY_COUNT(searchString))) { doSearch = true; searchDebounceFrames = 30; } + UIWidgets::PopStyleInput(); if (doSearch) { if (searchDebounceFrames == 0) { @@ -105,6 +110,7 @@ void DLViewerWindow::DrawElement() { searchDebounceFrames--; } + UIWidgets::PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("Active Display List", activeDisplayList.c_str())) { for (size_t i = 0; i < displayListSearchResults.size(); i++) { if (ImGui::Selectable(displayListSearchResults[i].c_str())) { @@ -114,8 +120,10 @@ void DLViewerWindow::DrawElement() { } ImGui::EndCombo(); } + UIWidgets::PopStyleCombobox(); if (activeDisplayList == "") { + ImGui::PopFont(); return; } @@ -144,6 +152,7 @@ void DLViewerWindow::DrawElement() { ImGui::SameLine(); ImGui::PushItemWidth(175.0f); + UIWidgets::PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo(("CMD" + id).c_str(), cmdLabel.c_str())) { if (ImGui::Selectable("gsDPSetPrimColor") && cmd != G_SETPRIMCOLOR) { *gfx = gsDPSetPrimColor(0, 0, 0, 0, 0, 255); @@ -162,6 +171,7 @@ void DLViewerWindow::DrawElement() { } ImGui::EndCombo(); } + UIWidgets::PopStyleCombobox(); ImGui::PopItemWidth(); @@ -194,9 +204,11 @@ void DLViewerWindow::DrawElement() { if (cmd == G_SETGRAYSCALE) { bool* state = (bool*)&gfx->words.w1; ImGui::SameLine(); + UIWidgets::PushStyleCheckbox(THEME_COLOR); if (ImGui::Checkbox(("state" + id).c_str(), state)) { // } + UIWidgets::PopStyleCheckbox(); } if (cmd == G_SETTILE) { ImGui::SameLine(); @@ -317,8 +329,11 @@ void DLViewerWindow::DrawElement() { } } catch (const std::exception& e) { ImGui::Text("Error displaying DL instructions."); + ImGui::PopFont(); return; } + + ImGui::PopFont(); } void DLViewerWindow::InitElement() { diff --git a/soh/soh/Enhancements/debugger/hookDebugger.cpp b/soh/soh/Enhancements/debugger/hookDebugger.cpp index 782cf4f7a..113862282 100644 --- a/soh/soh/Enhancements/debugger/hookDebugger.cpp +++ b/soh/soh/Enhancements/debugger/hookDebugger.cpp @@ -1,31 +1,33 @@ #include "hookDebugger.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/OTRGlobals.h" #include #include -static std::unordered_map*> hookData; +static std::map*> hookData; const ImVec4 grey = ImVec4(0.75, 0.75, 0.75, 1); const ImVec4 yellow = ImVec4(1, 1, 0, 1); const ImVec4 red = ImVec4(1, 0, 0, 1); void DrawHookRegisteringInfos(const char* hookName) { - if ((*hookData[hookName]).size() == 0) { + size_t numHooks = (*hookData[hookName]).size(); + + if (numHooks == 0) { ImGui::TextColored(grey, "No hooks found"); return; } - if (ImGui::BeginTable( - ("Table##" + std::string(hookName)).c_str(), - 4, - ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit - )) { - ImGui::TableSetupColumn("Id"); - ImGui::TableSetupColumn("Type"); - ImGui::TableSetupColumn("Registration Info"); - //ImGui::TableSetupColumn("Stub"); - ImGui::TableSetupColumn("Number of Calls"); + ImGui::Text("Total Registered: %d", numHooks); + + if (ImGui::BeginTable(("Table##" + std::string(hookName)).c_str(), 4, + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Registration Info", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("# Calls", ImGuiTableColumnFlags_WidthFixed); ImGui::TableHeadersRow(); for (auto& [id, hookInfo] : (*hookData[hookName])) { ImGui::TableNextRow(); @@ -39,7 +41,7 @@ void DrawHookRegisteringInfos(const char* hookName) { ImGui::Text("Normal"); break; case HOOK_TYPE_ID: - ImGui::Text("Id"); + ImGui::Text("ID"); break; case HOOK_TYPE_PTR: ImGui::Text("Ptr"); @@ -54,27 +56,19 @@ void DrawHookRegisteringInfos(const char* hookName) { ImGui::TableNextColumn(); if (hookInfo.registering.valid) { - ImGui::Text("%s(%d:%d) %s", hookInfo.registering.file, hookInfo.registering.line, hookInfo.registering.column, hookInfo.registering.function); + // Replace the space after the return type of the parent function with a non-breaking space + std::string parentFunction = std::string(hookInfo.registering.function); + size_t pos = parentFunction.find_first_of(" "); + if (pos != std::string::npos) { + parentFunction.replace(pos, 1, "\u00A0"); + } + // Non breaking space to keep the arrow with the parent function + ImGui::TextWrapped("%s(%d:%d) <-\u00A0%s", hookInfo.registering.file, hookInfo.registering.line, + hookInfo.registering.column, parentFunction.c_str()); } else { - ImGui::TextColored(yellow, "[Unavaliable]"); + ImGui::TextColored(yellow, "[Unavailable]"); } - //TODO: not currently possible - /* - ImGui::TableNextColumn(); - - ImGui::BeginDisabled(); - - bool stubButtonPressed = ImGui::Button(("Stub##" + std::to_string(id)).c_str()); - UIWidgets::SetLastItemHoverText("Stub this hook.\nThis is not possible to automatically undo."); - - if (stubButtonPressed) { - //stub - } - - ImGui::EndDisabled(); - */ - ImGui::TableNextColumn(); ImGui::Text("%d", hookInfo.calls); } @@ -84,26 +78,27 @@ void DrawHookRegisteringInfos(const char* hookName) { void HookDebuggerWindow::DrawElement() { #ifndef __cpp_lib_source_location - ImGui::TextColored( - yellow, - "Some features of the Hook Debugger are unavaliable because SoH was compiled " - "without \"\" support " - "(\"__cpp_lib_source_location\" not defined in \"\")." - ); + ImGui::TextColored(yellow, "Some features of the Hook Debugger are unavailable because SoH was compiled " + "without \"\" support " + "(\"__cpp_lib_source_location\" not defined in \"\")."); #endif + ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); + for (auto& [hookName, _] : hookData) { if (ImGui::TreeNode(hookName)) { DrawHookRegisteringInfos(hookName); ImGui::TreePop(); } } + + ImGui::PopFont(); } void HookDebuggerWindow::InitElement() { - #define DEFINE_HOOK(name, _) hookData.insert({#name, GameInteractor::Instance->GetHookData()}); +#define DEFINE_HOOK(name, _) hookData.insert({ #name, GameInteractor::Instance->GetHookData() }); - #include "../game-interactor/GameInteractor_HookTable.h" +#include "../game-interactor/GameInteractor_HookTable.h" - #undef DEFINE_HOOK +#undef DEFINE_HOOK } diff --git a/soh/soh/Enhancements/debugger/sohConsole.cpp b/soh/soh/Enhancements/debugger/sohConsole.cpp new file mode 100644 index 000000000..686152a1f --- /dev/null +++ b/soh/soh/Enhancements/debugger/sohConsole.cpp @@ -0,0 +1,30 @@ +#include "sohConsole.h" +#include "soh/OTRGlobals.h" +#include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" + +void SohConsoleWindow::InitElement() { + ConsoleWindow::InitElement(); +} + +void SohConsoleWindow::UpdateElement() { + ConsoleWindow::UpdateElement(); +} + +void SohConsoleWindow::DrawElement() { + UIWidgets::PushStyleInput(THEME_COLOR); + // Small font (13) to match hardcoded width values in the LUS window.. set large font after below TODO addressed + ImGui::PushFont(OTRGlobals::Instance->fontMonoSmall); + + // TODO: This can be removed after the LUS console window is designed better without hardcoding widths + ImGui::BeginChild("##Console Wrapper", ImVec2(0, 0), ImGuiChildFlags_None, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse); + + ConsoleWindow::DrawElement(); + + ImGui::EndChild(); + + ImGui::PopFont(); + UIWidgets::PopStyleInput(); +} diff --git a/soh/soh/Enhancements/debugger/sohConsole.h b/soh/soh/Enhancements/debugger/sohConsole.h new file mode 100644 index 000000000..db039625b --- /dev/null +++ b/soh/soh/Enhancements/debugger/sohConsole.h @@ -0,0 +1,17 @@ +#ifndef SOH_CONSOLE_H +#define SOH_CONSOLE_H + +#include "window/gui/GuiWindow.h" +#include "window/gui/ConsoleWindow.h" + +class SohConsoleWindow : public Ship::ConsoleWindow { + public: + using ConsoleWindow::ConsoleWindow; + + protected: + void InitElement() override; + void UpdateElement() override; + void DrawElement() override; +}; + +#endif // SOH_CONSOLE_H diff --git a/soh/soh/Enhancements/debugger/sohGfxDebugger.cpp b/soh/soh/Enhancements/debugger/sohGfxDebugger.cpp new file mode 100644 index 000000000..20b37ca93 --- /dev/null +++ b/soh/soh/Enhancements/debugger/sohGfxDebugger.cpp @@ -0,0 +1,16 @@ +#include "sohGfxDebugger.h" +#include "soh/OTRGlobals.h" + +void SohGfxDebuggerWindow::InitElement() { + GfxDebuggerWindow::InitElement(); +} + +void SohGfxDebuggerWindow::UpdateElement() { + GfxDebuggerWindow::UpdateElement(); +} + +void SohGfxDebuggerWindow::DrawElement() { + ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); + GfxDebuggerWindow::DrawElement(); + ImGui::PopFont(); +} diff --git a/soh/soh/Enhancements/debugger/sohGfxDebugger.h b/soh/soh/Enhancements/debugger/sohGfxDebugger.h new file mode 100644 index 000000000..789173b88 --- /dev/null +++ b/soh/soh/Enhancements/debugger/sohGfxDebugger.h @@ -0,0 +1,17 @@ +#ifndef SOH_GFX_DEBUGGER_H +#define SOH_GFX_DEBUGGER_H + +#include "window/gui/GuiWindow.h" +#include "window/gui/GfxDebuggerWindow.h" + +class SohGfxDebuggerWindow : public LUS::GfxDebuggerWindow { + public: + using GfxDebuggerWindow::GfxDebuggerWindow; + + protected: + void InitElement() override; + void UpdateElement() override; + void DrawElement() override; +}; + +#endif // SOH_GFX_DEBUGGER_H diff --git a/soh/soh/Enhancements/debugger/valueViewer.cpp b/soh/soh/Enhancements/debugger/valueViewer.cpp index 7d09dfb63..23ce4e26c 100644 --- a/soh/soh/Enhancements/debugger/valueViewer.cpp +++ b/soh/soh/Enhancements/debugger/valueViewer.cpp @@ -1,5 +1,6 @@ #include "valueViewer.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "soh/OTRGlobals.h" #include "soh/ShipInit.hpp" @@ -142,13 +143,14 @@ void RegisterValueViewerHooks() { RegisterShipInitFunc initFunc(RegisterValueViewerHooks, { CVAR_NAME }); void ValueViewerWindow::DrawElement() { - UIWidgets::PaddedEnhancementCheckbox("Enable Printing", CVAR_NAME); + UIWidgets::CVarCheckbox("Enable Printing", CVAR_NAME, UIWidgets::CheckboxOptions().Color(THEME_COLOR)); ImGui::BeginGroup(); static int selectedElement = -1; std::string selectedElementText = (selectedElement == -1) ? "Select a value" : ( std::string(valueTable[selectedElement].name) + " (" + std::string(valueTable[selectedElement].path) + ")" ); + UIWidgets::PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("##valueViewerElement", selectedElementText.c_str())) { for (int i = 0; i < valueTable.size(); i++) { if (valueTable[i].isActive) continue; @@ -165,20 +167,28 @@ void ValueViewerWindow::DrawElement() { } ImGui::EndCombo(); } + UIWidgets::PopStyleCombobox(); ImGui::SameLine(); + UIWidgets::PushStyleButton(THEME_COLOR); if (selectedElement != -1 && ImGui::Button("+")) { valueTable[selectedElement].isActive = true; selectedElement = -1; } + UIWidgets::PopStyleButton(); ImGui::EndGroup(); for (int i = 0; i < valueTable.size(); i++) { ValueTableElement& element = valueTable[i]; if (!element.isActive || (gPlayState == NULL && element.requiresPlayState)) continue; - if (ImGui::Button(("x##" + std::string(element.name)).c_str())) { + UIWidgets::PushStyleButton(THEME_COLOR); + UIWidgets::PushStyleCheckbox(THEME_COLOR); + ImGui::AlignTextToFramePadding(); + if (ImGui::Button((ICON_FA_TIMES + std::string("##") + std::string(element.name)).c_str())) { element.isActive = false; element.isPrinted = false; } + UIWidgets::PopStyleCheckbox(); + UIWidgets::PopStyleButton(); ImGui::SameLine(); ImGui::Text("%s:", element.name); ImGui::SameLine(); @@ -212,7 +222,7 @@ void ValueViewerWindow::DrawElement() { break; } ImGui::SameLine(); - + UIWidgets::PushStyleCheckbox(THEME_COLOR); if (element.type <= TYPE_U32) { ImGui::Checkbox(("Hex##" + std::string(element.name)).c_str(), &element.typeFormat); ImGui::SameLine(); @@ -220,23 +230,30 @@ void ValueViewerWindow::DrawElement() { ImGui::Checkbox(("Trim##" + std::string(element.name)).c_str(), &element.typeFormat); ImGui::SameLine(); } + UIWidgets::PopStyleCheckbox(); ImGui::BeginGroup(); if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("ValueViewerEnablePrinting"), 0)) { + UIWidgets::PushStyleCheckbox(THEME_COLOR); ImGui::Checkbox(("Print##" + std::string(element.name)).c_str(), &element.isPrinted); + UIWidgets::PopStyleCheckbox(); if (element.isPrinted) { char* prefix = (char*)element.prefix.c_str(); ImGui::SameLine(); ImGui::SetNextItemWidth(80.0f); + UIWidgets::PushStyleInput(THEME_COLOR); if (ImGui::InputText(("Prefix##" + std::string(element.name)).c_str(), prefix, 10)) { element.prefix = prefix; } + UIWidgets::PopStyleInput(); ImGui::SameLine(); ImGui::ColorEdit3(("##color" + std::string(element.name)).c_str(), (float*)&element.color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel); ImGui::SameLine(); + UIWidgets::PushStyleCheckbox(THEME_COLOR); if (ImGui::Button(("Position##" + std::string(element.name)).c_str())) { ImGui::OpenPopup(("Position Picker##" + std::string(element.name)).c_str()); } + UIWidgets::PopStyleCheckbox(); if (ImGui::BeginPopup(("Position Picker##" + std::string(element.name)).c_str())) { ImGui::DragInt("X", (int*)&element.x, 1.0f, 0, 44); ImGui::DragInt("Y", (int*)&element.y, 1.0f, 0, 29); diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index a2b72ca07..326a4a78d 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -186,7 +186,7 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t *actorId, f32 *po // Lengthen timer in non-MQ Jabu Jabu bubble room. if (!isMQ && *actorId == ACTOR_OBJ_ROOMTIMER && *params == 30760 && play->sceneNum == SCENE_JABU_JABU && play->roomCtx.curRoom.num == 12) { - *params = 92280; + *params = (*params & ~0x3FF) | 120; } if (IsEnemyFoundToRandomize(play->sceneNum, play->roomCtx.curRoom.num, *actorId, *params, *posX)) { @@ -286,7 +286,7 @@ void GetSelectedEnemies() { for (int i = 0; i < 49; i++) { if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) { selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); - } else if (CVarGetInteger(enemyCVarList[i], 0)) { + } else if (CVarGetInteger(enemyCVarList[i], 1)) { selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); } } diff --git a/soh/soh/Enhancements/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h index db96a7700..14228054b 100644 --- a/soh/soh/Enhancements/enhancementTypes.h +++ b/soh/soh/Enhancements/enhancementTypes.h @@ -39,7 +39,7 @@ typedef enum { MIRRORED_WORLD_ALWAYS, MIRRORED_WORLD_RANDOM, MIRRORED_WORLD_RANDOM_SEEDED, - MIRRORED_WORLD_DUNGEONS_All, + MIRRORED_WORLD_DUNGEONS_ALL, MIRRORED_WORLD_DUNGEONS_VANILLA, MIRRORED_WORLD_DUNGEONS_MQ, MIRRORED_WORLD_DUNGEONS_RANDOM, @@ -75,6 +75,18 @@ typedef enum { BONK_DAMAGE_OHKO, } BonkDamage; +typedef enum { + DAMAGE_VANILLA, + DAMAGE_DOUBLE, + DAMAGE_QUADRUPLE, + DAMAGE_OCTUPLE, + DAMAGE_FOOLISH, + DAMAGE_RIDICULOUS, + DAMAGE_MERCILESS, + DAMAGE_TORTURE, + DAMAGE_OHKO +} DamageMultType; + typedef enum { DEKU_STICK_NORMAL, DEKU_STICK_UNBREAKABLE, @@ -87,4 +99,16 @@ typedef enum { SWORD_TOGGLE_BOTH_AGES, } SwordToggleMode; +typedef enum { + TIME_TRAVEL_DISABLED, + TIME_TRAVEL_OOT, + TIME_TRAVEL_ANY +} TimeTravelType; + +typedef enum { + WATERFALL_ALWAYS, + WATERFALL_ONCE, + WATERFALL_NEVER +} SleepingWaterfallType; + #endif diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 8c6be086f..db68ad86a 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -4,8 +4,8 @@ #define GameInteractor_h #include "libultraship/libultraship.h" -#include "GameInteractionEffect.h" #include "vanilla-behavior/GIVanillaBehavior.h" +#include "GameInteractionEffect.h" #include "soh/Enhancements/item-tables/ItemTableTypes.h" #include @@ -92,19 +92,20 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state); } #endif - #ifdef __cplusplus #include #include +#include #include #include #include #include + #include #ifdef __cpp_lib_source_location #include #else -#pragma message("Compiling without support, the Hook Debugger will not be avaliable") +#pragma message("Compiling without support, the Hook Debugger will not be available") #endif typedef uint32_t HOOK_ID; @@ -124,24 +125,31 @@ struct HookRegisteringInfo { const char* function; HookType type; - HookRegisteringInfo() : valid(false), file("unknown file"), line(0), column(0), function("unknown function"), type(HOOK_TYPE_NORMAL) {} + HookRegisteringInfo() + : valid(false), file("unknown file"), line(0), column(0), function("unknown function"), type(HOOK_TYPE_NORMAL) { + } - HookRegisteringInfo(const char* _file, std::uint_least32_t _line, std::uint_least32_t _column, const char* _function, HookType _type) : - valid(true), file(_file), line(_line), column(_column), function(_function), type(_type) {} + HookRegisteringInfo(const char* _file, std::uint_least32_t _line, std::uint_least32_t _column, + const char* _function, HookType _type) + : valid(true), file(_file), line(_line), column(_column), function(_function), type(_type) { + // Trim off user parent directories + const char* trimmed = strstr(_file, "soh/soh/"); + if (trimmed != nullptr) { + file = trimmed; + } + } }; struct HookInfo { uint32_t calls; HookRegisteringInfo registering; - - HookInfo() : calls(0), registering(HookRegisteringInfo{}) {} - HookInfo(HookRegisteringInfo _registering) : calls(0), registering(_registering) {} }; #ifdef __cpp_lib_source_location -#define GET_CURRENT_REGISTERING_INFO(type) HookRegisteringInfo{location.file_name(), location.line(), location.column(), location.function_name(), type} +#define GET_CURRENT_REGISTERING_INFO(type) \ + (HookRegisteringInfo{ location.file_name(), location.line(), location.column(), location.function_name(), type }) #else -#define GET_CURRENT_REGISTERING_INFO(type) HookRegisteringInfo{} +#define GET_CURRENT_REGISTERING_INFO(type) (HookRegisteringInfo{}) #endif #define REGISTER_VB_SHOULD(flag, body) \ @@ -171,23 +179,23 @@ struct HookInfo { hookId = GameInteractor::Instance->RegisterGameHookForID(id, body); \ } \ } -#define COND_VB_SHOULD(id, condition, body) \ - { \ - static HOOK_ID hookId = 0; \ +#define COND_VB_SHOULD(id, condition, body) \ + { \ + static HOOK_ID hookId = 0; \ GameInteractor::Instance->UnregisterGameHookForID(hookId); \ - hookId = 0; \ - if (condition) { \ - hookId = REGISTER_VB_SHOULD(id, body); \ - } \ + hookId = 0; \ + if (condition) { \ + hookId = REGISTER_VB_SHOULD(id, body); \ + } \ } class GameInteractor { -public: + public: static GameInteractor* Instance; // Game State class State { - public: + public: static bool NoUIActive; static GILinkSize LinkSize; static bool InvisibleLinkActive; @@ -219,14 +227,15 @@ public: // Game Hooks HOOK_ID nextHookId = 1; + template struct RegisteredGameHooks { inline static std::unordered_map functions; inline static std::unordered_map> functionsForID; inline static std::unordered_map> functionsForPtr; inline static std::unordered_map> functionsForFilter; - //Used for the hook debugger - inline static std::unordered_map hookData; + // Used for the hook debugger + inline static std::map hookData; }; template struct HooksToUnregister { @@ -236,39 +245,43 @@ public: inline static std::vector hooksForFilter; }; - template std::unordered_map* GetHookData() { + template std::map* GetHookData() { return &RegisteredGameHooks::hookData; } // General Hooks - template HOOK_ID RegisterGameHook( - typename H::fn h + template #ifdef __cpp_lib_source_location - , const std::source_location location = std::source_location::current() + HOOK_ID RegisterGameHook(typename H::fn h, const std::source_location location = std::source_location::current()) { +#else + HOOK_ID RegisterGameHook(typename H::fn h) { #endif - ) { - // Ensure hook id is unique and not 0, which is reserved for invalid hooks - if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; + if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) + this->nextHookId = 1; while (RegisteredGameHooks::functions.find(this->nextHookId) != RegisteredGameHooks::functions.end()) { this->nextHookId++; } RegisteredGameHooks::functions[this->nextHookId] = h; - RegisteredGameHooks::hookData[this->nextHookId] = HookInfo{GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_NORMAL)}; + RegisteredGameHooks::hookData[this->nextHookId] = + HookInfo{ 0, GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_NORMAL) }; return this->nextHookId++; } template void UnregisterGameHook(HOOK_ID hookId) { - if (hookId == 0) return; + if (hookId == 0) + return; HooksToUnregister::hooks.push_back(hookId); } template void ExecuteHooks(Args&&... args) { + // Remove pending hooks for this type for (auto& hookId : HooksToUnregister::hooks) { RegisteredGameHooks::functions.erase(hookId); RegisteredGameHooks::hookData.erase(hookId); } HooksToUnregister::hooks.clear(); + // Execute hooks for (auto& hook : RegisteredGameHooks::functions) { hook.second(std::forward(args)...); RegisteredGameHooks::hookData[hook.first].calls += 1; @@ -276,39 +289,60 @@ public: } // ID based Hooks - template HOOK_ID RegisterGameHookForID( - int32_t id, typename H::fn h + template #ifdef __cpp_lib_source_location - , const std::source_location location = std::source_location::current() + HOOK_ID RegisterGameHookForID(int32_t id, typename H::fn h, + std::source_location location = std::source_location::current()) { +#else + HOOK_ID RegisterGameHookForID(int32_t id, typename H::fn h) { #endif - ) { - if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; - while (RegisteredGameHooks::functionsForID[id].find(this->nextHookId) != RegisteredGameHooks::functionsForID[id].end()) { + if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) + this->nextHookId = 1; + while (RegisteredGameHooks::functionsForID[id].find(this->nextHookId) != + RegisteredGameHooks::functionsForID[id].end()) { this->nextHookId++; } RegisteredGameHooks::functionsForID[id][this->nextHookId] = h; - RegisteredGameHooks::hookData[this->nextHookId] = HookInfo{GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_ID)}; + RegisteredGameHooks::hookData[this->nextHookId] = HookInfo{ 0, GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_ID) }; return this->nextHookId++; } template void UnregisterGameHookForID(HOOK_ID hookId) { - if (hookId == 0) return; + if (hookId == 0) + return; HooksToUnregister::hooksForID.push_back(hookId); } template void ExecuteHooksForID(int32_t id, Args&&... args) { - for (auto& hookId : HooksToUnregister::hooksForID) { - for (auto it = RegisteredGameHooks::functionsForID[id].begin(); it != RegisteredGameHooks::functionsForID[id].end(); ) { - if (it->first == hookId) { + // Remove pending hooks for this type + for (auto hookIdIt = HooksToUnregister::hooksForID.begin(); + hookIdIt != HooksToUnregister::hooksForID.end();) { + bool remove = false; + + if (RegisteredGameHooks::functionsForID[id].size() == 0) { + break; + } + + for (auto it = RegisteredGameHooks::functionsForID[id].begin(); + it != RegisteredGameHooks::functionsForID[id].end();) { + if (it->first == *hookIdIt) { it = RegisteredGameHooks::functionsForID[id].erase(it); - HooksToUnregister::hooksForID.erase(std::remove(HooksToUnregister::hooksForID.begin(), HooksToUnregister::hooksForID.end(), hookId), HooksToUnregister::hooksForID.end()); - RegisteredGameHooks::hookData.erase(hookId); + RegisteredGameHooks::hookData.erase(*hookIdIt); + remove = true; + break; } else { ++it; } } + + if (remove) { + hookIdIt = HooksToUnregister::hooksForID.erase(hookIdIt); + } else { + ++hookIdIt; + } } + // Execute hooks for (auto& hook : RegisteredGameHooks::functionsForID[id]) { hook.second(std::forward(args)...); RegisteredGameHooks::hookData[hook.first].calls += 1; @@ -316,39 +350,60 @@ public: } // PTR based Hooks - template HOOK_ID RegisterGameHookForPtr( - uintptr_t ptr, typename H::fn h + template #ifdef __cpp_lib_source_location - , const std::source_location location = std::source_location::current() + HOOK_ID RegisterGameHookForPtr(uintptr_t ptr, typename H::fn h, + const std::source_location location = std::source_location::current()) { +#else + HOOK_ID RegisterGameHookForPtr(uintptr_t ptr, typename H::fn h) { #endif - ) { - if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; - while (RegisteredGameHooks::functionsForPtr[ptr].find(this->nextHookId) != RegisteredGameHooks::functionsForPtr[ptr].end()) { + if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) + this->nextHookId = 1; + while (RegisteredGameHooks::functionsForPtr[ptr].find(this->nextHookId) != + RegisteredGameHooks::functionsForPtr[ptr].end()) { this->nextHookId++; } RegisteredGameHooks::functionsForPtr[ptr][this->nextHookId] = h; - RegisteredGameHooks::hookData[this->nextHookId] = HookInfo{GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_PTR)}; + RegisteredGameHooks::hookData[this->nextHookId] = HookInfo{ 0, GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_PTR) }; return this->nextHookId++; } template void UnregisterGameHookForPtr(HOOK_ID hookId) { - if (hookId == 0) return; + if (hookId == 0) + return; HooksToUnregister::hooksForPtr.push_back(hookId); } template void ExecuteHooksForPtr(uintptr_t ptr, Args&&... args) { - for (auto& hookId : HooksToUnregister::hooksForPtr) { - for (auto it = RegisteredGameHooks::functionsForPtr[ptr].begin(); it != RegisteredGameHooks::functionsForPtr[ptr].end(); ) { - if (it->first == hookId) { + // Remove pending hooks for this type + for (auto hookIdIt = HooksToUnregister::hooksForPtr.begin(); + hookIdIt != HooksToUnregister::hooksForPtr.end();) { + bool remove = false; + + if (RegisteredGameHooks::functionsForPtr[ptr].size() == 0) { + break; + } + + for (auto it = RegisteredGameHooks::functionsForPtr[ptr].begin(); + it != RegisteredGameHooks::functionsForPtr[ptr].end();) { + if (it->first == *hookIdIt) { it = RegisteredGameHooks::functionsForPtr[ptr].erase(it); - HooksToUnregister::hooksForPtr.erase(std::remove(HooksToUnregister::hooksForPtr.begin(), HooksToUnregister::hooksForPtr.end(), hookId), HooksToUnregister::hooksForPtr.end()); - RegisteredGameHooks::hookData.erase(hookId); + RegisteredGameHooks::hookData.erase(*hookIdIt); + remove = true; + break; } else { ++it; } } + + if (remove) { + hookIdIt = HooksToUnregister::hooksForPtr.erase(hookIdIt); + } else { + ++hookIdIt; + } } + // Execute hooks for (auto& hook : RegisteredGameHooks::functionsForPtr[ptr]) { hook.second(std::forward(args)...); RegisteredGameHooks::hookData[hook.first].calls += 1; @@ -356,33 +411,40 @@ public: } // Filter based Hooks - template HOOK_ID RegisterGameHookForFilter( - typename H::filter f, typename H::fn h + template #ifdef __cpp_lib_source_location - , const std::source_location location = std::source_location::current() + HOOK_ID RegisterGameHookForFilter(typename H::filter f, typename H::fn h, + const std::source_location location = std::source_location::current()) { +#else + HOOK_ID RegisterGameHookForFilter(typename H::filter f, typename H::fn h) { #endif - ) { - if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; - while (RegisteredGameHooks::functionsForFilter.find(this->nextHookId) != RegisteredGameHooks::functionsForFilter.end()) { + if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) + this->nextHookId = 1; + while (RegisteredGameHooks::functionsForFilter.find(this->nextHookId) != + RegisteredGameHooks::functionsForFilter.end()) { this->nextHookId++; } RegisteredGameHooks::functionsForFilter[this->nextHookId] = std::make_pair(f, h); - RegisteredGameHooks::hookData[this->nextHookId] = HookInfo{GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_FILTER)}; + RegisteredGameHooks::hookData[this->nextHookId] = + HookInfo{ 0, GET_CURRENT_REGISTERING_INFO(HOOK_TYPE_FILTER) }; return this->nextHookId++; } template void UnregisterGameHookForFilter(HOOK_ID hookId) { - if (hookId == 0) return; + if (hookId == 0) + return; HooksToUnregister::hooksForFilter.push_back(hookId); } template void ExecuteHooksForFilter(Args&&... args) { + // Remove pending hooks for this type for (auto& hookId : HooksToUnregister::hooksForFilter) { RegisteredGameHooks::functionsForFilter.erase(hookId); RegisteredGameHooks::hookData.erase(hookId); } HooksToUnregister::hooksForFilter.clear(); + // Execute hooks for (auto& hook : RegisteredGameHooks::functionsForFilter) { if (hook.second.first(std::forward(args)...)) { hook.second.second(std::forward(args)...); @@ -391,8 +453,62 @@ public: } } + template void ProcessUnregisteredHooks() { + // Normal + for (auto& hookId : HooksToUnregister::hooks) { + RegisteredGameHooks::functions.erase(hookId); + RegisteredGameHooks::hookData.erase(hookId); + } + HooksToUnregister::hooks.clear(); + + // ID + for (auto& hookId : HooksToUnregister::hooksForID) { + for (auto& idGroup : RegisteredGameHooks::functionsForID) { + for (auto it = idGroup.second.begin(); it != idGroup.second.end();) { + if (it->first == hookId) { + it = idGroup.second.erase(it); + RegisteredGameHooks::hookData.erase(hookId); + } else { + ++it; + } + } + } + } + HooksToUnregister::hooksForID.clear(); + + // Ptr + for (auto& hookId : HooksToUnregister::hooksForPtr) { + for (auto& ptrGroup : RegisteredGameHooks::functionsForPtr) { + for (auto it = ptrGroup.second.begin(); it != ptrGroup.second.end();) { + if (it->first == hookId) { + it = ptrGroup.second.erase(it); + RegisteredGameHooks::hookData.erase(hookId); + } else { + ++it; + } + } + } + } + HooksToUnregister::hooksForPtr.clear(); + + // Filter + for (auto& hookId : HooksToUnregister::hooksForFilter) { + RegisteredGameHooks::functionsForFilter.erase(hookId); + RegisteredGameHooks::hookData.erase(hookId); + } + HooksToUnregister::hooksForFilter.clear(); + } + + void RemoveAllQueuedHooks() { +#define DEFINE_HOOK(name, _) ProcessUnregisteredHooks(); + +#include "GameInteractor_HookTable.h" + +#undef DEFINE_HOOK + } + class HookFilter { - public: + public: static auto ActorNotPlayer(Actor* actor) { return actor->id != ACTOR_PLAYER; } @@ -401,15 +517,11 @@ public: return actor->id != ACTOR_PLAYER; } static auto ActorMatchIdAndParams(int16_t id, int16_t params) { - return [id, params](Actor* actor) { - return actor->id == id && actor->params == params; - }; + return [id, params](Actor* actor) { return actor->id == id && actor->params == params; }; } // For use with Should hooks static auto SActorMatchIdAndParams(int16_t id, int16_t params) { - return [id, params](Actor* actor, bool* result) { - return actor->id == id && actor->params == params; - }; + return [id, params](Actor* actor, bool* result) { return actor->id == id && actor->params == params; }; } }; @@ -430,7 +542,7 @@ public: static bool CanAddOrTakeAmmo(int16_t amount, int16_t item); class RawAction { - public: + public: static void SetSceneFlag(int16_t sceneNum, int16_t flagType, int16_t flag); static void UnsetSceneFlag(int16_t sceneNum, int16_t flagType, int16_t flag); static bool CheckFlag(int16_t flagType, int16_t flag); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index b41030cfa..9c861062b 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -19,6 +19,9 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum) { } void GameInteractor_ExecuteOnGameStateMainStart() { + // Cleanup all hooks at the start of each frame + GameInteractor::Instance->RemoveAllQueuedHooks(); + GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index f1b918cef..d20864012 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -1,5 +1,6 @@ #pragma once +#include "vanilla-behavior/GIVanillaBehavior.h" #include "GameInteractor.h" #include diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index aa8c72d00..1db2f1e33 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -220,6 +220,9 @@ bool GameInteractor::RawAction::CheckFlag(int16_t flagType, int16_t flag) { return Flags_GetRandomizerInf(static_cast(flag)); case FlagType::FLAG_GS_TOKEN: return GET_GS_FLAGS((flag & 0x1F00) >> 8); + default: + assert(false); + return false; } } @@ -564,7 +567,7 @@ void GameInteractor::RawAction::SetRandomWind(bool active) { void GameInteractor::RawAction::SetPlayerInvincibility(bool active) { Player* player = GET_PLAYER(gPlayState); if (active) { - player->invincibilityTimer = 1000; + player->invincibilityTimer = -20; } else { player->invincibilityTimer = 0; } diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index a95dabb00..d6154347c 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1,3 +1,8 @@ +#pragma once + +#ifndef GI_VANILLA_BEHAVIOR_H +#define GI_VANILLA_BEHAVIOR_H + typedef enum { // #### `result` // ```c @@ -95,10 +100,10 @@ typedef enum { // #### `args` // - `*BgDyYoseizo` VB_BE_ELIGIBLE_FOR_GREAT_FAIRY_REWARD, - + // #### `result` // ```c - // CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && + // CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && // CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) && // LINK_IS_ADULT && // !Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS) && @@ -118,11 +123,11 @@ typedef enum { // #### `result` // ```c - // (gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_FRONT_GATE) && + // (gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_FRONT_GATE) && // LINK_IS_ADULT && - // Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) && + // Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) && // Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) && - // Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) && + // Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) && // !Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL) // ``` // #### `args` @@ -400,6 +405,14 @@ typedef enum { // - `*int16_t` (item id) VB_DRAW_AMMO_COUNT, + // #### `result` + // ```c + // (Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play) + // ``` + // #### `args` + // - None + VB_END_GERUDO_MEMBERSHIP_TALK, + // #### `result` // ```c // !(this->stateFlags3 & PLAYER_STATE3_PAUSE_ACTION_FUNC) @@ -473,7 +486,7 @@ typedef enum { // and // ```c // EnGe2_CheckCarpentersFreed() - // ``` + // ``` // #### `args` // - None VB_GERUDOS_BE_FRIENDLY, @@ -1265,10 +1278,18 @@ typedef enum { // - None VB_PLAY_BOLERO_OF_FIRE_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnDaiku` + VB_PLAY_CARPENTER_FREE_CS, + // #### `result` // Close enough & various cutscene checks // ```c - // (func_80AEC5FC(this, play)) && (!Play_InCsMode(play)) && + // (func_80AEC5FC(this, play)) && (!Play_InCsMode(play)) && // (!(player->stateFlags1 & (PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_CLIMBING_LADDER))) && // (player->actor.bgCheckFlags & 1) // ``` @@ -1697,25 +1718,25 @@ typedef enum { // #### `args` // ##### In `z_boss_dodongo.c`: // - `*BossDodongo` - // + // // ##### In `z_boss_fd2.c` // - `*BossFd2` - // + // // ##### In `z_boss_ganondrof.c`: // - `*BossGanondrof` - // + // // ##### In `z_boss_goma.c`: // - `*BossGoma` - // + // // ##### In `z_boss_mo.c`: // - `*BossMo` - // + // // ##### In `z_boss_sst.c`: // - `*BossSst` - // + // // ##### In `z_boss_tw.c`: // - `*BossTw` - // + // // ##### In `z_boss_va.c`: // - `*BossVa` VB_SPAWN_BLUE_WARP, @@ -1860,7 +1881,7 @@ typedef enum { // #### `args` // - `*EnMk` VB_USE_EYEDROP_DIALOGUE, - + // #### `result` // ```c // true @@ -1869,3 +1890,5 @@ typedef enum { // - `*EnWonderTalk2` VB_WONDER_TALK, } GIVanillaBehavior; + +#endif diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index aff0d0726..937e81ede 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -1,11 +1,11 @@ #include "gameplaystats.h" -#include "gameplaystatswindow.h" #include "soh/SaveManager.h" #include "functions.h" #include "macros.h" #include "soh/cvar_prefixes.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "soh/util.h" #include @@ -376,14 +376,18 @@ void SaveStats(SaveContext* saveContext, int sectionID, bool fullSave) { }); } -void GameplayStatsRow(const char* label, const std::string& value, ImVec4 color = COLOR_WHITE) { +void GameplayStatsRow(const char* label, const std::string& value, ImVec4 color = COLOR_WHITE, + const char* tooltip = "") { ImGui::PushStyleColor(ImGuiCol_Text, color); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%s", label); - ImGui::SameLine(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize(value.c_str()).x - 8.0f)); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize(value.c_str()).x)); ImGui::Text("%s", value.c_str()); ImGui::PopStyleColor(); + if (tooltip != "" && ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", tooltip); + } } bool compareTimestampInfoByTime(const TimestampInfo& a, const TimestampInfo& b) { @@ -527,8 +531,8 @@ void DrawGameplayStatsCountsTab() { } } } - GameplayStatsRow("Rupees Collected:", formatIntGameplayStat(gSaveContext.ship.stats.count[COUNT_RUPEES_COLLECTED])); - UIWidgets::Tooltip("Includes rupees collected with a full wallet."); + GameplayStatsRow("Rupees Collected:", formatIntGameplayStat(gSaveContext.ship.stats.count[COUNT_RUPEES_COLLECTED]), + COLOR_WHITE, "Includes rupees collected with a full wallet."); GameplayStatsRow("Rupees Spent:", formatIntGameplayStat(gSaveContext.ship.stats.count[COUNT_RUPEES_SPENT])); GameplayStatsRow("Chests Opened:", formatIntGameplayStat(gSaveContext.ship.stats.count[COUNT_CHESTS_OPENED])); GameplayStatsRow("Ammo Used:", formatIntGameplayStat(ammoUsed)); @@ -606,26 +610,36 @@ void DrawGameplayStatsBreakdownTab() { } void DrawGameplayStatsOptionsTab() { - UIWidgets::PaddedEnhancementCheckbox("Show in-game total timer", CVAR_ENHANCEMENT("GameplayStats.ShowIngameTimer"), true, false); - UIWidgets::InsertHelpHoverText("Keep track of the timer as an in-game HUD element. The position of the timer can be changed in the Cosmetics Editor."); - UIWidgets::PaddedEnhancementCheckbox("Show latest timestamps on top", CVAR_ENHANCEMENT("GameplayStats.ReverseTimestamps"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Room Breakdown", CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), true, false); - ImGui::SameLine(); - UIWidgets::InsertHelpHoverText("Allows a more in-depth perspective of time spent in a certain map."); - UIWidgets::PaddedEnhancementCheckbox("RTA Timing on new files", CVAR_ENHANCEMENT("GameplayStats.RTATiming"), true, false); - ImGui::SameLine(); - UIWidgets::InsertHelpHoverText( - "Timestamps are relative to starting timestamp rather than in game time, usually necessary for races/speedruns.\n\n" - "Starting timestamp is on first non-c-up input after intro cutscene.\n\n" - "NOTE: THIS NEEDS TO BE SET BEFORE CREATING A FILE TO TAKE EFFECT" - ); - UIWidgets::PaddedEnhancementCheckbox("Show additional detail timers", CVAR_ENHANCEMENT("GameplayStats.ShowAdditionalTimers"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Show Debug Info", CVAR_ENHANCEMENT("GameplayStats.ShowDebugInfo")); + UIWidgets::CVarCheckbox("Show in-game total timer", CVAR_ENHANCEMENT("GameplayStats.ShowIngameTimer"), + UIWidgets::CheckboxOptions() + .Tooltip("Keep track of the timer as an in-game HUD element. The position of the " + "timer can be changed in the Cosmetics Editor.") + .Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Show latest timestamps on top", CVAR_ENHANCEMENT("GameplayStats.ReverseTimestamps"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Room Breakdown", CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), + UIWidgets::CheckboxOptions() + .Tooltip("Allows a more in-depth perspective of time spent in a certain map.") + .Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("RTA Timing on new files", CVAR_ENHANCEMENT("GameplayStats.RTATiming"), + UIWidgets::CheckboxOptions() + .Tooltip("Timestamps are relative to starting timestamp rather than in game time, " + "usually necessary for races/speedruns.\n\n" + "Starting timestamp is on first non-c-up input after intro cutscene.\n\n" + "NOTE: THIS NEEDS TO BE SET BEFORE CREATING A FILE TO TAKE EFFECT") + .Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Show additional detail timers", CVAR_ENHANCEMENT("GameplayStats.ShowAdditionalTimers"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Show Debug Info", CVAR_ENHANCEMENT("GameplayStats.ShowDebugInfo"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR)); } void GameplayStatsWindow::DrawElement() { + ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); DrawGameplayStatsHeader(); + UIWidgets::PushStyleTabs(THEME_COLOR); + if (ImGui::BeginTabBar("Stats", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("Timestamps")) { DrawGameplayStatsTimestampsTab(); @@ -646,6 +660,9 @@ void GameplayStatsWindow::DrawElement() { ImGui::EndTabBar(); } + UIWidgets::PopStyleTabs(); + ImGui::PopFont(); + ImGui::Text("Note: Gameplay stats are saved to the current file and will be\nlost if you quit without saving."); } void InitStats(bool isDebug) { diff --git a/soh/soh/Enhancements/gameplaystatswindow.h b/soh/soh/Enhancements/gameplaystatswindow.h index 453b24559..446cc1ef4 100644 --- a/soh/soh/Enhancements/gameplaystatswindow.h +++ b/soh/soh/Enhancements/gameplaystatswindow.h @@ -1,5 +1,4 @@ #include -#include "gameplaystats.h" class GameplayStatsWindow : public Ship::GuiWindow { public: diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index cd60f7d8c..38de50575 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -16,7 +16,6 @@ #include "soh/Enhancements/timesaver_hook_handlers.h" #include "soh/Enhancements/TimeSavers/TimeSavers.h" #include "soh/Enhancements/randomizer/hook_handlers.h" -#include "objects/object_gi_compass/object_gi_compass.h" #include "src/overlays/actors/ovl_En_Bb/z_en_bb.h" #include "src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h" @@ -463,7 +462,7 @@ void UpdateMirrorModeState(int32_t sceneNum) { mirroredMode == MIRRORED_WORLD_ALWAYS || ((mirroredMode == MIRRORED_WORLD_RANDOM || mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED) && randomMirror) || // Dungeon modes - (inDungeon && (mirroredMode == MIRRORED_WORLD_DUNGEONS_All || + (inDungeon && (mirroredMode == MIRRORED_WORLD_DUNGEONS_ALL || (mirroredMode == MIRRORED_WORLD_DUNGEONS_VANILLA && !ResourceMgr_IsSceneMasterQuest(sceneNum)) || (mirroredMode == MIRRORED_WORLD_DUNGEONS_MQ && ResourceMgr_IsSceneMasterQuest(sceneNum)) || ((mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM || mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED) && randomMirror))) @@ -810,8 +809,10 @@ void RegisterRandomizedEnemySizes() { Actor* actor = static_cast(refActor); // Exclude wobbly platforms in Jabu because they need to act like platforms. + // Exclude demo effect for Zora sapphire being re-categorized as a "boss". // Exclude Dead Hand hands and Bongo Bongo main body because they make the fights (near) impossible. - uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || (actor->id == ACTOR_BOSS_SST && actor->params == -1); + uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || + actor->id == ACTOR_DEMO_EFFECT || (actor->id == ACTOR_BOSS_SST && actor->params == -1); // Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger. uint8_t smallOnlyEnemy = actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD || @@ -998,26 +999,6 @@ void RegisterPauseMenuHooks() { }); } -extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); - -void PatchCompasses() { - s8 compassesCanBeOutsideDungeon = IS_RANDO && DUNGEON_ITEMS_CAN_BE_OUTSIDE_DUNGEON(RSK_SHUFFLE_MAPANDCOMPASS); - s8 isColoredCompassesEnabled = compassesCanBeOutsideDungeon && CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MatchCompassColors"), 1); - if (isColoredCompassesEnabled) { - ResourceMgr_PatchGfxByName(gGiCompassDL, "Compass_PrimColor", 5, gsDPNoOp()); - ResourceMgr_PatchGfxByName(gGiCompassDL, "Compass_EnvColor", 6, gsDPNoOp()); - } else { - ResourceMgr_UnpatchGfxByName(gGiCompassDL, "Compass_PrimColor"); - ResourceMgr_UnpatchGfxByName(gGiCompassDL, "Compass_EnvColor"); - } -} - -void RegisterRandomizerCompasses() { - GameInteractor::Instance->RegisterGameHook([](int32_t _unused) { - PatchCompasses(); - }); -} - void RegisterCustomSkeletons() { static int8_t previousTunic = -1; @@ -1065,7 +1046,6 @@ void InitMods() { RegisterRandomizedEnemySizes(); RegisterOpenAllHours(); RegisterToTMedallions(); - RegisterRandomizerCompasses(); NameTag_RegisterHooks(); RegisterFloorSwitchesHook(); RegisterPatchHandHandler(); diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 930522250..8ada99f1e 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -11,12 +11,11 @@ void UpdateDirtPathFixState(int32_t sceneNum); void UpdateMirrorModeState(int32_t sceneNum); void UpdateHurtContainerModeState(bool newState); void PatchToTMedallions(); -void PatchCompasses(); void UpdatePermanentHeartLossState(); void UpdateHyperEnemiesState(); void UpdateHyperBossesState(); void InitMods(); -void UpdatePatchHand(); +void UpdatePatchHand(); void SwitchAge(); #ifdef __cplusplus diff --git a/soh/soh/Enhancements/presets.cpp b/soh/soh/Enhancements/presets.cpp index 2b1812d21..c8760caca 100644 --- a/soh/soh/Enhancements/presets.cpp +++ b/soh/soh/Enhancements/presets.cpp @@ -3,8 +3,10 @@ #include #include #include -#include "soh/SohGui/UIWidgets.hpp" #include +#include "soh/SohGui/MenuTypes.h" +#include "soh/SohGui/SohMenu.h" +#include "soh/SohGui/SohGui.hpp" std::string FormatLocations(std::vector locs) { std::string locString = ""; @@ -30,6 +32,7 @@ void applyPreset(std::vector entries) { CVarSetString(cvar, std::get(value).c_str()); break; } + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } } @@ -47,21 +50,22 @@ void DrawPresetSelector(PresetType presetTypeId) { comboboxTooltip += std::string(iter->second.label) + " - " + std::string(iter->second.description); } - UIWidgets::PaddedText("Presets", false, true); + ImGui::Text("Presets"); + UIWidgets::PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("##PresetsComboBox", selectedPresetDef.label)) { for ( auto iter = presetTypeDef.presets.begin(); iter != presetTypeDef.presets.end(); ++iter ) { if (ImGui::Selectable(iter->second.label, iter->first == selectedPresetId)) { CVarSetInteger(presetTypeCvar.c_str(), iter->first); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } } ImGui::EndCombo(); } + UIWidgets::PopStyleCombobox(); UIWidgets::Tooltip(comboboxTooltip.c_str()); - UIWidgets::Spacer(0); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f)); + UIWidgets::PushStyleButton(THEME_COLOR); if (ImGui::Button(("Apply Preset##" + presetTypeCvar).c_str())) { for(const char* block : presetTypeDef.blocksToClear) { CVarClearBlock(block); @@ -75,5 +79,5 @@ void DrawPresetSelector(PresetType presetTypeId) { Rando::Settings::GetInstance()->ReloadOptions(); } } - ImGui::PopStyleVar(1); + UIWidgets::PopStyleButton(); } diff --git a/soh/soh/Enhancements/presets.h b/soh/soh/Enhancements/presets.h index 2922ec589..a7bca4e66 100644 --- a/soh/soh/Enhancements/presets.h +++ b/soh/soh/Enhancements/presets.h @@ -242,7 +242,7 @@ const std::vector enhancedPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("SeparateArrows"), 1), // Disable Navi Call Audio - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DisableNaviCallAudio"), 1), + PRESET_ENTRY_S32(CVAR_AUDIO("DisableNaviCallAudio"), 1), // Equipment Toggle PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EquipmentCanBeRemoved"), 1), @@ -373,7 +373,7 @@ const std::vector randomizerPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("SeparateArrows"), 1), // Disable Navi Call Audio - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DisableNaviCallAudio"), 1), + PRESET_ENTRY_S32(CVAR_AUDIO("DisableNaviCallAudio"), 1), // Equipment Toggle PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EquipmentCanBeRemoved"), 1), @@ -436,7 +436,7 @@ const std::vector spockRacePresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FasterBlockPush"), 5), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FasterHeavyBlockLift"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("NoForcedNavi"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DisableNaviCallAudio"), 1), + PRESET_ENTRY_S32(CVAR_AUDIO("DisableNaviCallAudio"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FastChests"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FastDrops"), 1), PRESET_ENTRY_S32(CVAR_SETTING("DpadInText"), 1), @@ -531,7 +531,7 @@ const std::vector spockRaceNoLogicPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("CustomizeFishing"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DampeAllNight"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DayGravePull"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DisableNaviCallAudio"), 1), + PRESET_ENTRY_S32(CVAR_AUDIO("DisableNaviCallAudio"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DpadNoDropOcarinaInput"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EquipmentCanBeRemoved"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FastBoomerang"), 1), diff --git a/soh/soh/Enhancements/randomizer/3drando/custom_messages.cpp b/soh/soh/Enhancements/randomizer/3drando/custom_messages.cpp index 179987dcc..fcab083b4 100644 --- a/soh/soh/Enhancements/randomizer/3drando/custom_messages.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/custom_messages.cpp @@ -2,258 +2,9 @@ #include "../../custom-message/CustomMessageManager.h" #include "z64item.h" -#include -#include -#include - namespace CustomMessages { - using namespace std::literals::string_literals; -class MessageEntryComp { -public: - bool operator()(const MessageEntry& lhs, const MessageEntry& rhs) const { - return lhs.id < rhs.id; - } -}; - -constexpr std::array EnglishDungeonNames = { - "Deku Tree", - "Dodongo's Cavern", - "Jabu Jabu's Belly", - "Forest Temple", - "Fire Temple", - "Water Temple", - "Spirit Temple", - "Shadow Temple", - "Bottom of the Well", - "Ice Cavern", - "Ganon's Tower", - "Gerudo Training Ground", - "Gerudo Fortress", - "Ganon's Castle", -}; - -constexpr std::array FrenchDungeonNames = { - "Vénérable Arbre Mojo", - "Caverne Dodongo", - "Ventre de Jabu-Jabu", - "Temple de la Forêt", - "Temple du Feu", - "Temple de l'Eau", - "Temple de l'Esprit", - "Temple de l'Ombre", - "Puits", - "Caverne Polaire", - "Tour de Ganon", - "Gymnase Gerudo", - "Repaire des Voleurs", - "Château de Ganon", -}; - -constexpr std::array FrenchDungeonArticles = { - "du ", - "de la ", - "du ", - "du ", - "du ", - "du ", - "du ", - "du ", - "du ", - "de la ", - "", - "du ", - "de la ", - "du ", -}; - -constexpr std::array SpanishDungeonNames = { - "Gran Árbol Deku", - "Cueva de los Dodongos", - "Tripa de Jabu-Jabu", - "Templo del Bosque", - "Templo del Fuego", - "Templo del Agua", - "Templo del Espíritu", - "Templo de las Sombras", - "Fondo del pozo", - "Caverna de hielo", - "Torre de Ganon", - "Centro de Instrucción Gerudo", - "Fortaleza Gerudo", - "Castillo de Ganon", -}; - -constexpr std::array SpanishDungeonArticles = { - "del", - "de la", - "de la", - "del", - "del", - "del", - "del", - "del", - "del", - "de la", - "de la", - "del", - "de la", - "del", -}; - -constexpr std::array DungeonColors = { - QM_GREEN, - QM_RED, - QM_BLUE, - QM_GREEN, - QM_RED, - QM_BLUE, - QM_YELLOW, - QM_PINK, - QM_PINK, - QM_LBLUE, - QM_BLACK, - QM_YELLOW, - QM_YELLOW, - QM_RED, -}; - - std::set messageEntries; - std::stringstream messageData; - - //textBoxType and textBoxPosition are defined here: https://wiki.cloudmodding.com/oot/Text_Format#Message_Id - void CreateMessage(uint32_t textId, uint32_t unk_04, uint32_t textBoxType, uint32_t textBoxPosition, - std::string englishText, std::string frenchText, std::string spanishText) { - MessageEntry newEntry = { textId, unk_04, textBoxType, textBoxPosition, { 0 } }; - - while ((englishText.size() % 4) != 0) { - englishText += "\0"s; - } - messageData.seekg(0, messageData.end); - newEntry.info[ENGLISH_U].offset = (char*)((int)messageData.tellg()); - newEntry.info[ENGLISH_U].length = englishText.size(); - messageData << englishText; - - while ((frenchText.size() % 4) != 0) { - frenchText += "\0"s; - } - messageData.seekg(0, messageData.end); - newEntry.info[FRENCH_U].offset = (char*)((int)messageData.tellg()); - newEntry.info[FRENCH_U].length = frenchText.size(); - messageData << frenchText; - - while ((spanishText.size() % 4) != 0) { - spanishText += "\0"s; - } - messageData.seekg(0, messageData.end); - newEntry.info[SPANISH_U].offset = (char*)((int)messageData.tellg()); - newEntry.info[SPANISH_U].length = spanishText.size(); - messageData << spanishText; - - messageEntries.insert(newEntry); - } - - void CreateMessageFromTextObject(uint32_t textId, uint32_t unk_04, uint32_t textBoxType, uint32_t textBoxPosition, const Text& text) { - CreateMessage(textId, unk_04, textBoxType, textBoxPosition, text.GetEnglish(), text.GetFrench(), text.GetSpanish()); - } - - Text AddColorsAndFormat(Text text, const std::vector& colors /*= {}*/) { - - //for each language - for (std::string* textStr : {&text.english, &text.french, &text.spanish}) { - - //insert playername - size_t atSymbol = textStr->find('@'); - while (atSymbol != std::string::npos) { - textStr->replace(atSymbol, 1, PLAYER_NAME()); - atSymbol = textStr->find('@'); - } - //insert newlines either manually or when encountering a '&' - constexpr size_t lineLength = 44; - size_t lastNewline = 0; - while (lastNewline + lineLength < textStr->length()) { - size_t carrot = textStr->find('^', lastNewline); - size_t ampersand = textStr->find('&', lastNewline); - size_t lastSpace = textStr->rfind(' ', lastNewline + lineLength); - size_t lastPeriod = textStr->rfind('.', lastNewline + lineLength); - //replace '&' first if it's within the newline range - if (ampersand < lastNewline + lineLength) { - textStr->replace(ampersand, 1, NEWLINE()); - lastNewline = ampersand + NEWLINE().length(); - //or move the lastNewline cursor to the next line if a '^' is encountered - } else if (carrot < lastNewline + lineLength) { - lastNewline = carrot + 1; - //some lines need to be split but don't have spaces, look for periods instead - } else if (lastSpace == std::string::npos) { - textStr->replace(lastPeriod, 1, "."+NEWLINE()); - lastNewline = lastPeriod + NEWLINE().length() + 1; - } else { - textStr->replace(lastSpace, 1, NEWLINE()); - lastNewline = lastSpace + NEWLINE().length(); - } - } - //clean up any remaining '&' characters - size_t ampersand = textStr->find('&'); - while (ampersand != std::string::npos) { - textStr->replace(ampersand, 1, NEWLINE()); - ampersand = textStr->find('&'); - } - - //insert box break - size_t carrotSymbol = textStr->find('^'); - while (carrotSymbol != std::string::npos) { - textStr->replace(carrotSymbol, 1, INSTANT_TEXT_OFF()+WAIT_FOR_INPUT()+INSTANT_TEXT_ON()); - carrotSymbol = textStr->find('^'); - } - - //If there's a two-way choice and only 1 newline before it in the same text box, add another one - size_t choice = textStr->find(TWO_WAY_CHOICE()); - if (choice != std::string::npos) { - size_t newLinesCount = 0; - size_t lastBoxBreak = textStr->rfind(WAIT_FOR_INPUT(), choice); - lastNewline = choice; - - if (lastBoxBreak == std::string::npos) { - lastBoxBreak = 0; - } - - while ((lastNewline != std::string::npos)) { - lastNewline = textStr->rfind(NEWLINE(), lastNewline - 1); - if (lastNewline != std::string::npos && lastNewline > lastBoxBreak) { - newLinesCount++; - } else { - break; - } - } - - if (newLinesCount <= 1) { - textStr->replace(choice, TWO_WAY_CHOICE().length(), NEWLINE()+TWO_WAY_CHOICE()); - } - } - - //add colors - for (auto color : colors) { - size_t firstHashtag = textStr->find('#'); - if (firstHashtag != std::string::npos) { - textStr->replace(firstHashtag, 1, COLOR(color)); - size_t secondHashtag = textStr->find('#'); - if (secondHashtag == std::string::npos) { - //CitraPrint("ERROR: Couldn't find second '#' in " + (*textStr)); - } else { - textStr->replace(secondHashtag, 1, COLOR(QM_WHITE)); - } - } - } - } - return Text{"","",""}+UNSKIPPABLE()+INSTANT_TEXT_ON()+text+INSTANT_TEXT_OFF()+MESSAGE_END(); - } - - void ClearMessages() { - messageEntries.clear(); - messageData.str(""); - } - std::string MESSAGE_END() { return "\x7F\x00"s; } std::string WAIT_FOR_INPUT() { return "\x7F\x01"s; } std::string HORIZONTAL_SPACE(uint8_t x) { @@ -280,7 +31,7 @@ constexpr std::array DungeonColors = { std::string SET_SPEED(uint8_t x) { return "\x7F\x10"s + char(x); } - std::string SKULLTULAS_DESTROYED() { return "\x7F\x15"s; } //RANDOTODO just refernce the versions in CustomMessage + std::string SKULLTULAS_DESTROYED() { return "\x7F\x15"s; } std::string CURRENT_TIME() { return "\x7F\x17"s; } std::string UNSKIPPABLE() { return "\x7F\x19"s; } std::string TWO_WAY_CHOICE() { return "\x1B"s; } diff --git a/soh/soh/Enhancements/randomizer/3drando/custom_messages.hpp b/soh/soh/Enhancements/randomizer/3drando/custom_messages.hpp index b9d2a08c6..4daacf51a 100644 --- a/soh/soh/Enhancements/randomizer/3drando/custom_messages.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/custom_messages.hpp @@ -1,50 +1,11 @@ #pragma once #include -#include -#include #include #include "text.hpp" namespace CustomMessages { -typedef struct { - // In the true file format, offset is the offset into the QM file. - // In randomizer, offset will be a pointer to the text in the game's address space. - // Since these pointers will be much larger as u32 than the original script's offsets, - // We will be able to distinguish between original and custom text using their numerical value. - const char* offset; - uint32_t length; -} MessageLanguageInfo; - -typedef enum { - /* 0x00 */ JAPANESE_J, - /* 0x01 */ ENGLISH_U, - /* 0x02 */ ENGLISH_E, - /* 0x03 */ GERMAN_E, - /* 0x04 */ FRENCH_E, - /* 0x05 */ FRENCH_U, - /* 0x06 */ SPANISH_E, - /* 0x07 */ SPANISH_U, - /* 0x08 */ ITALIAN_E, - /* 0x09 */ DUTCH_E, -} MessageLanguage; - -typedef struct { - uint32_t id; - uint32_t unk_04; - uint32_t unk_08; - uint32_t unk_0C; - MessageLanguageInfo info[10]; -} MessageEntry; // size = 0x60 - - void CreateMessage(uint32_t textId, uint32_t unk_04, uint32_t textBoxType, uint32_t textBoxPosition, - std::string englishText, std::string frenchText, std::string spanishText); - void CreateMessageFromTextObject(uint32_t textId, uint32_t unk_04, uint32_t textBoxType, uint32_t textBoxPosition, const Text& text); - - Text AddColorsAndFormat(Text text, const std::vector& colors = {}); - void ClearMessages(); - std::string MESSAGE_END(); std::string WAIT_FOR_INPUT(); std::string HORIZONTAL_SPACE(uint8_t x); diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 351c57fd2..b0548f5ee 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -1,6 +1,5 @@ #include "fill.hpp" -#include "custom_messages.hpp" #include "../dungeon.h" #include "../context.h" #include "item_pool.hpp" @@ -19,7 +18,6 @@ #include #include -using namespace CustomMessages; using namespace Rando; diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp index 867da2b2c..c786dd8de 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -2070,23 +2070,23 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a rightward tone", /*german*/"ein rechtsseitiger Ton", /*french*/"une tonalité vers la droite")}); // /*spanish*/un tono hacia la derecha - hintTextTable[RHT_FISHING_POLE] = HintText(CustomMessage("a fishing pole", /*german*/"eine Angelrute", /*french*/"canne à pêche"), + hintTextTable[RHT_FISHING_POLE] = HintText(CustomMessage("a fishing pole", /*german*/"eine Angelrute", /*french*/"une canne à pêche"), // /*spanish*/caña de pescar { - CustomMessage("the pond owner's property", /*german*/"der Besitz des Teicheigners", /*french*/"(canne à pêche)") + CustomMessage("the pond owner's property", /*german*/"der Besitz des Teicheigners", /*french*/"(un truc qui appartient au propriétaire de l'étang)") // /*spanish*/(caña de pescar) }, { - CustomMessage("a fish-puller", /*german*/"ein Fischzieher", /*french*/"(canne à pêche)")}); + CustomMessage("a fish-puller", /*german*/"ein Fischzieher", /*french*/"(un aimant à poisson)")}); // /*spanish*/(caña de pescar) - hintTextTable[RHT_BOMBCHU_BAG] = HintText(CustomMessage("Bombchu Bag", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_BOMBCHU_BAG] = HintText(CustomMessage("Bombchu Bag", /*german*/"!!!", /*french*/"un Sac de Missiles Teigneux"), { CustomMessage("explosives", /*german*/"ein Explosivpaket", /*french*/"un paquet d'explosifs"), // /*spanish*/un montón de explosivos CustomMessage("something that can remove boulders", /*german*/"etwas, das Geröll entfernen kann", /*french*/"une chose qui enlève les rochers") // /*spanish*/algo que pueda quitar rocas }, { - CustomMessage("sack of mice", /*german*/"!!!", /*french*/"!!!")}); + CustomMessage("sack of mice", /*german*/"!!!", /*french*/"un Sac rempli de souris")}); hintTextTable[RHT_SKELETON_KEY] = HintText(CustomMessage("a Skeleton Key", /*german*/ "ein Universalschlüssel", /*french*/ "une Clé Squelette"), // /*spanish*/una Llave Maestra @@ -2096,49 +2096,49 @@ void StaticData::HintTable_Init_Item() { }, { CustomMessage("a master unlocker", /*german*/ "ein Meisterentsperrer", /*french*/ "un Kit de Déverrouillage") }); // /*spanish*/un desbloqueador maestro - hintTextTable[RHT_QUIVER_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_QUIVER_INF] = HintText(CustomMessage("an infinite Quiver", /*german*/"!!!", /*french*/"un Carquois Infini"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); - hintTextTable[RHT_BOMB_BAG_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_BOMB_BAG_INF] = HintText(CustomMessage("an infinite Bomb Bag", /*german*/"!!!", /*french*/"un Sac de Bombe sans fond"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); - hintTextTable[RHT_BULLET_BAG_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_BULLET_BAG_INF] = HintText(CustomMessage("an infinite Bullet Bag", /*german*/"!!!", /*french*/"un Sac de Graine sans fond"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); - hintTextTable[RHT_STICK_UPGRADE_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_STICK_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Sticks", /*german*/"!!!", /*french*/" des Bâtons Mojo illimités"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); - hintTextTable[RHT_NUT_UPGRADE_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_NUT_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Nut", /*german*/"!!!", /*french*/"des Noix Mojo illimitées"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); - hintTextTable[RHT_MAGIC_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_MAGIC_INF] = HintText(CustomMessage("unlimited Magic", /*german*/"!!!", /*french*/"de la Magie infinie"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); - hintTextTable[RHT_BOMBCHU_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_BOMBCHU_INF] = HintText(CustomMessage("infinite Bombchus", /*german*/"!!!", /*french*/"des Missiles Teigneux illimités"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); - hintTextTable[RHT_WALLET_INF] = HintText(CustomMessage("", /*german*/"!!!", /*french*/"!!!"), + hintTextTable[RHT_WALLET_INF] = HintText(CustomMessage("an infinite Wallet", /*german*/"!!!", /*french*/"une Bourse sans fond"), { CustomMessage("", /*german*/"!!!", /*french*/"!!!"), }, { diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 310f762c4..4654a36a6 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -1297,7 +1297,7 @@ void GenerateItemPool() { if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) { ctx->PlaceItemInLocation(RC_KAK_100_GOLD_SKULLTULA_REWARD, RG_GANONS_CASTLE_BOSS_KEY); - } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Get() >= RO_GANON_BOSS_KEY_LACS_VANILLA && ctx->GetOption(RSK_GANONS_BOSS_KEY).IsNot(RO_GANON_BOSS_KEY_TRIFORCE_HUNT)) { + } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Get() >= RO_GANON_BOSS_KEY_LACS_VANILLA) { ctx->PlaceItemInLocation(RC_TOT_LIGHT_ARROWS_CUTSCENE, RG_GANONS_CASTLE_BOSS_KEY); } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_VANILLA)) { ctx->PlaceItemInLocation(RC_GANONS_TOWER_BOSS_KEY_CHEST, RG_GANONS_CASTLE_BOSS_KEY); diff --git a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp index 144dee80a..d8fd302c1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp @@ -2,7 +2,6 @@ #include #include -#include "custom_messages.hpp" #include "fill.hpp" #include "../location_access.h" #include "random.hpp" @@ -23,7 +22,6 @@ int Playthrough_Init(uint32_t seed, std::set excludedLocations, auto ctx = Rando::Context::GetInstance(); ctx->overrides.clear(); - CustomMessages::ClearMessages(); ctx->ItemReset(); ctx->HintReset(); ctx->GetLogic()->Reset(); diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index edaf8aa5d..75120fa41 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -275,6 +275,9 @@ int GetShopsanityReplaceAmount() { return 7; } else if (ctx->GetOption(RSK_SHOPSANITY_COUNT).Is(RO_SHOPSANITY_COUNT_EIGHT_ITEMS)) { return 8; //temporarily unreachable due to logic limitations + } else { + assert(false); + return 0; } } else { //Random, get number in [1, 7] return Random(1, 8); diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index c72b5f285..04a7e3040 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -57,7 +57,7 @@ void GenerateStartingInventory() { // Add Ganon's Boss key with Triforce Hunt so the game thinks it's obtainable from the start. // During save init, the boss key isn't actually given and it's instead given when completing the triforce. if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH) || - ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_TRIFORCE_HUNT)) { + ctx->GetOption(RSK_TRIFORCE_HUNT)) { AddItemToInventory(RG_GANONS_CASTLE_BOSS_KEY); } diff --git a/soh/soh/Enhancements/randomizer/ColoredMapsAndCompasses.cpp b/soh/soh/Enhancements/randomizer/ColoredMapsAndCompasses.cpp new file mode 100644 index 000000000..fcfa13d97 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ColoredMapsAndCompasses.cpp @@ -0,0 +1,43 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ResourceManagerHelpers.h" +#include "soh/ShipInit.hpp" +#include "z64save.h" +#include "objects/object_gi_compass/object_gi_compass.h" +#include "objects/object_gi_map/object_gi_map.h" + +extern "C" { + extern SaveContext gSaveContext; + #include "variables.h" + #include "macros.h" + u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); +} + +#define CVAR_COLORED_MAPS_AND_COMPASSES_NAME CVAR_RANDOMIZER_ENHANCEMENT("ColoredMapsAndCompasses") +#define CVAR_COLORED_MAPS_AND_COMPASSES_DEFAULT 1 +#define CVAR_COLORED_MAPS_AND_COMPASSES_VALUE CVarGetInteger(CVAR_COLORED_MAPS_AND_COMPASSES_NAME, CVAR_COLORED_MAPS_AND_COMPASSES_DEFAULT) + +void OnLoadFileColoredMapsAndCompasses(int32_t _) { + s8 mapsAndCompassesCanBeOutsideDungeon = IS_RANDO && DUNGEON_ITEMS_CAN_BE_OUTSIDE_DUNGEON(RSK_SHUFFLE_MAPANDCOMPASS); + s8 isColoredMapsAndCompassesEnabled = mapsAndCompassesCanBeOutsideDungeon && CVAR_COLORED_MAPS_AND_COMPASSES_VALUE; + if (isColoredMapsAndCompassesEnabled) { + ResourceMgr_PatchGfxByName(gGiDungeonMapDL, "Map_PrimColor", 5, gsDPNoOp()); + ResourceMgr_PatchGfxByName(gGiDungeonMapDL, "Map_EnvColor", 6, gsDPNoOp()); + ResourceMgr_PatchGfxByName(gGiCompassDL, "Compass_PrimColor", 5, gsDPNoOp()); + ResourceMgr_PatchGfxByName(gGiCompassDL, "Compass_EnvColor", 6, gsDPNoOp()); + } else { + ResourceMgr_UnpatchGfxByName(gGiDungeonMapDL, "Map_PrimColor"); + ResourceMgr_UnpatchGfxByName(gGiDungeonMapDL, "Map_EnvColor"); + ResourceMgr_UnpatchGfxByName(gGiCompassDL, "Compass_PrimColor"); + ResourceMgr_UnpatchGfxByName(gGiCompassDL, "Compass_EnvColor"); + } +} + +void RegisterColoredMapsAndCompasses() { + COND_HOOK(OnLoadFile, CVAR_COLORED_MAPS_AND_COMPASSES_VALUE, OnLoadFileColoredMapsAndCompasses) + + //Also need to call it directly to patch/unpatch on cvar change + OnLoadFileColoredMapsAndCompasses(0); +} + +static RegisterShipInitFunc initFunc(RegisterColoredMapsAndCompasses, { CVAR_COLORED_MAPS_AND_COMPASSES_NAME }); diff --git a/soh/soh/Enhancements/randomizer/Plandomizer.cpp b/soh/soh/Enhancements/randomizer/Plandomizer.cpp index e6c9399b5..39b20d876 100644 --- a/soh/soh/Enhancements/randomizer/Plandomizer.cpp +++ b/soh/soh/Enhancements/randomizer/Plandomizer.cpp @@ -1,4 +1,5 @@ #include "Plandomizer.h" +#include #include "soh/SohGui/UIWidgets.hpp" #include "soh/util.h" #include @@ -836,21 +837,9 @@ void PlandomizerDrawItemSlots(uint32_t index) { void PlandomizerDrawShopSlider(uint32_t index) { ImGui::PushID(index); - ImGui::Text("Price:"); - ImGui::SameLine(); - std::string MinusBTNName = " - ##Price"; - if (ImGui::Button(MinusBTNName.c_str()) && plandoLogData[index].shopPrice > 0) { - plandoLogData[index].shopPrice--; - } - ImGui::SameLine(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - 40.0f); - ImGui::SliderInt("", &plandoLogData[index].shopPrice, 0, 999, "%d Rupees"); - ImGui::PopItemWidth(); - ImGui::SameLine(); - std::string PlusBTNName = " + ##Price"; - if (ImGui::Button(PlusBTNName.c_str()) && plandoLogData[index].shopPrice < 999) { - plandoLogData[index].shopPrice++; - } + UIWidgets::SliderInt("Price:", &plandoLogData[index].shopPrice, UIWidgets::IntSliderOptions() + .Color(THEME_COLOR).Format("%d Rupees").Min(0).Max(999).LabelPosition(UIWidgets::LabelPositions::Near) + .ComponentAlignment(UIWidgets::ComponentAlignments::Right).Size(UIWidgets::Sizes::Inline)); ImGui::PopID(); } @@ -883,13 +872,13 @@ void PlandomizerDrawIceTrapSetup(uint32_t index) { ImGui::SameLine(); if (plandoLogData[index].iceTrapModel.GetRandomizerGet() != RG_NONE && plandoLogData[index].iceTrapModel.GetRandomizerGet() != RG_SOLD_OUT) { - if (ImGui::Button(randomizeButton.c_str())) { + if (UIWidgets::Button(randomizeButton.c_str(), UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline).Padding(ImVec2(10.f, 6.f)))) { plandoLogData[index].iceTrapName = GetIceTrapName(plandoLogData[index].iceTrapModel.GetRandomizerGet()).GetForLanguage(CVarGetInteger(CVAR_SETTING("Languages"), 0)).c_str(); } ImGui::SameLine(); } - if (UIWidgets::InputString("##TrapName", &trapTextInput)) { + if (UIWidgets::InputString("##TrapName", &trapTextInput, UIWidgets::InputOptions().Color(THEME_COLOR).LabelPosition(UIWidgets::LabelPositions::None))) { plandoLogData[index].iceTrapName = trapTextInput.c_str(); } @@ -900,39 +889,64 @@ void PlandomizerDrawIceTrapSetup(uint32_t index) { ImGui::PopID(); } - +static std::unordered_map rcAreaNameMap = { + { RCAREA_KOKIRI_FOREST, "Kokiri Forest" }, + { RCAREA_LOST_WOODS, "Lost Woods" }, + { RCAREA_SACRED_FOREST_MEADOW, "Sacred Forest Meadow" }, + { RCAREA_HYRULE_FIELD, "Hyrule Field" }, + { RCAREA_LAKE_HYLIA, "Lake Hylia" }, + { RCAREA_GERUDO_VALLEY, "Gerudo Valley" }, + { RCAREA_GERUDO_FORTRESS, "Gerudo Fortress" }, + { RCAREA_WASTELAND, "Haunted Wasteland" }, + { RCAREA_DESERT_COLOSSUS, "Desert Colossus" }, + { RCAREA_MARKET, "Hyrule Market" }, + { RCAREA_HYRULE_CASTLE, "Hyrule Castle" }, + { RCAREA_KAKARIKO_VILLAGE, "Kakariko Village" }, + { RCAREA_GRAVEYARD, "Graveyard" }, + { RCAREA_DEATH_MOUNTAIN_TRAIL, "Death Mountain Trail" }, + { RCAREA_GORON_CITY, "Goron City" }, + { RCAREA_DEATH_MOUNTAIN_CRATER, "Death Mountain Crater" }, + { RCAREA_ZORAS_RIVER, "Zora's River" }, + { RCAREA_ZORAS_DOMAIN, "Zora's Domain" }, + { RCAREA_ZORAS_FOUNTAIN, "Zora's Fountain" }, + { RCAREA_LON_LON_RANCH, "Lon Lon Ranch" }, + { RCAREA_DEKU_TREE, "Deku Tree" }, + { RCAREA_DODONGOS_CAVERN, "Dodongo's Cavern" }, + { RCAREA_JABU_JABUS_BELLY, "Jabu Jabu's Belly" }, + { RCAREA_FOREST_TEMPLE, "Forest Temple" }, + { RCAREA_FIRE_TEMPLE, "Fire Temple" }, + { RCAREA_WATER_TEMPLE, "Water Temple" }, + { RCAREA_SPIRIT_TEMPLE, "Spirit Temple" }, + { RCAREA_SHADOW_TEMPLE, "Shadow Temple" }, + { RCAREA_BOTTOM_OF_THE_WELL, "Bottom of the Well" }, + { RCAREA_ICE_CAVERN, "Ice Cavern" }, + { RCAREA_GERUDO_TRAINING_GROUND, "Gerudo Training Ground" }, + { RCAREA_GANONS_CASTLE, "Ganon's Castle" }, + { RCAREA_INVALID, "All" }, +}; void PlandomizerDrawOptions() { if (ImGui::BeginTable("LoadSpoiler", 2)) { + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextColumn(); ImGui::SeparatorText("Load/Save Spoiler Log"); PlandomizerPopulateSeedList(); static size_t selectedList = 0; if (existingSeedList.size() != 0) { - if (ImGui::BeginCombo("##JsonFiles", existingSeedList[selectedList].c_str())) { - for (size_t i = 0; i < existingSeedList.size(); i++) { - bool isSelected = (selectedList == i); - if (ImGui::Selectable(existingSeedList[i].c_str(), isSelected)) { - selectedList = i; - } - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } + UIWidgets::Combobox("##JsonFiles", &selectedList, existingSeedList, UIWidgets::ComboboxOptions().Color(THEME_COLOR).LabelPosition(UIWidgets::LabelPositions::None)); } else { ImGui::Text("No Spoiler Logs found."); } ImGui::BeginDisabled(existingSeedList.empty()); - if (ImGui::Button("Load")) { + if (UIWidgets::Button("Load", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { logTemp = existingSeedList[selectedList].c_str(); PlandomizerLoadSpoilerLog(logTemp.c_str()); } ImGui::EndDisabled(); ImGui::BeginDisabled(spoilerLogData.empty()); ImGui::SameLine(); - if (ImGui::Button("Save")) { + if (UIWidgets::Button("Save", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { PlandomizerSaveSpoilerLog(); } ImGui::EndDisabled(); @@ -1000,44 +1014,19 @@ void PlandomizerDrawOptions() { } if (getTabID == TAB_HINTS) { - if (ImGui::Button("Clear All Hints")) { + if (UIWidgets::Button("Clear All Hints", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { PlandomizerRemoveAllHints(); } ImGui::SameLine(); - if (ImGui::Button("Randomize All Hints")) { + if (UIWidgets::Button("Randomize All Hints", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { PlandomizerRandomizeHint(HINT_ALL, 0); } } if (getTabID == TAB_LOCATIONS) { if (plandoLogData.size() > 0) { - const char* comboLabel = rcAreaNames[selectedArea].c_str(); - if (selectedArea == RCAREA_INVALID) { - comboLabel = "All"; - } - ImGui::Text("Filter by Area:"); + UIWidgets::Combobox("Filter by Area:##AreaFilter", &selectedArea, rcAreaNameMap, UIWidgets::ComboboxOptions().Color(THEME_COLOR).LabelPosition(UIWidgets::LabelPositions::Near).ComponentAlignment(UIWidgets::ComponentAlignments::Right)); ImGui::SameLine(); - ImGui::PushItemWidth(300.0f); - if (ImGui::BeginCombo("##AreaFilter", comboLabel)) { - for (const auto& [area, name] : rcAreaNames) { - bool isSelected = (selectedArea == area); - - const char* displayName = name.c_str(); - if (area == RCAREA_INVALID) { - displayName = "All"; - } - if (ImGui::Selectable(displayName, isSelected)) { - selectedArea = area; - } - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - ImGui::PopItemWidth(); - - ImGui::SameLine(); - if (ImGui::Button("Empty All Rewards")) { + if (UIWidgets::Button("Empty All Rewards", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline).Padding(ImVec2(10.f, 6.f)))) { PlandomizerRemoveAllItems(); } } @@ -1067,16 +1056,14 @@ void PlandomizerDrawHintsWindow() { } ImGui::Text("New Hint: "); ImGui::SameLine(); - if (ImGui::Button(randomizeButton.c_str())) { + if (UIWidgets::Button(randomizeButton.c_str(), UIWidgets::ButtonOptions().Color(THEME_COLOR).Padding(ImVec2(10.f, 6.f)).Size(UIWidgets::Sizes::Inline).Tooltip("Randomize Hint"))) { PlandomizerRandomizeHint(HINT_SINGLE, index); } - UIWidgets::Tooltip("Randomize Hint"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 10); - if (UIWidgets::InputString("##HintMessage", &hintInputText)) { + if (UIWidgets::InputString("##HintMessage", &hintInputText, UIWidgets::InputOptions().Color(THEME_COLOR).LabelPosition(UIWidgets::LabelPositions::None).Tooltip(plandomizerHintsTooltip().c_str()))) { plandoHintData[index].hintText = hintInputText.c_str(); } - UIWidgets::Tooltip(plandomizerHintsTooltip().c_str()); index++; ImGui::PopID(); } @@ -1133,6 +1120,7 @@ void PlandomizerDrawLocationsWindow(RandomizerCheckArea rcArea) { void PlandomizerDrawSpoilerTable() { ImGui::BeginChild("Main"); + UIWidgets::PushStyleTabs(THEME_COLOR); if (ImGui::BeginTabBar("Check Tabs")) { if (ImGui::BeginTabItem("Gossip Stones")) { getTabID = TAB_HINTS; @@ -1146,12 +1134,13 @@ void PlandomizerDrawSpoilerTable() { } } ImGui::EndTabBar(); + UIWidgets::PopStyleTabs(); ImGui::EndChild(); } void PlandomizerWindow::DrawElement() { PlandomizerDrawOptions(); - UIWidgets::PaddedSeparator(); + UIWidgets::Separator(true, true, 0.f, 0.f); PlandomizerDrawSpoilerTable(); } diff --git a/soh/soh/Enhancements/randomizer/Plandomizer.h b/soh/soh/Enhancements/randomizer/Plandomizer.h index d337e4da6..1b7996ce4 100644 --- a/soh/soh/Enhancements/randomizer/Plandomizer.h +++ b/soh/soh/Enhancements/randomizer/Plandomizer.h @@ -39,14 +39,14 @@ typedef struct { std::string hintText; } SpoilerHintObject; -typedef enum { +typedef enum PlandoTabs { TAB_HINTS, TAB_LOCATIONS -}; +} PlandoTabs; -typedef enum { +typedef enum PlandoHints { HINT_SINGLE, HINT_ALL, -}; +} PlandoHints; #endif \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/ShufflePots.cpp b/soh/soh/Enhancements/randomizer/ShufflePots.cpp index 5b47613ef..c5696ba79 100644 --- a/soh/soh/Enhancements/randomizer/ShufflePots.cpp +++ b/soh/soh/Enhancements/randomizer/ShufflePots.cpp @@ -54,9 +54,6 @@ void ObjTsubo_RandomizerSpawnCollectible(ObjTsubo* potActor, PlayState* play) { void ObjTsubo_RandomizerInit(void* actorRef) { Actor* actor = static_cast(actorRef); - // Check for Lake Hylia specifically because the game spawns 2 pots out of bounds there for some reason. - if (actor->id != ACTOR_OBJ_TSUBO || gPlayState->sceneNum == SCENE_LAKE_HYLIA || gPlayState->sceneNum == SCENE_HYRULE_CASTLE) return; - ObjTsubo* potActor = static_cast(actorRef); potActor->potIdentity = OTRGlobals::Instance->gRandomizer->IdentifyPot(gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z); diff --git a/soh/soh/Enhancements/randomizer/draw.cpp b/soh/soh/Enhancements/randomizer/draw.cpp index 0a89e4dae..9c9cfd829 100644 --- a/soh/soh/Enhancements/randomizer/draw.cpp +++ b/soh/soh/Enhancements/randomizer/draw.cpp @@ -11,6 +11,7 @@ #include "objects/object_gi_key/object_gi_key.h" #include "objects/object_gi_bosskey/object_gi_bosskey.h" #include "objects/object_gi_compass/object_gi_compass.h" +#include "objects/object_gi_map/object_gi_map.h" #include "objects/object_gi_hearts/object_gi_hearts.h" #include "objects/object_gi_scale/object_gi_scale.h" #include "objects/object_gi_fire/object_gi_fire.h" @@ -80,7 +81,7 @@ Color_RGB8 SmallEmblemDefaultValue[10] = { extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); extern "C" void Randomizer_DrawSmallKey(PlayState* play, GetItemEntry* getItemEntry) { - s8 isCustomKeysEnabled = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("CustomKeyModels"), 0); + s8 isCustomKeysEnabled = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("CustomKeyModels"), 1); int slot = getItemEntry->drawItemId - RG_FOREST_TEMPLE_SMALL_KEY; Gfx* customIconDLs[] = { @@ -131,15 +132,35 @@ extern "C" void Randomizer_DrawSmallKey(PlayState* play, GetItemEntry* getItemEn CLOSE_DISPS(play->state.gfxCtx); } -extern "C" { - void GetItem_DrawCompass(PlayState* play, s16 drawId); - void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction); - void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName); +extern "C" void Randomizer_DrawMap(PlayState* play, GetItemEntry* getItemEntry) { + s16 color_slot = getItemEntry->drawItemId - RG_DEKU_TREE_MAP; + s16 colors[12][3] = { + { 4, 100, 46 }, // Deku Tree + { 140, 30, 30 }, // Dodongo's Cavern + { 30, 60, 255 }, // Jabu Jabu's Belly + { 4, 195, 46 }, // Forest Temple + { 237, 95, 95 }, // Fire Temple + { 85, 180, 223 }, // Water Temple + { 222, 158, 47 }, // Spirit Temple + { 126, 16, 177 }, // Shadow Temple + { 227, 110, 255 }, // Bottom of the Well + { 0, 255, 255 }, // Ice Cavern + }; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, colors[color_slot][0], colors[color_slot][1], colors[color_slot][2], 255); + + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gGiDungeonMapDL); + + CLOSE_DISPS(play->state.gfxCtx); } extern "C" void Randomizer_DrawCompass(PlayState* play, GetItemEntry* getItemEntry) { - - s16 color_slot = getItemEntry->getItemId - RG_DEKU_TREE_COMPASS; + s16 color_slot = getItemEntry->drawItemId - RG_DEKU_TREE_COMPASS; s16 colors[12][3] = { { 4, 100, 46 }, // Deku Tree { 140, 30, 30 }, // Dodongo's Cavern @@ -158,8 +179,7 @@ extern "C" void Randomizer_DrawCompass(PlayState* play, GetItemEntry* getItemEnt OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL_25Opa(play->state.gfxCtx); - gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), - G_MTX_MODELVIEW | G_MTX_LOAD); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD); gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, colors[color_slot][0], colors[color_slot][1], colors[color_slot][2], 255); gDPSetEnvColor(POLY_OPA_DISP++, colors[color_slot][0] / 2, colors[color_slot][1] / 2, colors[color_slot][2] / 2, 255); @@ -167,8 +187,7 @@ extern "C" void Randomizer_DrawCompass(PlayState* play, GetItemEntry* getItemEnt gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gGiCompassDL); POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, 5); - gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), - G_MTX_MODELVIEW | G_MTX_LOAD); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD); gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gGiCompassGlassDL); CLOSE_DISPS(play->state.gfxCtx); @@ -247,7 +266,7 @@ extern "C" void Randomizer_DrawBossKey(PlayState* play, GetItemEntry* getItemEnt } extern "C" void Randomizer_DrawKeyRing(PlayState* play, GetItemEntry* getItemEntry) { - s8 isCustomKeysEnabled = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("CustomKeyModels"), 0); + s8 isCustomKeysEnabled = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("CustomKeyModels"), 1); int slot = getItemEntry->drawItemId - RG_FOREST_TEMPLE_KEY_RING; Gfx* CustomIconDLs[] = { @@ -485,7 +504,7 @@ extern "C" void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry get CLOSE_DISPS(play->state.gfxCtx); } -extern "C" void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry getItemEntry) { +extern "C" void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry* getItemEntry) { Color_RGB8 color = { 0, 60, 100 }; if (CVarGetInteger(CVAR_COSMETIC("World.MysteryItem.Changed"), 0)) { color = CVarGetColor24(CVAR_COSMETIC("World.MysteryItem.Value"), color); diff --git a/soh/soh/Enhancements/randomizer/draw.h b/soh/soh/Enhancements/randomizer/draw.h index 3fc5c86b4..22d8466c6 100644 --- a/soh/soh/Enhancements/randomizer/draw.h +++ b/soh/soh/Enhancements/randomizer/draw.h @@ -10,6 +10,7 @@ typedef struct PlayState PlayState; extern "C" { #endif void Randomizer_DrawSmallKey(PlayState* play, GetItemEntry* getItemEntry); +void Randomizer_DrawMap(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawCompass(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawKeyRing(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBossKey(PlayState* play, GetItemEntry* getItemEntry); @@ -22,7 +23,7 @@ void Randomizer_DrawOcarinaButton(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBronzeScale(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawSkeletonKey(PlayState* play, GetItemEntry* getItemEntry); -void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry getItemEntry); +void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBombchuBagInLogic(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBombchuBag(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawOverworldKey(PlayState* play, GetItemEntry* getItemEntry); diff --git a/soh/soh/Enhancements/randomizer/hint.cpp b/soh/soh/Enhancements/randomizer/hint.cpp index 29a9a969f..2648c4623 100644 --- a/soh/soh/Enhancements/randomizer/hint.cpp +++ b/soh/soh/Enhancements/randomizer/hint.cpp @@ -587,6 +587,10 @@ CustomMessage Hint::GetGanonBossKeyText() { auto ctx = Rando::Context::GetInstance(); CustomMessage ganonBossKeyMessage; + if (ctx->GetOption(RSK_TRIFORCE_HUNT)) { + return StaticData::hintTextTable[RHT_GANON_BK_TRIFORCE_HINT].GetHintMessage(); + } + if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH)) { return StaticData::hintTextTable[RHT_GANON_BK_START_WITH_HINT].GetHintMessage(); } @@ -631,9 +635,6 @@ CustomMessage Hint::GetGanonBossKeyText() { ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_TOKENS_HINT].GetHintMessage(); ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_TOKEN_COUNT).Get()); } - else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_TRIFORCE_HUNT)) { - return StaticData::hintTextTable[RHT_GANON_BK_TRIFORCE_HINT].GetHintMessage(); - } return ganonBossKeyMessage; } diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index a299a69f3..e1e201774 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -297,14 +297,19 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() { rc != RC_MARKET_BOMBCHU_BOWLING_SECOND_PRIZE && // Always show ItemGet animation for ice traps !(getItemEntry.modIndex == MOD_RANDOMIZER && getItemEntry.getItemId == RG_ICE_TRAP) && + // Always show ItemGet animation outside of randomizer to keep behaviour consistent in vanilla + IS_RANDO && ( - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED) == SGIA_ALL || + CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED) == SGIA_ALL || ( - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED) == SGIA_JUNK && + CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED) == SGIA_JUNK && ( + //crude fix to ensure map hints are readable. Ideally replace with better hint tracking. + !(getItemEntry.getItemId >= RG_DEKU_TREE_MAP && getItemEntry.getItemId <= RG_ICE_CAVERN_MAP) && ( getItemEntry.getItemCategory == ITEM_CATEGORY_JUNK || getItemEntry.getItemCategory == ITEM_CATEGORY_SKULLTULA_TOKEN || - getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER + getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER + ) ) ) ) @@ -411,7 +416,7 @@ void EnExItem_WaitForObjectRandomized(EnExItem* enExItem, PlayState* play) { } void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play) { - f32 mtxScale = CVarGetFloat(CVAR_ENHANCEMENT("TimeSavers.SkipGetItemAnimationScale"), 10.0f); + f32 mtxScale = CVarGetFloat(CVAR_RANDOMIZER_ENHANCEMENT("TimeSavers.SkipGetItemAnimationScale"), 10.0f); Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY); GetItemEntry randoItem = enItem00->itemEntry; if (CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0) && @@ -542,27 +547,6 @@ u8 EnGm_RandoCanGetMedigoronItem() { !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON); } -RandomizerCheck EnFr_RandomizerCheckFromSongIndex(u16 songIndex) { - switch (songIndex) { - case FROG_ZL: - return RC_ZR_FROGS_ZELDAS_LULLABY; - case FROG_EPONA: - return RC_ZR_FROGS_EPONAS_SONG; - case FROG_SARIA: - return RC_ZR_FROGS_SARIAS_SONG; - case FROG_SUNS: - return RC_ZR_FROGS_SUNS_SONG; - case FROG_SOT: - return RC_ZR_FROGS_SONG_OF_TIME; - case FROG_STORMS: - return RC_ZR_FROGS_IN_THE_RAIN; - case FROG_CHOIR_SONG: - return RC_ZR_FROGS_OCARINA_GAME; - default: - return RC_UNKNOWN_CHECK; - } -} - void RandomizerSetChestGameRandomizerInf(RandomizerCheck rc) { switch (rc) { case RC_MARKET_TREASURE_CHEST_GAME_ITEM_1: @@ -783,7 +767,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l } case VB_PLAY_SLOW_CHEST_CS: { // We force fast chests if SkipGetItemAnimation is enabled because the camera in the CS looks pretty wonky otherwise - if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED)) { + if (CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED)) { *should = false; } break; @@ -2030,11 +2014,13 @@ void RandomizerOnActorInitHandler(void* actorRef) { break; } - //Deletes all actors in the boss category if the soul isn't found. - //Some actors, like Dark Link, Arwings, and Zora's Sapphire...?, are in this category despite not being actual bosses, - //so ignore any "boss" if `currentBossSoulRandInf` doesn't change from RAND_INF_MAX. + // Deletes all actors in the boss category if the soul isn't found. + // Some actors, like Dark Link, Arwings, and Zora's Sapphire...?, are in this category despite not being actual bosses, + // so ignore any "boss" if `currentBossSoulRandInf` doesn't change from RAND_INF_MAX. + // Iron Knuckle (Nabooru) in Twinrova's room is a special exception, so exclude knuckles too. if (currentBossSoulRandInf != RAND_INF_MAX) { - if (!Flags_GetRandomizerInf(currentBossSoulRandInf) && actor->category == ACTORCAT_BOSS) { + if (!Flags_GetRandomizerInf(currentBossSoulRandInf) && actor->category == ACTORCAT_BOSS && + actor->id != ACTOR_EN_IK) { Actor_Delete(&gPlayState->actorCtx, actor, gPlayState); } //Special case for Phantom Ganon's horse (and fake), as they're considered "background actors", @@ -2382,7 +2368,7 @@ void RandomizerRegisterHooks() { GameInteractor::Instance->UnregisterGameHook(fishsanityOnVanillaBehaviorHook); GameInteractor::Instance->UnregisterGameHook(fishsanityOnItemReceiveHook); - GameInteractor::Instance->UnregisterGameHook(shufflePotsOnActorInitHook); + GameInteractor::Instance->UnregisterGameHookForID(shufflePotsOnActorInitHook); GameInteractor::Instance->UnregisterGameHook(shufflePotsOnVanillaBehaviorHook); GameInteractor::Instance->UnregisterGameHook(shuffleFreestandingOnVanillaBehaviorHook); @@ -2458,7 +2444,7 @@ void RandomizerRegisterHooks() { } if (RAND_GET_OPTION(RSK_SHUFFLE_POTS) != RO_SHUFFLE_POTS_OFF) { - shufflePotsOnActorInitHook = GameInteractor::Instance->RegisterGameHook(ObjTsubo_RandomizerInit); + shufflePotsOnActorInitHook = GameInteractor::Instance->RegisterGameHookForID(ACTOR_OBJ_TSUBO, ObjTsubo_RandomizerInit); shufflePotsOnVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook(ShufflePots_OnVanillaBehaviorHandler); } diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp index 6f0309223..1c315b47f 100644 --- a/soh/soh/Enhancements/randomizer/item.cpp +++ b/soh/soh/Enhancements/randomizer/item.cpp @@ -94,6 +94,7 @@ std::shared_ptr Item::GetGIEntry() const { // NOLINT(*-no-recursio actual = RG_DEKU_STICK_BAG; break; } + [[fallthrough]]; case 1: if (infiniteUpgrades == RO_INF_UPGRADES_CONDENSED_PROGRESSIVE) { actual = RG_STICK_UPGRADE_INF; @@ -123,6 +124,7 @@ std::shared_ptr Item::GetGIEntry() const { // NOLINT(*-no-recursio actual = RG_DEKU_NUT_BAG; break; } + [[fallthrough]]; case 1: if (infiniteUpgrades == RO_INF_UPGRADES_CONDENSED_PROGRESSIVE) { actual = RG_NUT_UPGRADE_INF; diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 0b94aab39..45516ef18 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -98,15 +98,25 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_PRELUDE_OF_LIGHT] = Item(RG_PRELUDE_OF_LIGHT, Text{ "Prelude of Light", "Prélude de la Lumière", "Kantate des Lichts" }, ITEMTYPE_SONG, 0xC0, true, LOGIC_PRELUDE_OF_LIGHT, RHT_PRELUDE_OF_LIGHT, ITEM_SONG_PRELUDE, OBJECT_GI_MELODY, GID_SONG_PRELUDE, 0x78, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); // Maps and Compasses itemTable[RG_DEKU_TREE_MAP] = Item(RG_DEKU_TREE_MAP, Text{ "Great Deku Tree Map", "Carte de l'Arbre Mojo", "Karte des Deku-Baums" }, ITEMTYPE_MAP, 0xA5, false, LOGIC_MAP_DEKU_TREE, RHT_DEKU_TREE_MAP, RG_DEKU_TREE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_DEKU_TREE_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_DODONGOS_CAVERN_MAP] = Item(RG_DODONGOS_CAVERN_MAP, Text{ "Dodongo's Cavern Map", "Carte de la Caverne Dodongo", "Karte der Dodongo-Höhle" }, ITEMTYPE_MAP, 0xA6, false, LOGIC_MAP_DODONGOS_CAVERN, RHT_DODONGOS_CAVERN_MAP, RG_DODONGOS_CAVERN_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_DODONGOS_CAVERN_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_JABU_JABUS_BELLY_MAP] = Item(RG_JABU_JABUS_BELLY_MAP, Text{ "Jabu-Jabu's Belly Map", "Carte du Ventre de Jabu-Jabu", "Karte des Jabu-Jabu-Bauchs" }, ITEMTYPE_MAP, 0xA7, false, LOGIC_MAP_JABU_JABUS_BELLY, RHT_JABU_JABUS_BELLY_MAP, RG_JABU_JABUS_BELLY_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_JABU_JABUS_BELLY_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_FOREST_TEMPLE_MAP] = Item(RG_FOREST_TEMPLE_MAP, Text{ "Forest Temple Map", "Carte du Temple de la Forêt", "Karte des Waldtempels" }, ITEMTYPE_MAP, 0xA8, false, LOGIC_MAP_FOREST_TEMPLE, RHT_FOREST_TEMPLE_MAP, RG_FOREST_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_FOREST_TEMPLE_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_FIRE_TEMPLE_MAP] = Item(RG_FIRE_TEMPLE_MAP, Text{ "Fire Temple Map", "Carte due Temple de Feu", "Karte des Feuertempels" }, ITEMTYPE_MAP, 0xA9, false, LOGIC_MAP_FIRE_TEMPLE, RHT_FIRE_TEMPLE_MAP, RG_FIRE_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_FIRE_TEMPLE_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_WATER_TEMPLE_MAP] = Item(RG_WATER_TEMPLE_MAP, Text{ "Water Temple Map", "Carte du Temple de l'Eau", "Karte des Wassertempels" }, ITEMTYPE_MAP, 0xAA, false, LOGIC_MAP_WATER_TEMPLE, RHT_WATER_TEMPLE_MAP, RG_WATER_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_WATER_TEMPLE_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_SPIRIT_TEMPLE_MAP] = Item(RG_SPIRIT_TEMPLE_MAP, Text{ "Spirit Temple Map", "Carte due Temple de l'Esprit", "Karte des Geistertempels" }, ITEMTYPE_MAP, 0xAB, false, LOGIC_MAP_SPIRIT_TEMPLE, RHT_SPIRIT_TEMPLE_MAP, RG_SPIRIT_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_SPIRIT_TEMPLE_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_SHADOW_TEMPLE_MAP] = Item(RG_SHADOW_TEMPLE_MAP, Text{ "Shadow Temple Map", "Carte du Temple de l'Ombre", "Karte des Schattentempels" }, ITEMTYPE_MAP, 0xAC, false, LOGIC_MAP_SHADOW_TEMPLE, RHT_SHADOW_TEMPLE_MAP, RG_SHADOW_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_SHADOW_TEMPLE_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_BOTTOM_OF_THE_WELL_MAP] = Item(RG_BOTTOM_OF_THE_WELL_MAP, Text{ "Bottom of the Well Map", "Carte du Puits", "Karte des Grund des Brunnens" }, ITEMTYPE_MAP, 0xAD, false, LOGIC_MAP_BOTTOM_OF_THE_WELL, RHT_BOTTOM_OF_THE_WELL_MAP, RG_BOTTOM_OF_THE_WELL_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_BOTTOM_OF_THE_WELL_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_ICE_CAVERN_MAP] = Item(RG_ICE_CAVERN_MAP, Text{ "Ice Cavern Map", "Carte de la Caverne Polaire", "Karte der Eishöhle" }, ITEMTYPE_MAP, 0xAE, false, LOGIC_MAP_ICE_CAVERN, RHT_ICE_CAVERN_MAP, RG_ICE_CAVERN_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); + itemTable[RG_ICE_CAVERN_MAP].SetCustomDrawFunc(Randomizer_DrawMap); itemTable[RG_DEKU_TREE_COMPASS] = Item(RG_DEKU_TREE_COMPASS, Text{ "Great Deku Tree Compass", "Boussole de l'Arbre Mojo", "Kompaß des Deku-Baums" }, ITEMTYPE_COMPASS, 0x9B, false, LOGIC_COMPASS_DEKU_TREE, RHT_DEKU_TREE_COMPASS, RG_DEKU_TREE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_ITEM_DUNGEON_MAP, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); itemTable[RG_DEKU_TREE_COMPASS].SetCustomDrawFunc(Randomizer_DrawCompass); itemTable[RG_DODONGOS_CAVERN_COMPASS] = Item(RG_DODONGOS_CAVERN_COMPASS, Text{ "Dodongo's Cavern Compass", "Boussole de la Caverne Dodongo", "Kompaß der Dodongo-Höhle" }, ITEMTYPE_COMPASS, 0x9C, false, LOGIC_COMPASS_DODONGOS_CAVERN, RHT_DODONGOS_CAVERN_COMPASS, RG_DODONGOS_CAVERN_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_ITEM_COMPASS, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); @@ -339,7 +349,7 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "Écaille de Bronze", "Bronzene Schuppe" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, LOGIC_PROGRESSIVE_WALLET, RHT_BRONZE_SCALE, RG_BRONZE_SCALE, OBJECT_GI_SCALE, GID_SCALE_SILVER, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_BRONZE_SCALE].SetCustomDrawFunc(Randomizer_DrawBronzeScale); - itemTable[RG_BOMBCHU_BAG] = Item(RG_BOMBCHU_BAG, Text{ "Bombchu Bag", "!!!", "!!!" }, ITEMTYPE_ITEM, RG_BOMBCHU_BAG, true, LOGIC_BOMBCHUS, RHT_BOMBCHU_BAG, RG_BOMBCHU_BAG, OBJECT_GI_BOMB_2, GID_BOMBCHU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_BOMBCHU_BAG] = Item(RG_BOMBCHU_BAG, Text{ "Bombchu Bag", "!!!", "Sac de Missiles Teigneux" }, ITEMTYPE_ITEM, RG_BOMBCHU_BAG, true, LOGIC_BOMBCHUS, RHT_BOMBCHU_BAG, RG_BOMBCHU_BAG, OBJECT_GI_BOMB_2, GID_BOMBCHU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_BOMBCHU_BAG].SetCustomDrawFunc(Randomizer_DrawBombchuBag); itemTable[RG_QUIVER_INF] = Item(RG_QUIVER_INF, Text{ "Infinite Quiver", "Carquois Infini", "Unendlicher Köcher" }, ITEMTYPE_ITEM, RG_QUIVER_INF, true, LOGIC_PROGRESSIVE_BOW, RHT_QUIVER_INF, RG_QUIVER_INF, OBJECT_GI_ARROWCASE, GID_QUIVER_50, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp index 2500e4996..4740a7541 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp @@ -71,8 +71,8 @@ void RegionTable_Init_DekuTree() { }, { //Locations LOCATION(RC_DEKU_TREE_BASEMENT_CHEST, true), - LOCATION(RC_DEKU_TREE_GS_BASEMENT_GATE, logic->CanJumpslashExceptHammer() || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->HasExplosives() || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_DINS_FIRE)), - LOCATION(RC_DEKU_TREE_GS_BASEMENT_VINES, logic->CanUseProjectile() || logic->CanUse(RG_DINS_FIRE) || (ctx->GetTrickOption(RT_DEKU_MQ_COMPASS_GS) && logic->CanJumpslashExceptHammer())), + LOCATION(RC_DEKU_TREE_GS_BASEMENT_GATE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH)), + LOCATION(RC_DEKU_TREE_GS_BASEMENT_VINES, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_DEKU_MQ_COMPASS_GS) ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW)), }, { //Exits Entrance(RR_DEKU_TREE_LOBBY, []{return true;}), @@ -84,7 +84,7 @@ void RegionTable_Init_DekuTree() { areaTable[RR_DEKU_TREE_BASEMENT_SCRUB_ROOM] = Region("Deku Tree Basement Scrub Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_DEKU_TREE_BASEMENT_LOWER, []{return true;}), - Entrance(RR_DEKU_TREE_BASEMENT_WATER_ROOM_FRONT, []{return Here(RR_DEKU_TREE_BASEMENT_SCRUB_ROOM, []{return logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW);});}), + Entrance(RR_DEKU_TREE_BASEMENT_WATER_ROOM_FRONT, []{return Here(RR_DEKU_TREE_BASEMENT_SCRUB_ROOM, []{return logic->CanHitEyeTargets();});}), }); areaTable[RR_DEKU_TREE_BASEMENT_WATER_ROOM_FRONT] = Region("Deku Tree Basement Water Room Front", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index bb5e1754f..4ae2e1e70 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -154,6 +154,7 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_DODONGOS_CAVERN_BLADE_ROOM_HEART, true), }, { //Exits + Entrance(RR_DODONGOS_CAVERN_ARMOS_ROOM, []{return true;}), Entrance(RR_DODONGOS_CAVERN_2F_SIDE_ROOM, []{return Here(RR_DODONGOS_CAVERN_BOMB_ROOM_LOWER, []{return logic->CanBreakMudWalls() || (ctx->GetTrickOption(RT_DC_SCRUB_ROOM) && logic->HasItem(RG_GORONS_BRACELET));});}), Entrance(RR_DODONGOS_CAVERN_FIRST_SLINGSHOT_ROOM, []{return Here(RR_DODONGOS_CAVERN_BOMB_ROOM_LOWER, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}), Entrance(RR_DODONGOS_CAVERN_BOMB_ROOM_UPPER, []{return (logic->IsAdult && ctx->GetTrickOption(RT_DC_JUMP)) || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && logic->CanUse(RG_LONGSHOT)) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->HasExplosives() && logic->CanJumpslash());}), @@ -304,6 +305,7 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_DODONGOS_CAVERN_MQ_STAIRCASE_POT_4, logic->CanBreakPots()), }, { //Exits + Entrance(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return true;}), //This is possible with sticks and shield, igniting a first flower by "touch" then very quickly crouch stabbing in a way that cuts the corner to light the 3rd bomb on the other side, but that's a trick Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_UPPER, []{return Here(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return logic->HasExplosives() || logic->CanUse(RG_DINS_FIRE) || (ctx->GetTrickOption(RT_DC_STAIRCASE) && logic->CanUse(RG_FAIRY_BOW));});}), Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_PAST_MUD_WALL, []{return Here(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return logic->CanBreakMudWalls();});}), @@ -460,9 +462,9 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_DODONGOS_CAVERN_MQ_POE_ROOM_POT_4, logic->CanBreakPots()), }, { //Exits - Entrance(RR_DODONGOS_CAVERN_MQ_LOWER_RIGHT_SIDE, []{return Here(RR_DODONGOS_CAVERN_MQ_POES_ROOM, []{return logic->CanDetonateBombFlowers() || logic->HasItem(RG_GORONS_BRACELET);});}), - Entrance(RR_DODONGOS_CAVERN_MQ_LOWER_LIZALFOS, []{return true;}), - Entrance(RR_DODONGOS_CAVERN_MQ_MAD_SCRUB_ROOM, []{return Here(RR_DODONGOS_CAVERN_MQ_POES_ROOM, []{return logic->CanDetonateBombFlowers() || logic->HasItem(RG_GORONS_BRACELET);});}), + Entrance(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return Here(RR_DODONGOS_CAVERN_MQ_POES_ROOM, []{return logic->CanDetonateBombFlowers() || logic->HasItem(RG_GORONS_BRACELET);});}), + Entrance(RR_DODONGOS_CAVERN_MQ_LOWER_LIZALFOS, []{return true;}), + Entrance(RR_DODONGOS_CAVERN_MQ_MAD_SCRUB_ROOM, []{return Here(RR_DODONGOS_CAVERN_MQ_POES_ROOM, []{return logic->CanDetonateBombFlowers() || logic->HasItem(RG_GORONS_BRACELET);});}), }); areaTable[RR_DODONGOS_CAVERN_MQ_MAD_SCRUB_ROOM] = Region("Dodongos Cavern Mad Scrub Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 1e7214a3d..a17cfc598 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -357,7 +357,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER] = Region("Fire Temple MQ First Room Upper", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_LOWER, []{return true;}), - Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return logic->HasFireSource();}), + Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM, []{return logic->HasFireSource();}), Entrance(RR_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM, []{return Here(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);});}), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp index c9f4ebc23..04efd4994 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp @@ -28,7 +28,7 @@ void RegionTable_Init_ForestTemple() { areaTable[RR_FOREST_TEMPLE_SOUTH_CORRIDOR] = Region("Forest Temple South Corridor", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_FOREST_TEMPLE_FIRST_ROOM, []{return true;}), - Entrance(RR_FOREST_TEMPLE_LOBBY, []{return logic->CanAttack() || logic->CanUse(RG_NUTS);}), + Entrance(RR_FOREST_TEMPLE_LOBBY, []{return logic->CanPassEnemy(RE_BIG_SKULLTULA);}), }); areaTable[RR_FOREST_TEMPLE_LOBBY] = Region("Forest Temple Lobby", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index 2a7301701..c1a846564 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -28,7 +28,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}), - Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && @@ -61,7 +61,7 @@ void RegionTable_Init_GanonsCastle() { EventAccess(&logic->ForestTrialClear, []{return logic->CanUse(RG_LIGHT_ARROWS) && (logic->CanUse(RG_FIRE_ARROWS) || logic->CanUse(RG_DINS_FIRE));}), }, { //Locations - LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_CHEST, logic->CanDamage()), + LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_CHEST, logic->CanKillEnemy(RE_WOLFOS)), LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_1, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || logic->CanUse(RG_DINS_FIRE))), LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_2, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || logic->CanUse(RG_DINS_FIRE))), }, {}); @@ -78,7 +78,7 @@ void RegionTable_Init_GanonsCastle() { areaTable[RR_GANONS_CASTLE_WATER_TRIAL] = Region("Ganon's Castle Water Trial", "Ganon's Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->BlueFireAccess, []{return logic->BlueFireAccess || logic->HasBottle();}), + EventAccess(&logic->BlueFireAccess, []{return true;}), EventAccess(&logic->FairyPot, []{return logic->FairyPot || (logic->BlueFire() && logic->CanKillEnemy(RE_FREEZARD));}), EventAccess(&logic->WaterTrialClear, []{return logic->BlueFire() && logic->IsAdult && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_LIGHT_ARROWS);}), }, { @@ -112,7 +112,7 @@ void RegionTable_Init_GanonsCastle() { EventAccess(&logic->SpiritTrialClear, []{return logic->CanUse(RG_LIGHT_ARROWS) && (logic->CanUse(RG_MIRROR_SHIELD) || ctx->GetOption(RSK_SUNLIGHT_ARROWS)) && logic->CanUse(RG_BOMBCHU_5) && ((ctx->GetTrickOption(RT_GANON_SPIRIT_TRIAL_HOOKSHOT) && logic->CanJumpslashExceptHammer()) || logic->CanUse(RG_HOOKSHOT));}), }, { //Locations - LOCATION(RC_GANONS_CASTLE_SPIRIT_TRIAL_CRYSTAL_SWITCH_CHEST, (ctx->GetTrickOption(RT_GANON_SPIRIT_TRIAL_HOOKSHOT) || logic->CanUse(RG_HOOKSHOT)) && logic->CanJumpslashExceptHammer()), + LOCATION(RC_GANONS_CASTLE_SPIRIT_TRIAL_CRYSTAL_SWITCH_CHEST, (ctx->GetTrickOption(RT_GANON_SPIRIT_TRIAL_HOOKSHOT) || logic->CanUse(RG_HOOKSHOT)) && (logic->CanJumpslashExceptHammer() || logic->CanUse(RG_BOMBCHU_5))), LOCATION(RC_GANONS_CASTLE_SPIRIT_TRIAL_INVISIBLE_CHEST, (ctx->GetTrickOption(RT_GANON_SPIRIT_TRIAL_HOOKSHOT) || logic->CanUse(RG_HOOKSHOT)) && logic->CanUse(RG_BOMBCHU_5) && (ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH))), LOCATION(RC_GANONS_CASTLE_SPIRIT_TRIAL_POT_1, ((ctx->GetTrickOption(RT_GANON_SPIRIT_TRIAL_HOOKSHOT) && logic->CanJumpslashExceptHammer()) || logic->CanUse(RG_HOOKSHOT)) && logic->CanUse(RG_BOMBCHU_5) && logic->CanUse(RG_FAIRY_BOW) && (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS)))), LOCATION(RC_GANONS_CASTLE_SPIRIT_TRIAL_POT_2, ((ctx->GetTrickOption(RT_GANON_SPIRIT_TRIAL_HOOKSHOT) && logic->CanJumpslashExceptHammer()) || logic->CanUse(RG_HOOKSHOT)) && logic->CanUse(RG_BOMBCHU_5) && logic->CanUse(RG_FAIRY_BOW) && (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS)))), @@ -162,7 +162,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_DINOLFOS_ROOM, []{return Here(RR_GANONS_CASTLE_MQ_MAIN, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);});}), //RANDOTODO could we just set these events automatically based on the setting? - Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && @@ -431,10 +431,17 @@ void RegionTable_Init_GanonsCastle() { #pragma region Tower and Escape + areaTable[RR_GANONS_TOWER_ENTRYWAY] = Region("Ganon's Tower Entryway", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}), + Entrance(RR_GANONS_CASTLE_MQ_MAIN, []{return ctx->GetDungeon(GANONS_CASTLE)->IsMQ();}), + Entrance(RR_GANONS_TOWER_FLOOR_1, []{return true;}), + }); + areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_GANONS_CASTLE_LOBBY, []{return Here(RR_GANONS_TOWER_FLOOR_1, []{return logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2);});}), - Entrance(RR_GANONS_TOWER_FLOOR_2, []{return Here(RR_GANONS_TOWER_FLOOR_1, []{return logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2);});}), + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return Here(RR_GANONS_TOWER_FLOOR_1, []{return logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2);});}), + Entrance(RR_GANONS_TOWER_FLOOR_2, []{return Here(RR_GANONS_TOWER_FLOOR_1, []{return logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2);});}), }); areaTable[RR_GANONS_TOWER_FLOOR_2] = Region("Ganon's Tower Floor 2", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp index b144839bf..83374f152 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp @@ -17,8 +17,8 @@ void RegionTable_Init_GerudoTrainingGround() { areaTable[RR_GERUDO_TRAINING_GROUND_LOBBY] = Region("Gerudo Training Ground Lobby", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_GERUDO_TRAINING_GROUND_LOBBY_LEFT_CHEST, logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT)), - LOCATION(RC_GERUDO_TRAINING_GROUND_LOBBY_RIGHT_CHEST, logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_GERUDO_TRAINING_GROUND_LOBBY_LEFT_CHEST, logic->CanHitEyeTargets()), + LOCATION(RC_GERUDO_TRAINING_GROUND_LOBBY_RIGHT_CHEST, logic->CanHitEyeTargets()), LOCATION(RC_GERUDO_TRAINING_GROUND_STALFOS_CHEST, logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true)), LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_CHEST, logic->CanKillEnemy(RE_BEAMOS) && logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true)), LOCATION(RC_GERUDO_TRAINING_GROUND_ENTRANCE_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp index ff9b171c8..879ac1dce 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp @@ -21,18 +21,18 @@ void RegionTable_Init_IceCavern() { }, { //Exits Entrance(RR_ICE_CAVERN_ENTRYWAY, []{return true;}), - Entrance(RR_ICE_CAVERN_MAIN, []{return Here(RR_ICE_CAVERN_BEGINNING, []{return (logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD)) || logic->CanUse(RG_MEGATON_HAMMER) || logic->HasExplosives() || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_DINS_FIRE);});}), + Entrance(RR_ICE_CAVERN_MAIN, []{return Here(RR_ICE_CAVERN_BEGINNING, []{return logic->CanKillEnemy(RE_FREEZARD, ED_CLOSE, true, 4);});}), }); areaTable[RR_ICE_CAVERN_MAIN] = Region("Ice Cavern", "Ice Cavern", {RA_ICE_CAVERN}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->BlueFireAccess, []{return logic->BlueFireAccess || (logic->IsAdult && logic->HasBottle());}), + EventAccess(&logic->BlueFireAccess, []{return logic->IsAdult;}), }, { //Locations LOCATION(RC_ICE_CAVERN_MAP_CHEST, logic->BlueFire() && logic->IsAdult), LOCATION(RC_ICE_CAVERN_COMPASS_CHEST, logic->BlueFire()), - LOCATION(RC_ICE_CAVERN_IRON_BOOTS_CHEST, logic->BlueFire() && (logic->CanJumpslash() || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DINS_FIRE))), - LOCATION(RC_SHEIK_IN_ICE_CAVERN, logic->BlueFire() && (logic->CanJumpslash() || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DINS_FIRE)) && logic->IsAdult), + LOCATION(RC_ICE_CAVERN_IRON_BOOTS_CHEST, logic->BlueFire() && logic->CanKillEnemy(RE_WOLFOS)), + LOCATION(RC_SHEIK_IN_ICE_CAVERN, logic->BlueFire() && logic->CanKillEnemy(RE_WOLFOS) && logic->IsAdult), LOCATION(RC_ICE_CAVERN_FREESTANDING_POH, logic->BlueFire()), LOCATION(RC_ICE_CAVERN_GS_SPINNING_SCYTHE_ROOM, logic->HookshotOrBoomerang()), LOCATION(RC_ICE_CAVERN_GS_HEART_PIECE_ROOM, logic->BlueFire() && logic->HookshotOrBoomerang()), @@ -125,8 +125,8 @@ void RegionTable_Init_IceCavern() { LOCATION(RC_SHEIK_IN_ICE_CAVERN, logic->CanKillEnemy(RE_STALFOS)), }, { //Exits - Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire() && Here(RR_ICE_CAVERN_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), - Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanUse(RG_IRON_BOOTS) && Here(RR_ICE_CAVERN_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), + Entrance(RR_ICE_CAVERN_MQ_WEST_CORRIDOR, []{return Here(RR_ICE_CAVERN_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), + Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanUse(RG_IRON_BOOTS) && Here(RR_ICE_CAVERN_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), }); areaTable[RR_ICE_CAVERN_MQ_COMPASS_ROOM] = Region("Ice Cavern MQ Compass Room", "Ice Cavern", {RA_ICE_CAVERN}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp index fa9c7b085..7bfb98a87 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp @@ -24,7 +24,6 @@ void RegionTable_Init_JabuJabusBelly() { //Combines Lift room middle and lower, 1F holes room, the forked corridor, and it's side rooms areaTable[RR_JABU_JABUS_BELLY_MAIN] = Region("Jabu Jabus Belly Main", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->JabuRutoInB1, []{return true;}), EventAccess(&logic->JabuWestTentacle, []{return logic->JabuRutoIn1F && logic->CanKillEnemy(RE_TENTACLE, ED_BOOMERANG);}), }, { //Locations @@ -263,7 +262,7 @@ void RegionTable_Init_JabuJabusBelly() { areaTable[RR_JABU_JABUS_BELLY_MQ_WEST_FORKED_ROOMS] = Region("Jabu Jabus Belly MQ West Forked Rooms", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->JabuWestTentacle, []{return logic->CanUse(RG_BOOMERANG);}), + EventAccess(&logic->JabuWestTentacle, []{return logic->CanKillEnemy(RE_TENTACLE, ED_BOOMERANG);}), }, { //Locations LOCATION(RC_JABU_JABUS_BELLY_MQ_GS_TAILPASARAN_ROOM, Here(RR_JABU_JABUS_BELLY_MQ_WEST_FORKED_ROOMS, []{return logic->HasExplosives();}) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp index bdcf8a4fa..8cf70fc3f 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp @@ -79,8 +79,8 @@ void RegionTable_Init_ShadowTemple() { areaTable[RR_SHADOW_TEMPLE_WIND_TUNNEL] = Region("Shadow Temple Wind Tunnel", "Shadow Temple", {RA_SHADOW_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SHADOW_TEMPLE_WIND_HINT_CHEST, true), - LOCATION(RC_SHADOW_TEMPLE_AFTER_WIND_ENEMY_CHEST, logic->CanJumpslashExceptHammer()), - LOCATION(RC_SHADOW_TEMPLE_AFTER_WIND_HIDDEN_CHEST, true), + LOCATION(RC_SHADOW_TEMPLE_AFTER_WIND_ENEMY_CHEST, logic->CanKillEnemy(RE_GIBDO, ED_CLOSE, true, 2)), + LOCATION(RC_SHADOW_TEMPLE_AFTER_WIND_HIDDEN_CHEST, logic->HasExplosives()), LOCATION(RC_SHADOW_TEMPLE_GS_NEAR_SHIP, logic->CanUse(RG_LONGSHOT) && logic->SmallKeys(RR_SHADOW_TEMPLE, 4, 5)), LOCATION(RC_SHADOW_TEMPLE_WIND_HINT_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), LOCATION(RC_SHADOW_TEMPLE_AFTER_WIND_POT_1, logic->CanBreakPots()), @@ -96,7 +96,7 @@ void RegionTable_Init_ShadowTemple() { //Locations LOCATION(RC_SHADOW_TEMPLE_SPIKE_WALLS_LEFT_CHEST, logic->CanUse(RG_DINS_FIRE)), LOCATION(RC_SHADOW_TEMPLE_BOSS_KEY_CHEST, logic->CanUse(RG_DINS_FIRE)), - LOCATION(RC_SHADOW_TEMPLE_INVISIBLE_FLOORMASTER_CHEST, logic->CanJumpslashExceptHammer()), + LOCATION(RC_SHADOW_TEMPLE_INVISIBLE_FLOORMASTER_CHEST, logic->CanKillEnemy(RE_FLOORMASTER)), //RANDOTODO check if child can reach the token LOCATION(RC_SHADOW_TEMPLE_GS_TRIPLE_GIANT_POT, logic->IsAdult && logic->CanAttack()), LOCATION(RC_SHADOW_TEMPLE_AFTER_BOAT_POT_1, logic->CanBreakPots()), @@ -181,7 +181,7 @@ void RegionTable_Init_ShadowTemple() { //Room exists for if it's ever possible to go backwards or void warp into the middle of shadow areaTable[RR_SHADOW_TEMPLE_MQ_B2_TO_B3_CORRIDOR] = Region("Shadow Temple MQ B2 to B3 Corridor", "Shadow Temple", {RA_SHADOW_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SHADOW_TEMPLE_MQ_FIRST_BEAMOS, []{return logic->HasExplosives() && logic->SmallKeys(RR_SHADOW_TEMPLE, 2);}), + Entrance(RR_SHADOW_TEMPLE_MQ_FIRST_BEAMOS, []{return logic->SmallKeys(RR_SHADOW_TEMPLE, 2);}), Entrance(RR_SHADOW_TEMPLE_MQ_UPPER_HUGE_PIT, []{return true;}), //bunnyhovers + lens lets you go from the very top of upper pit to the stationary invisible platform below quite easily }); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp index c4dcf4f02..b4b77056b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp @@ -79,7 +79,7 @@ void RegionTable_Init_CastleGrounds() { LOCATION(RC_HC_STORMS_GROTTO_POT_4, logic->CanBreakPots()), }, { //Exits - Entrance(RR_CASTLE_GROUNDS, []{return true;}), + Entrance(RR_HC_STORMS_GROTTO, []{return true;}), }); areaTable[RR_GANONS_CASTLE_GROUNDS] = Region("Ganon's Castle Grounds", "Castle Grounds", {RA_OUTSIDE_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp index 87a2e3b48..28d272711 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp @@ -50,7 +50,7 @@ void RegionTable_Init_Graveyard() { LOCATION(RC_GRAVEYARD_SHIELD_GRAVE_FAIRY_8, true), }, { //Exits - Entrance(RR_THE_GRAVEYARD, []{return true;}), + Entrance(RR_GRAVEYARD_SHIELD_GRAVE, []{return true;}), }); areaTable[RR_GRAVEYARD_HEART_PIECE_GRAVE] = Region("Graveyard Heart Piece Grave", "Graveyard Heart Piece Grave", {}, NO_DAY_NIGHT_CYCLE, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_list.cpp b/soh/soh/Enhancements/randomizer/location_list.cpp index 7fe45c2d8..c12cc5f11 100644 --- a/soh/soh/Enhancements/randomizer/location_list.cpp +++ b/soh/soh/Enhancements/randomizer/location_list.cpp @@ -992,7 +992,7 @@ void Rando::StaticData::InitLocationTable() { // locationTable[RC_HF_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HF_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, -22947, "Open Grotto Gossip Stone"); locationTable[RC_KAK_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_KAK_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_GROTTOS, -22984, "Open Grotto Gossip Stone"); locationTable[RC_ZR_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_ZR_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_GROTTOS, -22985, "Open Grotto Gossip Stone"); - locationTable[RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_GROTTOS, -22964, "Near Shortcuts Gossip Stone"); + locationTable[RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_GROTTOS, -22964, "Near Shortcuts Grotto Gossip Stone"); locationTable[RC_DMT_STORMS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_DMT_STORMS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_GROTTOS, -23255, "Storms Grotto Gossip Stone"); locationTable[RC_DMC_UPPER_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_DMC_UPPER_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_CRATER, SCENE_GROTTOS, -23802, "Upper Grotto Gossip Stone"); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 294fff292..0979aae75 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -673,7 +673,7 @@ namespace Rando { } return killed; case RE_BIG_OCTO: - //If chasing octo is annoying but with rolls you can catch him, and you need rang to get into this room without shenanigains anyway. Bunny makes it free + //If chasing octo is annoying but with rolls you can catch him, and you need rang to get into this room without shenanigans anyway. Bunny makes it free return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_STICKS) || CanUse(RG_MASTER_SWORD); case RE_GOHMA: return HasBossSoul(RG_GOHMA_SOUL) && CanJumpslash() && @@ -1783,7 +1783,7 @@ namespace Rando { case ITEMTYPE_EQUIP: { RandomizerGet itemRG = item.GetRandomizerGet(); - if (itemRG == RG_GIANTS_KNIFE) { + if (itemRG == RG_GIANTS_KNIFE || itemRG == RG_DEKU_SHIELD || itemRG == RG_HYLIAN_SHIELD) { return; } uint32_t equipId = RandoGetToEquipFlag.find(itemRG)->second; @@ -2277,70 +2277,69 @@ namespace Rando { GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get(); //Events - ShowedMidoSwordAndShield = false; + ShowedMidoSwordAndShield = false; TH_CouldRescueF1NorthCarpenter = false; TH_CouldRescueF1SouthCarpenter = false; TH_CouldRescueF2NorthCarpenter = false; TH_CouldRescueF2SouthCarpenter = false; - GF_GateOpen = false; - GtG_GateOpen = false; - DampesWindmillAccess = false; - DrainWell = false; - GoronCityChildFire = false; - GCWoodsWarpOpen = false; - GCDaruniasDoorOpenChild = false; - StopGCRollingGoronAsAdult = false; - CanWaterTempleLowFromHigh = false; - CanWaterTempleLowFromMid = false; - CanWaterTempleMiddle = false; - CanWaterTempleHigh = false; - KakarikoVillageGateOpen = false; - KingZoraThawed = false; - ForestTempleJoelle = false; - ForestTempleBeth = false; - ForestTempleAmy = false; - ForestTempleMeg = false; - FireLoopSwitch = false; - LinksCow = false; - DeliverLetter = false; - ClearMQDCUpperLobbyRocks = false; - LoweredWaterInsideBotw = false; - OpenedWestRoomMQBotw = false; - OpenedMiddleHoleMQBotw = false; - BrokeDeku1FWeb = false; - ClearedMQDekuSERoom = false; - MQDekuWaterRoomTorches = false; - PushedDekuBasementBlock = false; - OpenedLowestGoronCage = false; - OpenedUpperFireShortcut = false; - HitFireTemplePlatform = false; - OpenedFireMQFireMazeDoor = false; - MQForestBlockRoomTargets = false; - ForestCanTwistHallway = false; - ForestClearBelowBowChest = false; - ForestOpenBossCorridor = false; - ShadowTrialFirstChest = false; - MQGTGMazeSwitch = false; - GTGPlatformSilverRupees = false; - MQJabuHolesRoomDoor = false; - JabuWestTentacle = false; - JabuEastTentacle = false; - JabuNorthTentacle = false; - LoweredJabuPath = false; - MQJabuLiftRoomCow = false; - MQShadowFloorSpikeRupees = false; - ShadowShortcutBlock = false; - MQWaterStalfosPit = false; - MQWaterDragonTorches = false; - MQWaterB1Switch = false; - //MQWaterPillarSoTBlock = false; - MQWaterOpenedPillarB1 = false; - MQSpiritCrawlBoulder = false; - MQSpiritMapRoomEnemies = false; - MQSpirit3SunsEnemies = false; - Spirit1FSilverRupees = false; - JabuRutoInB1 = false; - JabuRutoIn1F = false; + GF_GateOpen = false; + GtG_GateOpen = false; + DampesWindmillAccess = false; + DrainWell = false; + GoronCityChildFire = false; + GCWoodsWarpOpen = false; + GCDaruniasDoorOpenChild = false; + StopGCRollingGoronAsAdult = false; + CanWaterTempleLowFromHigh = false; + CanWaterTempleLowFromMid = false; + CanWaterTempleMiddle = false; + CanWaterTempleHigh = false; + KakarikoVillageGateOpen = false; + KingZoraThawed = false; + ForestTempleJoelle = false; + ForestTempleBeth = false; + ForestTempleAmy = false; + ForestTempleMeg = false; + FireLoopSwitch = false; + LinksCow = false; + DeliverLetter = false; + ClearMQDCUpperLobbyRocks = false; + LoweredWaterInsideBotw = false; + OpenedWestRoomMQBotw = false; + OpenedMiddleHoleMQBotw = false; + BrokeDeku1FWeb = false; + ClearedMQDekuSERoom = false; + MQDekuWaterRoomTorches = false; + PushedDekuBasementBlock = false; + OpenedLowestGoronCage = false; + OpenedUpperFireShortcut = false; + HitFireTemplePlatform = false; + OpenedFireMQFireMazeDoor = false; + MQForestBlockRoomTargets = false; + ForestCanTwistHallway = false; + ForestClearBelowBowChest = false; + ForestOpenBossCorridor = false; + ShadowTrialFirstChest = false; + MQGTGMazeSwitch = false; + GTGPlatformSilverRupees = false; + MQJabuHolesRoomDoor = false; + JabuWestTentacle = false; + JabuEastTentacle = false; + JabuNorthTentacle = false; + LoweredJabuPath = false; + MQJabuLiftRoomCow = false; + MQShadowFloorSpikeRupees = false; + ShadowShortcutBlock = false; + MQWaterStalfosPit = false; + MQWaterDragonTorches = false; + MQWaterB1Switch = false; + //MQWaterPillarSoTBlock = false; + MQWaterOpenedPillarB1 = false; + MQSpiritCrawlBoulder = false; + MQSpiritMapRoomEnemies = false; + MQSpirit3SunsEnemies = false; + Spirit1FSilverRupees = false; + JabuRutoIn1F = false; StopPerformanceTimer(PT_LOGIC_RESET); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index a766737f8..890f75d41 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -179,7 +179,6 @@ class Logic { bool MQSpiritTimeTravelChest = false; bool MQSpirit3SunsEnemies = false; bool Spirit1FSilverRupees = false; - bool JabuRutoInB1 = false; bool JabuRutoIn1F = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ diff --git a/soh/soh/Enhancements/randomizer/option.cpp b/soh/soh/Enhancements/randomizer/option.cpp index e032d1a2d..64ca107c5 100644 --- a/soh/soh/Enhancements/randomizer/option.cpp +++ b/soh/soh/Enhancements/randomizer/option.cpp @@ -2,7 +2,9 @@ #include "libultraship/bridge.h" #include #include +#include "soh/SohGui/SohGui.hpp" #include "soh/SohGui/UIWidgets.hpp" +#include namespace Rando { Option Option::Bool(RandomizerSettingKey key_, std::string name_, std::vector options_, @@ -130,11 +132,10 @@ void Option::Enable() { disabled = false; } -void Option::Disable(std::string text, const UIWidgets::CheckboxGraphics graphic) { - if (!disabled || disabledText != text || disabledGraphic != graphic) { +void Option::Disable(std::string text) { + if (!disabled || disabledText != text) { disabled = true; disabledText = std::move(text); - disabledGraphic = graphic; } } @@ -149,9 +150,6 @@ bool Option::RenderImGui() { case WidgetType::Checkbox: changed = RenderCheckbox(); break; - case WidgetType::TristateCheckbox: - changed = RenderTristateCheckbox(); - break; case WidgetType::Combobox: changed = RenderCombobox(); break; @@ -159,7 +157,6 @@ bool Option::RenderImGui() { changed = RenderSlider(); break; } - UIWidgets::Spacer(0); ImGui::EndGroup(); return changed; } @@ -213,50 +210,19 @@ Option::Option(size_t key_, std::string name_, std::vector options_ bool Option::RenderCheckbox() { bool changed = false; - if (disabled) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } bool val = static_cast(CVarGetInteger(cvarName.c_str(), defaultOption)); - if (CustomCheckbox(name.c_str(), &val, disabled, disabledGraphic)) { + UIWidgets::CheckboxOptions widgetOptions = static_cast(UIWidgets::CheckboxOptions().Color(THEME_COLOR).Tooltip(description.c_str())); + widgetOptions.disabled = disabled; + if (UIWidgets::Checkbox(name.c_str(), &val, widgetOptions)) { CVarSetInteger(cvarName.c_str(), val); changed = true; Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - if (!description.empty()) { - UIWidgets::InsertHelpHoverText(description.c_str()); - } - if (disabled) { - UIWidgets::ReEnableComponent(disabledText.c_str()); - } - return changed; -} - -bool Option::RenderTristateCheckbox() { - bool changed = false; - if (disabled) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - int val = CVarGetInteger(cvarName.c_str(), defaultOption); - if (CustomCheckboxTristate(name.c_str(), &val, disabled, disabledGraphic)) { - CVarSetInteger(cvarName.c_str(), val); - changed = true; - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - if (!description.empty()) { - UIWidgets::InsertHelpHoverText(description.c_str()); - } - if (disabled) { - UIWidgets::ReEnableComponent(disabledText.c_str()); - } return changed; } bool Option::RenderCombobox() { bool changed = false; - if (disabled) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - ImGui::Text("%s", name.c_str()); uint8_t selected = CVarGetInteger(cvarName.c_str(), defaultOption); if (selected >= options.size()) { selected = options.size(); @@ -264,25 +230,15 @@ bool Option::RenderCombobox() { changed = true; Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - if (!description.empty()) { - UIWidgets::InsertHelpHoverText(description.c_str()); + UIWidgets::ComboboxOptions widgetOptions = UIWidgets::ComboboxOptions().Color(THEME_COLOR).Tooltip(description.c_str()); + if (this->GetKey() == RSK_LOGIC_RULES) { + widgetOptions = widgetOptions.LabelPosition(UIWidgets::LabelPositions::None).ComponentAlignment(UIWidgets::ComponentAlignments::Right); } - const std::string comboName = std::string("##") + std::string(cvarName); - if (ImGui::BeginCombo(comboName.c_str(), options[selected].c_str())) { - for (size_t i = 0; i < options.size(); i++) { - if (!options[i].empty()) { - if (ImGui::Selectable(options[i].c_str(), i == selected)) { - CVarSetInteger(cvarName.c_str(), static_cast(i)); - changed = true; - selected = i; - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - } - } - ImGui::EndCombo(); - } - if (disabled) { - UIWidgets::ReEnableComponent(disabledText.c_str()); + widgetOptions.disabled = disabled; + if(UIWidgets::Combobox(name.c_str(), &selected, options, widgetOptions)) { + CVarSetInteger(cvarName.c_str(), static_cast(selected)); + changed = true; + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } return changed; } @@ -295,40 +251,11 @@ bool Option::RenderSlider() { CVarSetInteger(cvarName.c_str(), val); changed = true; } - if (disabled) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - const std::string formatName = name + ": %s"; - ImGui::Text(formatName.c_str(), options[val].c_str()); - if (!description.empty()) { - UIWidgets::InsertHelpHoverText(description.c_str()); - } - UIWidgets::Spacer(0); - ImGui::BeginGroup(); - const std::string MinusBTNName = " - ##" + cvarName; - if (ImGui::Button(MinusBTNName.c_str())) { - val--; + UIWidgets::IntSliderOptions widgetOptions = UIWidgets::IntSliderOptions().Color(THEME_COLOR).Min(0).Max(options.size() - 1).Tooltip(description.c_str()).Format(options[val].c_str()).DefaultValue(defaultOption); + widgetOptions.disabled = disabled; + if (UIWidgets::SliderInt(name.c_str(), &val, widgetOptions)) { changed = true; } - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - ImGui::PushItemWidth(std::min(ImGui::GetContentRegionAvail().x - 30.0f, 260.0f)); - const std::string id = "##Slider" + cvarName; - if (ImGui::SliderInt(id.c_str(), &val, 0, static_cast(options.size()) - 1, "", ImGuiSliderFlags_AlwaysClamp)) { - changed = true; - } - ImGui::PopItemWidth(); - const std::string PlusBTNName = " + ##" + cvarName; - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - if (ImGui::Button(PlusBTNName.c_str())) { - val++; - changed = true; - } - ImGui::EndGroup(); - if (disabled) { - UIWidgets::ReEnableComponent(disabledText.c_str()); - } if (val < 0) { val = 0; changed = true; @@ -465,7 +392,7 @@ bool OptionGroup::RenderImGui() const { // NOLINT(*-no-recursion) ImGui::TableSetColumnIndex(i); ImGui::TableHeader(mSubGroups[i]->GetName().c_str()); if (!mSubGroups[i]->GetDescription().empty()) { - UIWidgets::SetLastItemHoverText(mSubGroups[i]->GetDescription().c_str()); + UIWidgets::Tooltip(mSubGroups[i]->GetDescription().c_str()); } } ImGui::PopItemFlag(); @@ -473,12 +400,10 @@ bool OptionGroup::RenderImGui() const { // NOLINT(*-no-recursion) } } if (mContainerType == WidgetContainerType::SECTION && !mName.empty()) { - UIWidgets::PaddedSeparator(); - ImGui::Text("%s", mName.c_str()); + ImGui::SeparatorText(mName.c_str()); if (!mDescription.empty()) { - UIWidgets::InsertHelpHoverText(mDescription.c_str()); + UIWidgets::Tooltip(mDescription.c_str()); } - UIWidgets::PaddedSeparator(); } if (mContainerType == WidgetContainerType::COLUMN) { ImGui::TableNextColumn(); @@ -507,9 +432,6 @@ bool OptionGroup::RenderImGui() const { // NOLINT(*-no-recursion) if (option->HasFlag(IMFLAG_UNINDENT)) { ImGui::Unindent(); } - if (option->HasFlag(IMFLAG_SEPARATOR_BOTTOM)) { - UIWidgets::PaddedSeparator(false, true); - } } } if (mContainerType == WidgetContainerType::COLUMN) { diff --git a/soh/soh/Enhancements/randomizer/option.h b/soh/soh/Enhancements/randomizer/option.h index d2291463b..518487219 100644 --- a/soh/soh/Enhancements/randomizer/option.h +++ b/soh/soh/Enhancements/randomizer/option.h @@ -1,6 +1,7 @@ #pragma once -#include "soh/SohGui/UIWidgets.hpp" +#ifndef RANDOPTION_H +#define RANDOPTION_H #include #include @@ -35,7 +36,6 @@ enum class OptionCategory { */ enum class WidgetType { Checkbox, /** Default for Bools, not compatible if options.size() > 2. */ - TristateCheckbox, /** Compatible with U8s, not compatible if options.size() != 3. */ Combobox, /** Default for U8s, works with U8s and Bools. */ Slider, /** Compatible with U8s. If constructed with NumOpts, consider using this. Technically can be used for Bool or non-NumOpts options but it would be a bit weird semantically. */ }; @@ -312,7 +312,7 @@ class Option { * @param graphic What graphic to display in a disabled checkbox. Defaults to an * "X" symbol. */ - void Disable(std::string text, UIWidgets::CheckboxGraphics graphic = UIWidgets::CheckboxGraphics::Cross); + void Disable(std::string text); bool IsCategory(OptionCategory category) const; /** @@ -339,7 +339,6 @@ protected: private: bool RenderCheckbox(); - bool RenderTristateCheckbox(); bool RenderCombobox(); bool RenderSlider(); void PopulateTextToNum(); @@ -357,7 +356,6 @@ protected: bool defaultHidden = false; int imFlags = IMFLAG_NONE; bool disabled = false; - UIWidgets::CheckboxGraphics disabledGraphic = UIWidgets::CheckboxGraphics::Cross; std::string disabledText; std::unordered_map optionsTextToVar = {}; }; @@ -546,4 +544,6 @@ class OptionGroup { std::string mDescription; bool mDisabled = false; }; -} // namespace Rando \ No newline at end of file +} // namespace Rando + +#endif //RANDOPTION_H \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 18b5a1c36..f2344a952 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -11,9 +11,8 @@ #include "3drando/rando_main.hpp" #include "3drando/random.hpp" #include "soh/ResourceManagerHelpers.h" -#include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "3drando/custom_messages.hpp" -#include "soh/SohGui/UIWidgets.hpp" #include #include #include "../custom-message/CustomMessageTypes.h" @@ -27,6 +26,7 @@ #include #include #include "draw.h" +#include "soh/SohGui/UIWidgets.hpp" #include "static_data.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include @@ -1807,7 +1807,6 @@ PotIdentity Randomizer::IdentifyPot(s32 sceneNum, s32 posX, s32 posZ) { if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) { LUSLOG_WARN("IdentifyPot did not receive a valid RC value (%d).", location->GetRandomizerCheck()); - assert(false); } else { potIdentity.randomizerInf = rcToRandomizerInf[location->GetRandomizerCheck()]; potIdentity.randomizerCheck = location->GetRandomizerCheck(); @@ -1924,6 +1923,16 @@ bool GenerateRandomizer(std::string seed /*= ""*/) { return false; } +static const std::unordered_map randomizerPresetList = { + { RANDOMIZER_PRESET_DEFAULT, "Default" }, + { RANDOMIZER_PRESET_SPOCK_RACE, "Spock Race" }, + { RANDOMIZER_PRESET_SPOCK_RACE_NO_LOGIC, "Spock Race (No Logic)" }, + { RANDOMIZER_PRESET_S6, "S6" }, + { RANDOMIZER_PRESET_HELL_MODE, "Hell Mode" }, + { RANDOMIZER_PRESET_BENCHMARK, "Benchmark" } +}; +static int32_t randomizerPresetSelected = RANDOMIZER_PRESET_DEFAULT; + void RandomizerSettingsWindow::DrawElement() { auto ctx = Rando::Context::GetInstance(); if (generated) { @@ -1931,30 +1940,62 @@ void RandomizerSettingsWindow::DrawElement() { randoThread.join(); } bool disableEditingRandoSettings = CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0) || CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0); - if (disableEditingRandoSettings) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings); + const PresetTypeDefinition presetTypeDef = presetTypes.at(PRESET_TYPE_RANDOMIZER); + std::string comboboxTooltip = ""; + for (auto iter = presetTypeDef.presets.begin(); iter != presetTypeDef.presets.end(); ++iter) { + if (iter->first != 0) comboboxTooltip += "\n\n"; + comboboxTooltip += std::string(iter->second.label) + " - " + std::string(iter->second.description); + } + const std::string presetTypeCvar = CVAR_GENERAL("SelectedPresets.") + std::to_string(PRESET_TYPE_RANDOMIZER); + randomizerPresetSelected = CVarGetInteger(presetTypeCvar.c_str(), RANDOMIZER_PRESET_DEFAULT); + + if (UIWidgets::Combobox("Randomizer Presets", &randomizerPresetSelected, randomizerPresetList, UIWidgets::ComboboxOptions() + .DefaultIndex(RANDOMIZER_PRESET_DEFAULT) + .Tooltip(comboboxTooltip.c_str()) + .Color(THEME_COLOR)) + ) { + CVarSetInteger(presetTypeCvar.c_str(), randomizerPresetSelected); + } + ImGui::SameLine(); + ImGui::SetCursorPosY(ImGui::GetCursorPos().y + 35.f); + if (UIWidgets::Button("Apply Preset##Randomizer", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline).Padding(ImVec2(10.f, 6.f)))) { + if (randomizerPresetSelected >= presetTypeDef.presets.size()) { + randomizerPresetSelected = 0; + } + const PresetDefinition selectedPresetDef = presetTypeDef.presets.at(randomizerPresetSelected); + for(const char* block : presetTypeDef.blocksToClear) { + CVarClearBlock(block); + } + if (randomizerPresetSelected != 0) { + applyPreset(selectedPresetDef.entries); + } + CVarSetInteger(presetTypeCvar.c_str(), randomizerPresetSelected); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); - DrawPresetSelector(PRESET_TYPE_RANDOMIZER); - ImGui::EndDisabled(); - UIWidgets::Spacer(0); - UIWidgets::EnhancementCheckbox("Manual seed entry", CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), false, ""); + UIWidgets::CVarCheckbox("Manual seed entry", CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), UIWidgets::CheckboxOptions().Color(THEME_COLOR)); if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0)) { - ImGui::Text("Seed"); + UIWidgets::PushStyleInput(THEME_COLOR); ImGui::InputText("##RandomizerSeed", seedString, MAX_SEED_STRING_SIZE, ImGuiInputTextFlags_CallbackCharFilter, UIWidgets::TextFilters::FilterAlphaNum); UIWidgets::Tooltip( "Characters from a-z, A-Z, and 0-9 are supported.\n" "Character limit is 1023, after which the seed will be truncated.\n" ); - ImGui::SameLine(); - if (ImGui::Button("New Seed")) { + if (strnlen(seedString, MAX_SEED_STRING_SIZE) == 0) { + ImGui::SameLine(17.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Leave blank for random seed"); + } + UIWidgets::PopStyleInput(); + ImGui::SameLine(0.f, 50.f); + if (UIWidgets::Button(ICON_FA_RANDOM, UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR).Padding(ImVec2(10.f, 6.f)).Tooltip( + "Creates a new random seed value to be used when generating a randomizer" + ))) { SohUtils::CopyStringToCharArray(seedString, std::to_string(rand() & 0xFFFFFFFF), MAX_SEED_STRING_SIZE); } - UIWidgets::Tooltip("Creates a new random seed value to be used when generating a randomizer"); ImGui::SameLine(); - if (ImGui::Button("Clear Seed")) { + if (UIWidgets::Button(ICON_FA_ERASER, UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR).Padding(ImVec2(10.f, 6.f)))) { memset(seedString, 0, MAX_SEED_STRING_SIZE); } } @@ -1962,13 +2003,13 @@ void RandomizerSettingsWindow::DrawElement() { UIWidgets::Spacer(0); ImGui::BeginDisabled((CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0) && gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); - if (ImGui::Button("Generate Randomizer")) { + if (UIWidgets::Button("Generate Randomizer", UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR))) { ctx->SetSpoilerLoaded(false); GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : ""); } ImGui::EndDisabled(); - UIWidgets::Spacer(0); + ImGui::SameLine(); if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { std::string spoilerfilepath = CVarGetString(CVAR_GENERAL("SpoilerLog"), ""); ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str()); @@ -1978,13 +2019,13 @@ void RandomizerSettingsWindow::DrawElement() { // std::string presetfilepath = CVarGetString(CVAR_RANDOMIZER_SETTING("LoadedPreset"), ""); // ImGui::Text("Settings File: %s", presetfilepath.c_str()); - UIWidgets::PaddedSeparator(); - + UIWidgets::Separator(true, true, 0.f, 0.f); ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGuiWindow* window = ImGui::GetCurrentWindow(); static ImVec2 cellPadding(8.0f, 8.0f); + UIWidgets::PushStyleTabs(THEME_COLOR); if (ImGui::BeginTabBar("Randomizer Settings", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("World")) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); @@ -2048,7 +2089,9 @@ void RandomizerSettingsWindow::DrawElement() { window->DC.CurrLineTextBaseOffset = 0.0f; static ImGuiTextFilter locationSearch; + UIWidgets::PushStyleInput(THEME_COLOR); locationSearch.Draw(); + UIWidgets::PopStyleInput(); ImGui::BeginChild("ChildIncludedLocations", ImVec2(0, -8)); for (auto& [rcArea, locations] : RandomizerCheckObjects::GetAllRCObjectsByArea()) { @@ -2068,7 +2111,7 @@ void RandomizerSettingsWindow::DrawElement() { for (auto& location : locations) { if (ctx->GetItemLocation(location)->IsVisible() && !excludedLocations.count(location) && locationSearch.PassFilter(Rando::StaticData::GetLocation(location)->GetName().c_str())) { - + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); if (ImGui::ArrowButton(std::to_string(location).c_str(), ImGuiDir_Right)) { excludedLocations.insert(location); // todo: this efficently when we build out cvar array support @@ -2080,6 +2123,7 @@ void RandomizerSettingsWindow::DrawElement() { CVarSetString(CVAR_RANDOMIZER_SETTING("ExcludedLocations"), excludedLocationString.c_str()); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } + UIWidgets::PopStyleButton(); ImGui::SameLine(); ImGui::Text("%s", Rando::StaticData::GetLocation(location)->GetShortName().c_str()); } @@ -2110,6 +2154,7 @@ void RandomizerSettingsWindow::DrawElement() { for (auto& location : locations) { auto elfound = excludedLocations.find(location); if (ctx->GetItemLocation(location)->IsVisible() && elfound != excludedLocations.end()) { + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); if (ImGui::ArrowButton(std::to_string(location).c_str(), ImGuiDir_Left)) { excludedLocations.erase(elfound); // todo: this efficently when we build out cvar array support @@ -2125,6 +2170,7 @@ void RandomizerSettingsWindow::DrawElement() { } Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } + UIWidgets::PopStyleButton(); ImGui::SameLine(); ImGui::Text("%s", Rando::StaticData::GetLocation(location)->GetShortName().c_str()); } @@ -2272,10 +2318,12 @@ void RandomizerSettingsWindow::DrawElement() { //{ Rando::Tricks::Tag::GLITCH, false }, }; static ImGuiTextFilter trickSearch; + UIWidgets::PushStyleInput(THEME_COLOR); trickSearch.Draw("Filter (inc,-exc)", 490.0f); + UIWidgets::PopStyleInput(); if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC) { ImGui::SameLine(); - if (ImGui::Button("Disable All")) { + if (UIWidgets::Button("Disable All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(250.f, 0.f)))) { for (int i = 0; i < RT_MAX; i++) { auto etfound = enabledTricks.find(static_cast(i)); if (etfound != enabledTricks.end()) { @@ -2291,7 +2339,7 @@ void RandomizerSettingsWindow::DrawElement() { Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } ImGui::SameLine(); - if (ImGui::Button("Enable All")) { + if (UIWidgets::Button("Enable All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(250.f, 0.f)))) { for (int i = 0; i < RT_MAX; i++) { if (!enabledTricks.count(static_cast(i))) { enabledTricks.insert(static_cast(i)); @@ -2334,19 +2382,19 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::TableNextColumn(); window->DC.CurrLineTextBaseOffset = 0.0f; - if (ImGui::Button("Collapse All##disabled")) { + if (UIWidgets::Button("Collapse All##disabled", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { for (int i = 0; i < RA_MAX; i++) { areaTreeDisabled[static_cast(i)] = false; } } ImGui::SameLine(); - if (ImGui::Button("Open All##disabled")) { + if (UIWidgets::Button("Open All##disabled", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { for (int i = 0; i < RA_MAX; i++) { areaTreeDisabled[static_cast(i)] = true; } } ImGui::SameLine(); - if (ImGui::Button("Enable Visible")) { + if (UIWidgets::Button("Enable Visible", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { for (int i = 0; i < RT_MAX; i++) { auto option = mSettings->GetTrickOption(static_cast(i)); if (!enabledTricks.count(static_cast(i)) && @@ -2387,6 +2435,7 @@ void RandomizerSettingsWindow::DrawElement() { !enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { ImGui::TreeNodeSetOpen(ImGui::GetID((Rando::Tricks::GetAreaName(option.GetArea()) + "##disabled").c_str()), areaTreeDisabled[option.GetArea()]); ImGui::SetNextItemOpen(true, ImGuiCond_Once); + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); if (ImGui::ArrowButton(std::to_string(rt).c_str(), ImGuiDir_Right)) { enabledTricks.insert(rt); std::string enabledTrickString = ""; @@ -2397,10 +2446,11 @@ void RandomizerSettingsWindow::DrawElement() { CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - Rando::Tricks::DrawTagChips(option.GetTags()); + UIWidgets::PopStyleButton(); + Rando::Tricks::DrawTagChips(option.GetTags(), option.GetName()); ImGui::SameLine(); ImGui::Text("%s", option.GetName().c_str()); - UIWidgets::InsertHelpHoverText(option.GetDescription().c_str()); + UIWidgets::Tooltip(option.GetDescription().c_str()); } } areaTreeDisabled[area] = true; @@ -2416,19 +2466,19 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::TableNextColumn(); window->DC.CurrLineTextBaseOffset = 0.0f; - if (ImGui::Button("Collapse All##enabled")) { + if (UIWidgets::Button("Collapse All##enabled", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { for (int i = 0; i < RA_MAX; i++) { areaTreeEnabled[static_cast(i)] = false; } } ImGui::SameLine(); - if (ImGui::Button("Open All##enabled")) { + if (UIWidgets::Button("Open All##enabled", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { for (int i = 0; i < RA_MAX; i++) { areaTreeEnabled[static_cast(i)] = true; } } ImGui::SameLine(); - if (ImGui::Button("Disable Visible")) { + if (UIWidgets::Button("Disable Visible", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { for (int i = 0; i < RT_MAX; i++) { auto option = mSettings->GetTrickOption(static_cast(i)); if (enabledTricks.count(static_cast(i)) && @@ -2473,6 +2523,7 @@ void RandomizerSettingsWindow::DrawElement() { enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { ImGui::TreeNodeSetOpen(ImGui::GetID((Rando::Tricks::GetAreaName(option.GetArea()) + "##enabled").c_str()), areaTreeEnabled[option.GetArea()]); ImGui::SetNextItemOpen(true, ImGuiCond_Once); + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); if (ImGui::ArrowButton(std::to_string(rt).c_str(), ImGuiDir_Left)) { enabledTricks.erase(rt); std::string enabledTrickString = ""; @@ -2487,10 +2538,11 @@ void RandomizerSettingsWindow::DrawElement() { } Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - Rando::Tricks::DrawTagChips(option.GetTags()); + UIWidgets::PopStyleButton(); + Rando::Tricks::DrawTagChips(option.GetTags(), option.GetName()); ImGui::SameLine(); ImGui::Text("%s", option.GetName().c_str()); - UIWidgets::InsertHelpHoverText(option.GetDescription().c_str()); + UIWidgets::Tooltip(option.GetDescription().c_str()); } } areaTreeEnabled[area] = true; @@ -2532,12 +2584,10 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::EndTabBar(); } + UIWidgets::PopStyleTabs(); ImGui::EndDisabled(); - - if (disableEditingRandoSettings) { - UIWidgets::ReEnableComponent(""); - } + ImGui::EndDisabled(); } void RandomizerSettingsWindow::UpdateElement() { @@ -2698,10 +2748,10 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { } template -void CreateGetItemMessages(const std::array* messageEntries) { +void CreateGetItemMessages(const std::array& messageEntries) { CustomMessageManager* customMessageManager = CustomMessageManager::Instance; customMessageManager->AddCustomMessageTable(Randomizer::getItemMessageTableID); - for (const GetItemMessage& messageEntry : *messageEntries) { + for (const GetItemMessage& messageEntry : messageEntries) { customMessageManager->CreateGetItemMessage( Randomizer::getItemMessageTableID, messageEntry.giid, messageEntry.iid, CustomMessage(messageEntry.english, messageEntry.german, messageEntry.french, TEXTBOX_TYPE_BLUE, @@ -3418,251 +3468,247 @@ void Randomizer::CreateCustomMessages() { GIMESSAGE(RG_GUARD_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to the&%gGuard House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus der Wachen%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison des Gardes%w!"), + "Vous obtenez la %rClé %wde la&%gMaison des Gardes%w!"), GIMESSAGE(RG_MARKET_BAZAAR_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMarket Bazaar%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gBasar des Marktes%w!", - "Vous obtenez une %rPetite Clé %w&du %gMarché%w!"), + "Vous obtenez la %rClé %wdu %gBazar&de la Place du Marché%w!"), GIMESSAGE(RG_MARKET_POTION_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMarket Potion Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gMagie-Laden des Marktes%w!", - "Vous obtenez une %rPetite Clé %w&du %gMarché%w!"), + "Vous obtenez la %rClé %wde la&%gPlace du Marché%w!"), GIMESSAGE(RG_MASK_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMask Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gMaskenladen%w!", - "Vous obtenez une %rPetite Clé %w&du %gMagasin de Masques%w!"), + "Vous obtenez la %rClé %wde la&%gFoire aux Masques%w!"), GIMESSAGE(RG_MARKET_SHOOTING_GALLERY_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMarket Shooting Gallery%w!", "Du erhältst einen %rkleinen&Schlüssel%w für die %gSchießbude des Marktes%w!", - "Vous obtenez une %rPetite Clé %w&du %gStand de Tir%w!"), + "Vous obtenez la %rClé %wdu %gStand de&Tir de la Place du Marché%w!"), GIMESSAGE(RG_BOMBCHU_BOWLING_KEY, ITEM_KEY_SMALL, "You found the key to the&%gBombchu Bowling Alley%w!", "Du erhältst einen %rkleinen&Schlüssel%w für die %gMinenbowlingbahn%w!", - "Vous obtenez une %rPetite Clé %w&du %gBowling Bombchu%w!"), + "Vous obtenez la %rClé %wdu %gBowling&Teigneux%w!"), GIMESSAGE(RG_TREASURE_CHEST_GAME_BUILDING_KEY, ITEM_KEY_SMALL, "You found the key to the&%gTreasure Chest Game Building%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus des Schatzkisten-Pokers%w!", - "Vous obtenez une %rPetite Clé %w&du %gJeu de la Chasse au Trésor%w!"), + "Vous obtenez la %rClé %wdu %gJeu de la&Chasse au Trésor%w!"), GIMESSAGE(RG_BOMBCHU_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gBombchu Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gKrabbelminenladen%w!", - "Vous obtenez une %rPetite Clé %w&du %gMagasin de Bombchu%w!"), + "Vous obtenez la %rClé %wdu %gMagasin&de Missiles%w!"), GIMESSAGE(RG_RICHARDS_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to&%gRichard's House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus von Richard%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison de Richard%w!"), - GIMESSAGE(RG_RICHARDS_HOUSE_KEY, ITEM_KEY_SMALL, - "You found the key to&%gRichard's House%w!", - "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus von Richard%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison de Richard%w!"), + "Vous obtenez la %rClé %wde la %gMaison&de Kiki%w!"), GIMESSAGE(RG_ALLEY_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to&the %gAlley House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus in der Gasse%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison de la Ruelle%w!"), + "Vous obtenez la %rClé %wde la %gMaison&de la Ruelle%w!"), GIMESSAGE(RG_KAK_BAZAAR_KEY, ITEM_KEY_SMALL, "You found the key to the&%gKakariko Bazaar%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gBasar von Kakariko%w!", - "Vous obtenez une %rPetite Clé %w&du %gMarché de Cocorico%w!"), + "Vous obtenez la %rClé %wdu %gBazar&de Cocorico%w!"), GIMESSAGE(RG_KAK_POTION_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gKakariko Potion Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gMagie-Laden von Kakariko%w!", - "Vous obtenez une %rPetite Clé %w&du %gMagasin de Potions de Cocorico%w!"), + "Vous obtenez la %rClé %wdu %gMagasin de&Potions de Cocorico%w!"), GIMESSAGE(RG_BOSS_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to the&%gBoss's House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus des Chefs%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison du Boss%w!"), + "Vous obtenez la %rClé %wde la %gMaison&du chef des ouvriers%w!"), GIMESSAGE(RG_GRANNYS_POTION_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to&%gGranny's Potion Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für %gAsas Hexenladen%w!", - "Vous obtenez une %rPetite Clé %w&du %gMagasin de Potions de Grand-mère%w!"), + "Vous obtenez la %rClé %wde&l'%gApothicaire%w!"), GIMESSAGE(RG_SKULLTULA_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to the&%gSkulltula House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gSkulltula-Haus%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison des Skulltulas%w!"), + "Vous obtenez la %rClé %wde la %gMaison&des Araignées%w!"), GIMESSAGE(RG_IMPAS_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to&%gImpa's House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus von Impa%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison d'Impa%w!"), + "Vous obtenez la %rClé %wde la %gMaison&d'Impa%w!"), GIMESSAGE(RG_WINDMILL_KEY, ITEM_KEY_SMALL, "You found the key to the&%gWindmill%w!", "Du erhältst einen %rkleinen&Schlüssel%w für die %gWindmühle%w!", - "Vous obtenez une %rPetite Clé %w&du %gMoulin à Vent%w!"), + "Vous obtenez la %rClé %w du %gMoulin%w!"), GIMESSAGE(RG_KAK_SHOOTING_GALLERY_KEY, ITEM_KEY_SMALL, "You found the key to the&%gKakariko Shooting Gallery%w!", "Du erhältst einen %rkleinen&Schlüssel%w für die %gSchießbude von Kakariko%w!", - "Vous obtenez une %rPetite Clé %w&du %gStand de Tir de Cocorico%w!"), + "Vous obtenez la %rClé %w du %gStand de&Tir de Cocorico%w!"), GIMESSAGE(RG_DAMPES_HUT_KEY, ITEM_KEY_SMALL, "You found the key to&%gDampe's Hut%w!", "Du erhältst einen %rkleinen&Schlüssel%w für die %gHütte von Boris%w!", - "Vous obtenez une %rPetite Clé %w&du %gChalet de Dampe%w!"), + "Vous obtenez la %rClé %wde la %gCabane&d'Igor%w!"), GIMESSAGE(RG_TALONS_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to&%gTalon's House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus von Talon%w!", - "Vous obtenez une %rPetite Clé %w&de la %gMaison de Talon%w!"), + "Vous obtenez la %rClé %wde la %gMaison&de Talon%w!"), GIMESSAGE(RG_STABLES_KEY, ITEM_KEY_SMALL, "You found the key to the&%gStables%w!", "Du erhältst einen %rkleinen&Schlüssel%w für die %gStälle%w!", - "Vous obtenez une %rPetite Clé %w&des %gÉcuries%w!"), + "Vous obtenez la %rClé %wdes %gÉcuries%w!"), GIMESSAGE(RG_BACK_TOWER_KEY, ITEM_KEY_SMALL, "You found the key to the&%gBack Tower%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %ghinteren Turm%w!", - "Vous obtenez une %rPetite Clé %w&du %gTour Arrière%w!"), + "Vous obtenez la %rClé %wdu %gSilo%w!"), GIMESSAGE(RG_HYLIA_LAB_KEY, ITEM_KEY_SMALL, "You found the key to the&%gHylia Laboratory%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHylia-Labor%w!", - "Vous obtenez une %rPetite Clé %w&du %gLaboratoire d'Hylia%w!"), + "Vous obtenez la %rClé %wdu %gLaboratoire&du Lac Hylia%w!"), GIMESSAGE(RG_FISHING_HOLE_KEY, ITEM_KEY_SMALL, - "You found the key to the&%gFishing Hole%w!", + "You found the key to the&%gPond%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gFischweiher%w!", - "Vous obtenez une %rPetite Clé %w&du %gTrou de Pêche%w!"), + "Vous obtenez la %rClé %wde l'%gÉtang%w!"), GIMESSAGE(RG_GERUDO_FORTRESS_KEY_RING, ITEM_KEY_SMALL, - "You found a %yThieves Hideout &%wKeyring!", + "You found a %yThieves Hideout&%wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für das %yDiebesversteck%w!", - "Vous obtenez un trousseau de&clés du %yRepaire des Voleurs%w!"), + "Vous obtenez le trousseau de&clés du %yRepaire des Voleurs%w!"), GIMESSAGE(RG_FOREST_TEMPLE_KEY_RING, ITEM_KEY_SMALL, - "You found a %gForest Temple &%wKeyring!", + "You found a %gForest Temple&%wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für den %gWaldtempel%w!", - "Vous obtenez un trousseau de&clés du %gTemple de la Forêt%w!"), + "Vous obtenez le trousseau de&clés du %gTemple de la Forêt%w!"), GIMESSAGE(RG_FIRE_TEMPLE_KEY_RING, ITEM_KEY_SMALL, - "You found a %rFire Temple &%wKeyring!", + "You found a %rFire Temple&%wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für den %rFeuertempel%w!", - "Vous obtenez un trousseau de&clés du %rTemple du Feu%w!"), + "Vous obtenez le trousseau de&clés du %rTemple du Feu%w!"), GIMESSAGE(RG_WATER_TEMPLE_KEY_RING, ITEM_KEY_SMALL, - "You found a %bWater Temple &%wKeyring!", + "You found a %bWater Temple&%wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für den %bWassertempel%w!", - "Vous obtenez un trousseau de&clés du %bTemple de l'Eau%w!"), + "Vous obtenez le trousseau de&clés du %bTemple de l'Eau%w!"), GIMESSAGE(RG_SPIRIT_TEMPLE_KEY_RING, ITEM_KEY_SMALL, - "You found a %ySpirit Temple &%wKeyring!", + "You found a %ySpirit Temple&%wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für den %yGeistertempel%w!", - "Vous obtenez un trousseau de&clés du %yTemple de l'Esprit%w!"), + "Vous obtenez le trousseau de&clés du %yTemple de l'Esprit%w!"), GIMESSAGE(RG_SHADOW_TEMPLE_KEY_RING, ITEM_KEY_SMALL, - "You found a %pShadow Temple &%wKeyring!", + "You found a %pShadow Temple&%wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für den %pSchattentempel%w!", - "Vous obtenez un trousseau de&clés du %pTemple de l'Ombre%w!"), + "Vous obtenez le trousseau de&clés du %pTemple de l'Ombre%w!"), GIMESSAGE(RG_BOTTOM_OF_THE_WELL_KEY_RING, ITEM_KEY_SMALL, - "You found a %pBottom of the &Well %wKeyring!", + "You found a %pBottom of the&Well %wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für den %pGrund des Brunnens%w!", - "Vous obtenez un trousseau de&clés du %pPuits%w!"), + "Vous obtenez le trousseau de&clés du %pPuits%w!"), GIMESSAGE(RG_GERUDO_TRAINING_GROUND_KEY_RING, ITEM_KEY_SMALL, - "You found a %yGerudo Training &Grounds %wKeyring!", + "You found a %yGerudo Training&Grounds %wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für die %yGerudo-Trainingsarena%w!", - "Vous obtenez un trousseau de&clés du %yGymnase Gerudo%w!"), + "Vous obtenez le trousseau de&clés du %yGymnase Gerudo%w!"), GIMESSAGE(RG_GANONS_CASTLE_KEY_RING, ITEM_KEY_SMALL, - "You found a %rGanon's Castle &%wKeyring!", + "You found a %rGanon's Castle&%wKeyring!", "Du erhältst ein %rSchlüsselbund%w&für %rGanons Schloß%w!", - "Vous obtenez un trousseau de&clés du %rChâteau de Ganon%w!"), + "Vous obtenez le trousseau de&clés du %rChâteau de Ganon%w!"), GIMESSAGE(RG_TREASURE_GAME_KEY_RING, ITEM_KEY_SMALL, - "You found a %rTreasure Chest Game &%wKeyring!", + "You found a %rTreasure Chest Game&%wKeyring!", "!!!", - "!!!"), + "Vous obtenez le trousseau de&clés du %rJeu de la Chasse au Trésor%w!"), GIMESSAGE(RG_FOREST_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, - "You found the %gForest Temple &%wBoss Key!", + "You found the %gForest Temple&%wBoss Key!", "Du erhältst den %rMaster-Schlüssel%w&für den %gWaldtempel%w!", "Vous obtenez la %rClé d'or %wdu&%gTemple de la Forêt%w!"), GIMESSAGE(RG_FIRE_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, - "You found the %rFire Temple &%wBoss Key!", + "You found the %rFire Temple&%wBoss Key!", "Du erhältst den %rMaster-Schlüssel%w&für den %rFeuertempel%w!", "Vous obtenez la %rClé d'or %wdu&%rTemple du Feu%w!"), GIMESSAGE(RG_WATER_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, - "You found the %bWater Temple &%wBoss Key!", + "You found the %bWater Temple&%wBoss Key!", "Du erhältst den %rMaster-Schlüssel%w&für den %bWassertempel%w!", "Vous obtenez la %rClé d'or %wdu&%bTemple de l'Eau%w!"), GIMESSAGE(RG_SPIRIT_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, - "You found the %ySpirit Temple &%wBoss Key!", + "You found the %ySpirit Temple&%wBoss Key!", "Du erhältst den %rMaster-Schlüssel%w&für den %yGeistertempel%w!", "Vous obtenez la %rClé d'or %wdu&%yTemple de l'Esprit%w!"), GIMESSAGE(RG_SHADOW_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, - "You found the %pShadow Temple &%wBoss Key!", + "You found the %pShadow Temple&%wBoss Key!", "Du erhältst den %rMaster-Schlüssel%w&für den %pSchattentempel%w!", "Vous obtenez la %rClé d'or %wdu&%pTemple de l'Ombre%w!"), GIMESSAGE(RG_GANONS_CASTLE_BOSS_KEY, ITEM_KEY_BOSS, - "You found the %rGanon's Castle &%wBoss Key!", + "You found the %rGanon's Castle&%wBoss Key!", "Du erhältst den %rMaster-Schlüssel%w&für %rGanons Schloß%w!", "Vous obtenez la %rClé d'or %wdu&%rChâteau de Ganon%w!"), GIMESSAGE(RG_DEKU_TREE_MAP, ITEM_DUNGEON_MAP, - "You found the %gDeku Tree &%wMap![[typeHint]]", + "You found the %gDeku Tree&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für den&%gDeku-Baum%w![[typeHint]]", "Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w![[typeHint]]"), GIMESSAGE(RG_DODONGOS_CAVERN_MAP, ITEM_DUNGEON_MAP, - "You found the %rDodongo's Cavern &%wMap![[typeHint]]", + "You found the %rDodongo's Cavern&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für&%rDodongos Höhle%w![[typeHint]]", "Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w![[typeHint]]"), GIMESSAGE(RG_JABU_JABUS_BELLY_MAP, ITEM_DUNGEON_MAP, - "You found the %bJabu Jabu's Belly &%wMap![[typeHint]]", + "You found the %bJabu Jabu's Belly&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für&%bJabu-Jabus Bauch%w![[typeHint]]", - "Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w![[typeHint]]"), + "Vous obtenez la %rCarte %wdu&%bVentre de Jabu-Jabu%w![[typeHint]]"), GIMESSAGE(RG_FOREST_TEMPLE_MAP, ITEM_DUNGEON_MAP, - "You found the %gForest Temple &%wMap![[typeHint]]", + "You found the %gForest Temple&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für den&%gWaldtempel%w![[typeHint]]", - "Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w![[typeHint]]"), + "Vous obtenez la %rCarte %wdu&%gTemple de la Forêt%w![[typeHint]]"), GIMESSAGE(RG_FIRE_TEMPLE_MAP, ITEM_DUNGEON_MAP, - "You found the %rFire Temple &%wMap![[typeHint]]", + "You found the %rFire Temple&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für den&%rFeuertempel%w![[typeHint]]", - "Vous obtenez la %rCarte %wdu &%rTemple du Feu%w![[typeHint]]"), + "Vous obtenez la %rCarte %wdu&%rTemple du Feu%w![[typeHint]]"), GIMESSAGE(RG_WATER_TEMPLE_MAP, ITEM_DUNGEON_MAP, - "You found the %bWater Temple &%wMap![[typeHint]]", + "You found the %bWater Temple&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für den&%bWassertempel%w![[typeHint]]", - "Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w![[typeHint]]"), + "Vous obtenez la %rCarte %wdu&%bTemple de l'Eau%w![[typeHint]]"), GIMESSAGE(RG_SPIRIT_TEMPLE_MAP, ITEM_DUNGEON_MAP, - "You found the %ySpirit Temple &%wMap![[typeHint]]", + "You found the %ySpirit Temple&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für den&%yGeistertempel%w![[typeHint]]", - "Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w![[typeHint]]"), + "Vous obtenez la %rCarte %wdu&%yTemple de l'Esprit%w![[typeHint]]"), GIMESSAGE(RG_SHADOW_TEMPLE_MAP, ITEM_DUNGEON_MAP, - "You found the %pShadow Temple &%wMap![[typeHint]]", + "You found the %pShadow Temple&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für den&%pSchattentempel%w![[typeHint]]", - "Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w![[typeHint]]"), + "Vous obtenez la %rCarte %wdu&%pTemple de l'Ombre%w![[typeHint]]"), GIMESSAGE(RG_BOTTOM_OF_THE_WELL_MAP, ITEM_DUNGEON_MAP, - "You found the %pBottom of the &Well %wMap![[typeHint]]", + "You found the %pBottom of the&Well %wMap![[typeHint]]", "Du erhältst die %rKarte%w für den&%pGrund des Brunnens%w![[typeHint]]", - "Vous obtenez la %rCarte %wdu &%pPuits%w![[typeHint]]"), + "Vous obtenez la %rCarte %wdu&%pPuits%w![[typeHint]]"), GIMESSAGE(RG_ICE_CAVERN_MAP, ITEM_DUNGEON_MAP, - "You found the %cIce Cavern &%wMap![[typeHint]]", + "You found the %cIce Cavern&%wMap![[typeHint]]", "Du erhältst die %rKarte%w für die&%cEishöhle%w![[typeHint]]", - "Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w![[typeHint]]"), + "Vous obtenez la %rCarte %wde&la %cCaverne Polaire%w![[typeHint]]"), GIMESSAGE(RG_DEKU_TREE_COMPASS, ITEM_COMPASS, - "You found the %gDeku Tree &%wCompass!", + "You found the %gDeku Tree&%wCompass!", "Du erhältst den %rKompaß%w für den&%gDeku-Baum%w!", "Vous obtenez la %rBoussole %wde&l'%gArbre Mojo%w!"), GIMESSAGE(RG_DODONGOS_CAVERN_COMPASS, ITEM_COMPASS, - "You found the %rDodongo's Cavern &%wCompass!", + "You found the %rDodongo's Cavern&%wCompass!", "Du erhältst den %rKompaß%w für&%rDodongos Höhle%w!", "Vous obtenez la %rBoussole %wde la&%rCaverne Dodongo%w!"), GIMESSAGE(RG_JABU_JABUS_BELLY_COMPASS, ITEM_COMPASS, - "You found the %bJabu Jabu's Belly &%wCompass!", + "You found the %bJabu Jabu's Belly&%wCompass!", "Du erhältst den %rKompaß%w für den&%bJabu-Jabus Bauch%w!", - "Vous obtenez la %rBoussole %wdu &%bVentre de Jabu-Jabu%w!"), + "Vous obtenez la %rBoussole %wdu&%bVentre de Jabu-Jabu%w!"), GIMESSAGE(RG_FOREST_TEMPLE_COMPASS, ITEM_COMPASS, - "You found the %gForest Temple &%wCompass!", + "You found the %gForest Temple&%wCompass!", "Du erhältst den %rKompaß%w für den&%gWaldtempel%w!", - "Vous obtenez la %rBoussole %wdu &%gTemple de la Forêt%w!"), + "Vous obtenez la %rBoussole %wdu&%gTemple de la Forêt%w!"), GIMESSAGE(RG_FIRE_TEMPLE_COMPASS, ITEM_COMPASS, - "You found the %rFire Temple &%wCompass!", + "You found the %rFire Temple&%wCompass!", "Du erhältst den %rKompaß%w für den&%rFeuertempel%w!", - "Vous obtenez la %rBoussole %wdu &%rTemple du Feu%w!"), + "Vous obtenez la %rBoussole %wdu&%rTemple du Feu%w!"), GIMESSAGE(RG_WATER_TEMPLE_COMPASS, ITEM_COMPASS, - "You found the %bWater Temple &%wCompass!", + "You found the %bWater Temple&%wCompass!", "Du erhältst den %rKompaß%w für den&%bWassertempel%w!", - "Vous obtenez la %rBoussole %wdu &%bTemple de l'Eau%w!"), + "Vous obtenez la %rBoussole %wdu&%bTemple de l'Eau%w!"), GIMESSAGE(RG_SPIRIT_TEMPLE_COMPASS, ITEM_COMPASS, - "You found the %ySpirit Temple &%wCompass!", + "You found the %ySpirit Temple&%wCompass!", "Du erhältst den %rKompaß%w für den&%yGeistertempel%w!", - "Vous obtenez la %rBoussole %wdu &%yTemple de l'Esprit%w!"), + "Vous obtenez la %rBoussole %wdu&%yTemple de l'Esprit%w!"), GIMESSAGE(RG_SHADOW_TEMPLE_COMPASS, ITEM_COMPASS, - "You found the %pShadow Temple &%wCompass!", + "You found the %pShadow Temple&%wCompass!", "Du erhältst den %rKompaß%w für den&%pSchattentempel%w!", - "Vous obtenez la %rBoussole %wdu &%pTemple de l'Ombre%w!"), + "Vous obtenez la %rBoussole %wdu&%pTemple de l'Ombre%w!"), GIMESSAGE(RG_BOTTOM_OF_THE_WELL_COMPASS, ITEM_COMPASS, - "You found the %pBottom of the &Well %wCompass!", + "You found the %pBottom of the&Well %wCompass!", "Du erhältst den %rKompaß%w für den&%pGrund des Brunnens%w!", - "Vous obtenez la %rBoussole %wdu &%pPuits%w!"), + "Vous obtenez la %rBoussole %wdu&%pPuits%w!"), GIMESSAGE(RG_ICE_CAVERN_COMPASS, ITEM_COMPASS, - "You found the %cIce Cavern &%wCompass!", + "You found the %cIce Cavern&%wCompass!", "Du erhältst den %rKompaß%w für die&%cEishöhle%w!", - "Vous obtenez la %rBoussole %wde &la %cCaverne Polaire%w!"), + "Vous obtenez la %rBoussole %wde&la %cCaverne Polaire%w!"), GIMESSAGE(RG_MAGIC_BEAN_PACK, ITEM_BEAN, "You got a %rPack of Magic Beans%w!&Find a suitable spot for a garden&and plant them. Then, wait for&something fun to happen!", @@ -3688,7 +3734,7 @@ void Randomizer::CreateCustomMessages() { "Vous obtenez l'âme de %bBarinade%w!"), GIMESSAGE_NO_GERMAN(RG_PHANTOM_GANON_SOUL, ITEM_BIG_POE, "You found the soul for %gPhantom&Ganon%w!", - "Vous obtenez l'âme de %gGanon Spectral%w!"), + "Vous obtenez l'âme de %gGanon&Spectral%w!"), GIMESSAGE_NO_GERMAN(RG_VOLVAGIA_SOUL, ITEM_BIG_POE, "You found the soul for %rVolvagia%w!", "Vous obtenez l'âme de %rVulcania%w!"), @@ -3697,10 +3743,10 @@ void Randomizer::CreateCustomMessages() { "Vous obtenez l'âme de %bMorpha%w!"), GIMESSAGE_NO_GERMAN(RG_BONGO_BONGO_SOUL, ITEM_BIG_POE, "You found the soul for %pBongo&Bongo%w!", - "Vous obtenez l'âme de %pBongo Bongo%w!"), + "Vous obtenez l'âme de %pBongo&Bongo%w!"), GIMESSAGE_NO_GERMAN(RG_TWINROVA_SOUL, ITEM_BIG_POE, "You found the soul for %yTwinrova%w!", - "Vous obtenez l'âme du %yDuo Maléfique%w!"), + "Vous obtenez l'âme du %yDuo&Maléfique%w!"), GIMESSAGE_NO_GERMAN(RG_GANON_SOUL, ITEM_BIG_POE, "You found the soul for %cGanon%w!", "Vous obtenez l'âme de %cGanon%w!"), @@ -3708,68 +3754,68 @@ void Randomizer::CreateCustomMessages() { GIMESSAGE(RG_OCARINA_A_BUTTON, ITEM_OCARINA_TIME, "You got the %b\x9f%r button for the&Ocarina%w! You can now use it&while playing songs!", "Der %b\x9f%r Knopf%w!&Du kannst ihn nun zum Spielen&von Liedern auf der %rOkarina%w&verwenden!", - "Vous trouvez la %rtouche %b\x9f%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), + "Vous obtenez la %rtouche %b\x9f%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), GIMESSAGE(RG_OCARINA_C_LEFT_BUTTON, ITEM_OCARINA_TIME, "You got the %y\xa7%r button for the&Ocarina%w! You can now use it&while playing songs!", "Der %y\xa7%r Knopf%w!&Du kannst ihn nun zum Spielen&von Liedern auf der %rOkarina%w&verwenden!", - "Vous trouvez la %rtouche %y\xa7%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), + "Vous obtenez la %rtouche %y\xa7%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), GIMESSAGE(RG_OCARINA_C_RIGHT_BUTTON, ITEM_OCARINA_TIME, "You got the %y\xa8%r button for the&Ocarina%w! You can now use it&while playing songs!", "Der %y\xa8%r Knopf%w!&Du kannst ihn nun zum Spielen&von Liedern auf der %rOkarina%w&verwenden!", - "Vous trouvez la %rtouche %y\xa8%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), + "Vous obtenez la %rtouche %y\xa8%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), GIMESSAGE(RG_OCARINA_C_UP_BUTTON, ITEM_OCARINA_TIME, "You got the %y\xa5%r button for the&Ocarina%w! You can now use it&while playing songs!", "Der %y\xa5%r Knopf%w!&Du kannst ihn nun zum Spielen&von Liedern auf der %rOkarina%w&verwenden!", - "Vous trouvez la %rtouche %y\xa5%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), + "Vous obtenez la %rtouche %y\xa5%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), GIMESSAGE(RG_OCARINA_C_DOWN_BUTTON, ITEM_OCARINA_TIME, "You got the %y\xa6%r button for the&Ocarina%w! You can now use it&while playing songs!", "Der %y\xa6%r Knopf%w!&Du kannst ihn nun zum Spielen&von Liedern auf der %rOkarina%w&verwenden!", - "Vous trouvez la %rtouche %y\xa6%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), + "Vous obtenez la %rtouche %y\xa6%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), GIMESSAGE_NO_GERMAN(RG_BRONZE_SCALE, ITEM_SCALE_SILVER, "You got the %rBronze Scale%w!&The power of buoyancy is yours!", - "Vous avez obtenu l'%rÉcaille de Bronze%w!&Le pouvoir de la flottabilité est à vous!"), + "Vous obtenez l'%rÉcaille de Bronze%w!&Le pouvoir de la flottabilité est&à vous!"), GIMESSAGE_NO_GERMAN(RG_FISHING_POLE, ITEM_FISHING_POLE, "You found a lost %rFishing Pole%w!&Time to hit the pond!", - "Vous avez trouvé une %rCanne à pêche%w perdue!&Il est temps d'aller à l'étang!"), + "Vous obtenez une %rCanne à pêche%w&perdue!&Il est temps d'aller à %gl'étang%w!"), GIMESSAGE_NO_GERMAN(RG_BOMBCHU_BAG, ITEM_BOMBCHU, "You found the %rBombchu Bag%w!", - "!!!"), + "Vous obtenez un %rSac de Missiles&Teigneux%w!"), GIMESSAGE_NO_GERMAN(RG_BOMB_BAG_INF, ITEM_BOMB_BAG_40, "You got an %rInfinite Bomb Bag%w!&Now you have %yinfinite bombs%w!", - "Vous avez obtenu un %rSac à bombes à l'infini%w!&Vous avez maintenant des %ybombes à l'infini%w!"), + "Vous obtenez un %rSac de Bombes&sans fond%w!&Vous avez maintenant des %ybombes&en quantité illimitée%w!"), GIMESSAGE_NO_GERMAN(RG_QUIVER_INF, ITEM_QUIVER_50, "You got an %rInfinite Quiver%w!&Now you have %yinfinite arrows%w!", - "Vous avez obtenu un %rCarquois à l'infini%w!&Vous avez maintenant des %yflèches à l'infini%w!"), + "Vous obtenez un %rCarquois Infini%w!&Vous avez maintenant des %yflèches&de manière illimitée%w!"), GIMESSAGE_NO_GERMAN(RG_BULLET_BAG_INF, ITEM_BULLET_BAG_50, "You got an %rInfinite Bullet Bag%w!&Now you have %yinfinite&slingshot seeds%w!", - "Vous avez obtenu un %rSac de Graine à l'infini%w!&Vous avez maintenant des %ygraines de lance-pierres à l'infini%w!"), + "Vous obtenez un %rSac de Graines&sans fond%w!&Vous avez maintenant des %ygraines&de lance-pierres à l'infini%w!"), GIMESSAGE_NO_GERMAN(RG_STICK_UPGRADE_INF, ITEM_STICK, "You now have %yinfinite%w %rDeku Sticks%w!", - "Vous avez maintenant des %yBâtons Mojo à l'infini%w!"), + "Vous avez maintenant des %yBâtons&Mojo de manière illimitée%w!"), GIMESSAGE_NO_GERMAN(RG_NUT_UPGRADE_INF, ITEM_NUT, "You now have %yinfinite%w %rDeku Nuts%w!", - "Vous avez maintenant des %yNoix Mojo à l'infini%w!"), + "Vous avez maintenant des %yNoix&Mojo de manière illimitée%w!"), GIMESSAGE_NO_GERMAN(RG_MAGIC_INF, ITEM_MAGIC_LARGE, "You now have %yinfinite%w %rMagic%w!", - "Vous avez maintenant de la %ymagie à l'infini%w!"), + "Vous avez maintenant une quantité&de %ymagie illimitée%w!"), GIMESSAGE_NO_GERMAN(RG_BOMBCHU_INF, ITEM_BOMBCHU, "You now have %yinfinite%w %rBombchus%w!", - "Vous avez maintenant des %Missiles Teigneux à l'infini%w!"), + "Vous avez maintenant des %yMissiles&Teigneux en quantité illimités%w!"), GIMESSAGE_NO_GERMAN(RG_WALLET_INF, ITEM_WALLET_GIANT, "You now have %yinfinite%w %rmoney%w!", - "Vous avez maintenant de l'%yargent à l'infini%w!"), + "Vous avez maintenant des %yRubis en& quantité illimitée%w!"), GIMESSAGE_NO_GERMAN(RG_SKELETON_KEY, ITEM_KEY_SMALL, "You found the %rSkeleton Key%w!", "Vous avez trouvé la %rClé Squelette%w!"), GIMESSAGE_NO_GERMAN(RG_DEKU_STICK_BAG, ITEM_STICK, "You found the %rDeku Stick Bag%w!&You can now hold deku sticks!", - "Vous avez trouvé le %rSac de Bâtons Mojo%w!&Vous pouvez maintenant porter des Bâtons Mojo!"), + "Vous avez trouvé le %rSac de Bâtons&Mojo%w!&Vous pouvez maintenant porter des&Bâtons Mojo!"), GIMESSAGE_NO_GERMAN(RG_DEKU_NUT_BAG, ITEM_NUT, "You found the %rDeku Nut Bag%w!&You can now hold deku nuts!", - "Vous avez trouvé le %rSac de Noix Mojo%w!&Vous pouvez maintenant porter des Noix Mojo!"), + "Vous avez trouvé le %rSac de Noix& Mojo%w!&Vous pouvez maintenant porter des&Noix Mojo!"), }}; - CreateGetItemMessages(&getItemMessages); + CreateGetItemMessages(getItemMessages); CreateRupeeMessages(); CreateTriforcePieceMessages(); CreateNaviRandoMessages(); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index f653e1e2c..6e5b70ec3 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -1013,7 +1013,6 @@ typedef enum { RR_GANONS_CASTLE_SHADOW_TRIAL, RR_GANONS_CASTLE_SPIRIT_TRIAL, RR_GANONS_CASTLE_LIGHT_TRIAL, - RR_GANONS_CASTLE_TOWER, RR_GANONS_CASTLE_MQ_LOBBY, RR_GANONS_CASTLE_MQ_MAIN, @@ -1042,6 +1041,7 @@ typedef enum { RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_BOULDER_ROOM_BACK, RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_FINAL_ROOM, + RR_GANONS_TOWER_ENTRYWAY, RR_GANONS_TOWER_FLOOR_1, RR_GANONS_TOWER_FLOOR_2, RR_GANONS_TOWER_FLOOR_3, @@ -3393,6 +3393,8 @@ typedef enum { RG_HOOKSHOT, RG_LONGSHOT, RG_SCARECROW, + + //Overworld keys RG_GUARD_HOUSE_KEY, RG_MARKET_BAZAAR_KEY, RG_MARKET_POTION_SHOP_KEY, @@ -5450,7 +5452,6 @@ typedef enum { RO_GANON_BOSS_KEY_LACS_DUNGEONS, RO_GANON_BOSS_KEY_LACS_TOKENS, RO_GANON_BOSS_KEY_KAK_TOKENS, - RO_GANON_BOSS_KEY_TRIFORCE_HUNT, } RandoOptionGanonsBossKey; typedef enum { diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index cc5307c4a..971686d29 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -7,6 +7,7 @@ #include "soh/SaveManager.h" #include "soh/ResourceManagerHelpers.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "dungeon.h" #include "location_access.h" @@ -482,9 +483,13 @@ void CheckTrackerLoadGame(int32_t fileNum) { for (int i = RCAREA_KOKIRI_FOREST; i < RCAREA_INVALID; i++) { if (!IsAreaSpoiled(static_cast(i)) && (RandomizerCheckObjects::AreaIsOverworld(static_cast(i)) || !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_NONE || - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SELECTION || + (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SELECTION && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(static_cast(RSK_MQ_DEKU_TREE + (i - RCAREA_DEKU_TREE))) != RO_MQ_SET_RANDOM) || + (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_SET) == RO_GENERIC_ON && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(static_cast(RSK_MQ_DEKU_TREE + (i - RCAREA_DEKU_TREE))) != RO_MQ_SET_RANDOM) || (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SET_NUMBER && - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12))) { + (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12 || + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 0)))) { SetAreaSpoiled(static_cast(i)); } } @@ -821,6 +826,7 @@ void LoadFile() { SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0); UpdateAllOrdering(); UpdateAllAreas(); + RegionTable_Init(); } void Teardown() { @@ -844,6 +850,37 @@ void SetAreaSpoiled(RandomizerCheckArea rcArea) { } void CheckTrackerWindow::DrawElement() { + Color_Background = CVarGetColor(CVAR_TRACKER_CHECK("BgColor.Value"), Color_Bg_Default); + Color_Area_Incomplete_Main = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.MainColor.Value"), Color_Main_Default); + Color_Area_Incomplete_Extra = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor.Value"), Color_Area_Incomplete_Extra_Default); + Color_Area_Complete_Main = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.MainColor.Value"), Color_Main_Default); + Color_Area_Complete_Extra = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.ExtraColor.Value"), Color_Area_Complete_Extra_Default); + Color_Unchecked_Main = CVarGetColor(CVAR_TRACKER_CHECK("Unchecked.MainColor.Value"), Color_Main_Default); + Color_Unchecked_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Unchecked.ExtraColor.Value"), Color_Unchecked_Extra_Default); + Color_Skipped_Main = CVarGetColor(CVAR_TRACKER_CHECK("Skipped.MainColor.Value"), Color_Main_Default); + Color_Skipped_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Skipped.ExtraColor.Value"), Color_Skipped_Extra_Default); + Color_Seen_Main = CVarGetColor(CVAR_TRACKER_CHECK("Seen.MainColor.Value"), Color_Main_Default); + Color_Seen_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Seen.ExtraColor.Value"), Color_Seen_Extra_Default); + Color_Hinted_Main = CVarGetColor(CVAR_TRACKER_CHECK("Hinted.MainColor.Value"), Color_Main_Default); + Color_Hinted_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Hinted.ExtraColor.Value"), Color_Hinted_Extra_Default); + Color_Collected_Main = CVarGetColor(CVAR_TRACKER_CHECK("Collected.MainColor.Value"), Color_Main_Default); + Color_Collected_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Collected.ExtraColor.Value"), Color_Collected_Extra_Default); + Color_Scummed_Main = CVarGetColor(CVAR_TRACKER_CHECK("Scummed.MainColor.Value"), Color_Main_Default); + Color_Scummed_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Scummed.ExtraColor.Value"), Color_Scummed_Extra_Default); + Color_Saved_Main = CVarGetColor(CVAR_TRACKER_CHECK("Saved.MainColor.Value"), Color_Main_Default); + Color_Saved_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Saved.ExtraColor.Value"), Color_Saved_Extra_Default); + hideUnchecked = CVarGetInteger(CVAR_TRACKER_CHECK("Unchecked.Hide"), 0); + hideScummed = CVarGetInteger(CVAR_TRACKER_CHECK("Scummed.Hide"), 0); + hideSeen = CVarGetInteger(CVAR_TRACKER_CHECK("Seen.Hide"), 0); + hideSkipped = CVarGetInteger(CVAR_TRACKER_CHECK("Skipped.Hide"), 0); + hideSaved = CVarGetInteger(CVAR_TRACKER_CHECK("Saved.Hide"), 0); + hideCollected = CVarGetInteger(CVAR_TRACKER_CHECK("Collected.Hide"), 0); + showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0); + mystery = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0); + showLogicTooltip = CVarGetInteger(CVAR_TRACKER_CHECK("ShowLogic"), 0); + + hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 1); + alwaysShowGS = CVarGetInteger(CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), 0); if (CVarGetInteger(CVAR_TRACKER_CHECK("WindowType"), TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { if (CVarGetInteger(CVAR_TRACKER_CHECK("ShowOnlyPaused"), 0) && (gPlayState == nullptr || gPlayState->pauseCtx.state == 0)) { return; @@ -863,7 +900,6 @@ void CheckTrackerWindow::DrawElement() { } ImGui::SetNextWindowSize(ImVec2(400, 540), ImGuiCond_FirstUseEver); - BeginFloatWindows("Check Tracker", mIsVisible, ImGuiWindowFlags_NoScrollbar); if (!GameInteractor::IsSaveLoaded() || !initialized) { @@ -892,32 +928,33 @@ void CheckTrackerWindow::DrawElement() { ImGui::TableNextRow(0, headerHeight); ImGui::TableNextColumn(); - UIWidgets::EnhancementCheckbox( - "Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), false, - "When active, items will show hidden checks by default when updated to this state."); + UIWidgets::CVarCheckbox( + "Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions({{ .tooltip = "When active, items will show hidden checks by default when updated to this state." }}) + .Color(THEME_COLOR)); UIWidgets::PaddedSeparator(); - if (ImGui::Button("Expand All")) { + if (UIWidgets::Button("Expand All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { optCollapseAll = false; optExpandAll = true; doAreaScroll = true; } ImGui::SameLine(); - if (ImGui::Button("Collapse All")) { + if (UIWidgets::Button("Collapse All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { optExpandAll = false; optCollapseAll = true; } ImGui::SameLine(); - if (ImGui::Button("Clear")) { + if (UIWidgets::Button("Clear", UIWidgets::ButtonOptions({{ .tooltip = "Clear the search field" }}).Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { checkSearch.Clear(); UpdateFilters(); doAreaScroll = true; } - UIWidgets::Tooltip("Clear the search field"); + UIWidgets::PushStyleCombobox(THEME_COLOR); if (checkSearch.Draw()) { UpdateFilters(); } + UIWidgets::PopStyleCombobox(); - UIWidgets::PaddedSeparator(); + ImGui::Separator(); ImGui::Text("Total Checks: %d / %d", totalChecksGotten, totalChecks); @@ -946,16 +983,17 @@ void CheckTrackerWindow::DrawElement() { bool doingCollapseOrExpand = optExpandAll || optCollapseAll; bool isThisAreaSpoiled; RandomizerCheckArea lastArea = RCAREA_INVALID; - Color_RGBA8 areaCompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.MainColor"), Color_Main_Default); - Color_RGBA8 areaIncompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.MainColor"), Color_Main_Default); - Color_RGBA8 extraCompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.ExtraColor"), Color_Area_Complete_Extra_Default); - Color_RGBA8 extraIncompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor"), Color_Area_Incomplete_Extra_Default); + Color_RGBA8 areaCompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.MainColor.Value"), Color_Main_Default); + Color_RGBA8 areaIncompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.MainColor.Value"), Color_Main_Default); + Color_RGBA8 extraCompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.ExtraColor.Value"), Color_Area_Complete_Extra_Default); + Color_RGBA8 extraIncompleteColor = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor.Value"), Color_Area_Incomplete_Extra_Default); Color_RGBA8 mainColor; Color_RGBA8 extraColor; std::string stemp; bool shouldHideFilteredAreas = CVarGetInteger(CVAR_TRACKER_CHECK("HideFilteredAreas"), 1); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)); for (auto& [rcArea, checks] : checksByArea) { RandomizerCheckArea thisArea = currentArea; @@ -1039,6 +1077,7 @@ void CheckTrackerWindow::DrawElement() { } } } + ImGui::PopStyleVar(); ImGui::EndTable(); //Checks Lead-out ImGui::EndTable(); //Quick Options Lead-out @@ -1099,7 +1138,7 @@ void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flag windowFlags |= ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar; - if (!CVarGetInteger(CVAR_TRACKER_CHECK("Draggable"), 0)) { + if (!CVarGetInteger(CVAR_TRACKER_CHECK("Draggable"), 1)) { windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; } } @@ -1468,54 +1507,54 @@ void DrawLocation(RandomizerCheck rc) { } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO - ? Color_Collected_Extra_Default + ? Color_Collected_Extra : Color_Collected_Main; - extraColor = Color_Collected_Extra_Default; + extraColor = Color_Collected_Extra; } else if (status == RCSHOW_SAVED) { if (!showHidden && hideSaved) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO - ? Color_Saved_Extra_Default + ? Color_Saved_Extra : Color_Saved_Main; - extraColor = Color_Saved_Extra_Default; + extraColor = Color_Saved_Extra; } else if (skipped) { if (!showHidden && hideSkipped) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO - ? Color_Skipped_Extra_Default + ? Color_Skipped_Extra : Color_Skipped_Main; - extraColor = Color_Skipped_Extra_Default; + extraColor = Color_Skipped_Extra; } else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) { if (!showHidden && hideSeen) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO - ? Color_Seen_Extra_Default + ? Color_Seen_Extra : Color_Seen_Main; - extraColor = Color_Seen_Extra_Default; + extraColor = Color_Seen_Extra; } else if (status == RCSHOW_SCUMMED) { if (!showHidden && hideScummed) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO - ? Color_Scummed_Extra_Default + ? Color_Scummed_Extra : Color_Scummed_Main; - extraColor = Color_Scummed_Extra_Default; + extraColor = Color_Scummed_Extra; } else if (status == RCSHOW_UNCHECKED) { if (!showHidden && hideUnchecked) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO - ? Color_Unchecked_Extra_Default + ? Color_Unchecked_Extra : Color_Unchecked_Main; - extraColor = Color_Unchecked_Extra_Default; + extraColor = Color_Unchecked_Extra; } //Main Text @@ -1530,8 +1569,10 @@ void DrawLocation(RandomizerCheck rc) { } // Draw button - for Skipped/Seen/Scummed/Unchecked only + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {4.0f, 3.0f}); + float sz = ImGui::GetFrameHeight(); if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) { - if (UIWidgets::StateButton(std::to_string(rc).c_str(), skipped ? ICON_FA_PLUS : ICON_FA_TIMES)) { + if (UIWidgets::StateButton(std::to_string(rc).c_str(), skipped ? ICON_FA_PLUS : ICON_FA_TIMES, ImVec2(sz, sz), UIWidgets::ButtonOptions().Color(THEME_COLOR))) { if (skipped) { OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false); areaChecksGotten[loc->GetArea()]--; @@ -1546,8 +1587,10 @@ void DrawLocation(RandomizerCheck rc) { SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); } } else { - ImGui::Dummy(ImVec2(20.0f, 10.0f)); + ImGui::Dummy(ImVec2(sz, sz)); } + ImGui::PopStyleVar(); + ImGui::SameLine(); //Draw @@ -1621,7 +1664,7 @@ void DrawLocation(RandomizerCheck rc) { if (locationInRegion.GetLocation() == rc) { std::string conditionStr = locationInRegion.GetConditionStr(); if (conditionStr != "true") { - UIWidgets::InsertHelpHoverText(conditionStr); + UIWidgets::Tooltip(conditionStr.c_str()); } return; } @@ -1645,7 +1688,7 @@ int hue = 0; void RainbowTick() { float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat(CVAR_COSMETIC("RainbowSpeed"), 0.6f)); for (auto& cvar : rainbowCVars) { - if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) { + if (CVarGetInteger((cvar + ".Rainbow").c_str(), 0) == 0) { continue; } @@ -1655,7 +1698,7 @@ void RainbowTick() { newColor.b = sin(freqHue + (4 * M_PI / 3)) * 127 + 128; newColor.a = 255; - CVarSetColor(cvar.c_str(), newColor); + CVarSetColor((cvar + ".Value").c_str(), newColor); } hue++; @@ -1664,46 +1707,33 @@ void RainbowTick() { void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, const char* cvarExtraName, Color_RGBA8& main_color, Color_RGBA8& extra_color, Color_RGBA8& main_default_color, - Color_RGBA8& extra_default_color, const char* cvarHideName, const char* tooltip) { + Color_RGBA8& extra_default_color, const char* cvarHideName, const char* tooltip, UIWidgets::Colors theme) { Color_RGBA8 cvarMainColor = CVarGetColor(cvarMainName, main_default_color); Color_RGBA8 cvarExtraColor = CVarGetColor(cvarExtraName, extra_default_color); main_color = cvarMainColor; extra_color = cvarExtraColor; + UIWidgets::PushStyleCombobox(theme); if (ImGui::CollapsingHeader(text)) { if (*cvarHideName != '\0') { std::string label = cvarHideName; label += "##Hidden"; ImGui::PushID(label.c_str()); - UIWidgets::EnhancementCheckbox("Hidden", cvarHideName, false, - "When active, checks will hide by default when updated to this state. Can " - "be overriden with the \"Show Hidden Items\" option."); + UIWidgets::CVarCheckbox("Hidden", cvarHideName, + UIWidgets::CheckboxOptions({{ .tooltip = "When active, checks will hide by default when updated to this state. Can " + "be overriden with the \"Show Hidden Items\" option." }}).Color(theme)); ImGui::PopID(); } - if (ImGui::BeginTable(text, 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV | ImGuiTableFlags_Hideable)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - if (UIWidgets::EnhancementColor("Check", cvarMainName, - ImVec4(main_color.r, main_color.g, main_color.b, main_color.a), - ImVec4(main_default_color.r, main_default_color.g, main_default_color.b, main_default_color.a))) - { - main_color = CVarGetColor(cvarMainName, main_default_color); - }; - ImGui::PopItemWidth(); + std::string mainLabel = "Name##" + std::string(cvarMainName); + if (UIWidgets::CVarColorPicker(mainLabel.c_str(), cvarMainName, main_default_color, false, + UIWidgets::ColorPickerRandomButton | UIWidgets::ColorPickerResetButton | UIWidgets::ColorPickerRainbowCheck, theme)) { + main_color = CVarGetColor(cvarMainName, main_default_color); + } - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - if (UIWidgets::EnhancementColor("Details", cvarExtraName, - ImVec4(extra_color.r, extra_color.g, extra_color.b, extra_color.a), - ImVec4(extra_default_color.r, extra_default_color.g, extra_default_color.b, extra_default_color.a))) - { - extra_color = CVarGetColor(cvarExtraName, extra_default_color); - } - ImGui::PopItemWidth(); - - ImGui::EndTable(); + std::string extraLabel = "Details##" + std::string(cvarExtraName); + if (UIWidgets::CVarColorPicker(extraLabel.c_str(), cvarExtraName, extra_default_color, false, + UIWidgets::ColorPickerRandomButton | UIWidgets::ColorPickerResetButton | UIWidgets::ColorPickerRainbowCheck, theme)) { + extra_color = CVarGetColor(cvarExtraName, extra_default_color); } } if (tooltip != NULL && strlen(tooltip) != 0) { @@ -1711,6 +1741,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, ImGui::Text(" ?"); UIWidgets::Tooltip(tooltip); } + UIWidgets::PopStyleCombobox(); } void CheckTrackerWindow::Draw() { @@ -1722,72 +1753,81 @@ void CheckTrackerWindow::Draw() { SyncVisibilityConsoleVariable(); } -static const char* windowType[] = { "Floating", "Window" }; -static const char* displayType[] = { "Always", "Combo Button Hold" }; -static const char* buttonStrings[] = { "A Button", "B Button", "C-Up", "C-Down", "C-Left", "C-Right", "L Button", - "Z Button", "R Button", "Start", "D-Up", "D-Down", "D-Left", "D-Right" }; +static std::unordered_map windowType = {{ TRACKER_WINDOW_FLOATING, "Floating" }, { TRACKER_WINDOW_WINDOW, "Window" }}; +static std::unordered_map displayType = {{ 0, "Always" }, { 1, "Combo Button Hold" }}; +static std::unordered_map buttonStrings = { + { TRACKER_COMBO_BUTTON_A, "A Button" }, { TRACKER_COMBO_BUTTON_B, "B Button" }, { TRACKER_COMBO_BUTTON_C_UP, "C-Up" }, + { TRACKER_COMBO_BUTTON_C_DOWN, "C-Down" }, { TRACKER_COMBO_BUTTON_C_LEFT, "C-Left" }, { TRACKER_COMBO_BUTTON_C_RIGHT, "C-Right" }, + { TRACKER_COMBO_BUTTON_L, "L Button" }, { TRACKER_COMBO_BUTTON_Z, "Z Button" }, { TRACKER_COMBO_BUTTON_R, "R Button" }, + { TRACKER_COMBO_BUTTON_START, "Start" }, { TRACKER_COMBO_BUTTON_D_UP, "D-Up" }, { TRACKER_COMBO_BUTTON_D_DOWN, "D-Down" }, + { TRACKER_COMBO_BUTTON_D_LEFT, "D-Left" }, { TRACKER_COMBO_BUTTON_D_RIGHT, "D-Right" }}; void CheckTrackerSettingsWindow::DrawElement() { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); - ImGui::BeginTable("CheckTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV); - ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableHeadersRow(); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - if (UIWidgets::EnhancementColor("BG Color", CVAR_TRACKER_CHECK("BgColor"), - ImVec4(Color_Background.r, Color_Background.g, Color_Background.b, Color_Background.a), - ImVec4(Color_Bg_Default.r, Color_Bg_Default.g, Color_Bg_Default.b, Color_Bg_Default.a), - false, true)) - { - Color_Background = CVarGetColor(CVAR_TRACKER_CHECK("BgColor"), Color_Bg_Default); - } - ImGui::PopItemWidth(); + if (ImGui::BeginTable("CheckTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { + ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + UIWidgets::CVarColorPicker("BG Color", CVAR_TRACKER_CHECK("BgColor"), Color_Bg_Default, true, + UIWidgets::ColorPickerResetButton | UIWidgets::ColorPickerRandomButton, THEME_COLOR); + ImGui::PopItemWidth(); - UIWidgets::LabeledRightAlignedEnhancementCombobox("Window Type", CVAR_TRACKER_CHECK("WindowType"), windowType, TRACKER_WINDOW_WINDOW); - if (CVarGetInteger(CVAR_TRACKER_CHECK("WindowType"), TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { - UIWidgets::EnhancementCheckbox("Enable Dragging", CVAR_TRACKER_CHECK("Draggable")); - UIWidgets::EnhancementCheckbox("Only enable while paused", CVAR_TRACKER_CHECK("ShowOnlyPaused")); - UIWidgets::LabeledRightAlignedEnhancementCombobox("Display Mode", CVAR_TRACKER_CHECK("DisplayType"), displayType, 0); - if (CVarGetInteger(CVAR_TRACKER_CHECK("DisplayType"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_COMBO_BUTTON) { - UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 1", CVAR_TRACKER_CHECK("ComboButton1"), buttonStrings, TRACKER_COMBO_BUTTON_L); - UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 2", CVAR_TRACKER_CHECK("ComboButton2"), buttonStrings, TRACKER_COMBO_BUTTON_R); + UIWidgets::CVarCombobox("Window Type", CVAR_TRACKER_CHECK("WindowType"), windowType, + UIWidgets::ComboboxOptions().LabelPosition(UIWidgets::LabelPositions::Far).ComponentAlignment(UIWidgets::ComponentAlignments::Right) + .Color(THEME_COLOR).DefaultIndex(TRACKER_WINDOW_WINDOW)); + + if (CVarGetInteger(CVAR_TRACKER_CHECK("WindowType"), TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { + UIWidgets::CVarCheckbox("Enable Dragging", CVAR_TRACKER_CHECK("Draggable"), UIWidgets::CheckboxOptions().Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Only enable while paused", CVAR_TRACKER_CHECK("ShowOnlyPaused"), UIWidgets::CheckboxOptions().Color(THEME_COLOR)); + UIWidgets::CVarCombobox("Display Mode", CVAR_TRACKER_CHECK("DisplayType"), displayType, + UIWidgets::ComboboxOptions().LabelPosition(UIWidgets::LabelPositions::Far).ComponentAlignment(UIWidgets::ComponentAlignments::Right) + .Color(THEME_COLOR).DefaultIndex(0)); + if (CVarGetInteger(CVAR_TRACKER_CHECK("DisplayType"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_COMBO_BUTTON) { + UIWidgets::CVarCombobox("Combo Button 1", CVAR_TRACKER_CHECK("ComboButton1"), buttonStrings, + UIWidgets::ComboboxOptions().LabelPosition(UIWidgets::LabelPositions::Far).ComponentAlignment(UIWidgets::ComponentAlignments::Right) + .Color(THEME_COLOR).DefaultIndex(TRACKER_COMBO_BUTTON_L)); + UIWidgets::CVarCombobox("Combo Button 2", CVAR_TRACKER_CHECK("ComboButton2"), buttonStrings, + UIWidgets::ComboboxOptions().LabelPosition(UIWidgets::LabelPositions::Far).ComponentAlignment(UIWidgets::ComponentAlignments::Right) + .Color(THEME_COLOR).DefaultIndex(TRACKER_COMBO_BUTTON_L)); + } } + UIWidgets::CVarCheckbox("Vanilla/MQ Dungeon Spoilers", CVAR_TRACKER_CHECK("MQSpoilers"), UIWidgets::CheckboxOptions() + .Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.").Color(THEME_COLOR)); + if (UIWidgets::CVarCheckbox("Hide unshuffled shop item checks", CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), + UIWidgets::CheckboxOptions().Tooltip("If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles.").Color(THEME_COLOR))) { + hideShopUnshuffledChecks = !hideShopUnshuffledChecks; + UpdateFilters(); + } + if (UIWidgets::CVarCheckbox("Always show gold skulltulas", CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), + UIWidgets::CheckboxOptions().Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings.").Color(THEME_COLOR))) { + alwaysShowGS = !alwaysShowGS; + UpdateFilters(); + } + UIWidgets::CVarCheckbox("Show Logic", CVAR_TRACKER_CHECK("ShowLogic"), + UIWidgets::CheckboxOptions().Tooltip("If enabled, will show a check's logic when hovering over it.").Color(THEME_COLOR)); + + // Filtering settings + UIWidgets::PaddedSeparator(); + UIWidgets::CVarCheckbox("Filter Empty Areas", CVAR_TRACKER_CHECK("HideFilteredAreas"), + UIWidgets::CheckboxOptions().Tooltip("If enabled, will hide area headers that have no locations matching filter").Color(THEME_COLOR).DefaultValue(true)); + + ImGui::TableNextColumn(); + + CheckTracker::ImGuiDrawTwoColorPickerSection("Area Incomplete", CVAR_TRACKER_CHECK("AreaIncomplete.MainColor"), CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor"), Color_Area_Incomplete_Main, Color_Area_Incomplete_Extra, Color_Main_Default, Color_Area_Incomplete_Extra_Default, CVAR_TRACKER_CHECK("AreaIncomplete.Hide"), "", THEME_COLOR); + CheckTracker::ImGuiDrawTwoColorPickerSection("Area Complete", CVAR_TRACKER_CHECK("AreaComplete.MainColor"), CVAR_TRACKER_CHECK("AreaComplete.ExtraColor"), Color_Area_Complete_Main, Color_Area_Complete_Extra, Color_Main_Default, Color_Area_Complete_Extra_Default, CVAR_TRACKER_CHECK("AreaComplete.Hide"), "", THEME_COLOR); + CheckTracker::ImGuiDrawTwoColorPickerSection("Unchecked", CVAR_TRACKER_CHECK("Unchecked.MainColor"), CVAR_TRACKER_CHECK("Unchecked.ExtraColor"), Color_Unchecked_Main, Color_Unchecked_Extra, Color_Main_Default, Color_Unchecked_Extra_Default, CVAR_TRACKER_CHECK("Unchecked.Hide"), "Checks you have not interacted with at all.", THEME_COLOR); + CheckTracker::ImGuiDrawTwoColorPickerSection("Skipped", CVAR_TRACKER_CHECK("Skipped.MainColor"), CVAR_TRACKER_CHECK("Skipped.ExtraColor"), Color_Skipped_Main, Color_Skipped_Extra, Color_Main_Default, Color_Skipped_Extra_Default, CVAR_TRACKER_CHECK("Skipped.Hide"), "", THEME_COLOR); + CheckTracker::ImGuiDrawTwoColorPickerSection("Seen", CVAR_TRACKER_CHECK("Seen.MainColor"), CVAR_TRACKER_CHECK("Seen.ExtraColor"), Color_Seen_Main, Color_Seen_Extra, Color_Main_Default, Color_Seen_Extra_Default, CVAR_TRACKER_CHECK("Seen.Hide"), "Used for shops. Shows item names for shop slots when walking in, and prices when highlighting them in buy mode.", THEME_COLOR); + CheckTracker::ImGuiDrawTwoColorPickerSection("Scummed", CVAR_TRACKER_CHECK("Scummed.MainColor"), CVAR_TRACKER_CHECK("Scummed.ExtraColor"), Color_Scummed_Main, Color_Scummed_Extra, Color_Main_Default, Color_Scummed_Extra_Default, CVAR_TRACKER_CHECK("Scummed.Hide"), "Checks you collect, but then reload before saving so you no longer have them.", THEME_COLOR); + //CheckTracker::ImGuiDrawTwoColorPickerSection("Hinted (WIP)", CVAR_TRACKER_CHECK("Hinted.MainColor"), CVAR_TRACKER_CHECK("Hinted.ExtraColor"), Color_Hinted_Main, Color_Hinted_Extra, Color_Main_Default, Color_Hinted_Extra_Default, CVAR_TRACKER_CHECK("Hinted.Hide"), "", THEME_COLOR); + CheckTracker::ImGuiDrawTwoColorPickerSection("Collected", CVAR_TRACKER_CHECK("Collected.MainColor"), CVAR_TRACKER_CHECK("Collected.ExtraColor"), Color_Collected_Main, Color_Collected_Extra, Color_Main_Default, Color_Collected_Extra_Default, CVAR_TRACKER_CHECK("Collected.Hide"), "Checks you have collected without saving or reloading yet.", THEME_COLOR); + CheckTracker::ImGuiDrawTwoColorPickerSection("Saved", CVAR_TRACKER_CHECK("Saved.MainColor"), CVAR_TRACKER_CHECK("Saved.ExtraColor"), Color_Saved_Main, Color_Saved_Extra, Color_Main_Default, Color_Saved_Extra_Default, CVAR_TRACKER_CHECK("Saved.Hide"), "Checks that you saved the game while having collected.", THEME_COLOR); + + ImGui::PopStyleVar(1); } - UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", CVAR_TRACKER_CHECK("MQSpoilers")); - UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked."); - if (UIWidgets::EnhancementCheckbox("Hide unshuffled shop item checks", CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), false, "", UIWidgets::CheckboxGraphics::Cross, false)) { - hideShopUnshuffledChecks = !hideShopUnshuffledChecks; - UpdateFilters(); - } - UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles."); - if (UIWidgets::EnhancementCheckbox("Always show gold skulltulas", CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), false, "")) { - alwaysShowGS = !alwaysShowGS; - UpdateFilters(); - } - UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings."); - UIWidgets::EnhancementCheckbox("Show Logic", "gCheckTrackerOptionShowLogic"); - UIWidgets::Tooltip("If enabled, will show a check's logic when hovering over it."); - - // Filtering settings - UIWidgets::PaddedSeparator(); - UIWidgets::EnhancementCheckbox("Filter Empty Areas", CVAR_TRACKER_CHECK("HideFilteredAreas"), false, "", UIWidgets::CheckboxGraphics::Checkmark, true); - UIWidgets::Tooltip("If enabled, will hide area headers that have no locations matching filter"); - - ImGui::TableNextColumn(); - - CheckTracker::ImGuiDrawTwoColorPickerSection("Area Incomplete", CVAR_TRACKER_CHECK("AreaIncomplete.MainColor"), CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor"), Color_Area_Incomplete_Main, Color_Area_Incomplete_Extra, Color_Main_Default, Color_Area_Incomplete_Extra_Default, CVAR_TRACKER_CHECK("AreaIncomplete.Hide"), ""); - CheckTracker::ImGuiDrawTwoColorPickerSection("Area Complete", CVAR_TRACKER_CHECK("AreaComplete.MainColor"), CVAR_TRACKER_CHECK("AreaComplete.ExtraColor"), Color_Area_Complete_Main, Color_Area_Complete_Extra, Color_Main_Default, Color_Area_Complete_Extra_Default, CVAR_TRACKER_CHECK("AreaComplete.Hide"), ""); - CheckTracker::ImGuiDrawTwoColorPickerSection("Unchecked", CVAR_TRACKER_CHECK("Unchecked.MainColor"), CVAR_TRACKER_CHECK("Unchecked.ExtraColor"), Color_Unchecked_Main, Color_Unchecked_Extra, Color_Main_Default, Color_Unchecked_Extra_Default, CVAR_TRACKER_CHECK("Unchecked.Hide"), "Checks you have not interacted with at all."); - CheckTracker::ImGuiDrawTwoColorPickerSection("Skipped", CVAR_TRACKER_CHECK("Skipped.MainColor"), CVAR_TRACKER_CHECK("Skipped.ExtraColor"), Color_Skipped_Main, Color_Skipped_Extra, Color_Main_Default, Color_Skipped_Extra_Default, CVAR_TRACKER_CHECK("Skipped.Hide"), ""); - CheckTracker::ImGuiDrawTwoColorPickerSection("Seen", CVAR_TRACKER_CHECK("Seen.MainColor"), CVAR_TRACKER_CHECK("Seen.ExtraColor"), Color_Seen_Main, Color_Seen_Extra, Color_Main_Default, Color_Seen_Extra_Default, CVAR_TRACKER_CHECK("Seen.Hide"), "Used for shops. Shows item names for shop slots when walking in, and prices when highlighting them in buy mode."); - CheckTracker::ImGuiDrawTwoColorPickerSection("Scummed", CVAR_TRACKER_CHECK("Scummed.MainColor"), CVAR_TRACKER_CHECK("Scummed.ExtraColor"), Color_Scummed_Main, Color_Scummed_Extra, Color_Main_Default, Color_Scummed_Extra_Default, CVAR_TRACKER_CHECK("Scummed.Hide"), "Checks you collect, but then reload before saving so you no longer have them."); - //CheckTracker::ImGuiDrawTwoColorPickerSection("Hinted (WIP)", CVAR_TRACKER_CHECK("Hinted.MainColor"), CVAR_TRACKER_CHECK("Hinted.ExtraColor"), Color_Hinted_Main, Color_Hinted_Extra, Color_Main_Default, Color_Hinted_Extra_Default, CVAR_TRACKER_CHECK("Hinted.Hide"), ""); - CheckTracker::ImGuiDrawTwoColorPickerSection("Collected", CVAR_TRACKER_CHECK("Collected.MainColor"), CVAR_TRACKER_CHECK("Collected.ExtraColor"), Color_Collected_Main, Color_Collected_Extra, Color_Main_Default, Color_Collected_Extra_Default, CVAR_TRACKER_CHECK("Collected.Hide"), "Checks you have collected without saving or reloading yet."); - CheckTracker::ImGuiDrawTwoColorPickerSection("Saved", CVAR_TRACKER_CHECK("Saved.MainColor"), CVAR_TRACKER_CHECK("Saved.ExtraColor"), Color_Saved_Main, Color_Saved_Extra, Color_Main_Default, Color_Saved_Extra_Default, CVAR_TRACKER_CHECK("Saved.Hide"), "Checks that you saved the game while having collected."); - - ImGui::PopStyleVar(1); ImGui::EndTable(); } @@ -1807,36 +1847,5 @@ void CheckTrackerWindow::InitElement() { } void CheckTrackerWindow::UpdateElement() { - Color_Background = CVarGetColor(CVAR_TRACKER_CHECK("BgColor"), Color_Bg_Default); - Color_Area_Incomplete_Main = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.MainColor"), Color_Main_Default); - Color_Area_Incomplete_Extra = CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor"), Color_Area_Incomplete_Extra_Default); - Color_Area_Complete_Main = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.MainColor"), Color_Main_Default); - Color_Area_Complete_Extra = CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.ExtraColor"), Color_Area_Complete_Extra_Default); - Color_Unchecked_Main = CVarGetColor(CVAR_TRACKER_CHECK("Unchecked.MainColor"), Color_Main_Default); - Color_Unchecked_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Unchecked.ExtraColor"), Color_Unchecked_Extra_Default); - Color_Skipped_Main = CVarGetColor(CVAR_TRACKER_CHECK("Skipped.MainColor"), Color_Main_Default); - Color_Skipped_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Skipped.ExtraColor"), Color_Skipped_Extra_Default); - Color_Seen_Main = CVarGetColor(CVAR_TRACKER_CHECK("Seen.MainColor"), Color_Main_Default); - Color_Seen_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Seen.ExtraColor"), Color_Seen_Extra_Default); - Color_Hinted_Main = CVarGetColor(CVAR_TRACKER_CHECK("Hinted.MainColor"), Color_Main_Default); - Color_Hinted_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Hinted.ExtraColor"), Color_Hinted_Extra_Default); - Color_Collected_Main = CVarGetColor(CVAR_TRACKER_CHECK("Collected.MainColor"), Color_Main_Default); - Color_Collected_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Collected.ExtraColor"), Color_Collected_Extra_Default); - Color_Scummed_Main = CVarGetColor(CVAR_TRACKER_CHECK("Scummed.MainColor"), Color_Main_Default); - Color_Scummed_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Scummed.ExtraColor"), Color_Scummed_Extra_Default); - Color_Saved_Main = CVarGetColor(CVAR_TRACKER_CHECK("Saved.MainColor"), Color_Main_Default); - Color_Saved_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Saved.ExtraColor"), Color_Saved_Extra_Default); - hideUnchecked = CVarGetInteger(CVAR_TRACKER_CHECK("Unchecked.Hide"), 0); - hideScummed = CVarGetInteger(CVAR_TRACKER_CHECK("Scummed.Hide"), 0); - hideSeen = CVarGetInteger(CVAR_TRACKER_CHECK("Seen.Hide"), 0); - hideSkipped = CVarGetInteger(CVAR_TRACKER_CHECK("Skipped.Hide"), 0); - hideSaved = CVarGetInteger(CVAR_TRACKER_CHECK("Saved.Hide"), 0); - hideCollected = CVarGetInteger(CVAR_TRACKER_CHECK("Collected.Hide"), 0); - showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0); - mystery = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0); - showLogicTooltip = CVarGetInteger("gCheckTrackerOptionShowLogic", 0); - - hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 1); - alwaysShowGS = CVarGetInteger(CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), 0); } } // namespace CheckTracker diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h index 851910489..c5bcaa074 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -2,6 +2,7 @@ #include #include "randomizerTypes.h" #include "randomizer_check_objects.h" +#include "soh/SohGui/UIWidgets.hpp" #include diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index 3ad26b1f6..26592b0ce 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -201,12 +201,12 @@ void Entrance_Init(void) { } // Overwrite grotto related indices - if (originalIndex >= ENTRANCE_GROTTO_EXIT_START) { + if (originalIndex >= ENTRANCE_GROTTO_EXIT_START && originalIndex < ENTRANCE_GROTTO_EXIT_START + NUM_GROTTOS) { Grotto_SetExitOverride(originalIndex, overrideIndex); continue; } - if (originalIndex >= ENTRANCE_GROTTO_LOAD_START && originalIndex < ENTRANCE_GROTTO_EXIT_START) { + if (originalIndex >= ENTRANCE_GROTTO_LOAD_START && originalIndex < ENTRANCE_GROTTO_LOAD_START + NUM_GROTTOS) { Grotto_SetLoadOverride(originalIndex, overrideIndex); continue; } diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp index 4c3cb971e..fab17acf1 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp @@ -1,7 +1,7 @@ #include "randomizer_entrance_tracker.h" #include "soh/OTRGlobals.h" #include "soh/cvar_prefixes.h" -#include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include #include @@ -659,77 +659,67 @@ void InitEntranceTrackingData() { } void EntranceTrackerSettingsWindow::DrawElement() { - if (ImGui::BeginTable("entranceTrackerSettings", 1, ImGuiTableFlags_BordersInnerH)) { + + ImGui::TextWrapped("The entrance tracker will only track shuffled entrances"); + UIWidgets::Spacer(0); + + ImGui::TableNextColumn(); + + if (ImGui::BeginTable("entranceTrackerSubSettings", 2, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("column 1", ImGuiTableColumnFlags_WidthStretch, 150.0f); + ImGui::TableSetupColumn("column 2", ImGuiTableColumnFlags_WidthStretch, 150.0f); ImGui::TableNextColumn(); - UIWidgets::Spacer(0); - ImGui::TextWrapped("The entrance tracker will only track shuffled entrances"); - UIWidgets::Spacer(0); + ImGui::Text("Sort By"); + UIWidgets::CVarRadioButton("To", CVAR_TRACKER_ENTRANCE("SortBy"), 0, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR).Tooltip("Sort entrances by the original source entrance")); + UIWidgets::CVarRadioButton("From", CVAR_TRACKER_ENTRANCE("SortBy"), 1, + UIWidgets::RadioButtonsOptions() + .Color(THEME_COLOR).Tooltip("Sort entrances by the overrided destination")); + + ImGui::Text("List Items"); + UIWidgets::CVarCheckbox("Auto scroll", CVAR_TRACKER_ENTRANCE("AutoScroll"), + UIWidgets::CheckboxOptions().Tooltip("Automatically scroll to the first aviable entrance in the current scene").Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Highlight previous", CVAR_TRACKER_ENTRANCE("HighlightPrevious"), + UIWidgets::CheckboxOptions().Tooltip("Highlight the previous entrance that Link came from").Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Highlight available", CVAR_TRACKER_ENTRANCE("HighlightAvailable"), + UIWidgets::CheckboxOptions().Tooltip("Highlight available entrances in the current scene").Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Hide undiscovered", CVAR_TRACKER_ENTRANCE("CollapseUndiscovered"), + UIWidgets::CheckboxOptions().Tooltip("Collapse undiscovered entrances towards the bottom of each group").Color(THEME_COLOR)); + bool disableHideReverseEntrances = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_DECOUPLED_ENTRANCES) == RO_GENERIC_ON; + static const char* disableHideReverseEntrancesText = "This option is disabled because \"Decouple Entrances\" is enabled."; + UIWidgets::CVarCheckbox("Hide reverse", CVAR_TRACKER_ENTRANCE("HideReverseEntrances"), + UIWidgets::CheckboxOptions({ {.disabled = disableHideReverseEntrances, .disabledTooltip = disableHideReverseEntrancesText }}) + .Tooltip("Hide reverse entrance transitions when Decouple Entrances is off").DefaultValue(true).Color(THEME_COLOR)); ImGui::TableNextColumn(); - if (ImGui::BeginTable("entranceTrackerSubSettings", 2, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingStretchProp)) { + ImGui::Text("Group By"); + UIWidgets::CVarRadioButton( + "Area", CVAR_TRACKER_ENTRANCE("GroupBy"), 0, + UIWidgets::RadioButtonsOptions().Color(THEME_COLOR).Tooltip("Group entrances by their area")); + UIWidgets::CVarRadioButton( + "Type", CVAR_TRACKER_ENTRANCE("GroupBy"), 1, + UIWidgets::RadioButtonsOptions().Color(THEME_COLOR).Tooltip("Group entrances by their entrance type")); - ImGui::TableNextColumn(); - - ImGui::Text("Sort By"); - UIWidgets::EnhancementRadioButton("To", CVAR_TRACKER_ENTRANCE("SortBy"), 0); - UIWidgets::Tooltip("Sort entrances by the original source entrance"); - UIWidgets::EnhancementRadioButton("From", CVAR_TRACKER_ENTRANCE("SortBy"), 1); - UIWidgets::Tooltip("Sort entrances by the overrided destination"); - - UIWidgets::Spacer(2.0f); - - ImGui::Text("List Items"); - UIWidgets::PaddedEnhancementCheckbox("Auto scroll", CVAR_TRACKER_ENTRANCE("AutoScroll"), true, false); - UIWidgets::Tooltip("Automatically scroll to the first aviable entrance in the current scene"); - UIWidgets::PaddedEnhancementCheckbox("Highlight previous", CVAR_TRACKER_ENTRANCE("HighlightPrevious"), true, false); - UIWidgets::Tooltip("Highlight the previous entrance that Link came from"); - UIWidgets::PaddedEnhancementCheckbox("Highlight available", CVAR_TRACKER_ENTRANCE("HighlightAvailable"), true, false); - UIWidgets::Tooltip("Highlight available entrances in the current scene"); - UIWidgets::PaddedEnhancementCheckbox("Hide undiscovered", CVAR_TRACKER_ENTRANCE("CollapseUndiscovered"), true, false); - UIWidgets::Tooltip("Collapse undiscovered entrances towards the bottom of each group"); - bool disableHideReverseEntrances = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_DECOUPLED_ENTRANCES) == RO_GENERIC_ON; - static const char* disableHideReverseEntrancesText = "This option is disabled because \"Decouple Entrances\" is enabled."; - UIWidgets::PaddedEnhancementCheckbox("Hide reverse", CVAR_TRACKER_ENTRANCE("HideReverseEntrances"), true, false, - disableHideReverseEntrances, disableHideReverseEntrancesText, UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip("Hide reverse entrance transitions when Decouple Entrances is off"); - UIWidgets::Spacer(0); - - ImGui::TableNextColumn(); - - ImGui::Text("Group By"); - UIWidgets::EnhancementRadioButton("Area", CVAR_TRACKER_ENTRANCE("GroupBy"), 0); - UIWidgets::Tooltip("Group entrances by their area"); - UIWidgets::EnhancementRadioButton("Type", CVAR_TRACKER_ENTRANCE("GroupBy"), 1); - UIWidgets::Tooltip("Group entrances by their entrance type"); - - UIWidgets::Spacer(2.0f); - - ImGui::Text("Spoiler Reveal"); - UIWidgets::PaddedEnhancementCheckbox("Show Source", CVAR_TRACKER_ENTRANCE("ShowFrom"), true, false); - UIWidgets::Tooltip("Reveal the sourcefor undiscovered entrances"); - UIWidgets::PaddedEnhancementCheckbox("Show Destination", CVAR_TRACKER_ENTRANCE("ShowTo"), true, false); - UIWidgets::Tooltip("Reveal the destination for undiscovered entrances"); - - ImGui::EndTable(); - } - - ImGui::TableNextColumn(); - - ImGui::SetNextItemOpen(false, ImGuiCond_Once); - if (ImGui::TreeNode("Legend")) { - ImGui::TextColored(ImColor(COLOR_ORANGE), "Last Entrance"); - ImGui::TextColored(ImColor(COLOR_GREEN), "Available Entrances"); - ImGui::TextColored(ImColor(COLOR_GRAY), "Undiscovered Entrances"); - ImGui::TreePop(); - } - - UIWidgets::Spacer(0); + ImGui::Text("Spoiler Reveal"); + UIWidgets::CVarCheckbox("Show Source", CVAR_TRACKER_ENTRANCE("ShowFrom"), + UIWidgets::CheckboxOptions().Tooltip("Reveal the sourcefor undiscovered entrances").Color(THEME_COLOR)); + UIWidgets::CVarCheckbox("Show Destination", CVAR_TRACKER_ENTRANCE("ShowTo"), + UIWidgets::CheckboxOptions().Tooltip("Reveal the destination for undiscovered entrances").Color(THEME_COLOR)); ImGui::EndTable(); } + + ImGui::SetNextItemOpen(false, ImGuiCond_Once); + if (ImGui::TreeNode("Legend")) { + ImGui::TextColored(ImColor(COLOR_ORANGE), "Last Entrance"); + ImGui::TextColored(ImColor(COLOR_GREEN), "Available Entrances"); + ImGui::TextColored(ImColor(COLOR_GRAY), "Undiscovered Entrances"); + ImGui::TreePop(); + } } void EntranceTrackerWindow::Draw() { @@ -752,24 +742,26 @@ void EntranceTrackerWindow::DrawElement() { static ImGuiTextFilter locationSearch; uint8_t nextTreeState = 0; - if (ImGui::Button("Collapse All")) { + if (UIWidgets::Button("Collapse All", UIWidgets::ButtonOptions({{ .tooltip = "Collapse all entrance groups" }}) + .Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { nextTreeState = 1; } - UIWidgets::Tooltip("Collapse all entrance groups"); ImGui::SameLine(); - if (ImGui::Button("Expand All")) { + if (UIWidgets::Button("Expand All", UIWidgets::ButtonOptions({{ .tooltip = "Expand all entrance groups" }}) + .Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { nextTreeState = 2; } - UIWidgets::Tooltip("Expand all entrance groups"); ImGui::SameLine(); - if (ImGui::Button("Clear")) { + if (UIWidgets::Button("Clear", UIWidgets::ButtonOptions({{ .tooltip = "Clear the search field" }}) + .Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { locationSearch.Clear(); } - UIWidgets::Tooltip("Clear the search field"); + UIWidgets::PushStyleCombobox(THEME_COLOR); if (locationSearch.Draw()) { nextTreeState = 2; } + UIWidgets::PopStyleCombobox(); uint8_t destToggle = CVarGetInteger(CVAR_TRACKER_ENTRANCE("SortBy"), 0); uint8_t groupToggle = CVarGetInteger(CVAR_TRACKER_ENTRANCE("GroupBy"), 0); @@ -940,7 +932,6 @@ void EntranceTrackerWindow::DrawElement() { } } ImGui::EndChild(); - ImGui::End(); } diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h index ef217b7f0..0e1271e57 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.h @@ -100,8 +100,8 @@ class EntranceTrackerSettingsWindow : public Ship::GuiWindow { class EntranceTrackerWindow : public Ship::GuiWindow { public: using GuiWindow::GuiWindow; - void Draw() override; + void InitElement() override; void DrawElement() override; void UpdateElement() override {}; diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index b2343b000..13046874f 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -5,6 +5,7 @@ #include "soh/SaveManager.h" #include "soh/ResourceManagerHelpers.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohGui.hpp" #include "randomizerTypes.h" #include @@ -35,6 +36,8 @@ void DrawSong(ItemTrackerItem item); int itemTrackerSectionId; +using namespace UIWidgets; + bool shouldUpdateVectors = true; std::vector mainWindowItems = {}; @@ -104,11 +107,40 @@ std::vector bossSoulItems = { std::vector ocarinaButtonItems = { //Hack for right now, just gonna draw ocarina buttons as ocarinas. //Will replace with other macro once we have a custom texture - ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_A_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem ), - ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_UP_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem ), - ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_DOWN_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem ), - ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_LEFT_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem ), - ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_RIGHT_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem ), + ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_A_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_UP_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_DOWN_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_LEFT_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_OCARINA_C_RIGHT_BUTTON, ITEM_OCARINA_TIME, ITEM_OCARINA_TIME, 0, DrawItem), +}; + +std::vector overworldKeyItems = { + // Hack for right now, just gonna overworld keys as dungeon keys. + // Will replace with other macro once we have a custom texture + ITEM_TRACKER_ITEM_CUSTOM(RG_GUARD_HOUSE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_MARKET_BAZAAR_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_MARKET_POTION_SHOP_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_MASK_SHOP_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_MARKET_SHOOTING_GALLERY_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_BOMBCHU_BOWLING_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_TREASURE_CHEST_GAME_BUILDING_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_BOMBCHU_SHOP_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_RICHARDS_HOUSE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_ALLEY_HOUSE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_KAK_BAZAAR_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_KAK_POTION_SHOP_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_BOSS_HOUSE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_GRANNYS_POTION_SHOP_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_SKULLTULA_HOUSE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_IMPAS_HOUSE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_WINDMILL_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_KAK_SHOOTING_GALLERY_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_DAMPES_HUT_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_TALONS_HOUSE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_STABLES_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_BACK_TOWER_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_HYLIA_LAB_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_FISHING_HOLE_KEY, ITEM_KEY_SMALL, ITEM_KEY_SMALL, 0, DrawItem), }; std::vector fishingPoleItems = { @@ -205,6 +237,33 @@ std::map itemTrackerOcarinaButtonShortNames = { { RG_OCARINA_C_RIGHT_BUTTON, "C-R"}, }; +std::map itemTrackerOverworldKeyShortNames = { + { RG_GUARD_HOUSE_KEY, "GUARD" }, + { RG_MARKET_BAZAAR_KEY, "MKBAZ" }, + { RG_MARKET_POTION_SHOP_KEY, "MKPOT" }, + { RG_MASK_SHOP_KEY, "MASK" }, + { RG_MARKET_SHOOTING_GALLERY_KEY, "MKSHO" }, + { RG_BOMBCHU_BOWLING_KEY, "BOWL" }, + { RG_TREASURE_CHEST_GAME_BUILDING_KEY, "TREASU" }, + { RG_BOMBCHU_SHOP_KEY, "CHUSHO" }, + { RG_RICHARDS_HOUSE_KEY, "RICH" }, + { RG_ALLEY_HOUSE_KEY, "ALLEY" }, + { RG_KAK_BAZAAR_KEY, "KAKBAZ" }, + { RG_KAK_POTION_SHOP_KEY, "KAKPO" }, + { RG_BOSS_HOUSE_KEY, "BOSS" }, + { RG_GRANNYS_POTION_SHOP_KEY, "GRANNY" }, + { RG_SKULLTULA_HOUSE_KEY, "SKULL" }, + { RG_IMPAS_HOUSE_KEY, "IMPAS" }, + { RG_WINDMILL_KEY, "WIND" }, + { RG_KAK_SHOOTING_GALLERY_KEY, "KAKSHO" }, + { RG_DAMPES_HUT_KEY, "DAMPES" }, + { RG_TALONS_HOUSE_KEY, "TALONS" }, + { RG_STABLES_KEY, "STABLE" }, + { RG_BACK_TOWER_KEY, "TOWER" }, + { RG_HYLIA_LAB_KEY, "LAB" }, + { RG_FISHING_HOLE_KEY, "FISH" }, +}; + std::vector dungeonItems = {}; std::unordered_map actualItemTrackerItemMap = { @@ -616,7 +675,7 @@ void DrawEquip(ItemTrackerItem item) { ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasEquip && IsValidSaveFile() ? item.name : item.nameFaded), ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1)); - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(item.id)); + Tooltip(SohUtils::GetItemName(item.id).c_str()); } void DrawQuest(ItemTrackerItem item) { @@ -632,7 +691,7 @@ void DrawQuest(ItemTrackerItem item) { ImGui::EndGroup(); - UIWidgets::SetLastItemHoverText(SohUtils::GetQuestItemName(item.id)); + Tooltip(SohUtils::GetQuestItemName(item.id).c_str()); }; void DrawItem(ItemTrackerItem item) { @@ -642,7 +701,9 @@ void DrawItem(ItemTrackerItem item) { bool hasItem = actualItemId != ITEM_NONE; std::string itemName = ""; - if (item.id == ITEM_NONE) { + // Hack fix as RG_MARKET_SHOOTING_GALLERY_KEY is RandomizerGet #255 which collides + // with ITEM_NONE (ItemId #255) due to the lack of a modid to separate them + if (item.name != "ITEM_KEY_SMALL" && item.id == ITEM_NONE) { return; } @@ -729,7 +790,6 @@ void DrawItem(ItemTrackerItem item) { actualItemId = item.id; hasItem = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS) == RO_BOSS_SOULS_ON_PLUS_GANON ? Flags_GetRandomizerInf(RAND_INF_GANON_SOUL) : true; - itemName = "Ganon's Soul"; break; @@ -763,6 +823,127 @@ void DrawItem(ItemTrackerItem item) { hasItem = IS_RANDO && Flags_GetRandomizerInf(RAND_INF_FISHING_POLE_FOUND); itemName = "Fishing Pole"; break; + + case RG_GUARD_HOUSE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_GUARD_HOUSE_KEY_OBTAINED); + itemName = "Guard House Key"; + break; + case RG_MARKET_BAZAAR_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_MARKET_BAZAAR_KEY_OBTAINED); + itemName = "Market Bazaar Key"; + break; + case RG_MARKET_POTION_SHOP_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_MARKET_POTION_SHOP_KEY_OBTAINED); + itemName = "Market Potion Shop Key"; + break; + case RG_MASK_SHOP_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_MASK_SHOP_KEY_OBTAINED); + itemName = "Mask Shop Key"; + break; + case RG_MARKET_SHOOTING_GALLERY_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_MARKET_SHOOTING_GALLERY_KEY_OBTAINED); + itemName = "Market Shooting Gallery Key"; + break; + case RG_BOMBCHU_BOWLING_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_BOMBCHU_BOWLING_KEY_OBTAINED); + itemName = "Bombchu Bowling Key"; + break; + case RG_TREASURE_CHEST_GAME_BUILDING_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_TREASURE_CHEST_GAME_BUILDING_KEY_OBTAINED); + itemName = "Treasure Chest Game Building Key"; + break; + case RG_BOMBCHU_SHOP_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_BOMBCHU_SHOP_KEY_OBTAINED); + itemName = "Bombchu Shop Key"; + break; + case RG_RICHARDS_HOUSE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_RICHARDS_HOUSE_KEY_OBTAINED); + itemName = "Richards House Key"; + break; + case RG_ALLEY_HOUSE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_ALLEY_HOUSE_KEY_OBTAINED); + itemName = "Alley House Key"; + break; + case RG_KAK_BAZAAR_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_KAK_BAZAAR_KEY_OBTAINED); + itemName = "Kak Bazaar Key"; + break; + case RG_KAK_POTION_SHOP_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_KAK_POTION_SHOP_KEY_OBTAINED); + itemName = "Kak Potion Shop Key"; + break; + case RG_BOSS_HOUSE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_BOSS_HOUSE_KEY_OBTAINED); + itemName = "Boss House Key"; + break; + case RG_GRANNYS_POTION_SHOP_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_GRANNYS_POTION_SHOP_KEY_OBTAINED); + itemName = "Granny's Potion Shop Key"; + break; + case RG_SKULLTULA_HOUSE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_SKULLTULA_HOUSE_KEY_OBTAINED); + itemName = "Skulltula House Key"; + break; + case RG_IMPAS_HOUSE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_IMPAS_HOUSE_KEY_OBTAINED); + itemName = "Impa's House Key"; + break; + case RG_WINDMILL_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_WINDMILL_KEY_OBTAINED); + itemName = "Windmill Key"; + break; + case RG_KAK_SHOOTING_GALLERY_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_KAK_SHOOTING_GALLERY_KEY_OBTAINED); + itemName = "Kak Shooting Gallery Key"; + break; + case RG_DAMPES_HUT_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_DAMPES_HUT_KEY_OBTAINED); + itemName = "Dampé's Hut Key"; + break; + case RG_TALONS_HOUSE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_TALONS_HOUSE_KEY_OBTAINED); + itemName = "Talon's House Key"; + break; + case RG_STABLES_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_STABLES_KEY_OBTAINED); + itemName = "Stables Key"; + break; + case RG_BACK_TOWER_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_BACK_TOWER_KEY_OBTAINED); + itemName = "Back Tower Key"; + break; + case RG_HYLIA_LAB_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_HYLIA_LAB_KEY_OBTAINED); + itemName = "Hylia Lab Key"; + break; + case RG_FISHING_HOLE_KEY: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_FISHING_HOLE_KEY_OBTAINED); + itemName = "Fishing Hole Key"; + break; } if (GameInteractor::IsSaveLoaded() && (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end())) { @@ -794,13 +975,22 @@ void DrawItem(ItemTrackerItem item) { ImGui::PopStyleColor(); } + if (item.id >= RG_GUARD_HOUSE_KEY && item.id <= RG_FISHING_HOLE_KEY) { + ImVec2 p = ImGui::GetCursorScreenPos(); + std::string overworldKeyName = itemTrackerOverworldKeyShortNames[item.id]; + ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(overworldKeyName.c_str()).x / 2), p.y - (iconSize + 13))); + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL_WHITE); + ImGui::Text("%s", overworldKeyName.c_str()); + ImGui::PopStyleColor(); + } + ImGui::EndGroup(); if (itemName == "") { itemName = SohUtils::GetItemName(item.id); } - UIWidgets::SetLastItemHoverText(itemName); + Tooltip(itemName.c_str()); } void DrawBottle(ItemTrackerItem item) { @@ -815,7 +1005,7 @@ void DrawBottle(ItemTrackerItem item) { ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasItem && IsValidSaveFile() ? item.name : item.nameFaded), ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1)); - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(item.id)); + Tooltip(SohUtils::GetItemName(item.id).c_str()); }; void DrawDungeonItem(ItemTrackerItem item) { @@ -864,7 +1054,7 @@ void DrawDungeonItem(ItemTrackerItem item) { } ImGui::EndGroup(); - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(item.id)); + Tooltip(SohUtils::GetItemName(item.id).c_str()); } void DrawSong(ItemTrackerItem item) { @@ -874,7 +1064,7 @@ void DrawSong(ItemTrackerItem item) { ImGui::SetCursorScreenPos(ImVec2(p.x + 6, p.y)); ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasSong && IsValidSaveFile() ? item.name : item.nameFaded), ImVec2(iconSize / 1.5, iconSize), ImVec2(0, 0), ImVec2(1, 1)); - UIWidgets::SetLastItemHoverText(SohUtils::GetQuestItemName(item.id)); + Tooltip(SohUtils::GetQuestItemName(item.id).c_str()); } void DrawNotes(bool resizeable = false) { @@ -931,7 +1121,6 @@ void DrawTotalChecks() { } // Windowing stuff -ImVec4 ChromaKeyBackground = { 0, 0, 0, 0 }; // Float value, 1 = 255 in rgb value. void BeginFloatingWindows(std::string UniqueName, ImGuiWindowFlags flags = 0) { ImGuiWindowFlags windowFlags = flags; @@ -947,7 +1136,7 @@ void BeginFloatingWindows(std::string UniqueName, ImGuiWindowFlags flags = 0) { windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; } } - ImGui::PushStyleColor(ImGuiCol_WindowBg, ChromaKeyBackground); + ImGui::PushStyleColor(ImGuiCol_WindowBg, VecFromRGBA8(CVarGetColor(CVAR_TRACKER_ITEM("BgColor.Value"), {0, 0, 0, 0}))); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::Begin(UniqueName.c_str(), nullptr, windowFlags); @@ -1187,6 +1376,17 @@ void UpdateVectors() { mainWindowItems.insert(mainWindowItems.end(), ocarinaButtonItems.begin(), ocarinaButtonItems.end()); } + //If we're adding overworld keys to the main window... + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.OverworldKeys"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW) { + //...add empty items on the main window to get the keys on their own row. (Too many to sit with Greg/Triforce pieces/boss souls/ocarina buttons) + while (mainWindowItems.size() % 6) { + mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem)); + } + + //Add overworld keys + mainWindowItems.insert(mainWindowItems.end(), overworldKeyItems.begin(), overworldKeyItems.end()); + } + shouldUpdateVectors = false; } @@ -1214,9 +1414,11 @@ void ItemTrackerWindow::Draw() { if (!IsVisible()) { return; } + ImGui::PushFont(OTRGlobals::Instance->fontMono); DrawElement(); // Sync up the IsVisible flag if it was changed by ImGui SyncVisibilityConsoleVariable(); + ImGui::PopFont(); } void ItemTrackerWindow::DrawElement() { @@ -1329,6 +1531,12 @@ void ItemTrackerWindow::DrawElement() { EndFloatingWindows(); } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.OverworldKeys"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE) { + BeginFloatingWindows("Overworld Key Tracker"); + DrawItemsInRows(overworldKeyItems); + EndFloatingWindows(); + } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.FishingPole"), SECTION_DISPLAY_EXTENDED_HIDDEN) == SECTION_DISPLAY_EXTENDED_SEPARATE) { BeginFloatingWindows("Fishing Pole Tracker"); DrawItemsInRows(fishingPoleItems); @@ -1352,15 +1560,23 @@ void ItemTrackerWindow::DrawElement() { } } -static const char* itemTrackerCapacityTrackOptions[5] = { "No Numbers", "Current Capacity", "Current Ammo", "Current Capacity / Max Capacity", "Current Ammo / Current Capacity" }; -static const char* itemTrackerKeyTrackOptions[3] = { "Collected / Max", "Current / Collected / Max", "Current / Max" }; -static const char* itemTrackerTriforcePieceTrackOptions[2] = { "Collected / Required", "Collected / Required / Max" }; -static const char* windowTypes[2] = { "Floating", "Window" }; -static const char* displayModes[2] = { "Always", "Combo Button Hold" }; -static const char* buttons[14] = { "A", "B", "C-Up", "C-Down", "C-Left", "C-Right", "L", "Z", "R", "Start", "D-Up", "D-Down", "D-Left", "D-Right" }; -static const char* displayTypes[3] = { "Hidden", "Main Window", "Separate" }; -static const char* extendedDisplayTypes[4] = { "Hidden", "Main Window", "Misc Window", "Separate" }; -static const char* minimalDisplayTypes[2] = { "Hidden", "Separate" }; +static std::unordered_map itemTrackerCapacityTrackOptions = { + { ITEM_TRACKER_NUMBER_NONE, "No Numbers" }, { ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY, "Current Capacity" }, { ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY, "Current Ammo" }, + { ITEM_TRACKER_NUMBER_CAPACITY, "Current Capacity / Max Capacity" }, { ITEM_TRACKER_NUMBER_AMMO, "Current Ammo / Current Capacity" }}; +static std::unordered_map itemTrackerKeyTrackOptions = { + { KEYS_COLLECTED_MAX, "Collected / Max" }, { KEYS_CURRENT_COLLECTED_MAX, "Current / Collected / Max" }, { KEYS_CURRENT_MAX, "Current / Max" }}; +static std::unordered_map itemTrackerTriforcePieceTrackOptions = { + { TRIFORCE_PIECE_COLLECTED_REQUIRED, "Collected / Required" }, { TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX, "Collected / Required / Max" }}; +static std::unordered_map windowTypes = {{ TRACKER_WINDOW_FLOATING, "Floating" }, { TRACKER_WINDOW_WINDOW, "Window" }}; +static std::unordered_map displayModes = {{ TRACKER_DISPLAY_ALWAYS, "Always" }, { TRACKER_DISPLAY_COMBO_BUTTON, "Combo Button Hold" }}; +static std::unordered_map buttons = {{ TRACKER_COMBO_BUTTON_A, "A" }, { TRACKER_COMBO_BUTTON_B, "B"}, { TRACKER_COMBO_BUTTON_C_UP, "C-Up"}, + { TRACKER_COMBO_BUTTON_C_DOWN, "C-Down" }, { TRACKER_COMBO_BUTTON_C_LEFT, "C-Left" }, { TRACKER_COMBO_BUTTON_C_RIGHT, "C-Right" }, { TRACKER_COMBO_BUTTON_L, "L" }, + { TRACKER_COMBO_BUTTON_Z, "Z" }, { TRACKER_COMBO_BUTTON_R, "R" }, { TRACKER_COMBO_BUTTON_START, "Start" }, { TRACKER_COMBO_BUTTON_D_UP, "D-Up" }, + { TRACKER_COMBO_BUTTON_D_DOWN, "D-Down" }, { TRACKER_COMBO_BUTTON_D_LEFT, "D-Left" }, { TRACKER_COMBO_BUTTON_D_RIGHT, "D-Right" }}; +static std::unordered_map displayTypes = {{ SECTION_DISPLAY_HIDDEN, "Hidden" }, { SECTION_DISPLAY_MAIN_WINDOW, "Main Window" }, { SECTION_DISPLAY_SEPARATE, "Separate" }}; +static std::unordered_map extendedDisplayTypes = {{ SECTION_DISPLAY_EXTENDED_HIDDEN, "Hidden" }, + { SECTION_DISPLAY_EXTENDED_MAIN_WINDOW, "Main Window" }, { SECTION_DISPLAY_EXTENDED_MISC_WINDOW, "Misc Window" }, { SECTION_DISPLAY_EXTENDED_SEPARATE, "Separate" }}; +static std::unordered_map minimalDisplayTypes = {{ SECTION_DISPLAY_MINIMAL_HIDDEN, "Hidden" }, { SECTION_DISPLAY_MINIMAL_SEPARATE, "Separate" }}; void ItemTrackerSettingsWindow::DrawElement() { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); @@ -1370,154 +1586,173 @@ void ItemTrackerSettingsWindow::DrawElement() { ImGui::TableHeadersRow(); ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text("BG Color"); - ImGui::SameLine(); ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::ColorEdit4("BG Color##gItemTrackerBgColor", (float*)&ChromaKeyBackground, ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_NoLabel)) { - CVarSetFloat(CVAR_TRACKER_ITEM("BgColorR"), ChromaKeyBackground.x); - CVarSetFloat(CVAR_TRACKER_ITEM("BgColorG"), ChromaKeyBackground.y); - CVarSetFloat(CVAR_TRACKER_ITEM("BgColorB"), ChromaKeyBackground.z); - CVarSetFloat(CVAR_TRACKER_ITEM("BgColorA"), ChromaKeyBackground.w); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - ImGui::PopItemWidth(); + CVarColorPicker("Background Color##gItemTrackerBgColor", CVAR_TRACKER_ITEM("BgColor"), { 0, 0, 0, 0 }, true, + ColorPickerRandomButton | ColorPickerResetButton, THEME_COLOR); - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Window Type", CVAR_TRACKER_ITEM("WindowType"), windowTypes, TRACKER_WINDOW_FLOATING)) { + ImGui::PopItemWidth(); + if (CVarCombobox("Window Type", CVAR_TRACKER_ITEM("WindowType"), windowTypes, ComboboxOptions() + .DefaultIndex(TRACKER_WINDOW_FLOATING).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING) { - if (UIWidgets::PaddedEnhancementCheckbox("Enable Dragging", CVAR_TRACKER_ITEM("Draggable"))) { + if (CVarCheckbox("Enable Dragging", CVAR_TRACKER_ITEM("Draggable"), CheckboxOptions().Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::PaddedEnhancementCheckbox("Only enable while paused", CVAR_TRACKER_ITEM("ShowOnlyPaused"))) { + if (CVarCheckbox("Only enable while paused", CVAR_TRACKER_ITEM("ShowOnlyPaused"), CheckboxOptions().Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Display Mode", CVAR_TRACKER_ITEM("DisplayType.Main"), displayModes, TRACKER_DISPLAY_ALWAYS)) { + if (CVarCombobox("Display Mode", CVAR_TRACKER_ITEM("DisplayType.Main"), displayModes, ComboboxOptions() + .DefaultIndex(TRACKER_DISPLAY_ALWAYS).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_COMBO_BUTTON) { - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 1", CVAR_TRACKER_ITEM("ComboButton1"), buttons, TRACKER_COMBO_BUTTON_L)) { + if (CVarCombobox("Combo Button 1", CVAR_TRACKER_ITEM("ComboButton1"), buttons, ComboboxOptions() + .DefaultIndex(TRACKER_COMBO_BUTTON_L).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 2", CVAR_TRACKER_ITEM("ComboButton2"), buttons, TRACKER_COMBO_BUTTON_R)) { + if (CVarCombobox("Combo Button 2", CVAR_TRACKER_ITEM("ComboButton2"), buttons, ComboboxOptions() + .DefaultIndex(TRACKER_COMBO_BUTTON_R).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } } } - UIWidgets::PaddedSeparator(); - UIWidgets::EnhancementSliderInt("Icon size : %dpx", "##ITEMTRACKERICONSIZE", CVAR_TRACKER_ITEM("IconSize"), 25, 128, "", 36); - UIWidgets::EnhancementSliderInt("Icon margins : %dpx", "##ITEMTRACKERSPACING", CVAR_TRACKER_ITEM("IconSpacing"), -5, 50, "", 12); - UIWidgets::EnhancementSliderInt("Text size : %dpx", "##ITEMTRACKERTEXTSIZE", CVAR_TRACKER_ITEM("TextSize"), 1, 30, "", 13); + ImGui::Separator(); + CVarSliderInt("Icon size : %dpx", CVAR_TRACKER_ITEM("IconSize"), IntSliderOptions().Min(25).Max(128).DefaultValue(36).Color(THEME_COLOR)); + CVarSliderInt("Icon margins : %dpx", CVAR_TRACKER_ITEM("IconSpacing"), IntSliderOptions().Min(-5).Max(50).DefaultValue(12).Color(THEME_COLOR)); + CVarSliderInt("Text size : %dpx", CVAR_TRACKER_ITEM("TextSize"), IntSliderOptions().Min(1).Max(30).DefaultValue(13).Color(THEME_COLOR)); - UIWidgets::Spacer(0); - - ImGui::Text("Ammo/Capacity Tracking"); - UIWidgets::EnhancementCombobox(CVAR_TRACKER_ITEM("ItemCountType"), itemTrackerCapacityTrackOptions, ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY); - UIWidgets::InsertHelpHoverText("Customize what the numbers under each item are tracking." - "\n\nNote: items without capacity upgrades will track ammo even in capacity mode"); + ImGui::NewLine(); + CVarCombobox("Ammo/Capacity Tracking", CVAR_TRACKER_ITEM("ItemCountType"), itemTrackerCapacityTrackOptions, ComboboxOptions() + .DefaultIndex(ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY).ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above).Color(THEME_COLOR) + .Tooltip("Customize what the numbers under each item are tracking." + "\n\nNote: items without capacity upgrades will track ammo even in capacity mode")); if (CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY || CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY) { - if (UIWidgets::PaddedEnhancementCheckbox("Align count to left side", CVAR_TRACKER_ITEM("ItemCountAlignLeft"))) { + if (CVarCheckbox("Align count to left side", CVAR_TRACKER_ITEM("ItemCountAlignLeft"), CheckboxOptions().Color(THEME_COLOR))) { shouldUpdateVectors = true; } } - UIWidgets::Spacer(0); + CVarCombobox("Key Count Tracking", CVAR_TRACKER_ITEM("KeyCounts"), itemTrackerKeyTrackOptions, ComboboxOptions() + .DefaultIndex(KEYS_COLLECTED_MAX).ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above).Color(THEME_COLOR) + .Tooltip("Customize what numbers are shown for key tracking.")); - ImGui::Text("Key Count Tracking"); - UIWidgets::EnhancementCombobox(CVAR_TRACKER_ITEM("KeyCounts"), itemTrackerKeyTrackOptions, KEYS_COLLECTED_MAX); - UIWidgets::InsertHelpHoverText("Customize what numbers are shown for key tracking."); - - UIWidgets::Spacer(0); - - ImGui::Text("Triforce Piece Count Tracking"); - UIWidgets::EnhancementCombobox(CVAR_TRACKER_ITEM("TriforcePieceCounts"), itemTrackerTriforcePieceTrackOptions, TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX); - UIWidgets::InsertHelpHoverText("Customize what numbers are shown for triforce piece tracking."); + CVarCombobox("Triforce Piece Count Tracking", CVAR_TRACKER_ITEM("TriforcePieceCounts"), itemTrackerTriforcePieceTrackOptions, ComboboxOptions() + .DefaultIndex(TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX).ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above).Color(THEME_COLOR) + .Tooltip("Customize what numbers are shown for triforce piece tracking.")); ImGui::TableNextColumn(); - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Inventory", CVAR_TRACKER_ITEM("DisplayType.Inventory"), displayTypes, SECTION_DISPLAY_MAIN_WINDOW)) { + if (CVarCombobox("Inventory", CVAR_TRACKER_ITEM("DisplayType.Inventory"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Equipment", CVAR_TRACKER_ITEM("DisplayType.Equipment"), displayTypes, SECTION_DISPLAY_MAIN_WINDOW)) { + if (CVarCombobox("Equipment", CVAR_TRACKER_ITEM("DisplayType.Equipment"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Misc", CVAR_TRACKER_ITEM("DisplayType.Misc"), displayTypes, SECTION_DISPLAY_MAIN_WINDOW)) { + if (CVarCombobox("Misc", CVAR_TRACKER_ITEM("DisplayType.Misc"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Dungeon Rewards", CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), displayTypes, SECTION_DISPLAY_MAIN_WINDOW)) { + if (CVarCombobox("Dungeon Rewards", CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), SECTION_DISPLAY_MAIN_WINDOW) == SECTION_DISPLAY_SEPARATE) { - if (UIWidgets::PaddedEnhancementCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"), true, true, false, "", UIWidgets::CheckboxGraphics::Cross, false)) { + if (CVarCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"))) { shouldUpdateVectors = true; } } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Songs", CVAR_TRACKER_ITEM("DisplayType.Songs"), displayTypes, SECTION_DISPLAY_MAIN_WINDOW)) { + if (CVarCombobox("Songs", CVAR_TRACKER_ITEM("DisplayType.Songs"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Dungeon Items", CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), displayTypes, SECTION_DISPLAY_HIDDEN)) { + if (CVarCombobox("Dungeon Items", CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) != SECTION_DISPLAY_HIDDEN) { if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE) { - if (UIWidgets::PaddedEnhancementCheckbox("Horizontal display", CVAR_TRACKER_ITEM("DungeonItems.Layout"), true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true)) { + if (CVarCheckbox("Horizontal display", CVAR_TRACKER_ITEM("DungeonItems.Layout"), CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { shouldUpdateVectors = true; } } - if (UIWidgets::PaddedEnhancementCheckbox("Maps and compasses", CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true)) { + if (CVarCheckbox("Maps and compasses", CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { shouldUpdateVectors = true; } } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Greg", CVAR_TRACKER_ITEM("DisplayType.Greg"), extendedDisplayTypes, SECTION_DISPLAY_EXTENDED_HIDDEN)) { + if (CVarCombobox("Greg", CVAR_TRACKER_ITEM("DisplayType.Greg"), extendedDisplayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Triforce Pieces", CVAR_TRACKER_ITEM("DisplayType.TriforcePieces"), displayTypes, SECTION_DISPLAY_HIDDEN)) { + if (CVarCombobox("Triforce Pieces", CVAR_TRACKER_ITEM("DisplayType.TriforcePieces"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Boss Souls", CVAR_TRACKER_ITEM("DisplayType.BossSouls"), displayTypes, SECTION_DISPLAY_HIDDEN)) { + if (CVarCombobox("Boss Souls", CVAR_TRACKER_ITEM("DisplayType.BossSouls"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Ocarina Buttons", CVAR_TRACKER_ITEM("DisplayType.OcarinaButtons"), displayTypes, SECTION_DISPLAY_HIDDEN)) { + if (CVarCombobox("Ocarina Buttons", CVAR_TRACKER_ITEM("DisplayType.OcarinaButtons"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Fishing Pole", CVAR_TRACKER_ITEM("DisplayType.FishingPole"), extendedDisplayTypes, SECTION_DISPLAY_EXTENDED_HIDDEN)) { + if (CVarCombobox("Overworld Keys", CVAR_TRACKER_ITEM("DisplayType.OverworldKeys"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Total Checks", "gTrackers.ItemTracker.TotalChecks.DisplayType", minimalDisplayTypes, SECTION_DISPLAY_MINIMAL_HIDDEN)) { + if (CVarCombobox("Fishing Pole", CVAR_TRACKER_ITEM("DisplayType.FishingPole"), extendedDisplayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + + if (CVarCombobox("Total Checks", "gTrackers.ItemTracker.TotalChecks.DisplayType", minimalDisplayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MINIMAL_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { - if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, SECTION_DISPLAY_HIDDEN)) { + if (CVarCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN).ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far).Color(THEME_COLOR))) { shouldUpdateVectors = true; } } - UIWidgets::EnhancementCheckbox("Show Hookshot Identifiers", CVAR_TRACKER_ITEM("HookshotIdentifier")); - UIWidgets::InsertHelpHoverText("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot."); - - UIWidgets::Spacer(0); + CVarCheckbox("Show Hookshot Identifiers", CVAR_TRACKER_ITEM("HookshotIdentifier"), CheckboxOptions() + .Tooltip("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot.").Color(THEME_COLOR)); ImGui::PopStyleVar(1); ImGui::EndTable(); } void ItemTrackerWindow::InitElement() { - float trackerBgR = CVarGetFloat(CVAR_TRACKER_ITEM("BgColorR"), 0); - float trackerBgG = CVarGetFloat(CVAR_TRACKER_ITEM("BgColorG"), 0); - float trackerBgB = CVarGetFloat(CVAR_TRACKER_ITEM("BgColorB"), 0); - float trackerBgA = CVarGetFloat(CVAR_TRACKER_ITEM("BgColorA"), 1); - ChromaKeyBackground = { - trackerBgR, - trackerBgG, - trackerBgB, - trackerBgA - }; // Float value, 1 = 255 in rgb value. // Crashes when the itemTrackerNotes is empty, so add an empty character to it if (itemTrackerNotes.empty()) { itemTrackerNotes.push_back(0); diff --git a/soh/soh/Enhancements/randomizer/randomizer_settings_window.h b/soh/soh/Enhancements/randomizer/randomizer_settings_window.h index 9f297c29d..034101aba 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_settings_window.h +++ b/soh/soh/Enhancements/randomizer/randomizer_settings_window.h @@ -1,3 +1,5 @@ +#pragma once + #include namespace Rando { diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index d48d7fb0d..d24b9c2ba 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -237,7 +237,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_KEYSANITY, "Small Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Keysanity"), mOptionDescriptions[RSK_KEYSANITY], WidgetType::Combobox, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); OPT_U8(RSK_GERUDO_KEYS, "Gerudo Fortress Keys", {"Vanilla", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GerudoKeys"), mOptionDescriptions[RSK_GERUDO_KEYS], WidgetType::Combobox, RO_GERUDO_KEYS_VANILLA); OPT_U8(RSK_BOSS_KEYSANITY, "Boss Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BossKeysanity"), mOptionDescriptions[RSK_BOSS_KEYSANITY], WidgetType::Combobox, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); - OPT_U8(RSK_GANONS_BOSS_KEY, "Ganon's Boss Key", {"Vanilla", "Own Dungeon", "Start With", "Any Dungeon", "Overworld", "Anywhere", "LACS-Vanilla", "LACS-Stones", "LACS-Medallions", "LACS-Rewards", "LACS-Dungeons", "LACS-Tokens", "100 GS Reward", "Triforce Hunt"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), mOptionDescriptions[RSK_GANONS_BOSS_KEY], WidgetType::Combobox, RO_GANON_BOSS_KEY_VANILLA); + OPT_U8(RSK_GANONS_BOSS_KEY, "Ganon's Boss Key", {"Vanilla", "Own Dungeon", "Start With", "Any Dungeon", "Overworld", "Anywhere", "LACS-Vanilla", "LACS-Stones", "LACS-Medallions", "LACS-Rewards", "LACS-Dungeons", "LACS-Tokens", "100 GS Reward"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), mOptionDescriptions[RSK_GANONS_BOSS_KEY], WidgetType::Combobox, RO_GANON_BOSS_KEY_VANILLA); OPT_U8(RSK_LACS_STONE_COUNT, "GCBK Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsStoneCount"), "", WidgetType::Slider, 3, true); OPT_U8(RSK_LACS_MEDALLION_COUNT, "GCBK Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsMedallionCount"), "", WidgetType::Slider, 6, true); OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WidgetType::Slider, 9, true); @@ -246,15 +246,15 @@ void Settings::CreateOptions() { OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), "", WidgetType::Combobox, RO_LACS_STANDARD_REWARD); OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WidgetType::Combobox, RO_KEYRINGS_OFF); OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WidgetType::Slider, 8); - OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_FOREST_TEMPLE, "Forest Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsForestTemple"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_FIRE_TEMPLE, "Fire Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsFireTemple"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_WATER_TEMPLE, "Water Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsWaterTemple"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_SPIRIT_TEMPLE, "Spirit Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsSpiritTemple"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_SHADOW_TEMPLE, "Shadow Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsShadowTemple"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_BOTTOM_OF_THE_WELL, "Bottom of the Well Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsBottomOfTheWell"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_GTG, "Gerudo Training Ground Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGTG"), "", WidgetType::TristateCheckbox, 0); - OPT_U8(RSK_KEYRINGS_GANONS_CASTLE, "Ganon's Castle Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGanonsCastle"), "", WidgetType::TristateCheckbox, 0); + OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_FOREST_TEMPLE, "Forest Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsForestTemple"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_FIRE_TEMPLE, "Fire Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsFireTemple"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_WATER_TEMPLE, "Water Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsWaterTemple"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_SPIRIT_TEMPLE, "Spirit Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsSpiritTemple"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_SHADOW_TEMPLE, "Shadow Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsShadowTemple"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_BOTTOM_OF_THE_WELL, "Bottom of the Well Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsBottomOfTheWell"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_GTG, "Gerudo Training Ground Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGTG"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_KEYRINGS_GANONS_CASTLE, "Ganon's Castle Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGanonsCastle"), "", WidgetType::Combobox, 0); //Dummied out due to redundancy with TimeSavers.SkipChildStealth until such a time that logic needs to consider child stealth e.g. because it's freestanding checks are added to freestanding shuffle. //To undo this dummying, readd this setting to an OptionGroup so it appears in the UI, then edit the timesaver check hooks to look at this, and the timesaver setting to lock itself as needed. OPT_BOOL(RSK_SKIP_CHILD_STEALTH, "Skip Child Stealth", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildStealth"), mOptionDescriptions[RSK_SKIP_CHILD_STEALTH], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP); @@ -1674,7 +1674,7 @@ void Settings::UpdateOptionProperties() { } // Shuffle 100 GS Reward - Force-Enabled if Ganon's Boss Key is on the 100 GS Reward if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) == RO_GANON_BOSS_KEY_KAK_TOKENS) { - mOptions[RSK_SHUFFLE_100_GS_REWARD].Disable("This option is force-enabled because \"Ganon's Boss Key\" is set to \"100 GS Reward.\"", UIWidgets::CheckboxGraphics::Checkmark); + mOptions[RSK_SHUFFLE_100_GS_REWARD].Disable("This option is force-enabled because \"Ganon's Boss Key\" is set to \"100 GS Reward.\""); } else { mOptions[RSK_SHUFFLE_100_GS_REWARD].Enable(); } @@ -1856,10 +1856,6 @@ void Context::FinalizeSettings(const std::set& excludedLocation mOptions[RSK_STARTING_AGE].Set(RO_AGE_CHILD); } - if (mOptions[RSK_TRIFORCE_HUNT]) { - mOptions[RSK_GANONS_BOSS_KEY].Set(RO_GANON_BOSS_KEY_TRIFORCE_HUNT); - } - // Force 100 GS Shuffle if that's where Ganon's Boss Key is if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) { mOptions[RSK_SHUFFLE_100_GS_REWARD].Set(1); diff --git a/soh/soh/Enhancements/randomizer/tricks.cpp b/soh/soh/Enhancements/randomizer/tricks.cpp index 6008ba332..69b3db88d 100644 --- a/soh/soh/Enhancements/randomizer/tricks.cpp +++ b/soh/soh/Enhancements/randomizer/tricks.cpp @@ -1,4 +1,5 @@ #include "tricks.h" +#include "soh/SohGui/UIWidgets.hpp" #include namespace Rando { @@ -71,7 +72,6 @@ namespace Rando { const ImVec4 Tricks::GetTextColor(const Tag tag) { switch(tag) { - case Tag::EXPERIMENTAL: case Tag::GLITCH: return { 0.00f, 0.00f, 0.00f, 1.0f }; default: @@ -82,32 +82,33 @@ namespace Rando { const ImVec4 Tricks::GetTagColor(const Tag tag) { switch(tag) { case Tag::NOVICE: - return { 0.09f, 0.55f, 0.37f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::Green); case Tag::INTERMEDIATE: - return { 0.95f, 0.52f, 0.00f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::Orange); case Tag::ADVANCED: - return { 0.00f, 0.29f, 0.71f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::Blue); case Tag::EXPERT: - return { 0.53f, 0.05f, 0.14f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::Red); case Tag::EXTREME: - return { 0.27f, 0.00f, 0.27f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::Purple); case Tag::EXPERIMENTAL: - return { 0.00f, 1.00f, 1.00f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::LightBlue); case Tag::GLITCH: - return { 1.00f, 1.00f, 1.00f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::White); default: assert(false); - return { 0.50f, 0.50f, 0.50f, 1.00f }; + return UIWidgets::ColorValues.at(UIWidgets::Colors::Gray); } } - void Tricks::DrawTagChips(const std::set& rtTags) { + void Tricks::DrawTagChips(const std::set& rtTags, std::string trickName) { for (const Tag rtTag : rtTags) { + std::string tagId = GetTagName(rtTag) + "##" + trickName; ImGui::SameLine(); ImGui::BeginDisabled(); - ImGui::PushStyleColor(ImGuiCol_Button, GetTagColor(rtTag)); - ImGui::SmallButton(GetTagName(rtTag).c_str()); - ImGui::PopStyleColor(); + UIWidgets::PushStyleButton(GetTagColor(rtTag)); + ImGui::SmallButton(tagId.c_str()); + UIWidgets::PopStyleButton(); ImGui::EndDisabled(); } } diff --git a/soh/soh/Enhancements/randomizer/tricks.h b/soh/soh/Enhancements/randomizer/tricks.h index b3b635555..ddaa0f27c 100644 --- a/soh/soh/Enhancements/randomizer/tricks.h +++ b/soh/soh/Enhancements/randomizer/tricks.h @@ -28,7 +28,7 @@ namespace Rando { static const std::string GetTagName(Tag tag); static const ImVec4 GetTextColor(Tag tag); static const ImVec4 GetTagColor(Tag tag); - static void DrawTagChips(const std::set& rtTags); + static void DrawTagChips(const std::set& rtTags, std::string trickName = ""); }; } diff --git a/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp deleted file mode 100644 index a18aea8dc..000000000 --- a/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp +++ /dev/null @@ -1,489 +0,0 @@ -#include "ResolutionEditor.h" -#include -#include - -#include "soh/SohGui/UIWidgets.hpp" -#include -#include "soh/OTRGlobals.h" -#include "soh/cvar_prefixes.h" - -/* Console Variables are grouped under gAdvancedResolution. (e.g. CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled") - - The following cvars are used in Libultraship and can be edited here: - - Enabled - Turns Advanced Resolution Mode on. - - AspectRatioX, AspectRatioY - Aspect ratio controls. To toggle off, set either to zero. - - VerticalPixelCount, VerticalResolutionToggle - Resolution controls. - - PixelPerfectMode, IntegerScale.Factor - Pixel Perfect Mode a.k.a. integer scaling controls. - - IntegerScale.FitAutomatically - Automatic resizing for Pixel Perfect Mode. - - IntegerScale.NeverExceedBounds - Prevents manual resizing from exceeding screen bounds. - - The following cvars are also implemented in LUS for niche use cases: - - IgnoreAspectCorrection - Stretch framebuffer to fill screen. - This is something of a power-user setting for niche setups that most people won't need or care about, - but may be useful if playing the Switch/Wii U ports on a 4:3 television. - - IntegerScale.ExceedBoundsBy - Offset the max screen bounds, usually by +1. - This isn't that useful at the moment, so it's unused here. -*/ - -namespace AdvancedResolutionSettings { -enum setting { UPDATE_aspectRatioX, UPDATE_aspectRatioY, UPDATE_verticalPixelCount }; - -const char* aspectRatioPresetLabels[] = { - "Off", "Custom", "Original (4:3)", "Widescreen (16:9)", "Nintendo 3DS (5:3)", "16:10 (8:5)", "Ultrawide (21:9)" -}; -const float aspectRatioPresetsX[] = { 0.0f, 16.0f, 4.0f, 16.0f, 5.0f, 16.0f, 21.0f }; -const float aspectRatioPresetsY[] = { 0.0f, 9.0f, 3.0f, 9.0f, 3.0f, 10.0f, 9.0f }; -const int default_aspectRatio = 1; // Default combo list option - -const char* pixelCountPresetLabels[] = { "Custom", "Native N64 (240p)", "2x (480p)", "3x (720p)", "4x (960p)", - "5x (1200p)", "6x (1440p)", "Full HD (1080p)", "4K (2160p)" }; -const int pixelCountPresets[] = { 480, 240, 480, 720, 960, 1200, 1440, 1080, 2160 }; -const int default_pixelCount = 0; // Default combo list option - -// Resolution clamp values as hardcoded in LUS::Gui::ApplyResolutionChanges() -const uint32_t minVerticalPixelCount = SCREEN_HEIGHT; -const uint32_t maxVerticalPixelCount = 4320; // 18x native, or 8K TV resolution - -const unsigned short default_maxIntegerScaleFactor = 6; // Default size of Integer scale factor slider. - -enum messageType { MESSAGE_ERROR, MESSAGE_WARNING, MESSAGE_QUESTION, MESSAGE_INFO, MESSAGE_GRAY_75 }; -const ImVec4 messageColor[]{ - { 0.85f, 0.0f, 0.0f, 1.0f }, // MESSAGE_ERROR - { 0.85f, 0.85f, 0.0f, 1.0f }, // MESSAGE_WARNING - { 0.0f, 0.85f, 0.85f, 1.0f }, // MESSAGE_QUESTION - { 0.0f, 0.85f, 0.55f, 1.0f }, // MESSAGE_INFO - { 0.75f, 0.75f, 0.75f, 1.0f } // MESSAGE_GRAY_75 -}; -const float enhancementSpacerHeight = 19.0f; - -void AdvancedResolutionSettingsWindow::InitElement() { -} - -void AdvancedResolutionSettingsWindow::DrawElement() { - // Initialise update flags. - bool update[3]; - for (uint8_t i = 0; i < sizeof(update); i++) - update[i] = false; - - // Initialise integer scale bounds. - short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get - // overridden depending on viewport res - - short integerScale_maximumBounds = 1; // can change when window is resized - // This is mostly just for UX purposes, as Fit Automatically logic is part of LUS. - if (((float)gfx_current_game_window_viewport.width / gfx_current_game_window_viewport.height) > - ((float)gfx_current_dimensions.width / gfx_current_dimensions.height)) { - // Scale to window height - integerScale_maximumBounds = gfx_current_game_window_viewport.height / gfx_current_dimensions.height; - } else { - // Scale to window width - integerScale_maximumBounds = gfx_current_game_window_viewport.width / gfx_current_dimensions.width; - } - // Lower-clamping maximum bounds value to 1 is no-longer necessary as that's accounted for in LUS. - // Letting it go below 1 in this Editor will even allow for checking if screen bounds are being exceeded. - if (default_maxIntegerScaleFactor < integerScale_maximumBounds) { - max_integerScaleFactor = - integerScale_maximumBounds + CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0); - } - - // Combo List defaults - static int item_aspectRatio = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", 3); - static int item_pixelCount = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", default_pixelCount); - // Stored Values for non-UIWidgets elements - static float aspectRatioX = - CVarGetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioPresetsX[item_aspectRatio]); - static float aspectRatioY = - CVarGetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioPresetsY[item_aspectRatio]); - static int verticalPixelCount = - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", pixelCountPresets[item_pixelCount]); - // Additional settings - static bool showHorizontalResField = false; - static int horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; - // Disabling flags - const bool disabled_everything = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); - const bool disabled_pixelCount = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0); - -#ifdef __APPLE__ - // Display HiDPI warning. (Remove this once we can definitively say it's fixed.) - ImGui::TextColored(messageColor[MESSAGE_INFO], - ICON_FA_INFO_CIRCLE " These settings may behave incorrectly on Retina displays."); - UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); -#endif - - if (ImGui::CollapsingHeader("Original Settings", ImGuiTreeNodeFlags_DefaultOpen)) { - // The original resolution slider (for convenience) - const bool disabled_resolutionSlider = (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0) && - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0)) || - CVarGetInteger(CVAR_LOW_RES_MODE, 0); - if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %.1f%%", "##IMul", CVAR_INTERNAL_RESOLUTION, 0.5f, - 2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) { - Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier( - CVarGetFloat(CVAR_INTERNAL_RESOLUTION, 1)); - } - UIWidgets::Tooltip("Multiplies your output resolution by the value entered."); - - // The original MSAA slider (also for convenience) -#ifndef __WIIU__ - if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", CVAR_MSAA_VALUE, 1, 8, "", 1, true, true, - false)) { - Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger(CVAR_MSAA_VALUE, 1)); - }; - UIWidgets::Tooltip( - "Activates multi-sample anti-aliasing when above 1x, up to 8x for 8 samples for every pixel.\n\n" - " " ICON_FA_INFO_CIRCLE - " (Higher MSAA with low resolution can approximate an authentic \"real N64\" look!)"); -#endif - - // N64 Mode toggle (again for convenience) - // UIWidgets::PaddedEnhancementCheckbox("(Enhancements>Graphics) N64 Mode", CVAR_LOW_RES_MODE, false, false, false, "", UIWidgets::CheckboxGraphics::Cross, false); - } - - UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); - // Activator - UIWidgets::PaddedEnhancementCheckbox("Enable advanced settings.", CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", false, false, - false, "", UIWidgets::CheckboxGraphics::Cross, false); - // Error/Warning display - if (!CVarGetInteger(CVAR_LOW_RES_MODE, 0)) { - if (IsDroppingFrames()) { // Significant frame drop warning - ImGui::TextColored(messageColor[MESSAGE_WARNING], - ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring."); - UIWidgets::Spacer(2); - } else { // No warnings - UIWidgets::Spacer(enhancementSpacerHeight); - } - } else { // N64 Mode warning - ImGui::TextColored(messageColor[MESSAGE_QUESTION], - ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings."); - ImGui::SameLine(); - if (ImGui::Button("Click to disable")) { - CVarSetInteger(CVAR_LOW_RES_MODE, 0); - CVarSave(); - } - } - // Resolution visualiser - ImGui::Text("Viewport dimensions: %d x %d", gfx_current_game_window_viewport.width, - gfx_current_game_window_viewport.height); - ImGui::Text("Internal resolution: %d x %d", gfx_current_dimensions.width, gfx_current_dimensions.height); - - UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); - if (disabled_everything) { // Hide aspect ratio controls. - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - // Aspect Ratio - ImGui::Text("Force aspect ratio:"); - ImGui::SameLine(); - ImGui::TextColored(messageColor[MESSAGE_GRAY_75], "(Select \"Off\" to disable.)"); - // Presets - if (ImGui::Combo(" ", &item_aspectRatio, aspectRatioPresetLabels, - IM_ARRAYSIZE(aspectRatioPresetLabels)) && - item_aspectRatio != default_aspectRatio) { // don't change anything if "Custom" is selected. - aspectRatioX = aspectRatioPresetsX[item_aspectRatio]; - aspectRatioY = aspectRatioPresetsY[item_aspectRatio]; - - if (showHorizontalResField) { - horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; - } - - CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX); - CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY); - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio); - CVarSave(); - } - // Hide aspect ratio input fields if using one of the presets. - if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) { - // Declare input interaction bools outside of IF statement to prevent Y field from disappearing. - const bool input_X = ImGui::InputFloat("X", &aspectRatioX, 0.1f, 1.0f, "%.3f"); - const bool input_Y = ImGui::InputFloat("Y", &aspectRatioY, 0.1f, 1.0f, "%.3f"); - if (input_X || input_Y) { - item_aspectRatio = default_aspectRatio; - update[UPDATE_aspectRatioX] = true; - update[UPDATE_aspectRatioY] = true; - } - } else if (showHorizontalResField) { // Show calculated aspect ratio - if (item_aspectRatio) { - UIWidgets::Spacer(2); - const float resolvedAspectRatio = (float)gfx_current_dimensions.width / gfx_current_dimensions.height; - ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio); - } else { - UIWidgets::Spacer(enhancementSpacerHeight); - } - } - - if (disabled_everything) { // Hide aspect ratio controls. - UIWidgets::ReEnableComponent("disabledTooltipText"); - } - UIWidgets::Spacer(0); - - // Vertical Resolution - UIWidgets::PaddedEnhancementCheckbox("Set fixed vertical resolution (disables Resolution slider)", - CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", true, false, - disabled_everything, "", UIWidgets::CheckboxGraphics::Cross, false); - UIWidgets::Tooltip( - "Override the resolution scale slider and use the settings below, irrespective of window size."); - if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls. - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - if (ImGui::Combo("Pixel Count Presets", &item_pixelCount, pixelCountPresetLabels, - IM_ARRAYSIZE(pixelCountPresetLabels)) && - item_pixelCount != default_pixelCount) { // don't change anything if "Custom" is selected. - verticalPixelCount = pixelCountPresets[item_pixelCount]; - - if (showHorizontalResField) { - horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; - } - - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount); - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount); - CVarSave(); - } - // Horizontal Resolution, if visibility is enabled for it. - if (showHorizontalResField) { - // Only show the field if Aspect Ratio is being enforced. - if ((aspectRatioX > 0.0f) && (aspectRatioY > 0.0f)) { - // So basically we're "faking" this one by setting aspectRatioX instead. - if (ImGui::InputInt("Horiz. Pixel Count", &horizontalPixelCount, 8, 320)) { - item_aspectRatio = default_aspectRatio; - if (horizontalPixelCount < SCREEN_WIDTH) { - horizontalPixelCount = SCREEN_WIDTH; - } - aspectRatioX = horizontalPixelCount; - aspectRatioY = verticalPixelCount; - update[UPDATE_aspectRatioX] = true; - update[UPDATE_aspectRatioY] = true; - } - } else { // Display a notice instead. - ImGui::TextColored(messageColor[MESSAGE_QUESTION], - ICON_FA_QUESTION_CIRCLE " \"Force aspect ratio\" required."); - // ImGui::Text(" "); - ImGui::SameLine(); - if (ImGui::Button("Click to resolve")) { - item_aspectRatio = default_aspectRatio; // Set it to Custom - aspectRatioX = aspectRatioPresetsX[2]; // but use the 4:3 defaults - aspectRatioY = aspectRatioPresetsY[2]; - update[UPDATE_aspectRatioX] = true; - update[UPDATE_aspectRatioY] = true; - horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; - } - } - } - // Vertical Resolution part 2 - if (ImGui::InputInt("Vertical Pixel Count", &verticalPixelCount, 8, 240)) { - item_pixelCount = default_pixelCount; - update[UPDATE_verticalPixelCount] = true; - - // Account for the natural instinct to enter horizontal first. - // Ignore vertical resolutions that are below the lower clamp constant. - if (showHorizontalResField && !(verticalPixelCount < minVerticalPixelCount)) { - item_aspectRatio = default_aspectRatio; - aspectRatioX = horizontalPixelCount; - aspectRatioY = verticalPixelCount; - update[UPDATE_aspectRatioX] = true; - update[UPDATE_aspectRatioY] = true; - } - } - if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls. - UIWidgets::ReEnableComponent("disabledTooltipText"); - } - - UIWidgets::Spacer(0); - - // Integer scaling settings group (Pixel-perfect Mode) - static const ImGuiTreeNodeFlags IntegerScalingResolvedImGuiFlag = - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) ? ImGuiTreeNodeFlags_DefaultOpen - : ImGuiTreeNodeFlags_None; - if (ImGui::CollapsingHeader("Integer Scaling Settings", IntegerScalingResolvedImGuiFlag)) { - const bool disabled_pixelPerfectMode = - !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || disabled_everything; - // Pixel-perfect Mode - UIWidgets::PaddedEnhancementCheckbox("Pixel-perfect Mode", CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", true, - true, disabled_pixelCount || disabled_everything, "", - UIWidgets::CheckboxGraphics::Cross, false); - UIWidgets::Tooltip("Don't scale image to fill window."); - if (disabled_pixelCount && CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0)) { - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0); - CVarSave(); - } - - // Integer Scaling - UIWidgets::EnhancementSliderInt( - "Integer scale factor: %d", "##ARSIntScale", CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", 1, - max_integerScaleFactor, "%d", 1, true, - disabled_pixelPerfectMode || CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0)); - UIWidgets::Tooltip("Integer scales the image. Only available in pixel-perfect mode."); - // Display warning if size is being clamped or if framebuffer is larger than viewport. - if (!disabled_pixelPerfectMode && - (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", 1) && - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", 1) > integerScale_maximumBounds)) { - ImGui::SameLine(); - ImGui::TextColored(messageColor[MESSAGE_WARNING], ICON_FA_EXCLAMATION_TRIANGLE " Window exceeded."); - } - - UIWidgets::PaddedEnhancementCheckbox( - "Automatically scale image to fit viewport", CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", true, - true, disabled_pixelPerfectMode, "", UIWidgets::CheckboxGraphics::Cross, false); - UIWidgets::Tooltip("Automatically sets scale factor to fit window. Only available in pixel-perfect mode."); - if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0)) { - // This is just here to update the value shown on the slider. - // The function in LUS to handle this setting will ignore IntegerScaleFactor while active. - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", integerScale_maximumBounds); - // CVarSave(); - } - } // End of integer scaling settings - - UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); - - // Collapsible panel for additional settings - if (ImGui::CollapsingHeader("Additional Settings")) { - UIWidgets::Spacer(0); - -#if defined(__SWITCH__) || defined(__WIIU__) - // Disable aspect correction, stretching the framebuffer to fill the viewport. - // This option is only really needed on systems limited to 16:9 TV resolutions, such as consoles. - // The associated cvar is still functional on PC platforms if you want to use it though. - UIWidgets::PaddedEnhancementCheckbox("Disable aspect correction and stretch the output image.\n" - "(Might be useful for 4:3 televisions!)\n" - "Not available in Pixel Perfect Mode.", - CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", false, true, - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || - disabled_everything, - "", UIWidgets::CheckboxGraphics::Cross, false); -#else - if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", 0)) { - // This setting is intentionally not exposed on PC platforms, - // but may be accidentally activated for varying reasons. - // Having this button should hopefully prevent support headaches. - ImGui::TextColored(messageColor[MESSAGE_QUESTION], ICON_FA_QUESTION_CIRCLE - " If the image is stretched and you don't know why, click this."); - if (ImGui::Button("Click to reenable aspect correction.")) { - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", 0); - CVarSave(); - } - UIWidgets::Spacer(2); - } -#endif - - // A requested addition; an alternative way of displaying the resolution field. - if (ImGui::Checkbox("Show a horizontal resolution field, instead of aspect ratio.", &showHorizontalResField)) { - if (!showHorizontalResField && (aspectRatioX > 0.0f)) { // when turning this setting off - // Refresh relevant values - aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; - horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; - } else { // when turning this setting on - item_aspectRatio = default_aspectRatio; - if (aspectRatioX > 0.0f) { - // Refresh relevant values in the opposite order - horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; - aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; - } - } - update[UPDATE_aspectRatioX] = true; - } - - // Beginning of Integer Scaling additional settings. - { - // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); - - // Integer Scaling - Never Exceed Bounds. - const bool disabled_neverExceedBounds = - !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0) || disabled_everything; - const bool checkbox_neverExceedBounds = - UIWidgets::PaddedEnhancementCheckbox("Prevent integer scaling from exceeding screen bounds.\n" - "(Makes screen bounds take priority over specified factor.)", - CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", - true, false, disabled_neverExceedBounds, "", - UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip( - "Prevents integer scaling factor from exceeding screen bounds.\n\n" - "Enabled: Will clamp the scaling factor and display a gentle warning in the resolution editor.\n" - "Disabled: Will allow scaling to exceed screen bounds, for users who want to crop overscan.\n\n" - " " ICON_FA_INFO_CIRCLE - " Please note that exceeding screen bounds may show a scroll bar on-screen."); - - // Initialise the (currently unused) "Exceed Bounds By" cvar if it's been changed. - if (checkbox_neverExceedBounds && - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) { - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0); - CVarSave(); - } - - // Integer Scaling - Exceed Bounds By 1x/Offset. - // A popular feature in some retro frontends/upscalers, sometimes called "crop overscan" or "1080p 5x". - /* - UIWidgets::PaddedEnhancementCheckbox("Allow integer scale factor to go +1 above maximum screen bounds.", CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", false, false, !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || disabled_everything, "", UIWidgets::CheckboxGraphics::Cross, false); - */ - // It does actually function as expected, but exceeding the bottom of the screen shows a scroll bar. - // I've ended up commenting this one out because of the scroll bar, and for simplicity. - - // Display an info message about the scroll bar. - if (!CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", 1) || - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) { - if (disabled_neverExceedBounds) { // Dim this help text accordingly - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - ImGui::TextColored(messageColor[MESSAGE_INFO], - " " ICON_FA_INFO_CIRCLE - " A scroll bar may become visible if screen bounds are exceeded."); - if (disabled_neverExceedBounds) { // Dim this help text accordingly - UIWidgets::ReEnableComponent("disabledTooltipText"); - } - - // Another support helper button, to disable the unused "Exceed Bounds By" cvar. - // (Remove this button if uncommenting the checkbox.) - if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) { - if (ImGui::Button("Click to reset a console variable that may be causing this.")) { - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0); - CVarSave(); - } - } - } else { - ImGui::Text(" "); - } - // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); - } // End of Integer Scaling additional settings. - - } // End of additional settings - - // Clamp and update the cvars that don't use UIWidgets - if (update[UPDATE_aspectRatioX] || update[UPDATE_aspectRatioY] || update[UPDATE_verticalPixelCount]) { - if (update[UPDATE_aspectRatioX]) { - if (aspectRatioX < 0.0f) { - aspectRatioX = 0.0f; - } - CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX); - } - if (update[UPDATE_aspectRatioY]) { - if (aspectRatioY < 0.0f) { - aspectRatioY = 0.0f; - } - CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY); - } - if (update[UPDATE_verticalPixelCount]) { - // There's a upper and lower clamp on the Libultraship side too, - // so clamping it here is entirely visual, so the vertical resolution field reflects it. - if (verticalPixelCount < minVerticalPixelCount) { - verticalPixelCount = minVerticalPixelCount; - } - if (verticalPixelCount > maxVerticalPixelCount) { - verticalPixelCount = maxVerticalPixelCount; - } - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount); - } - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio); - CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount); - CVarSave(); - } -} - -void AdvancedResolutionSettingsWindow::UpdateElement() { -} - -bool AdvancedResolutionSettingsWindow::IsDroppingFrames() { - // a rather imprecise way of checking for frame drops. - // but it's mostly there to inform the player of large drops. - const short targetFPS = CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 20); - const float threshold = targetFPS / 20.0f + 4.1f; - return ImGui::GetIO().Framerate < targetFPS - threshold; -} -} // namespace AdvancedResolutionSettings diff --git a/soh/soh/Enhancements/resolution-editor/ResolutionEditor.h b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.h deleted file mode 100644 index f75931451..000000000 --- a/soh/soh/Enhancements/resolution-editor/ResolutionEditor.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include - -namespace AdvancedResolutionSettings { -class AdvancedResolutionSettingsWindow : public Ship::GuiWindow { - private: - bool IsDroppingFrames(); - - public: - using GuiWindow::GuiWindow; - - void InitElement() override; - void DrawElement() override; - void UpdateElement() override; -}; -} // namespace AdvancedResolutionSettings diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index eee2a0e9b..1db34b4f6 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -23,6 +23,7 @@ extern "C" { #include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h" #include "src/overlays/actors/ovl_En_Tk/z_en_tk.h" #include "src/overlays/actors/ovl_En_Fu/z_en_fu.h" +#include "src/overlays/actors/ovl_En_Daiku/z_en_daiku.h" #include "src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h" #include "src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h" #include "src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.h" @@ -42,6 +43,8 @@ extern void BgSpot03Taki_ApplyOpeningAlpha(BgSpot03Taki* bgSpot03Taki, s32 buffe extern void EnGo2_CurledUp(EnGo2* enGo2, PlayState* play); extern void EnRu2_SetEncounterSwitchFlag(EnRu2* enRu2, PlayState* play); + +extern void EnDaiku_EscapeSuccess(EnDaiku* enDaiku, PlayState* play); } #define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get() @@ -354,6 +357,18 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li !(gPlayState->sceneNum == SCENE_ZORAS_RIVER && IS_RANDO && RAND_GET_OPTION(RSK_FROGS_HINT))) { *should = false; } + + // If it's near a jailed carpenter, skip it along with introduction of Gerudo mini-boss + if (gPlayState->sceneNum == SCENE_THIEVES_HIDEOUT && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) { + EnWonderTalk2* enWonderTalk = va_arg(args, EnWonderTalk2*); + EnDaiku* enDaiku = + (EnDaiku*)Actor_FindNearby(gPlayState, &enWonderTalk->actor, ACTOR_EN_DAIKU, ACTORCAT_NPC, 999.0f); + if (enDaiku != NULL) { + Flags_SetSwitch(gPlayState, enDaiku->startFightSwitchFlag); + *should = false; + } + } break; } case VB_NAVI_TALK: { @@ -371,6 +386,12 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } break; } + case VB_END_GERUDO_MEMBERSHIP_TALK: { + if (ForcedDialogIsDisabled(FORCED_DIALOG_SKIP_NPC)) { + *should = true; + } + break; + } case VB_GORON_LINK_BE_SCARED: { if (ForcedDialogIsDisabled(FORCED_DIALOG_SKIP_NPC)) { EnGo2* goronLink = va_arg(args, EnGo2*); @@ -551,6 +572,17 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } break; } + case VB_PLAY_CARPENTER_FREE_CS: { + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) { + EnDaiku* enDaiku = va_arg(args, EnDaiku*); + if (enDaiku->subCamActive) { + enDaiku->subCamActive = false; + EnDaiku_EscapeSuccess(enDaiku, gPlayState); + } + *should = false; + } + break; + } case VB_PLAY_GORON_FREE_CS: { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { *should = false; @@ -1034,6 +1066,11 @@ void TimeSaverOnSceneInitHandler(int16_t sceneNum) { static GetItemEntry vanillaQueuedItemEntry = GET_ITEM_NONE; void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) { + // Do nothing when in a boss rush + if (IS_BOSS_RUSH) { + return; + } + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { switch (flagType) { case FLAG_EVENT_CHECK_INF: @@ -1106,12 +1143,18 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) { break; case FLAG_ITEM_GET_INF: switch (flag) { - case ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE: - vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DEKU_STICK_CAPACITY_30).GetGIEntry_Copy(); + case ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE: { + RandomizerGet stickUpgrade = + CUR_UPG_VALUE(UPG_STICKS) == 2 ? RG_DEKU_STICK_CAPACITY_30 : RG_DEKU_STICK_CAPACITY_20; + vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(stickUpgrade).GetGIEntry_Copy(); break; - case ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE: - vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DEKU_NUT_CAPACITY_40).GetGIEntry_Copy(); + } + case ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE: { + RandomizerGet nutUpgrade = + CUR_UPG_VALUE(UPG_NUTS) == 2 ? RG_DEKU_NUT_CAPACITY_40 : RG_DEKU_NUT_CAPACITY_30; + vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(nutUpgrade).GetGIEntry_Copy(); break; + } } break; } diff --git a/soh/soh/Enhancements/timesplits/TimeSplits.cpp b/soh/soh/Enhancements/timesplits/TimeSplits.cpp index de9e157c3..046536f45 100644 --- a/soh/soh/Enhancements/timesplits/TimeSplits.cpp +++ b/soh/soh/Enhancements/timesplits/TimeSplits.cpp @@ -1,5 +1,4 @@ #include "TimeSplits.h" -#include "soh/SohGui/UIWidgets.hpp" #include "soh/Enhancements/gameplaystats.h" #include "soh/SaveManager.h" #include "soh/util.h" @@ -14,12 +13,16 @@ #include "soh/Enhancements/debugger/debugSaveEditor.h" #include "soh_assets.h" #include "assets/textures/parameter_static/parameter_static.h" +#include +#include "soh/SohGui/UIWidgets.hpp" extern "C" { extern SaveContext gSaveContext; extern PlayState* gPlayState; } +using namespace UIWidgets; + // ImVec4 Colors #define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f) #define COLOR_LIGHT_RED ImVec4(1.0f, 0.05f, 0.0f, 1.0f) @@ -636,59 +639,60 @@ void TimeSplitsDrawSplitsList() { uint32_t dragIndex = 0; ImGui::BeginChild("SplitTable", ImVec2(0.0f, ImGui::GetWindowHeight() - 128.0f)); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4, 0)); - ImGui::BeginTable("Splits", 5, ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable); - ImGui::TableSetupColumn("Item Image", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHeaderLabel, 34.0f); - ImGui::TableSetupColumn("Item Name"); - ImGui::TableSetupColumn("Current Time"); - ImGui::TableSetupColumn("+/-"); - ImGui::TableSetupColumn("Prev. Best"); - ImGui::TableHeadersRow(); + if (ImGui::BeginTable("Splits", 5, ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable)) { + ImGui::TableSetupColumn("Item Image", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHeaderLabel, 34.0f); + ImGui::TableSetupColumn("Item Name"); + ImGui::TableSetupColumn("Current Time"); + ImGui::TableSetupColumn("+/-"); + ImGui::TableSetupColumn("Prev. Best"); + ImGui::TableHeadersRow(); - SplitsPushImageButtonStyle(); - for (auto& split : splitList) { - ImGui::TableNextColumn(); - TimeSplitsSplitBestTimeDisplay(split); - - ImGui::PushID(split.splitID); - if (split.splitTimeStatus == SPLIT_STATUS_ACTIVE) { - ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(47, 79, 90, 255)); + SplitsPushImageButtonStyle(); + for (auto& split : splitList) { + ImGui::TableNextColumn(); + TimeSplitsSplitBestTimeDisplay(split); + + ImGui::PushID(split.splitID); + if (split.splitTimeStatus == SPLIT_STATUS_ACTIVE) { + ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(47, 79, 90, 255)); + } + TimeSplitsGetImageSize(split.splitID); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(imagePadding, imagePadding)); + auto ret = ImGui::ImageButton(split.splitImage.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(split.splitImage), + imageSize, ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), split.splitTint); + ImGui::PopStyleVar(); + if (ret) { + TimeSplitsSkipSplit(dragIndex); + } + HandleDragAndDrop(splitList, dragIndex, split.splitName); + ImGui::TableNextColumn(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 5.0f)); + ImGui::AlignTextToFramePadding(); + ImGui::TextWrapped("%s", split.splitName.c_str()); + ImGui::TableNextColumn(); + // Current Time + ImGui::Text("%s", (split.splitTimeStatus == SPLIT_STATUS_ACTIVE) + ? formatTimestampTimeSplit(GAMEPLAYSTAT_TOTAL_TIME).c_str() : (split.splitTimeStatus == SPLIT_STATUS_COLLECTED) + ? formatTimestampTimeSplit(split.splitTimeCurrent).c_str() : "--:--:-"); + ImGui::TableNextColumn(); + // +/- Difference + ImGui::TextColored(splitTimeColor, "%s", formatTimestampTimeSplit(splitBestTimeDisplay).c_str()); + ImGui::TableNextColumn(); + // Previous Best + ImGui::Text("%s", (split.splitTimePreviousBest != 0) ? formatTimestampTimeSplit(split.splitTimePreviousBest).c_str() : "--:--:-"); + ImGui::PopID(); + ImGui::PopStyleVar(1); + + dragIndex++; } - TimeSplitsGetImageSize(split.splitID); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(imagePadding, imagePadding)); - auto ret = ImGui::ImageButton(split.splitImage.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(split.splitImage), - imageSize, ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), split.splitTint); - ImGui::PopStyleVar(); - if (ret) { - TimeSplitsSkipSplit(dragIndex); - } - HandleDragAndDrop(splitList, dragIndex, split.splitName); - ImGui::TableNextColumn(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 5.0f)); - ImGui::AlignTextToFramePadding(); - ImGui::TextWrapped("%s", split.splitName.c_str()); - ImGui::TableNextColumn(); - // Current Time - ImGui::Text("%s", (split.splitTimeStatus == SPLIT_STATUS_ACTIVE) - ? formatTimestampTimeSplit(GAMEPLAYSTAT_TOTAL_TIME).c_str() : (split.splitTimeStatus == SPLIT_STATUS_COLLECTED) - ? formatTimestampTimeSplit(split.splitTimeCurrent).c_str() : "--:--:-"); - ImGui::TableNextColumn(); - // +/- Difference - ImGui::TextColored(splitTimeColor, "%s", formatTimestampTimeSplit(splitBestTimeDisplay).c_str()); - ImGui::TableNextColumn(); - // Previous Best - ImGui::Text("%s", (split.splitTimePreviousBest != 0) ? formatTimestampTimeSplit(split.splitTimePreviousBest).c_str() : "--:--:-"); - ImGui::PopID(); - ImGui::PopStyleVar(1); + SplitsPopImageButtonStyle(); - dragIndex++; + TimeSplitsPostDragAndDrop(); + + ImGui::EndTable(); } - SplitsPopImageButtonStyle(); - - TimeSplitsPostDragAndDrop(); - - ImGui::PopStyleVar(1); - ImGui::EndTable(); + ImGui::PopStyleVar(); ImGui::EndChild(); } @@ -769,7 +773,7 @@ void TimeSplitsDrawItemList(uint32_t type) { } void TimeSplitsUpdateWindowSize() { - timeSplitsWindowSize = CVarGetFloat(CVAR_ENHANCEMENT("TimeSplits.WindowSize"), 0); + timeSplitsWindowSize = CVarGetFloat(CVAR_ENHANCEMENT("TimeSplits.WindowScale"), 0); if (timeSplitsWindowSize < 1.0f) { timeSplitsWindowSize = 1.0f; } @@ -777,24 +781,13 @@ void TimeSplitsUpdateWindowSize() { void TimeSplitsDrawOptionsMenu() { ImGui::SeparatorText("Window Options"); - if (ImGui::ColorEdit4("Background Color", (float*)&windowColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) { - Color_RGBA8 color; - color.r = windowColor.x * 255.0; - color.g = windowColor.y * 255.0; - color.b = windowColor.z * 255.0; - color.a = windowColor.w * 255.0; - CVarSetColor("TimeSplits.WindowColor", color); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - ImGui::SameLine(); - if (ImGui::Button("Reset")) { - windowColor = { 0.0f, 0.0f, 0.0f, 1.0f }; - CVarSetColor("TimeSplits.WindowColor", {0, 0, 0, 1}); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + Color_RGBA8 defaultColor = { 0, 0, 0, 255 }; + if (CVarColorPicker("Background Color", CVAR_ENHANCEMENT("TimeSplits.WindowColor"), defaultColor, true, 0, THEME_COLOR)) { + windowColor = VecFromRGBA8(CVarGetColor(CVAR_ENHANCEMENT("TimeSplits.WindowColor.Value"), defaultColor)); } - if (UIWidgets::PaddedEnhancementSliderFloat("Window Size: %.1fx", "##windowSize", - CVAR_ENHANCEMENT("TimeSplits.WindowSize"), 1.0f, 3.0f, "", 1.0f, false, true, true, false)) { + if (CVarSliderFloat("Window Scale", CVAR_ENHANCEMENT("TimeSplits.WindowScale"), + FloatSliderOptions().Min(1.0f).Max(3.0f).DefaultValue(1.0f).Format("%.1fx").Size({300.0f, 0.0f}).Step(0.1f).Color(THEME_COLOR))) { TimeSplitsUpdateWindowSize(); } @@ -802,55 +795,44 @@ void TimeSplitsDrawOptionsMenu() { ImGui::Text("New List Name: "); ImGui::PushItemWidth(150.0f); + PushStyleInput(THEME_COLOR); ImGui::InputText("##listName", listNameBuf, 25); + PopStyleInput(); ImGui::PopItemWidth(); ImGui::SameLine(); - if (ImGui::Button("Create List")) { + if (Button("Create List", ButtonOptions().Color(THEME_COLOR).Size(Sizes::Inline))) { TimeSplitsFileManagement(SPLIT_ACTION_SAVE, listNameBuf, splitList); } UIWidgets::PaddedSeparator(); TimeSplitsFileManagement(SPLIT_ACTION_COLLECT, "", emptyList); static uint32_t selectedItem = 0; - static std::string listItem = keys[0]; ImGui::Text("Select List to Load: "); ImGui::PushItemWidth(150.0f); - if (ImGui::BeginCombo("##listEntries", keys[selectedItem].c_str())) { - for (int i = 0; i < keys.size(); i++) { - bool isSelected = (selectedItem == i); - if (ImGui::Selectable(keys[i].c_str(), isSelected)) { - selectedItem = i; - listItem = keys[i].c_str(); - } - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } + Combobox("", &selectedItem, keys, ComboboxOptions().Color(THEME_COLOR).LabelPosition(LabelPositions::Near)); ImGui::PopItemWidth(); ImGui::SameLine(); - if (ImGui::Button("Load List")) { + if (Button("Load List", ButtonOptions().Color(THEME_COLOR).Size(Sizes::Inline))) { TimeSplitsFileManagement(SPLIT_ACTION_LOAD, keys[selectedItem].c_str(), emptyList); } ImGui::SameLine(); - if (ImGui::Button("Save List")) { + if (Button("Save List", ButtonOptions().Color(THEME_COLOR).Size(Sizes::Inline))) { TimeSplitsFileManagement(SPLIT_ACTION_SAVE, keys[selectedItem].c_str(), splitList); } ImGui::SameLine(); - if (ImGui::Button("Delete List")) { + if (Button("Delete List", ButtonOptions().Color(THEME_COLOR).Size(Sizes::Inline))) { TimeSplitsFileManagement(SPLIT_ACTION_DELETE, keys[selectedItem].c_str(), emptyList); } - UIWidgets::PaddedSeparator(); + UIWidgets::Separator(true, true, ImGui::GetStyle().ItemSpacing.y, ImGui::GetStyle().ItemSpacing.y); - if (ImGui::Button("New Attempt")) { + if (Button("New Attempt", ButtonOptions().Color(THEME_COLOR).Size(Sizes::Inline))) { for (auto& data : splitList) { data.splitTimeStatus = SPLIT_STATUS_INACTIVE; } splitList[0].splitTimeStatus = SPLIT_STATUS_ACTIVE; } ImGui::SameLine(); - if (ImGui::Button("Update Splits")) { + if (Button("Update Splits", ButtonOptions().Color(THEME_COLOR).Size(Sizes::Inline))) { TimeSplitsFileManagement(SPLIT_ACTION_UPDATE, keys[selectedItem].c_str(), splitList); } } @@ -864,75 +846,80 @@ void TimeSplitsRemoveSplitEntry(uint32_t index) { void TimeSplitsDrawManageList() { uint32_t index = 0; - ImGui::BeginTable("List Management", 2, ImGuiTableFlags_BordersInnerV); - ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthFixed, 60.0f); - ImGui::TableSetupColumn("Options", ImGuiTableColumnFlags_NoHeaderLabel); + ImGui::BeginChild("SplitTable", ImVec2(0.0f, ImGui::GetWindowHeight() - 128.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4, 0)); + if (ImGui::BeginTable("List Management", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthFixed, 120.0f); + ImGui::TableSetupColumn("Options", ImGuiTableColumnFlags_NoHeaderLabel); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); - ImGui::TableNextColumn(); - ImGui::BeginTabBar("List Preview"); - if (ImGui::BeginTabItem("Preview")) { - ImGui::BeginChild("PreviewChild"); - for (auto& data : splitList) { - float availableWidth = ImGui::GetContentRegionAvail().x; - float imageWidth = 38.0f; // Width of your image button - float offsetX = (availableWidth - imageWidth) * 0.5f; // Centering offset + ImGui::TableNextColumn(); + ImGui::BeginTabBar("List Preview"); + if (ImGui::BeginTabItem("Preview")) { + ImGui::BeginChild("PreviewChild"); + for (auto& data : splitList) { + float availableWidth = ImGui::GetContentRegionAvail().x; + float imageWidth = 38.0f; // Width of your image button + float offsetX = (availableWidth - imageWidth) * 0.5f; // Centering offset - if (offsetX > 0.0f) { - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX); // Apply the offset to center + if (offsetX > 0.0f) { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX); // Apply the offset to center + } + TimeSplitsGetImageSize(data.splitID); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(imagePadding, imagePadding)); + auto ret = ImGui::ImageButton(data.splitImage.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(data.splitImage), + imageSize, ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), data.splitTint); + ImGui::PopStyleVar(); + if (ret) { + removeIndex = index; + } + HandleDragAndDrop(splitList, index, splitList[index].splitName); + index++; } - TimeSplitsGetImageSize(data.splitID); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(imagePadding, imagePadding)); - auto ret = ImGui::ImageButton(data.splitImage.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(data.splitImage), - imageSize, ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), data.splitTint); - ImGui::PopStyleVar(); - if (ret) { - removeIndex = index; - } - HandleDragAndDrop(splitList, index, splitList[index].splitName); - index++; + TimeSplitsRemoveSplitEntry(removeIndex); + ImGui::EndChild(); + ImGui::EndTabItem(); } - TimeSplitsRemoveSplitEntry(removeIndex); - ImGui::EndChild(); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - - ImGui::PopStyleColor(3); - ImGui::TableNextColumn(); - ImGui::BeginTabBar("List Options"); - if (ImGui::BeginTabItem("Equipment")) { - TimeSplitsDrawItemList(SPLIT_TYPE_EQUIPMENT); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Inventory")) { - TimeSplitsDrawItemList(SPLIT_TYPE_ITEM); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Quest")) { - TimeSplitsDrawItemList(SPLIT_TYPE_QUEST); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Entrances")) { - TimeSplitsDrawItemList(SPLIT_TYPE_ENTRANCE); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Bosses")) { - TimeSplitsDrawItemList(SPLIT_TYPE_BOSS); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Miscellaneous")) { - TimeSplitsDrawItemList(SPLIT_TYPE_MISC); - ImGui::EndTabItem(); - } + ImGui::EndTabBar(); - TimeSplitsPostDragAndDrop(); + ImGui::PopStyleColor(3); + ImGui::TableNextColumn(); + ImGui::BeginTabBar("List Options"); + if (ImGui::BeginTabItem("Equipment")) { + TimeSplitsDrawItemList(SPLIT_TYPE_EQUIPMENT); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Inventory")) { + TimeSplitsDrawItemList(SPLIT_TYPE_ITEM); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Quest")) { + TimeSplitsDrawItemList(SPLIT_TYPE_QUEST); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Entrances")) { + TimeSplitsDrawItemList(SPLIT_TYPE_ENTRANCE); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Bosses")) { + TimeSplitsDrawItemList(SPLIT_TYPE_BOSS); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Miscellaneous")) { + TimeSplitsDrawItemList(SPLIT_TYPE_MISC); + ImGui::EndTabItem(); + } - ImGui::EndTabBar(); - ImGui::EndTable(); + TimeSplitsPostDragAndDrop(); + + ImGui::EndTabBar(); + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + ImGui::EndChild(); } void InitializeSplitDataFile() { @@ -954,14 +941,8 @@ static bool initialized = false; void TimeSplitWindow::DrawElement() { ImGui::SetWindowFontScale(timeSplitsWindowSize); - if (!initialized) { - Color_RGBA8 defaultColour = {0, 0, 0, 255}; - Color_RGBA8 color = CVarGetColor("TimeSplits.WindowColor", defaultColour); - windowColor = {(float)color.r / 255.0f, (float)color.g / 255.0f, (float)color.b / 255.0f, (float)color.a / 255.0f}; - InitializeSplitDataFile(); - initialized = true; - } + PushStyleTabs(THEME_COLOR); if (ImGui::BeginTabBar("Split Tabs")) { if (ImGui::BeginTabItem("Splits")) { TimeSplitsDrawSplitsList(); @@ -977,6 +958,7 @@ void TimeSplitWindow::DrawElement() { } ImGui::EndTabBar(); } + PopStyleTabs(); } void TimeSplitWindow::InitElement() { @@ -984,6 +966,9 @@ void TimeSplitWindow::InitElement() { Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("SPECIAL_TRIFORCE_PIECE_WHITE", gWTriforcePieceTex, ImVec4(1, 1, 1, 1)); Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("SPECIAL_SPLIT_ENTRANCE", gSplitEntranceTex, ImVec4(1, 1, 1, 1)); + Color_RGBA8 defaultColour = {0, 0, 0, 255}; + windowColor = VecFromRGBA8(CVarGetColor(CVAR_ENHANCEMENT("TimeSplits.WindowColor.Value"), defaultColour)); + InitializeSplitDataFile(); GameInteractor::Instance->RegisterGameHook([](u8 item) { if (item != ITEM_SKULL_TOKEN) { diff --git a/soh/soh/Network/CrowdControl/CrowdControl.cpp b/soh/soh/Network/CrowdControl/CrowdControl.cpp index 184d6cad2..34532f73b 100644 --- a/soh/soh/Network/CrowdControl/CrowdControl.cpp +++ b/soh/soh/Network/CrowdControl/CrowdControl.cpp @@ -764,69 +764,4 @@ CrowdControl::Effect* CrowdControl::ParseMessage(nlohmann::json dataReceived) { return effect; } - -void CrowdControl::DrawMenu() { - ImGui::PushID("CrowdControl"); - - static std::string host = CVarGetString(CVAR_REMOTE_CROWD_CONTROL("Host"), "127.0.0.1"); - static uint16_t port = CVarGetInteger(CVAR_REMOTE_CROWD_CONTROL("Port"), 43384); - bool isFormValid = !SohUtils::IsStringEmpty(host) && port > 1024 && port < 65535; - - ImGui::SeparatorText("Crowd Control"); - UIWidgets::Tooltip( - "Crowd Control is a platform that allows viewers to interact " - "with a streamer's game in real time.\n" - "\n" - "Click the question mark to copy the link to the Crowd Control " - "website to your clipboard." - ); - if (ImGui::IsItemClicked()) { - ImGui::SetClipboardText("https://crowdcontrol.live"); - } - - ImGui::BeginDisabled(isEnabled); - ImGui::Text("Host & Port"); - if (UIWidgets::InputString("##Host", &host)) { - CVarSetString(CVAR_REMOTE_CROWD_CONTROL("Host"), host.c_str()); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - - ImGui::SameLine(); - ImGui::PushItemWidth(ImGui::GetFontSize() * 5); - if (ImGui::InputScalar("##Port", ImGuiDataType_U16, &port)) { - CVarSetInteger(CVAR_REMOTE_CROWD_CONTROL("Port"), port); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - ImGui::PopItemWidth(); - ImGui::EndDisabled(); - - ImGui::Spacing(); - - ImGui::BeginDisabled(!isFormValid); - const char* buttonLabel = isEnabled ? "Disable" : "Enable"; - if (ImGui::Button(buttonLabel, ImVec2(-1.0f, 0.0f))) { - if (isEnabled) { - CVarClear(CVAR_REMOTE_CROWD_CONTROL("Enabled")); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - Disable(); - } else { - CVarSetInteger(CVAR_REMOTE_CROWD_CONTROL("Enabled"), 1); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - Enable(); - } - } - ImGui::EndDisabled(); - - if (isEnabled) { - ImGui::Spacing(); - if (isConnected) { - ImGui::Text("Connected"); - } else { - ImGui::Text("Connecting..."); - } - } - - ImGui::PopID(); -} - #endif diff --git a/soh/soh/Network/CrowdControl/CrowdControl.h b/soh/soh/Network/CrowdControl/CrowdControl.h index 534197807..8aff1c96e 100644 --- a/soh/soh/Network/CrowdControl/CrowdControl.h +++ b/soh/soh/Network/CrowdControl/CrowdControl.h @@ -84,7 +84,6 @@ class CrowdControl : public Network { void OnIncomingJson(nlohmann::json payload); void OnConnected(); void OnDisconnected(); - void DrawMenu(); }; #endif // __cplusplus diff --git a/soh/soh/Network/Sail/Sail.cpp b/soh/soh/Network/Sail/Sail.cpp index f9ae7b313..272214115 100644 --- a/soh/soh/Network/Sail/Sail.cpp +++ b/soh/soh/Network/Sail/Sail.cpp @@ -501,72 +501,4 @@ void Sail::RegisterHooks() { }); } -void Sail::DrawMenu() { - ImGui::PushID("Sail"); - - static std::string host = CVarGetString(CVAR_REMOTE_SAIL("Host"), "127.0.0.1"); - static uint16_t port = CVarGetInteger(CVAR_REMOTE_SAIL("Port"), 43384); - bool isFormValid = !SohUtils::IsStringEmpty(host) && port > 1024 && port < 65535; - - ImGui::SeparatorText("Sail"); - UIWidgets::Tooltip( - "Sail is a networking protocol designed to facilitate remote " - "control of the Ship of Harkinian client. It is intended to " - "be utilized alongside a Sail server, for which we provide a " - "few straightforward implementations on our GitHub. The current " - "implementations available allow integration with Twitch chat " - "and SAMMI Bot, feel free to contribute your own!\n" - "\n" - "Click the question mark to copy the link to the Sail Github " - "page to your clipboard." - ); - if (ImGui::IsItemClicked()) { - ImGui::SetClipboardText("https://github.com/HarbourMasters/sail"); - } - - ImGui::BeginDisabled(isEnabled); - ImGui::Text("Host & Port"); - if (UIWidgets::InputString("##Host", &host)) { - CVarSetString(CVAR_REMOTE_SAIL("Host"), host.c_str()); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - - ImGui::SameLine(); - ImGui::PushItemWidth(ImGui::GetFontSize() * 5); - if (ImGui::InputScalar("##Port", ImGuiDataType_U16, &port)) { - CVarSetInteger(CVAR_REMOTE_SAIL("Port"), port); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - ImGui::PopItemWidth(); - ImGui::EndDisabled(); - - ImGui::Spacing(); - - ImGui::BeginDisabled(!isFormValid); - const char* buttonLabel = isEnabled ? "Disable" : "Enable"; - if (ImGui::Button(buttonLabel, ImVec2(-1.0f, 0.0f))) { - if (isEnabled) { - CVarClear(CVAR_REMOTE_SAIL("Enabled")); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - Disable(); - } else { - CVarSetInteger(CVAR_REMOTE_SAIL("Enabled"), 1); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - Enable(); - } - } - ImGui::EndDisabled(); - - if (isEnabled) { - ImGui::Spacing(); - if (isConnected) { - ImGui::Text("Connected"); - } else { - ImGui::Text("Connecting..."); - } - } - - ImGui::PopID(); -} - #endif // ENABLE_REMOTE_CONTROL diff --git a/soh/soh/Network/Sail/Sail.h b/soh/soh/Network/Sail/Sail.h index 8c7c9d55e..2a41b8723 100644 --- a/soh/soh/Network/Sail/Sail.h +++ b/soh/soh/Network/Sail/Sail.h @@ -18,7 +18,6 @@ class Sail : public Network { void OnIncomingJson(nlohmann::json payload); void OnConnected(); void OnDisconnected(); - void DrawMenu(); }; #endif // __cplusplus diff --git a/soh/soh/Notification/Notification.cpp b/soh/soh/Notification/Notification.cpp index ebc4e168b..5bc0e8974 100644 --- a/soh/soh/Notification/Notification.cpp +++ b/soh/soh/Notification/Notification.cpp @@ -20,7 +20,7 @@ void Window::Draw() { const float margin = 30.0f; const float padding = 10.0f; - int position = CVarGetInteger(CVAR_SETTING("Notifications.Position"), 0); + int position = CVarGetInteger(CVAR_SETTING("Notifications.Position"), 3); // Top Left ImVec2 basePosition; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 2acc01d7f..3690e8648 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -40,6 +40,7 @@ #include "z64.h" #include "macros.h" #include "Fonts.h" +#include "window/gui/resource/Font.h" #include #include "Enhancements/custom-message/CustomMessageManager.h" #include "Enhancements/presets.h" @@ -345,7 +346,7 @@ OTRGlobals::OTRGlobals() { context->InitCrashHandler(); context->InitConsole(); - auto sohInputEditorWindow = std::make_shared(CVAR_WINDOW("ControllerConfiguration"), "Controller Configuration"); + auto sohInputEditorWindow = std::make_shared(CVAR_WINDOW("ControllerConfiguration"), "Configure Controller"); auto sohFast3dWindow = std::make_shared(std::vector>({sohInputEditorWindow})); context->InitWindow(sohFast3dWindow); @@ -399,9 +400,15 @@ OTRGlobals::OTRGlobals() { hasMasterQuest = hasOriginal = false; previousImGuiScale = defaultImGuiScale; - defaultFontSmaller = CreateDefaultFontWithSize(10.0f); - defaultFontLarger = CreateDefaultFontWithSize(16.0f); - defaultFontLargest = CreateDefaultFontWithSize(20.0f); + + fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf"); + fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf"); + fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf"); + fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf"); + fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf"); + fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf"); + fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf"); + ImGui::GetIO().FontDefault = fontStandardLarger; ScaleImGui(); // Move the camera strings from read only memory onto the heap (writable memory) @@ -493,15 +500,12 @@ bool OTRGlobals::HasOriginal() { } uint32_t OTRGlobals::GetInterpolationFPS() { - if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) { - return CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 20); - } - if (CVarGetInteger(CVAR_SETTING("MatchRefreshRate"), 0)) { return Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); + } else if (CVarGetInteger(CVAR_VSYNC_ENABLED, 1) || !Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync()) { + return std::min(Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(), CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 20)); } - - return std::min(Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(), CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 20)); + return CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 20); } extern "C" void OTRMessage_Init(); @@ -1290,6 +1294,15 @@ extern "C" void Graph_StartFrame() { OTRGlobals::Instance->context->GetWindow()->SetLastScancode(-1); switch (dwScancode) { + case KbScancode::LUS_KB_F1: { + std::shared_ptr modal = static_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Modal Window")); + if (modal->IsPopupOpen("Menu Moved")) { + modal->DismissPopup(); + } else { + modal->RegisterPopup("Menu Moved", "The menubar, accessed by hitting F1, no longer exists.\nThe new menu can be accessed by hitting the Esc button instead.", "OK"); + } + break; + } case KbScancode::LUS_KB_F5: { if (CVarGetInteger(CVAR_CHEAT("SaveStatesEnabled"), 0) == 0) { Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()-> @@ -1363,6 +1376,10 @@ extern "C" void Graph_StartFrame() { break; } #endif + case KbScancode::LUS_KB_F11: { + CVarSetInteger(CVAR_SETTING("Fullscreen"), !CVarGetInteger(CVAR_SETTING("Fullscreen"), 0)); + break; + } case KbScancode::LUS_KB_TAB: { CVarSetInteger(CVAR_ENHANCEMENT("AltAssets"), !CVarGetInteger(CVAR_ENHANCEMENT("AltAssets"), 0)); break; @@ -1390,9 +1407,12 @@ void RunCommands(Gfx* Commands, const std::vector // Process window events for resize, mouse, keyboard events wnd->HandleEvents(); + UIWidgets::Colors themeColor = static_cast(CVarGetInteger(CVAR_SETTING("Menu.Theme"), UIWidgets::Colors::LightBlue)); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIWidgets::ColorValues.at(themeColor)); for (const auto& m : mtx_replacements) { wnd->DrawAndRunGraphicsCommands(Commands, m); } + ImGui::PopStyleColor(); } // C->C++ Bridge @@ -1559,6 +1579,37 @@ extern "C" SoundFontSample* ReadCustomSample(const char* path) { */ } +ImFont* OTRGlobals::CreateFontWithSize(float size, std::string fontPath) { + auto mImGuiIo = &ImGui::GetIO(); + ImFont* font; + if (fontPath == "") { + ImFontConfig fontCfg = ImFontConfig(); + fontCfg.OversampleH = fontCfg.OversampleV = 1; + fontCfg.PixelSnapH = true; + fontCfg.SizePixels = size; + font = mImGuiIo->Fonts->AddFontDefault(&fontCfg); + } else { + auto initData = std::make_shared(); + initData->Format = RESOURCE_FORMAT_BINARY; + initData->Type = static_cast(RESOURCE_TYPE_FONT); + initData->ResourceVersion = 0; + initData->Path = fontPath; + std::shared_ptr fontData = std::static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fontPath, false, initData)); + font = mImGuiIo->Fonts->AddFontFromMemoryTTF(fontData->Data, fontData->DataSize, size); + } + // FontAwesome fonts need to have their sizes reduced by 2.0f/3.0f in order to align correctly + float iconFontSize = size * 2.0f / 3.0f; + static const ImWchar sIconsRanges[] = { ICON_MIN_FA, ICON_MAX_16_FA, 0 }; + ImFontConfig iconsConfig; + iconsConfig.MergeMode = true; + iconsConfig.PixelSnapH = true; + iconsConfig.GlyphMinAdvanceX = iconFontSize; + mImGuiIo->Fonts->AddFontFromMemoryCompressedBase85TTF(fontawesome_compressed_data_base85, iconFontSize, + &iconsConfig, sIconsRanges); + return font; +} + std::filesystem::path GetSaveFile(std::shared_ptr Conf) { const std::string fileName = Conf->GetString("Game.SaveName", Ship::Context::GetPathRelativeToAppDirectory("oot_save.sav")); std::filesystem::path saveFile = std::filesystem::absolute(fileName); diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index f056cad5e..2f20b0b15 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -54,6 +54,14 @@ class OTRGlobals { ImFont* defaultFontLarger; ImFont* defaultFontLargest; + ImFont* fontMonoSmall; + ImFont* fontStandard; + ImFont* fontStandardLarger; + ImFont* fontStandardLargest; + ImFont* fontMono; + ImFont* fontMonoLarger; + ImFont* fontMonoLargest; + OTRGlobals(); ~OTRGlobals(); @@ -69,6 +77,7 @@ class OTRGlobals { bool hasMasterQuest; bool hasOriginal; ImFont* CreateDefaultFontWithSize(float size); + ImFont* CreateFontWithSize(float size, std::string fontPath); }; #endif diff --git a/soh/soh/ShipUtils.cpp b/soh/soh/ShipUtils.cpp new file mode 100644 index 000000000..4760dbcd0 --- /dev/null +++ b/soh/soh/ShipUtils.cpp @@ -0,0 +1,115 @@ +#include "ShipUtils.h" +#include + +extern "C" { +#include "z64.h" +#include "functions.h" +#include "macros.h" + +extern float OTRGetAspectRatio(); + +//extern f32 sNESFontWidths[160]; +extern const char* fontTbl[156]; +//extern TexturePtr gItemIcons[131]; +//extern TexturePtr gQuestIcons[14]; +//extern TexturePtr gBombersNotebookPhotos[24]; +} + +constexpr f32 fourByThree = 4.0f / 3.0f; + +// Gets the additional ratio of the screen compared to the original 4:3 ratio, clamping to 1 if smaller +extern "C" f32 Ship_GetExtendedAspectRatioMultiplier() { + f32 currentRatio = OTRGetAspectRatio(); + return MAX(currentRatio / fourByThree, 1.0f); +} + +// Enables Extended Culling options on specific actors by applying an inverse ratio of the draw distance slider +// to the projected Z value of the actor. This tricks distance checks without having to replace hardcoded values. +// Requires that Ship_ExtendedCullingActorRestoreProjectedPos is called within the same function scope. +extern "C" void Ship_ExtendedCullingActorAdjustProjectedZ(Actor* actor) { + s32 multiplier = CVarGetInteger("gEnhancements.Graphics.IncreaseActorDrawDistance", 1); + multiplier = MAX(multiplier, 1); + if (multiplier > 1) { + actor->projectedPos.z /= multiplier; + } +} + +// Enables Extended Culling options on specific actors by applying an inverse ratio of the widescreen aspect ratio +// to the projected X value of the actor. This tricks distance checks without having to replace hardcoded values. +// Requires that Ship_ExtendedCullingActorRestoreProjectedPos is called within the same function scope. +extern "C" void Ship_ExtendedCullingActorAdjustProjectedX(Actor* actor) { + if (CVarGetInteger("gEnhancements.Graphics.ActorCullingAccountsForWidescreen", 0)) { + f32 ratioAdjusted = Ship_GetExtendedAspectRatioMultiplier(); + actor->projectedPos.x /= ratioAdjusted; + } +} + +// Restores the projectedPos values on the actor after modifications from the Extended Culling hacks +//extern "C" void Ship_ExtendedCullingActorRestoreProjectedPos(PlayState* play, Actor* actor) { +// f32 invW = 0.0f; +// Actor_GetProjectedPos(play, &actor->world.pos, &actor->projectedPos, &invW); +//} + +extern "C" bool Ship_IsCStringEmpty(const char* str) { + return str == NULL || str[0] == '\0'; +} + +// Build vertex coordinates for a quad command +// In order of top left, top right, bottom left, then bottom right +// Supports flipping the texture horizontally +extern "C" void Ship_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH) { + vtxList[0].v.ob[0] = xStart; + vtxList[0].v.ob[1] = yStart; + vtxList[0].v.tc[0] = (flippedH ? width : 0) << 5; + vtxList[0].v.tc[1] = 0 << 5; + + vtxList[1].v.ob[0] = xStart + width; + vtxList[1].v.ob[1] = yStart; + vtxList[1].v.tc[0] = (flippedH ? width * 2 : width) << 5; + vtxList[1].v.tc[1] = 0 << 5; + + vtxList[2].v.ob[0] = xStart; + vtxList[2].v.ob[1] = yStart + height; + vtxList[2].v.tc[0] = (flippedH ? width : 0) << 5; + vtxList[2].v.tc[1] = height << 5; + + vtxList[3].v.ob[0] = xStart + width; + vtxList[3].v.ob[1] = yStart + height; + vtxList[3].v.tc[0] = (flippedH ? width * 2 : width) << 5; + vtxList[3].v.tc[1] = height << 5; +} + +//extern "C" f32 Ship_GetCharFontWidthNES(u8 character) { +// u8 adjustedChar = character - ' '; +// +// if (adjustedChar >= ARRAY_COUNTU(sNESFontWidths)) { +// return 0.0f; +// } +// +// return sNESFontWidths[adjustedChar]; +//} + +//extern "C" TexturePtr Ship_GetCharFontTextureNES(u8 character) { +// u8 adjustedChar = character - ' '; +// +// if (adjustedChar >= ARRAY_COUNTU(sNESFontWidths)) { +// return (TexturePtr)gEmptyTexture; +// } +// +// return (TexturePtr)fontTbl[adjustedChar]; +//} + +//void LoadGuiTextures() { +// for (TexturePtr entry : gItemIcons) { +// const char* path = static_cast(entry); +// Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(path, path, ImVec4(1, 1, 1, 1)); +// } +// for (TexturePtr entry : gQuestIcons) { +// const char* path = static_cast(entry); +// Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(path, path, ImVec4(1, 1, 1, 1)); +// } +// for (TexturePtr entry : gBombersNotebookPhotos) { +// const char* path = static_cast(entry); +// Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(path, path, ImVec4(1, 1, 1, 1)); +// } +//} diff --git a/soh/soh/ShipUtils.h b/soh/soh/ShipUtils.h new file mode 100644 index 000000000..a1ac3081b --- /dev/null +++ b/soh/soh/ShipUtils.h @@ -0,0 +1,31 @@ +#ifndef SHIP_UTILS_H +#define SHIP_UTILS_H + +#include +//#include "PR/ultratypes.h" + +#ifdef __cplusplus + +void LoadGuiTextures(); + +extern "C" { +#endif + +struct PlayState; +struct Actor; + +f32 Ship_GetExtendedAspectRatioMultiplier(); +void Ship_ExtendedCullingActorAdjustProjectedZ(Actor* actor); +void Ship_ExtendedCullingActorAdjustProjectedX(Actor* actor); +void Ship_ExtendedCullingActorRestoreProjectedPos(PlayState* play, Actor* actor); + +bool Ship_IsCStringEmpty(const char* str); +void Ship_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH); +f32 Ship_GetCharFontWidthNES(u8 character); +//TexturePtr Ship_GetCharFontTextureNES(u8 character); + +#ifdef __cplusplus +} +#endif + +#endif // SHIP_UTILS_H diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp new file mode 100644 index 000000000..955b048d6 --- /dev/null +++ b/soh/soh/SohGui/Menu.cpp @@ -0,0 +1,841 @@ +#include "Menu.h" +#include "UIWidgets.hpp" +#include "soh/OTRGlobals.h" +#include "soh/Enhancements/controls/SohInputEditorWindow.h" +#include "window/gui/GuiMenuBar.h" +#include "window/gui/GuiElement.h" +#include "SohModals.h" +#include +#include +#include "variables.h" +#include + +extern "C" { +#include "z64.h" +#include "functions.h" +extern PlayState* gPlayState; +} +std::vector windowTypeSizes = { {} }; + +extern std::unordered_map warpPointSceneList; +extern void Warp(); + +namespace SohGui { +extern std::shared_ptr mModalWindow; +} + +namespace Ship { +std::string disabledTempTooltip; +const char* disabledTooltip; +bool disabledValue = false; + +bool operator==(Color_RGB8 const& l, Color_RGB8 const& r) noexcept { + return l.r == r.r && l.g == r.g && l.b == r.b; +} + +bool operator==(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept { + return l.r == r.r && l.g == r.g && l.b == r.b && l.a == r.a; +} + +bool operator<(Color_RGB8 const& l, Color_RGB8 const& r) noexcept { + return (l.r < r.r && l.g <= r.g && l.b <= r.b) || (l.r <= r.r && l.g < r.g && l.b <= r.b) || + (l.r <= r.r && l.g <= r.g && l.b < r.b); +} + +bool operator<(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept { + return (l.r < r.r && l.g <= r.g && l.b <= r.b && l.a <= r.a) || + (l.r <= r.r && l.g < r.g && l.b <= r.b && l.a <= r.a) || + (l.r <= r.r && l.g <= r.g && l.b < r.b && l.a <= r.a) || + (l.r <= r.r && l.g <= r.g && l.b <= r.b && l.a < r.a); +} + +bool operator>(Color_RGB8 const& l, Color_RGB8 const& r) noexcept { + return (l.r > r.r && l.g >= r.g && l.b >= r.b) || (l.r >= r.r && l.g > r.g && l.b >= r.b) || + (l.r >= r.r && l.g >= r.g && l.b > r.b); +} + +bool operator>(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept { + return (l.r > r.r && l.g >= r.g && l.b >= r.b && l.a >= r.a) || + (l.r >= r.r && l.g > r.g && l.b >= r.b && l.a >= r.a) || + (l.r >= r.r && l.g >= r.g && l.b > r.b && l.a >= r.a) || + (l.r >= r.r && l.g >= r.g && l.b >= r.b && l.a > r.a); +} + +uint32_t GetVectorIndexOf(std::vector& vector, std::string value) { + return std::distance(vector.begin(), std::find(vector.begin(), vector.end(), value)); +} + +void Menu::InsertSidebarSearch() { + menuEntries["Settings"].sidebars.emplace("Search", searchSidebarEntry); + uint32_t curIndex = 0; + if (!Ship_IsCStringEmpty(CVarGetString(menuEntries["Settings"].sidebarCvar, ""))) { + curIndex = GetVectorIndexOf(menuEntries["Settings"].sidebarOrder, + CVarGetString(menuEntries["Settings"].sidebarCvar, "")); + } + menuEntries["Settings"].sidebarOrder.insert(menuEntries["Settings"].sidebarOrder.begin() + searchSidebarIndex, + "Search"); + if (curIndex > searchSidebarIndex) { + CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str()); + } +} + +void Menu::RemoveSidebarSearch() { + uint32_t curIndex = + GetVectorIndexOf(menuEntries["Settings"].sidebarOrder, CVarGetString(menuEntries["Settings"].sidebarCvar, "General")); + menuEntries["Settings"].sidebars.erase("Search"); + std::erase_if(menuEntries["Settings"].sidebarOrder, [](std::string& name) { return name == "Search"; }); + if (curIndex > searchSidebarIndex) { + curIndex--; + } else if (curIndex >= menuEntries["Settings"].sidebarOrder.size()) { + curIndex = menuEntries["Settings"].sidebarOrder.size() - 1; + } + CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str()); +} + +void Menu::UpdateWindowBackendObjects() { + Ship::WindowBackend runningWindowBackend = Ship::Context::GetInstance()->GetWindow()->GetWindowBackend(); + int32_t configWindowBackendId = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1); + if (Ship::Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(configWindowBackendId)) { + configWindowBackend = static_cast(configWindowBackendId); + } else { + configWindowBackend = runningWindowBackend; + } + + availableWindowBackends = Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends(); + for (auto& backend : *availableWindowBackends) { + availableWindowBackendsMap[backend] = windowBackendsMap.at(backend); + } +} + +bool Menu::IsMenuPopped() { + return popped; +} + +UIWidgets::Colors Menu::GetMenuThemeColor() { + return menuThemeIndex; +} + +Menu::Menu(const std::string& cVar, const std::string& name, uint8_t searchSidebarIndex_, + UIWidgets::Colors defaultThemeIndex_) + : GuiWindow(cVar, name), searchSidebarIndex(searchSidebarIndex_), defaultThemeIndex(defaultThemeIndex_) { +} + +void Menu::InitElement() { + popped = CVarGetInteger(CVAR_SETTING("Menu.Popout"), 0); + poppedSize.x = CVarGetInteger(CVAR_SETTING("Menu.PoppedWidth"), 1280); + poppedSize.y = CVarGetInteger(CVAR_SETTING("Menu.PoppedHeight"), 800); + poppedPos.x = CVarGetInteger(CVAR_SETTING("Menu.PoppedPos.x"), 0); + poppedPos.y = CVarGetInteger(CVAR_SETTING("Menu.PoppedPos.y"), 0); + + UpdateWindowBackendObjects(); +} + +void Menu::UpdateElement() { + menuThemeIndex = static_cast(CVarGetInteger(CVAR_SETTING("Menu.Theme"), defaultThemeIndex)); +} + +bool ModernMenuSidebarEntry(std::string label) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStyle& style = ImGui::GetStyle(); + ImVec2 pos = window->DC.CursorPos; + const ImGuiID sidebarId = window->GetID(std::string(label + "##Sidebar").c_str()); + ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + pos.y += style.FramePadding.y; + pos.x = window->WorkRect.GetCenter().x - labelSize.x / 2; + ImRect bb = { pos - style.FramePadding, pos + labelSize + style.FramePadding }; + ImGui::ItemSize(bb, style.FramePadding.y); + ImGui::ItemAdd(bb, sidebarId); + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, sidebarId, &hovered, &held); + if (pressed) { + ImGui::MarkItemEdited(sidebarId); + } + window->DrawList->AddRectFilled(pos - style.FramePadding, pos + labelSize + style.FramePadding, + ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive + : hovered ? ImGuiCol_ButtonHovered + : ImGuiCol_Button), + 3.0f); + UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + return pressed; +} + +bool ModernMenuHeaderEntry(std::string label) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStyle& style = ImGui::GetStyle(); + ImVec2 pos = window->DC.CursorPos; + const ImGuiID headerId = window->GetID(std::string(label + "##Header").c_str()); + ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + ImRect bb = { pos, pos + labelSize + style.FramePadding * 2 }; + ImGui::ItemSize(bb, style.FramePadding.y); + ImGui::ItemAdd(bb, headerId); + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, headerId, &hovered, &held); + window->DrawList->AddRectFilled(bb.Min, bb.Max, + ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive + : hovered ? ImGuiCol_ButtonHovered + : ImGuiCol_Button), + 3.0f); + pos += style.FramePadding; + UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + return pressed; +} + +uint32_t Menu::DrawSearchResults(std::string& menuSearchText) { + ImGui::BeginChild("Search Results"); + int searchCount = 0; + for (auto& menuLabel : menuOrder) { + auto& menuEntry = menuEntries.at(menuLabel); + for (auto& sidebarLabel : menuEntry.sidebarOrder) { + auto& sidebar = menuEntry.sidebars[sidebarLabel]; + for (int i = 0; i < sidebar.columnWidgets.size(); i++) { + auto& column = sidebar.columnWidgets.at(i); + for (auto& info : column) { + if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR || info.type == WIDGET_SEPARATOR_TEXT || + info.isHidden) { + continue; + } + const char* tooltip = info.options->tooltip; + std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : ""); + std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower); + menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), + menuSearchText.end()); + std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); + widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); + if (widgetStr.find(menuSearchText) != std::string::npos) { + MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex); + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); + std::string origin = fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1); + ImGui::Text("%s", origin.c_str()); + ImGui::PopStyleColor(); + searchCount++; + } + } + } + } + } + return searchCount; +} + +void Menu::AddMenuEntry(std::string entryName, const char* entryCvar) { + menuEntries.emplace(entryName, MainMenuEntry{ entryName, entryCvar }); + menuOrder.push_back(entryName); +} + +std::unordered_map& Menu::GetDisabledMap() { + return disabledMap; +} + +void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex) { + disabledTempTooltip = "This setting is disabled because: \n\n"; + disabledValue = false; + disabledTooltip = " "; + + if (widget.preFunc != nullptr) { + widget.ResetDisables(); + widget.preFunc(widget); + if (widget.isHidden) { + return; + } + if (!widget.activeDisables.empty()) { + widget.options->disabled = true; + for (auto option : widget.activeDisables) { + disabledTempTooltip += std::string("- ") + disabledMap.at(option).reason + std::string("\n"); + } + widget.options->disabledTooltip = disabledTempTooltip.c_str(); + } + } + + if (widget.sameLine) { + ImGui::SameLine(); + } + + try { + switch (widget.type) { + case WIDGET_CHECKBOX: { + bool* pointer = std::get(widget.valuePointer); + if (pointer == nullptr) { + SPDLOG_ERROR("Checkbox Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::Checkbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), pointer, + *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_CVAR_CHECKBOX: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarCheckbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), widget.cVar, + *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_AUDIO_BACKEND: { + auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetCurrentAudioBackend(); + UIWidgets::ComboboxOptions options = {}; + options.color = menuThemeIndex; + options.tooltip = "Sets the audio API used by the game. Requires a relaunch to take effect."; + options.disabled = Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1; + options.disabledTooltip = "Only one audio API is available on this platform."; + if (UIWidgets::Combobox("Audio API", ¤tAudioBackend, audioBackendsMap, options)) { + Ship::Context::GetInstance()->GetAudio()->SetCurrentAudioBackend(currentAudioBackend); + } + } break; + case WIDGET_VIDEO_BACKEND: { + UIWidgets::ComboboxOptions options = {}; + options.color = menuThemeIndex; + options.tooltip = "Sets the renderer API used by the game."; + options.disabled = availableWindowBackends->size() <= 1; + options.disabledTooltip = "Only one renderer API is available on this platform."; + if (UIWidgets::Combobox("Renderer API (Needs reload)", &configWindowBackend, availableWindowBackendsMap, + options)) { + Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", + (int32_t)(configWindowBackend)); + Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name", + windowBackendsMap.at(configWindowBackend)); + Ship::Context::GetInstance()->GetConfig()->Save(); + UpdateWindowBackendObjects(); + } + } break; + case WIDGET_SEPARATOR: { + ImGui::Separator(); + } break; + case WIDGET_SEPARATOR_TEXT: { + auto options = std::static_pointer_cast(widget.options); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(options->color)); + } + ImGui::SeparatorText(widget.name.c_str()); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PopStyleColor(); + } + } break; + case WIDGET_TEXT: { + auto options = std::static_pointer_cast(widget.options); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(options->color)); + } + ImGui::AlignTextToFramePadding(); + ImGui::TextWrapped("%s", widget.name.c_str()); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PopStyleColor(); + } + } break; + case WIDGET_COMBOBOX: { + int32_t* pointer = std::get(widget.valuePointer); + if (pointer == nullptr) { + SPDLOG_ERROR("Combobox Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::Combobox(widget.name.c_str(), pointer, options->comboMap, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_CVAR_COMBOBOX: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarCombobox(widget.name.c_str(), widget.cVar, options->comboMap, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_SLIDER_INT: { + int32_t* pointer = std::get(widget.valuePointer); + if (pointer == nullptr) { + SPDLOG_ERROR("int32 Slider Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::SliderInt(widget.name.c_str(), pointer, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_CVAR_SLIDER_INT: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarSliderInt(widget.name.c_str(), widget.cVar, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_SLIDER_FLOAT: { + float* pointer = std::get(widget.valuePointer); + + if (pointer == nullptr) { + SPDLOG_ERROR("float Slider Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::SliderFloat(widget.name.c_str(), pointer, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_CVAR_SLIDER_FLOAT: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarSliderFloat(widget.name.c_str(), widget.cVar, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_BUTTON: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::Button(widget.name.c_str(), *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_CUSTOM: { + if (widget.customFunction != nullptr) { + widget.customFunction(widget); + } + } break; + case WIDGET_WINDOW_BUTTON: { + if (widget.windowName == nullptr || widget.windowName[0] == '\0') { + std::string msg = + fmt::format("Error drawing window contents for {}: windowName not defined", widget.name); + SPDLOG_ERROR(msg.c_str()); + break; + } + auto window = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow(widget.windowName); + if (!window) { + std::string msg = + fmt::format("Error drawing window contents: windowName {} does not exist", widget.windowName); + SPDLOG_ERROR(msg.c_str()); + break; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (options->showButton) { + UIWidgets::WindowButton(widget.name.c_str(), widget.cVar, window, *options); + } + if (!window->IsVisible() && options->embedWindow) { + window->DrawElement(); + } + } break; + case WIDGET_SEARCH: { + UIWidgets::PushStyleButton(menuThemeIndex); + if (ImGui::Button("Clear")) { + menuSearch.Clear(); + } + ImGui::SameLine(); + if (CVarGetInteger(CVAR_SETTING("Menu.SearchAutofocus"), 0) && + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() && + !ImGui::IsMouseClicked(0)) { + ImGui::SetKeyboardFocusHere(0); + } + UIWidgets::PushStyleCombobox(menuThemeIndex); + ImGui::PushStyleColor(ImGuiCol_Border, UIWidgets::ColorValues.at(menuThemeIndex)); + menuSearch.Draw(); + ImGui::PopStyleColor(); + UIWidgets::PopStyleCombobox(); + UIWidgets::PopStyleButton(); + std::string menuSearchText(menuSearch.InputBuf); + + if (menuSearchText == "") { + ImGui::Text("Start typing to see results."); + return; + } + DrawSearchResults(menuSearchText); + ImGui::EndChild(); + } break; + default: + break; + } + if (widget.postFunc != nullptr) { + widget.postFunc(widget); + } + } catch (const std::bad_variant_access& e) { + SPDLOG_ERROR("Failed to draw menu item \"{}\" due to: {}", widget.name, e.what()); + assert(false); + } +} + +void Menu::Draw() { + if (!IsVisible()) { + return; + } + DrawElement(); + // Sync up the IsVisible flag if it was changed by ImGui + SyncVisibilityConsoleVariable(); +} + +void Menu::DrawElement() { + for (auto& [reason, info] : disabledMap) { + info.active = info.evaluation(info); + } + + windowHeight = ImGui::GetMainViewport()->WorkSize.y; + windowWidth = ImGui::GetMainViewport()->WorkSize.x; + auto windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; + bool popout = CVarGetInteger(CVAR_SETTING("Menu.Popout"), 0) && allowPopout; + if (popout) { + windowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking; + } + if (popout != popped) { + if (popout) { + windowHeight = poppedSize.y; + windowWidth = poppedSize.x; + ImGui::SetNextWindowSize({ static_cast(windowWidth), static_cast(windowHeight) }, + ImGuiCond_Always); + ImGui::SetNextWindowPos(poppedPos, ImGuiCond_Always); + } else if (popped) { + CVarSetFloat(CVAR_SETTING("Menu.PoppedWidth"), poppedSize.x); + CVarSetFloat(CVAR_SETTING("Menu.PoppedHeight"), poppedSize.y); + CVarSave(); + } + } + popped = popout; + auto windowCond = ImGuiCond_Always; + if (!popout) { + ImGui::SetNextWindowSize({ static_cast(windowWidth), static_cast(windowHeight) }, windowCond); + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), windowCond, { 0.5f, 0.5f }); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + } + + ImGui::PushStyleColor(ImGuiCol_WindowBg, + ImVec4(0, 0, 0, CVarGetFloat(CVAR_SETTING("Menu.BackgroundOpacity"), 0.85f))); + + if (!ImGui::Begin("Main Menu", NULL, windowFlags)) { + if (!popout) { + ImGui::PopStyleVar(); + } + ImGui::End(); + return; + } + + ImGui::PopStyleColor(); + + if (popped != popout) { + if (!popout) { + ImGui::PopStyleVar(); + } + CVarSetInteger(CVAR_SETTING("Menu.Popout"), popped); + CVarSetFloat(CVAR_SETTING("Menu.PoppedWidth"), poppedSize.x); + CVarSetFloat(CVAR_SETTING("Menu.PoppedHeight"), poppedSize.y); + CVarSetFloat(CVAR_SETTING("Menu.PoppedPos.x"), poppedSize.x); + CVarSetFloat(CVAR_SETTING("Menu.PoppedPos.y"), poppedSize.y); + CVarSave(); + ImGui::End(); + return; + } + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStyle& style = ImGui::GetStyle(); + windowHeight = window->WorkRect.GetHeight(); + windowWidth = window->WorkRect.GetWidth(); + + ImGui::PushFont(OTRGlobals::Instance->fontStandardLargest); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); + const char* headerCvar = CVAR_SETTING("Menu.ActiveHeader"); + std::string headerIndex = CVarGetString(headerCvar, "Settings"); + ImVec2 pos = window->DC.CursorPos; + float centerX = pos.x + windowWidth / 2 - (style.ItemSpacing.x * (menuEntries.size() + 1)); + std::vector headerSizes; + float headerWidth = style.ItemSpacing.x + 20; + bool headerSearch = !CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0); + if (headerSearch) { + headerWidth += 200.0f + style.ItemSpacing.x + style.FramePadding.x; + } + for (auto& label : menuOrder) { + ImVec2 size = ImGui::CalcTextSize(label.c_str()); + headerSizes.push_back(size); + headerWidth += size.x + style.FramePadding.x * 2; + if (label == headerIndex) { + headerWidth += style.ItemSpacing.x; + } + } + + // Full screen menu with widths below 1280, heights below 800. + // 5% of screen width/height padding on both sides above those resolutions. + // Menu width will never exceed a 16:9 aspect ratio. + ImVec2 menuSize = { windowWidth, windowHeight }; + if (windowWidth > 1280) { + menuSize.x = std::fminf(windowWidth * 0.9f, (windowHeight * 1.77f)); + } + if (windowHeight > 800) { + menuSize.y = windowHeight * 0.9f; + } + + pos += window->WorkRect.GetSize() / 2 - menuSize / 2; + ImGui::SetNextWindowPos(pos); + ImGui::BeginChild("Menu Block", menuSize, + ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar); + + std::unordered_map* sidebar; + float headerHeight = headerSizes.at(0).y + style.FramePadding.y * 2; + ImVec2 buttonSize = ImGui::CalcTextSize(ICON_FA_TIMES_CIRCLE) + style.FramePadding * 2; + bool scrollbar = false; + if (headerWidth > menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3) { + headerHeight += style.ScrollbarSize; + scrollbar = true; + } + ImGui::SetNextWindowSizeConstraints({ 0, headerHeight }, { headerWidth, headerHeight }); + ImVec2 headerSelSize = { menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3, headerHeight }; + if (scrollbar) { + headerSelSize.y += style.ScrollbarSize; + } + bool autoFocus = CVarGetInteger(CVAR_SETTING("Menu.SearchAutofocus"), 0); + ImGui::BeginChild("Header Selection", headerSelSize, + ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_HorizontalScrollbar); + uint8_t curIndex = 0; + for (auto& label : menuOrder) { + if (curIndex != 0) { + ImGui::SameLine(); + } + auto& entry = menuEntries.at(label); + std::string nextIndex = label; + UIWidgets::PushStyleButton(menuThemeIndex); + if (headerIndex != label) { + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + } + if (ModernMenuHeaderEntry(entry.label)) { + if (headerSearch) { + menuSearch.Clear(); + } + CVarSetString(headerCvar, label.c_str()); + CVarSave(); + nextIndex = label; + } + if (headerIndex != label) { + ImGui::PopStyleColor(); + } + UIWidgets::PopStyleButton(); + if (headerIndex == label) { + sidebar = &entry.sidebars; + } + if (nextIndex != label) { + headerIndex = nextIndex; + } + curIndex++; + } + std::string menuSearchText = ""; + if (headerSearch) { + ImGui::SameLine(); + if (autoFocus && ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() && + !ImGui::IsMouseClicked(0)) { + ImGui::SetKeyboardFocusHere(0); + } + auto color = UIWidgets::ColorValues.at(menuThemeIndex); + color.w = 0.2f; + ImGui::PushStyleColor(ImGuiCol_FrameBg, color); + menuSearch.Draw("##search", 200.0f); + menuSearchText = menuSearch.InputBuf; + menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end()); + if (menuSearchText.length() < 1) { + ImGui::SameLine(headerWidth - 200.0f + style.ItemSpacing.x); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search..."); + } + ImGui::PopStyleColor(); + } + ImGui::EndChild(); + ImGui::SameLine(menuSize.x - (buttonSize.x * 3) - (style.ItemSpacing.x * 2)); + UIWidgets::ButtonOptions options3 = {}; + options3.color = UIWidgets::Colors::Red; + options3.size = UIWidgets::Sizes::Inline; + options3.tooltip = "Quit SoH"; + if (UIWidgets::Button(ICON_FA_POWER_OFF, options3)) { + SohGui::mModalWindow->RegisterPopup("Quit SoH", "Are you sure you want to quit SoH?", "Quit", "Cancel", []() { + std::shared_ptr menu = static_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenu()); + if (!menu->IsMenuPopped()) { + menu->ToggleVisibility(); + } + Ship::Context::GetInstance()->GetWindow()->Close(); + }, nullptr); + } + ImGui::PopStyleVar(); + ImGui::SameLine(); + UIWidgets::ButtonOptions options2 = {}; + options2.color = UIWidgets::Colors::Red; + options2.size = UIWidgets::Sizes::Inline; + options2.tooltip = "Reset" +#ifdef __APPLE__ + " (Command-R)" +#elif !defined(__SWITCH__) && !defined(__WIIU__) + " (Ctrl+R)" +#else + "" +#endif + ; + if (UIWidgets::Button(ICON_FA_UNDO, options2)) { + std::reinterpret_pointer_cast( + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console")) + ->Dispatch("reset"); + } + ImGui::SameLine(); + UIWidgets::ButtonOptions options = {}; + options.size = UIWidgets::Sizes::Inline; + options.tooltip = "Close Menu (Esc)"; + if (UIWidgets::Button(ICON_FA_TIMES_CIRCLE, options)) { + ToggleVisibility(); + + // Update gamepad navigation after close based on if other menus are still visible + auto mImGuiIo = &ImGui::GetIO(); + if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenuOrMenubarVisible()) { + mImGuiIo->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + } else { + mImGuiIo->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; + } + } + + pos.y += headerHeight + style.ItemSpacing.y; + pos.x = centerX - menuSize.x / 2 + (style.ItemSpacing.x * (menuEntries.size() + 1)); + window->DrawList->AddRectFilled(pos, pos + ImVec2{ menuSize.x, 4 }, ImGui::GetColorU32({ 255, 255, 255, 255 }), + true, style.WindowRounding); + pos.y += style.ItemSpacing.y; + float sectionHeight = menuSize.y - headerHeight - 4 - style.ItemSpacing.y * 2; + float columnHeight = sectionHeight - style.ItemSpacing.y * 4; + ImGui::SetNextWindowPos(pos + style.ItemSpacing * 2); + + // Increase sidebar width on larger screens to accomodate people scaling their menus. + float sidebarWidth = 200 - style.ItemSpacing.x; + if (menuSize.x > 1600) { + sidebarWidth = menuSize.x * 0.15f; + } + + const char* sidebarCvar = menuEntries.at(headerIndex).sidebarCvar; + + std::string sectionIndex = CVarGetString(sidebarCvar, ""); + if (!sidebar->contains(sectionIndex)) { + sectionIndex = sidebar->begin()->first; + } + float sectionCenterX = pos.x + (sidebarWidth / 2); + float topY = pos.y; + ImGui::SetNextWindowSizeConstraints({ sidebarWidth, 0 }, { sidebarWidth, columnHeight }); + ImGui::BeginChild((menuEntries.at(headerIndex).label + " Section").c_str(), { sidebarWidth, columnHeight * 3 }, + ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoTitleBar); + for (auto& sidebarLabel : menuEntries.at(headerIndex).sidebarOrder) { + std::string nextIndex = ""; + UIWidgets::PushStyleButton(menuThemeIndex); + if (sectionIndex != sidebarLabel) { + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + } + if (ModernMenuSidebarEntry(sidebarLabel)) { + if (headerSearch) { + menuSearch.Clear(); + } + CVarSetString(sidebarCvar, sidebarLabel.c_str()); + CVarSave(); + nextIndex = sidebarLabel; + } + if (sectionIndex != sidebarLabel) { + ImGui::PopStyleColor(); + } + UIWidgets::PopStyleButton(); + if (nextIndex != "") { + sectionIndex = nextIndex; + } + } + ImGui::EndChild(); + ImGui::PopFont(); + + pos = ImVec2{ sectionCenterX + (sidebarWidth / 2), topY } + style.ItemSpacing * 2; + window->DrawList->AddRectFilled(pos, pos + ImVec2{ 4, sectionHeight - style.FramePadding.y * 2 }, + ImGui::GetColorU32({ 255, 255, 255, 255 }), true, style.WindowRounding); + pos.x += 4 + style.ItemSpacing.x; + ImGui::SetNextWindowPos(pos + style.ItemSpacing); + float sectionWidth = menuSize.x - sidebarWidth - 4 - style.ItemSpacing.x * 4; + std::string sectionMenuId = sectionIndex + " Settings"; + int columns = sidebar->at(sectionIndex).columnCount; + size_t columnFuncs = sidebar->at(sectionIndex).columnWidgets.size(); + if (windowWidth < 800) { + columns = 1; + } + float columnWidth = (sectionWidth - style.ItemSpacing.x * columns) / columns; + bool useColumns = columns > 1; + if (!useColumns || (headerSearch && menuSearchText.length() > 0)) { + ImGui::SameLine(); + ImGui::SetNextWindowSizeConstraints({ sectionWidth, 0 }, { sectionWidth, columnHeight }); + ImGui::BeginChild(sectionMenuId.c_str(), { sectionWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY, + ImGuiWindowFlags_NoTitleBar); + } + if (headerSearch && menuSearchText.length() > 0) { + uint32_t searchCount = DrawSearchResults(menuSearchText); + if (searchCount == 0) { + ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("No results found").x) / 2); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "No results found"); + } + ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("Clear Search").x) / 2 - 10.0f); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f); + UIWidgets::ButtonOptions clearBtnOpts = {}; + clearBtnOpts.size = UIWidgets::Sizes::Inline; + if (UIWidgets::Button("Clear Search", clearBtnOpts)) { + menuSearch.Clear(); + } + + ImGui::EndChild(); + } else { + std::string menuLabel = menuEntries.at(headerIndex).label; + if (MenuInit::GetUpdateFuncs().contains(menuLabel)) { + if (MenuInit::GetUpdateFuncs()[menuLabel].contains(sectionIndex)) { + for (auto& updateFunc : MenuInit::GetUpdateFuncs()[menuLabel][sectionIndex]) { + updateFunc(); + } + } + } + for (int i = 0; i < columnFuncs; i++) { + std::string sectionId = fmt::format("{} Column {}", sectionMenuId, i); + if (useColumns) { + ImGui::SetNextWindowSizeConstraints({ columnWidth, 0 }, { columnWidth, columnHeight }); + ImGui::BeginChild(sectionId.c_str(), { columnWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY, + ImGuiWindowFlags_NoTitleBar); + } + // for (auto& entryName : sidebar->at(sectionIndex).sidebarOrder) { + for (auto& entry : sidebar->at(sectionIndex).columnWidgets.at(i)) { + MenuDrawItem(entry, 90 / sidebar->at(sectionIndex).columnCount, menuThemeIndex); + } + //} + if (useColumns) { + ImGui::EndChild(); + } + if (i < columns - 1) { + ImGui::SameLine(); + } + } + } + if (!useColumns || menuSearchText.length() > 0) { + ImGui::EndChild(); + } + + if (!popout) { + ImGui::PopStyleVar(); + } + ImGui::EndChild(); + if (popout) { + poppedSize = ImGui::GetWindowSize(); + poppedPos = ImGui::GetWindowPos(); + } + ImGui::End(); +} +} // namespace Ship diff --git a/soh/soh/SohGui/Menu.h b/soh/soh/SohGui/Menu.h new file mode 100644 index 000000000..139f90030 --- /dev/null +++ b/soh/soh/SohGui/Menu.h @@ -0,0 +1,66 @@ +#ifndef MENU_H +#define MENU_H + +#include +#include "graphic/Fast3D/gfx_rendering_api.h" +#include "MenuTypes.h" + +namespace Ship { +uint32_t GetVectorIndexOf(std::vector& vector, std::string value); +class Menu : public GuiWindow { + public: + using GuiWindow::GuiWindow; + + Menu(const std::string& cVar, const std::string& name, uint8_t searchSidebarIndex_ = 0, + UIWidgets::Colors menuThemeIndex_ = UIWidgets::Colors::LightBlue); + + void InitElement() override; + void DrawElement() override; + void UpdateElement() override; + void Draw() override; + void InsertSidebarSearch(); + void RemoveSidebarSearch(); + void UpdateWindowBackendObjects(); + bool IsMenuPopped(); + UIWidgets::Colors GetMenuThemeColor(); + + void MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex); + void AddMenuEntry(std::string entryName, const char* entryCvar); + std::unordered_map& GetDisabledMap(); + + protected: + ImVec2 mOriginalSize; + std::string mName; + uint32_t mWindowFlags; + std::unordered_map menuEntries; + std::vector menuOrder; + uint32_t DrawSearchResults(std::string& menuSearchText); + ImGuiTextFilter menuSearch; + uint8_t searchSidebarIndex; + UIWidgets::Colors defaultThemeIndex; + std::shared_ptr> availableWindowBackends; + std::unordered_map availableWindowBackendsMap; + Ship::WindowBackend configWindowBackend; + + std::unordered_map disabledMap; + std::vector disabledVector; + const SidebarEntry searchSidebarEntry = { + .columnCount = 1, + .columnWidgets = { { { .name = "Sidebar Search", + .type = WIDGET_SEARCH, + .options = std::make_shared(UIWidgets::WidgetOptions{}.Tooltip( + "Searches all menus for the given text, including tooltips.")) } } } + }; + + private: + bool allowPopout = true; // PortNote: should be set to false on small screen ports + bool popped; + ImVec2 poppedSize; + ImVec2 poppedPos; + float windowHeight; + float windowWidth; + UIWidgets::Colors menuThemeIndex; +}; +} // namespace Ship + +#endif // MENU_H diff --git a/soh/soh/SohGui/MenuTypes.h b/soh/soh/SohGui/MenuTypes.h new file mode 100644 index 000000000..28a89820d --- /dev/null +++ b/soh/soh/SohGui/MenuTypes.h @@ -0,0 +1,282 @@ +#ifndef MENUTYPES_H +#define MENUTYPES_H + +#include +#include "UIWidgets.hpp" + +typedef enum { + DISABLE_FOR_NO_VSYNC, + DISABLE_FOR_NO_WINDOWED_FULLSCREEN, + DISABLE_FOR_NO_MULTI_VIEWPORT, + DISABLE_FOR_NOT_DIRECTX, + DISABLE_FOR_DIRECTX, + DISABLE_FOR_MATCH_REFRESH_RATE_ON, + DISABLE_FOR_ADVANCED_RESOLUTION_ON, + DISABLE_FOR_VERTICAL_RES_TOGGLE_ON, + DISABLE_FOR_LOW_RES_MODE_ON, + DISABLE_FOR_NULL_PLAY_STATE, + DISABLE_FOR_DEBUG_MODE_OFF, + DISABLE_FOR_FRAME_ADVANCE_OFF, + DISABLE_FOR_ADVANCED_RESOLUTION_OFF, + DISABLE_FOR_VERTICAL_RESOLUTION_OFF, +} DisableOption; + +struct WidgetInfo; +struct disabledInfo; +using VoidFunc = void (*)(); +using DisableInfoFunc = bool (*)(disabledInfo&); +using DisableVec = std::vector; +using WidgetFunc = void (*)(WidgetInfo&); + +typedef enum { + WIDGET_CHECKBOX, + WIDGET_COMBOBOX, + WIDGET_SLIDER_INT, + WIDGET_SLIDER_FLOAT, + WIDGET_CVAR_CHECKBOX, + WIDGET_CVAR_COMBOBOX, + WIDGET_CVAR_SLIDER_INT, + WIDGET_CVAR_SLIDER_FLOAT, + WIDGET_BUTTON, + WIDGET_INPUT, + WIDGET_CVAR_INPUT, + WIDGET_COLOR_24, // color picker without alpha + WIDGET_COLOR_32, // color picker with alpha + WIDGET_SEARCH, + WIDGET_SEPARATOR, + WIDGET_SEPARATOR_TEXT, + WIDGET_TEXT, + WIDGET_WINDOW_BUTTON, + WIDGET_AUDIO_BACKEND, // needed for special operations that can't be handled easily with the normal combobox widget + WIDGET_VIDEO_BACKEND, // same as above + WIDGET_CUSTOM, +} WidgetType; + +typedef enum { + SECTION_COLUMN_1, + SECTION_COLUMN_2, + SECTION_COLUMN_3, +} SectionColumns; + +typedef enum { + DEBUG_LOG_TRACE, + DEBUG_LOG_DEBUG, + DEBUG_LOG_INFO, + DEBUG_LOG_WARN, + DEBUG_LOG_ERROR, + DEBUG_LOG_CRITICAL, + DEBUG_LOG_OFF, +} DebugLogOption; + +// holds the widget values for a widget, contains all CVar types available from LUS. int32_t is used for boolean +// evaluation +using CVarVariant = std::variant; +using OptionsVariant = + std::variant; + +// All the info needed for display and search of all widgets in the menu. +// `name` is the label displayed, +// `cVar` is the string representation of the CVar used to store the widget value +// `tooltip` is what is displayed when hovering (except when disabled, more on that later) +// `type` is the WidgetType for the widget, which is what determines how the information is used in the draw func +// `options` is a variant that holds the UIWidgetsOptions struct for the widget type +// blank objects need to be initialized with specific typing matching the expected Options struct for the widget +// `callback` is a lambda used for running code on widget change. may need `SohGui::GetMenu()` for specific menu actions +// `preFunc` is a lambda called before drawing code starts. It can be used to determine a widget's status, +// whether disabled or hidden, as well as update pointers for non-CVar widget types. +// `postFunc` is a lambda called after all drawing code is finished, for reacting to states other than +// widgets having been changed, like holding buttons. +// All three lambdas accept a `widgetInfo` reference in case it needs information on the widget for these operations +// `activeDisables` is a vector of DisableOptions for specifying what reasons a widget is disabled, which are displayed +// in the disabledTooltip for the widget. Can display multiple reasons. Handling the reasons is done in `preFunc`. +// It is recommended to utilize `disabledInfo`/`DisableReason` to list out all reasons for disabling and isHidden so +// the info can be shown. +// `windowName` is what is displayed and searched for `windowButton` type and window interactions +// `isHidden` just prevents the widget from being drawn under whatever circumstances you specify in the `preFunc` +// `sameLine` allows for specifying that the widget should be on the same line as the previous widget +struct WidgetInfo { + std::string name; // Used by all widgets + const char* cVar; // Used by all widgets except + WidgetType type; + std::shared_ptr options; + std::variant valuePointer; + WidgetFunc callback = nullptr; + WidgetFunc preFunc = nullptr; + WidgetFunc postFunc = nullptr; + WidgetFunc customFunction = nullptr; + DisableVec activeDisables = {}; + const char* windowName = ""; + bool isHidden = false; + bool sameLine = false; + + WidgetInfo& CVar(const char* cVar_) { + cVar = cVar_; + return *this; + } + WidgetInfo& Options(OptionsVariant options_) { + switch (type) { + case WIDGET_AUDIO_BACKEND: + case WIDGET_VIDEO_BACKEND: + case WIDGET_COMBOBOX: + case WIDGET_CVAR_COMBOBOX: + options = std::make_shared(std::get(options_)); + break; + case WIDGET_CHECKBOX: + case WIDGET_CVAR_CHECKBOX: + options = std::make_shared(std::get(options_)); + break; + case WIDGET_SLIDER_FLOAT: + case WIDGET_CVAR_SLIDER_FLOAT: + options = + std::make_shared(std::get(options_)); + break; + case WIDGET_SLIDER_INT: + case WIDGET_CVAR_SLIDER_INT: + options = + std::make_shared(std::get(options_)); + break; + case WIDGET_BUTTON: + options = std::make_shared(std::get(options_)); + break; + case WIDGET_WINDOW_BUTTON: + options = std::make_shared(std::get(options_)); + break; + case WIDGET_TEXT: + case WIDGET_SEPARATOR_TEXT: + options = std::make_shared(std::get(options_)); + break; + case WIDGET_SEPARATOR: + default: + options = std::make_shared(std::get(options_)); + } + return *this; + } + void ResetDisables() { + isHidden = false; + options->disabled = false; + options->disabledTooltip = ""; + activeDisables.clear(); + } + WidgetInfo& Options(std::shared_ptr options_) { + options = options_; + return *this; + } + WidgetInfo& Callback(WidgetFunc callback_) { + callback = callback_; + return *this; + } + WidgetInfo& PreFunc(WidgetFunc preFunc_) { + preFunc = preFunc_; + return *this; + } + WidgetInfo& PostFunc(WidgetFunc postFunc_) { + postFunc = postFunc_; + return *this; + } + WidgetInfo& WindowName(const char* windowName_) { + windowName = windowName_; + return *this; + } + WidgetInfo& ValuePointer(std::variant valuePointer_) { + valuePointer = valuePointer_; + return *this; + } + WidgetInfo& SameLine(bool sameLine_) { + sameLine = sameLine_; + return *this; + } + WidgetInfo& CustomFunction(WidgetFunc customFunction_) { + customFunction = customFunction_; + return *this; + } +}; + +struct WidgetPath { + std::string sectionName; + std::string sidebarName; + SectionColumns column; +}; + +// `disabledInfo` holds information on reasons for hiding or disabling a widget, as well as an evaluation lambda that +// is run once per frame to update its status (this is done to prevent dozens of redundant CVarGets in each frame loop) +// `evaluation` returns a bool which can be determined by whatever code you want that changes its status +// `reason` is the text displayed in the disabledTooltip when a widget is disabled by a particular DisableReason +// `active` is what's referenced when determining disabled status for a widget that uses this This can also be used to +// hold reasons to hide widgets so that their evaluations are also only run once per frame +struct disabledInfo { + DisableInfoFunc evaluation; + const char* reason; + bool active = false; + int32_t value = 0; +}; + +// Contains the name displayed in the sidebar (label), the number of columns to use in drawing (columnCount; for visual +// separation, 1-3), and nested vectors of the widgets, grouped by column (columnWidgets). The number of widget vectors +// added to the column groups does not need to match the specified columnCount, e.g. you can have one vector added to +// the sidebar, but still separate the window into 3 columns and display only in the first column +struct SidebarEntry { + uint32_t columnCount; + std::vector> columnWidgets; +}; + +// Contains entries for what's listed in the header at the top, including the name displayed on the top bar (label), +// a vector of the SidebarEntries for that header entry, and the name of the cvar used to track what sidebar entry is +// the last viewed for that header. +struct MainMenuEntry { + std::string label; + const char* sidebarCvar; + std::unordered_map sidebars = {}; + std::vector sidebarOrder = {}; +}; + +static const std::unordered_map audioBackendsMap = { + { Ship::AudioBackend::WASAPI, "Windows Audio Session API" }, + { Ship::AudioBackend::SDL, "SDL" }, +}; + +static const std::unordered_map windowBackendsMap = { + { Ship::WindowBackend::FAST3D_DXGI_DX11, "DirectX" }, + { Ship::WindowBackend::FAST3D_SDL_OPENGL, "OpenGL" }, + { Ship::WindowBackend::FAST3D_SDL_METAL, "Metal" }, +}; + +struct MenuInit { + static std::vector>& GetInitFuncs() { + static std::vector> menuInitFuncs; + return menuInitFuncs; + } + + static std::unordered_map>>>& + GetUpdateFuncs() { + static std::unordered_map>>> + menuUpdateFuncs; + return menuUpdateFuncs; + } + + static void InitAll() { + auto& menuInitFuncs = MenuInit::GetInitFuncs(); + for (const auto& initFunc : menuInitFuncs) { + initFunc(); + } + } +}; + +struct RegisterMenuInitFunc { + RegisterMenuInitFunc(std::function initFunc) { + auto& menuInitFuncs = MenuInit::GetInitFuncs(); + + menuInitFuncs.push_back(initFunc); + } +}; + +struct RegisterMenuUpdateFunc { + RegisterMenuUpdateFunc(std::function updateFunc, std::string sectionName, std::string sidebarName) { + auto& menuUpdateFuncs = MenuInit::GetUpdateFuncs(); + + menuUpdateFuncs[sectionName][sidebarName].push_back(updateFunc); + } +}; + +#endif // MENUTYPES_H diff --git a/soh/soh/SohGui/ResolutionEditor.cpp b/soh/soh/SohGui/ResolutionEditor.cpp new file mode 100644 index 000000000..6b287fde5 --- /dev/null +++ b/soh/soh/SohGui/ResolutionEditor.cpp @@ -0,0 +1,528 @@ +#include "ResolutionEditor.h" +#include +#include + +#include "soh/SohGui/UIWidgets.hpp" +#include +#include "soh/OTRGlobals.h" +#include "soh/SohGui/SohMenu.h" +#include "soh/SohGui/SohGui.hpp" + +/* Console Variables are grouped under gAdvancedResolution. (e.g. CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled") + + The following cvars are used in Libultraship and can be edited here: + - Enabled - Turns Advanced Resolution Mode on. + - AspectRatioX, AspectRatioY - Aspect ratio controls. To toggle off, set either to zero. + - VerticalPixelCount, VerticalResolutionToggle - Resolution controls. + - PixelPerfectMode, IntegerScale.Factor - Pixel Perfect Mode a.k.a. integer scaling controls. + - IntegerScale.FitAutomatically - Automatic resizing for Pixel Perfect Mode. + - IntegerScale.NeverExceedBounds - Prevents manual resizing from exceeding screen bounds. + + The following cvars are also implemented in LUS for niche use cases: + - IgnoreAspectCorrection - Stretch framebuffer to fill screen. + This is something of a power-user setting for niche setups that most people won't need or care about, + but may be useful if playing the Switch/Wii U ports on a 4:3 television. + - IntegerScale.ExceedBoundsBy - Offset the max screen bounds, usually by +1. + This isn't that useful at the moment, so it's unused here. +*/ + +namespace SohGui { +extern std::shared_ptr mSohMenu; +enum setting { UPDATE_aspectRatioX, UPDATE_aspectRatioY, UPDATE_verticalPixelCount }; + +std::unordered_map aspectRatioPresetLabels = { { 0, "Off" }, + { 1, "Custom" }, + { 2, "Original (4:3)" }, + { 3, "Widescreen (16:9)" }, + { 4, "Nintendo 3DS (5:3)" }, + { 5, "16:10 (8:5)" }, + { 6, "Ultrawide (21:9)" } }; +const float aspectRatioPresetsX[] = { 0.0f, 16.0f, 4.0f, 16.0f, 5.0f, 16.0f, 21.0f }; +const float aspectRatioPresetsY[] = { 0.0f, 9.0f, 3.0f, 9.0f, 3.0f, 10.0f, 9.0f }; +const int default_aspectRatio = 1; // Default combo list option + +const char* pixelCountPresetLabels[] = { "Custom", "Native N64 (240p)", "2x (480p)", "3x (720p)", "4x (960p)", + "5x (1200p)", "6x (1440p)", "Full HD (1080p)", "4K (2160p)" }; +const int pixelCountPresets[] = { 480, 240, 480, 720, 960, 1200, 1440, 1080, 2160 }; +const int default_pixelCount = 0; // Default combo list option + +// Resolution clamp values as hardcoded in LUS::Gui::ApplyResolutionChanges() +const uint32_t minVerticalPixelCount = SCREEN_HEIGHT; +const uint32_t maxVerticalPixelCount = 4320; // 18x native, or 8K TV resolution + +const unsigned short default_maxIntegerScaleFactor = 6; // Default size of Integer scale factor slider. + +enum messageType { MESSAGE_ERROR, MESSAGE_WARNING, MESSAGE_QUESTION, MESSAGE_INFO, MESSAGE_GRAY_75 }; +const ImVec4 messageColor[]{ + { 0.85f, 0.0f, 0.0f, 1.0f }, // MESSAGE_ERROR + { 0.85f, 0.85f, 0.0f, 1.0f }, // MESSAGE_WARNING + { 0.0f, 0.85f, 0.85f, 1.0f }, // MESSAGE_QUESTION + { 0.0f, 0.85f, 0.55f, 1.0f }, // MESSAGE_INFO + { 0.75f, 0.75f, 0.75f, 1.0f } // MESSAGE_GRAY_75 +}; +static const float enhancementSpacerHeight = 19.0f; +// Initialise update flags. +static bool update[3]; + +// Initialise integer scale bounds. +static short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get + // overridden depending on viewport res + +static short integerScale_maximumBounds = 1; // can change when window is resized + +// Combo List defaults +static int32_t item_aspectRatio; +static int32_t item_pixelCount; +// Stored Values for non-UIWidgets elements +static float aspectRatioX; +static float aspectRatioY; +static int32_t verticalPixelCount; +// Additional settings +static bool showHorizontalResField; +static int32_t horizontalPixelCount; +// Disabling flags +static bool disabled_everything; +static bool disabled_pixelCount; + +using namespace UIWidgets; + +void ResolutionCustomWidget(WidgetInfo& info) { + ImGui::BeginDisabled(disabled_everything); + // Vertical Resolution + UIWidgets::CVarCheckbox("Set fixed vertical resolution (disables Resolution slider)", CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", + UIWidgets::CheckboxOptions({ {.disabled = disabled_everything} }).Tooltip("Override the resolution scale slider and use the settings below, irrespective of window size.") + .Color(THEME_COLOR)); + //if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls. + // UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); + //} + UIWidgets::PushStyleCombobox(THEME_COLOR); + if (ImGui::Combo("Pixel Count Presets", &item_pixelCount, pixelCountPresetLabels, + IM_ARRAYSIZE(pixelCountPresetLabels)) && + item_pixelCount != default_pixelCount) { // don't change anything if "Custom" is selected. + verticalPixelCount = pixelCountPresets[item_pixelCount]; + + if (showHorizontalResField) { + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } + + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount); + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + UIWidgets::PopStyleCombobox(); + // Horizontal Resolution, if visibility is enabled for it. + if (showHorizontalResField) { + // Only show the field if Aspect Ratio is being enforced. + if ((aspectRatioX > 0.0f) && (aspectRatioY > 0.0f)) { + // So basically we're "faking" this one by setting aspectRatioX instead. + UIWidgets::PushStyleInput(THEME_COLOR); + if (ImGui::InputInt("Horiz. Pixel Count", &horizontalPixelCount, 8, 320)) { + item_aspectRatio = default_aspectRatio; + if (horizontalPixelCount < SCREEN_WIDTH) { + horizontalPixelCount = SCREEN_WIDTH; + } + aspectRatioX = horizontalPixelCount; + aspectRatioY = verticalPixelCount; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + } + UIWidgets::PopStyleInput(); + } else { // Display a notice instead. + ImGui::TextColored(messageColor[MESSAGE_QUESTION], + ICON_FA_QUESTION_CIRCLE " \"Force aspect ratio\" required."); + // ImGui::Text(" "); + ImGui::SameLine(); + if (UIWidgets::Button("Click to resolve", UIWidgets::ButtonOptions().Color(THEME_COLOR))) { + item_aspectRatio = default_aspectRatio; // Set it to Custom + aspectRatioX = aspectRatioPresetsX[2]; // but use the 4:3 defaults + aspectRatioY = aspectRatioPresetsY[2]; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } + } + } + // Vertical Resolution part 2 + UIWidgets::PushStyleInput(THEME_COLOR); + if (ImGui::InputInt("Vertical Pixel Count", &verticalPixelCount, 8, 240)) { + item_pixelCount = default_pixelCount; + update[UPDATE_verticalPixelCount] = true; + + // Account for the natural instinct to enter horizontal first. + // Ignore vertical resolutions that are below the lower clamp constant. + if (showHorizontalResField && !(verticalPixelCount < minVerticalPixelCount)) { + item_aspectRatio = default_aspectRatio; + aspectRatioX = horizontalPixelCount; + aspectRatioY = verticalPixelCount; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + } + } + ImGui::EndDisabled(); + UIWidgets::PopStyleInput(); + + // Integer scaling settings group (Pixel-perfect Mode) + static const ImGuiTreeNodeFlags IntegerScalingResolvedImGuiFlag = + CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) ? ImGuiTreeNodeFlags_DefaultOpen + : ImGuiTreeNodeFlags_None; + UIWidgets::PushStyleHeader(THEME_COLOR); + if (ImGui::CollapsingHeader("Integer Scaling Settings", IntegerScalingResolvedImGuiFlag)) { + const bool disabled_pixelPerfectMode = + !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || disabled_everything; + // Pixel-perfect Mode + UIWidgets::CVarCheckbox("Pixel-perfect Mode", CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", + UIWidgets::CheckboxOptions({{ .disabled = disabled_pixelCount || disabled_everything }}).Tooltip("Don't scale image to fill window.") + .Color(THEME_COLOR)); + if (disabled_pixelCount && CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0)) { + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + + // Integer Scaling + UIWidgets::CVarSliderInt(fmt::format("Integer scale factor: {}", max_integerScaleFactor).c_str(), CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", + UIWidgets::IntSliderOptions({ {.disabled = disabled_pixelPerfectMode || CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0)} }) + .Min(1).Max(max_integerScaleFactor).DefaultValue(1).Tooltip("Integer scales the image. Only available in pixel-perfect mode.").Color(THEME_COLOR)); + // Display warning if size is being clamped or if framebuffer is larger than viewport. + if (!disabled_pixelPerfectMode && + (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", 1) && + CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", 1) > + integerScale_maximumBounds)) { + ImGui::SameLine(); + ImGui::TextColored(messageColor[MESSAGE_WARNING], ICON_FA_EXCLAMATION_TRIANGLE " Window exceeded."); + } + + UIWidgets::CVarCheckbox("Automatically scale image to fit viewport", CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", + UIWidgets::CheckboxOptions({ {.disabled = disabled_pixelPerfectMode} }).DefaultValue(true).Color(THEME_COLOR) + .Tooltip("Automatically sets scale factor to fit window. Only available in pixel-perfect mode.")); + if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0)) { + // This is just here to update the value shown on the slider. + // The function in LUS to handle this setting will ignore IntegerScaleFactor while active. + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", integerScale_maximumBounds); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + } // End of integer scaling settings + UIWidgets::PopStyleHeader(); + + // Collapsible panel for additional settings + UIWidgets::PushStyleHeader(THEME_COLOR); + if (ImGui::CollapsingHeader("Additional Settings")) { + #if defined(__SWITCH__) || defined(__WIIU__) + // Disable aspect correction, stretching the framebuffer to fill the viewport. + // This option is only really needed on systems limited to 16:9 TV resolutions, such as consoles. + // The associated cvar is still functional on PC platforms if you want to use it though. + UIWidgets::CVarCheckbox("Disable aspect correction and stretch the output image.\n" + "(Might be useful for 4:3 televisions!)\n" + "Not available in Pixel Perfect Mode.", + CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", + UIWidgets::CheckboxOptions({{ .disabled = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || disabled_everything}}) + .Color(THEME_COLOR)); + #else + if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", 0)) { + // This setting is intentionally not exposed on PC platforms, + // but may be accidentally activated for varying reasons. + // Having this button should hopefully prevent support headaches. + ImGui::TextColored(messageColor[MESSAGE_QUESTION], ICON_FA_QUESTION_CIRCLE + " If the image is stretched and you don't know why, click this."); + if (ImGui::Button("Click to reenable aspect correction.")) { + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", 0); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + UIWidgets::Spacer(2); + } + #endif + + // A requested addition; an alternative way of displaying the resolution field. + if (UIWidgets::Checkbox("Show a horizontal resolution field, instead of aspect ratio.", + &showHorizontalResField, UIWidgets::CheckboxOptions().Color(THEME_COLOR))) { + if (!showHorizontalResField && (aspectRatioX > 0.0f)) { // when turning this setting off + // Refresh relevant values + aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } else { // when turning this setting on + item_aspectRatio = default_aspectRatio; + if (aspectRatioX > 0.0f) { + // Refresh relevant values in the opposite order + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; + } + } + update[UPDATE_aspectRatioX] = true; + } + + // Beginning of Integer Scaling additional settings. + { + // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); + + // Integer Scaling - Never Exceed Bounds. + const bool disabled_neverExceedBounds = + !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || + CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0) || + disabled_everything; + if (UIWidgets::CVarCheckbox("Prevent integer scaling from exceeding screen bounds.\n" + "(Makes screen bounds take priority over specified factor.)", + CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", UIWidgets::CheckboxOptions({{ .disabled = disabled_neverExceedBounds}}) + .Tooltip( + "Prevents integer scaling factor from exceeding screen bounds.\n\n" + "Enabled: Will clamp the scaling factor and display a gentle warning in the resolution editor.\n" + "Disabled: Will allow scaling to exceed screen bounds, for users who want to crop overscan.\n\n" + " " ICON_FA_INFO_CIRCLE + " Please note that exceeding screen bounds may show a scroll bar on-screen.").Color(THEME_COLOR).DefaultValue(true))) { + + // Initialise the (currently unused) "Exceed Bounds By" cvar if it's been changed. + if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) { + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + } + + // Integer Scaling - Exceed Bounds By 1x/Offset. + // A popular feature in some retro frontends/upscalers, sometimes called "crop overscan" or "1080p 5x". + UIWidgets::CVarCheckbox("Allow integer scale factor to go +1 above maximum screen bounds.", + CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", + UIWidgets::CheckboxOptions({{ .disabled = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || disabled_everything }}).Color(THEME_COLOR)); + + // It does actually function as expected, but exceeding the bottom of the screen shows a scroll bar. + // I've ended up commenting this one out because of the scroll bar, and for simplicity. + + // Display an info message about the scroll bar. + if (!CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", 1) || + CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) { + ImGui::TextColored(messageColor[MESSAGE_INFO], + " " ICON_FA_INFO_CIRCLE + " A scroll bar may become visible if screen bounds are exceeded."); + + // Another support helper button, to disable the unused "Exceed Bounds By" cvar. + // (Remove this button if uncommenting the checkbox.) + if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) { + if (UIWidgets::Button("Click to reset a console variable that may be causing this.", UIWidgets::ButtonOptions().Color(THEME_COLOR))) { + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + } + } else { + ImGui::Text(" "); + } + } // End of Integer Scaling additional settings. + + } // End of additional settings + UIWidgets::PopStyleHeader(); + + // Clamp and update the cvars that don't use UIWidgets + if (update[UPDATE_aspectRatioX] || update[UPDATE_aspectRatioY] || update[UPDATE_verticalPixelCount]) { + if (update[UPDATE_aspectRatioX]) { + if (aspectRatioX < 0.0f) { + aspectRatioX = 0.0f; + } + CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX); + } + if (update[UPDATE_aspectRatioY]) { + if (aspectRatioY < 0.0f) { + aspectRatioY = 0.0f; + } + CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY); + } + if (update[UPDATE_verticalPixelCount]) { + // There's a upper and lower clamp on the Libultraship side too, + // so clamping it here is entirely visual, so the vertical resolution field reflects it. + if (verticalPixelCount < minVerticalPixelCount) { + verticalPixelCount = minVerticalPixelCount; + } + if (verticalPixelCount > maxVerticalPixelCount) { + verticalPixelCount = maxVerticalPixelCount; + } + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount); + } + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio); + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } +} + +void RegisterResolutionWidgets() { + WidgetPath path = { "Settings", "Graphics", SECTION_COLUMN_2 }; + + // Resolution visualiser + mSohMenu->AddWidget(path, "Viewport dimensions: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { + info.name = fmt::format("Viewport dimensions: {} x {}", gfx_current_game_window_viewport.width, + gfx_current_game_window_viewport.height); + }); + mSohMenu->AddWidget(path, "Internal resolution: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { + info.name = + fmt::format("Internal resolution: {} x {}", gfx_current_dimensions.width, gfx_current_dimensions.height); + }); + + // Activator + mSohMenu->AddWidget(path, "Enable advanced settings.", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled"); + // Error/Warning display + mSohMenu + ->AddWidget(path, ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring.", + WIDGET_TEXT) + .PreFunc( + [](WidgetInfo& info) { info.isHidden = !(!CVarGetInteger(CVAR_LOW_RES_MODE, 0) && IsDroppingFrames()); }) + .Options(TextOptions().Color(Colors::Orange)); + mSohMenu->AddWidget(path, ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings.", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); }) + .Options(TextOptions().Color(Colors::LightBlue)); + mSohMenu->AddWidget(path, "Click to disable N64 mode", WIDGET_BUTTON) + .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); }) + .Callback([](WidgetInfo& info) { + CVarSetInteger(CVAR_LOW_RES_MODE, 0); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }); + + // Aspect Ratio + mSohMenu->AddWidget(path, "AspectSep", WIDGET_SEPARATOR) + .PreFunc([](WidgetInfo& info) { + if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); + } + }); + mSohMenu->AddWidget(path, "Force aspect ratio:", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { + if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); + } + }); + mSohMenu->AddWidget(path, "(Select \"Off\" to disable.)", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { + if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); + } + }) + .SameLine(true) + .Options(TextOptions().Color(Colors::Gray)); + // Presets + mSohMenu->AddWidget(path, "Aspect Ratio", WIDGET_COMBOBOX) + .ValuePointer(&item_aspectRatio) + .PreFunc([](WidgetInfo& info) { + if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); + } + }) + .Callback([](WidgetInfo& info) { + if (item_aspectRatio != default_aspectRatio) { // don't change anything if "Custom" is selected. + aspectRatioX = aspectRatioPresetsX[item_aspectRatio]; + aspectRatioY = aspectRatioPresetsY[item_aspectRatio]; + + if (showHorizontalResField) { + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } + + CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX); + CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY); + } + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }) + .Options(ComboboxOptions().ComboMap(aspectRatioPresetLabels)); + mSohMenu->AddWidget(path, "AspectRatioCustom", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + // Hide aspect ratio input fields if using one of the presets. + if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) { + // Declare input interaction bools outside of IF statement to prevent Y field from disappearing. + const bool input_X = UIWidgets::SliderFloat("X", &aspectRatioX, + UIWidgets::FloatSliderOptions({{ .disabled = disabled_everything }}).Min(0.1f).Max(32.0f).Step(0.001f).Format("%3f") + .Color(THEME_COLOR).LabelPosition(UIWidgets::LabelPositions::Near).ComponentAlignment(UIWidgets::ComponentAlignments::Right)); + const bool input_Y = UIWidgets::SliderFloat("Y", &aspectRatioY, + UIWidgets::FloatSliderOptions({{ .disabled = disabled_everything }}).Min(0.1f).Max(24.0f).Step(0.001f).Format("%3f") + .Color(THEME_COLOR).LabelPosition(UIWidgets::LabelPositions::Near).ComponentAlignment(UIWidgets::ComponentAlignments::Right)); + if (input_X || input_Y) { + item_aspectRatio = default_aspectRatio; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + } + } else if (showHorizontalResField) { // Show calculated aspect ratio + if (item_aspectRatio) { + ImGui::Dummy({ 0, 2 }); + const float resolvedAspectRatio = (float)gfx_current_dimensions.width / gfx_current_dimensions.height; + ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio); + } + } + }); + mSohMenu->AddWidget(path, "MoreResolutionSettings", WIDGET_CUSTOM).CustomFunction(ResolutionCustomWidget); +} + +void UpdateResolutionVars() { + // Clamp and update the cvars that don't use UIWidgets + if (update[UPDATE_aspectRatioX] || update[UPDATE_aspectRatioY] || update[UPDATE_verticalPixelCount]) { + if (update[UPDATE_aspectRatioX]) { + if (aspectRatioX < 0.0f) { + aspectRatioX = 0.0f; + } + CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX); + } + if (update[UPDATE_aspectRatioY]) { + if (aspectRatioY < 0.0f) { + aspectRatioY = 0.0f; + } + CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY); + } + if (update[UPDATE_verticalPixelCount]) { + // There's a upper and lower clamp on the Libultraship side too, + // so clamping it here is entirely visual, so the vertical resolution field reflects it. + if (verticalPixelCount < minVerticalPixelCount) { + verticalPixelCount = minVerticalPixelCount; + } + if (verticalPixelCount > maxVerticalPixelCount) { + verticalPixelCount = maxVerticalPixelCount; + } + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount); + } + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio); + CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + // Initialise update flags. + for (uint8_t i = 0; i < sizeof(update); i++) { + update[i] = false; + } + + // Initialise integer scale bounds. + short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get + // overridden depending on viewport res + + short integerScale_maximumBounds = 1; // can change when window is resized + // This is mostly just for UX purposes, as Fit Automatically logic is part of LUS. + if (((float)gfx_current_game_window_viewport.width / gfx_current_game_window_viewport.height) > + ((float)gfx_current_dimensions.width / gfx_current_dimensions.height)) { + // Scale to window height + integerScale_maximumBounds = gfx_current_game_window_viewport.height / gfx_current_dimensions.height; + } else { + // Scale to window width + integerScale_maximumBounds = gfx_current_game_window_viewport.width / gfx_current_dimensions.width; + } + // Lower-clamping maximum bounds value to 1 is no-longer necessary as that's accounted for in LUS. + // Letting it go below 1 in this Editor will even allow for checking if screen bounds are being exceeded. + if (default_maxIntegerScaleFactor < integerScale_maximumBounds) { + max_integerScaleFactor = integerScale_maximumBounds + + CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0); + } + + // Combo List defaults + item_aspectRatio = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", 3); + item_pixelCount = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", default_pixelCount); + // Stored Values for non-UIWidgets elements + aspectRatioX = CVarGetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioPresetsX[item_aspectRatio]); + aspectRatioY = CVarGetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioPresetsY[item_aspectRatio]); + verticalPixelCount = + CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", pixelCountPresets[item_pixelCount]); + // Additional settings + showHorizontalResField = false; + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + // Disabling flags + disabled_everything = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); + disabled_pixelCount = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0); +} + +bool IsDroppingFrames() { + // a rather imprecise way of checking for frame drops. + // but it's mostly there to inform the player of large drops. + const short targetFPS = OTRGlobals::Instance->GetInterpolationFPS(); + const float threshold = targetFPS / 20.0f + 4.1f; + return ImGui::GetIO().Framerate < targetFPS - threshold; +} + + static RegisterMenuUpdateFunc updateFunc(UpdateResolutionVars, "Settings", "Graphics"); + static RegisterMenuInitFunc initFunc(RegisterResolutionWidgets); + +} // namespace BenGui diff --git a/soh/soh/SohGui/ResolutionEditor.h b/soh/soh/SohGui/ResolutionEditor.h new file mode 100644 index 000000000..20d613b3b --- /dev/null +++ b/soh/soh/SohGui/ResolutionEditor.h @@ -0,0 +1,12 @@ +#ifndef RESOLUTIONEDITOR_H +#define RESOLUTIONEDITOR_H + +#include + +namespace SohGui { +bool IsDroppingFrames(); +void RegisterResolutionWidgets(); +void UpdateResolutionVars(); +} // namespace BenGui + +#endif // RESOLUTIONEDITOR_H diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 8d7a4ba45..56da5eba0 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -20,8 +20,7 @@ #ifdef __SWITCH__ #include #endif - -#include "UIWidgets.hpp" +#include "SohMenu.h" #include "include/global.h" #include "include/z64audio.h" #include "soh/SaveManager.h" @@ -32,48 +31,14 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/cosmetics/authenticGfxPatches.h" -#include "soh/Enhancements/resolution-editor/ResolutionEditor.h" #include "soh/Enhancements/debugger/MessageViewer.h" #include "soh/Notification/Notification.h" #include "soh/Enhancements/TimeDisplay/TimeDisplay.h" -bool isBetaQuestEnabled = false; - -extern "C" { - void enableBetaQuest() { isBetaQuestEnabled = true; } - void disableBetaQuest() { isBetaQuestEnabled = false; } -} - - namespace SohGui { // MARK: - Properties - - static const char* chestSizeAndTextureMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" }; static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" }; - static const char* allPowers[9] = { - "Vanilla (1x)", - "Double (2x)", - "Quadruple (4x)", - "Octuple (8x)", - "Foolish (16x)", - "Ridiculous (32x)", - "Merciless (64x)", - "Pure Torture (128x)", - "OHKO (256x)" }; - static const char* subPowers[8] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6], allPowers[7] }; - static const char* subSubPowers[7] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6] }; - static const char* zFightingOptions[3] = { "Disabled", "Consistent Vanish", "No Vanish" }; - static const char* bonkDamageValues[8] = { - "No Damage", - "0.25 Heart", - "0.5 Heart", - "1 Heart", - "2 Hearts", - "4 Hearts", - "8 Hearts", - "OHKO" - }; static const inline std::vector> audioBackends = { #ifdef _WIN32 @@ -98,7 +63,6 @@ namespace SohGui { return buttonText; } - // MARK: - Delegates std::shared_ptr mSohMenuBar; @@ -107,7 +71,8 @@ namespace SohGui { std::shared_ptr mStatsWindow; std::shared_ptr mGfxDebuggerWindow; std::shared_ptr mInputEditorWindow; - + + std::shared_ptr mSohMenu; std::shared_ptr mAudioEditorWindow; std::shared_ptr mInputViewer; std::shared_ptr mInputViewerSettings; @@ -129,40 +94,41 @@ namespace SohGui { std::shared_ptr mTimeSplitWindow; std::shared_ptr mPlandomizerWindow; std::shared_ptr mRandomizerSettingsWindow; - std::shared_ptr mAdvancedResolutionSettingsWindow; std::shared_ptr mModalWindow; std::shared_ptr mNotificationWindow; std::shared_ptr mTimeDisplayWindow; - std::shared_ptr mAboutWindow; + + UIWidgets::Colors GetMenuThemeColor() { + return mSohMenu->GetMenuThemeColor(); + } void SetupGuiElements() { auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); - mSohMenuBar = std::make_shared(CVAR_MENU_BAR_OPEN, CVarGetInteger(CVAR_MENU_BAR_OPEN, 0)); + /*mSohMenuBar = std::make_shared(CVAR_MENU_BAR_OPEN, CVarGetInteger(CVAR_MENU_BAR_OPEN, 0)); gui->SetMenuBar(std::reinterpret_pointer_cast(mSohMenuBar)); - if (gui->GetMenuBar() && !gui->GetMenuBar()->IsVisible()) { + if (!gui->GetMenuBar() && !CVarGetInteger("gSettings.DisableMenuShortcutNotify", 0)) { #if defined(__SWITCH__) || defined(__WIIU__) - Notification::Emit({ .message = "Press - to access enhancements menu", .remainingTime = 10.0f }); + gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press - to access enhancements menu"); #else - Notification::Emit({ .message = "Press F1 to access enhancements menu", .remainingTime = 10.0f }); + gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press F1 to access enhancements menu"); #endif - } + }*/ + + mSohMenu = std::make_shared(CVAR_WINDOW("Menu"), "Port Menu"); + gui->SetMenu(mSohMenu); mStatsWindow = gui->GetGuiWindow("Stats"); if (mStatsWindow == nullptr) { SPDLOG_ERROR("Could not find stats window"); } - mConsoleWindow = gui->GetGuiWindow("Console"); - if (mConsoleWindow == nullptr) { - SPDLOG_ERROR("Could not find console window"); - } + mConsoleWindow = std::make_shared(CVAR_WINDOW("Console"), "Console##SoH", ImVec2(820, 630)); + gui->AddGuiWindow(mConsoleWindow); - mGfxDebuggerWindow = gui->GetGuiWindow("GfxDebuggerWindow"); - if (mGfxDebuggerWindow == nullptr) { - SPDLOG_ERROR("Could not find input GfxDebuggerWindow"); - } + mGfxDebuggerWindow = std::make_shared(CVAR_WINDOW("GfxDebugger"), "GfxDebugger##SoH", ImVec2(820, 630)); + gui->AddGuiWindow(mGfxDebuggerWindow); mInputEditorWindow = gui->GetGuiWindow("Controller Configuration"); if (mInputEditorWindow == nullptr) { @@ -185,7 +151,7 @@ namespace SohGui { gui->AddGuiWindow(mSaveEditorWindow); mHookDebuggerWindow = std::make_shared(CVAR_WINDOW("HookDebugger"), "Hook Debugger", ImVec2(1250, 850)); gui->AddGuiWindow(mHookDebuggerWindow); - mDLViewerWindow = std::make_shared(CVAR_WINDOW("DLViewer"), "Display List Viewer", ImVec2(520, 600)); + mDLViewerWindow = std::make_shared(CVAR_WINDOW("DisplayListViewer"), "Display List Viewer", ImVec2(520, 600)); gui->AddGuiWindow(mDLViewerWindow); mValueViewerWindow = std::make_shared(CVAR_WINDOW("ValueViewer"), "Value Viewer", ImVec2(520, 600)); gui->AddGuiWindow(mValueViewerWindow); @@ -193,26 +159,24 @@ namespace SohGui { gui->AddGuiWindow(mMessageViewerWindow); mGameplayStatsWindow = std::make_shared(CVAR_WINDOW("GameplayStats"), "Gameplay Stats", ImVec2(480, 550)); gui->AddGuiWindow(mGameplayStatsWindow); - mCheckTrackerWindow = std::make_shared(CVAR_WINDOW("CheckTracker"), "Check Tracker"); + mCheckTrackerWindow = std::make_shared(CVAR_WINDOW("CheckTracker"), "Check Tracker", ImVec2(400, 540)); gui->AddGuiWindow(mCheckTrackerWindow); mCheckTrackerSettingsWindow = std::make_shared(CVAR_WINDOW("CheckTrackerSettings"), "Check Tracker Settings", ImVec2(600, 375)); gui->AddGuiWindow(mCheckTrackerSettingsWindow); - mEntranceTrackerWindow = std::make_shared(CVAR_WINDOW("EntranceTracker"), "Entrance Tracker"); + mEntranceTrackerWindow = std::make_shared(CVAR_WINDOW("EntranceTracker"), "Entrance Tracker", ImVec2(500, 750)); gui->AddGuiWindow(mEntranceTrackerWindow); mEntranceTrackerSettingsWindow = std::make_shared(CVAR_WINDOW("EntranceTrackerSettings"), "Entrance Tracker Settings", ImVec2(600, 375)); gui->AddGuiWindow(mEntranceTrackerSettingsWindow); - mItemTrackerWindow = std::make_shared(CVAR_WINDOW("ItemTracker"), "Item Tracker"); + mItemTrackerWindow = std::make_shared(CVAR_WINDOW("ItemTracker"), "Item Tracker", ImVec2(350, 600)); gui->AddGuiWindow(mItemTrackerWindow); mItemTrackerSettingsWindow = std::make_shared(CVAR_WINDOW("ItemTrackerSettings"), "Item Tracker Settings", ImVec2(733, 472)); gui->AddGuiWindow(mItemTrackerSettingsWindow); mRandomizerSettingsWindow = std::make_shared(CVAR_WINDOW("RandomizerSettings"), "Randomizer Settings", ImVec2(920, 600)); gui->AddGuiWindow(mRandomizerSettingsWindow); - mTimeSplitWindow = std::make_shared(CVAR_WINDOW("TimeSplitEnabled"), "Time Splits", ImVec2(450, 660)); + mTimeSplitWindow = std::make_shared(CVAR_WINDOW("TimeSplits"), "Time Splits", ImVec2(450, 660)); gui->AddGuiWindow(mTimeSplitWindow); - mPlandomizerWindow = std::make_shared(CVAR_WINDOW("PlandomizerWindow"), "Plandomizer Editor", ImVec2(850, 760)); + mPlandomizerWindow = std::make_shared(CVAR_WINDOW("PlandomizerEditor"), "Plandomizer Editor", ImVec2(850, 760)); gui->AddGuiWindow(mPlandomizerWindow); - mAdvancedResolutionSettingsWindow = std::make_shared(CVAR_WINDOW("AdvancedResolutionEditor"), "Advanced Resolution Settings", ImVec2(497, 599)); - gui->AddGuiWindow(mAdvancedResolutionSettingsWindow); mModalWindow = std::make_shared(CVAR_WINDOW("ModalWindow"), "Modal Window"); gui->AddGuiWindow(mModalWindow); mModalWindow->Show(); @@ -221,8 +185,6 @@ namespace SohGui { mNotificationWindow->Show(); mTimeDisplayWindow = std::make_shared(CVAR_WINDOW("TimeDisplayEnabled"), "Additional Timers"); gui->AddGuiWindow(mTimeDisplayWindow); - mAboutWindow = std::make_shared(CVAR_WINDOW("AboutWindow"), "About"); - gui->AddGuiWindow(mAboutWindow); } void Destroy() { @@ -231,7 +193,6 @@ namespace SohGui { mNotificationWindow = nullptr; mModalWindow = nullptr; - mAdvancedResolutionSettingsWindow = nullptr; mRandomizerSettingsWindow = nullptr; mItemTrackerWindow = nullptr; mItemTrackerSettingsWindow = nullptr; @@ -259,7 +220,6 @@ namespace SohGui { mTimeSplitWindow = nullptr; mPlandomizerWindow = nullptr; mTimeDisplayWindow = nullptr; - mAboutWindow = nullptr; } void RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function button1callback, std::function button2callback) { diff --git a/soh/soh/SohGui/SohGui.hpp b/soh/soh/SohGui/SohGui.hpp index 4544c13e9..35c93b353 100644 --- a/soh/soh/SohGui/SohGui.hpp +++ b/soh/soh/SohGui/SohGui.hpp @@ -18,6 +18,8 @@ #include "soh/Enhancements/debugger/debugSaveEditor.h" #include "soh/Enhancements/debugger/hookDebugger.h" #include "soh/Enhancements/debugger/dlViewer.h" +#include "soh/Enhancements/debugger/sohConsole.h" +#include "soh/Enhancements/debugger/sohGfxDebugger.h" #include "soh/Enhancements/debugger/valueViewer.h" #include "soh/Enhancements/gameplaystatswindow.h" #include "soh/Enhancements/randomizer/randomizer_check_tracker.h" @@ -26,18 +28,8 @@ #include "soh/Enhancements/randomizer/randomizer_settings_window.h" #include "soh/Enhancements/timesplits/TimeSplits.h" #include "soh/Enhancements/randomizer/Plandomizer.h" -#include "soh/AboutWindow.h" #include "SohModals.h" -#ifdef __cplusplus -extern "C" { -#endif - void enableBetaQuest(); - void disableBetaQuest(); -#ifdef __cplusplus -} -#endif - namespace SohGui { void SetupHooks(); void SetupGuiElements(); @@ -45,6 +37,9 @@ namespace SohGui { void Destroy(); void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function button1callback = nullptr, std::function button2callback = nullptr); void ShowRandomizerSettingsMenu(); + UIWidgets::Colors GetMenuThemeColor(); } +#define THEME_COLOR SohGui::GetMenuThemeColor() + #endif /* SohGui_hpp */ diff --git a/soh/soh/SohGui/SohMenu.cpp b/soh/soh/SohGui/SohMenu.cpp new file mode 100644 index 000000000..26770c986 --- /dev/null +++ b/soh/soh/SohGui/SohMenu.cpp @@ -0,0 +1,170 @@ +#include "SohMenu.h" +#include "soh/OTRGlobals.h" +#include "soh/Enhancements/controls/SohInputEditorWindow.h" +#include "window/gui/GuiMenuBar.h" +#include "window/gui/GuiElement.h" +#include +#include "StringHelper.h" +#include +#include + +extern std::unordered_map warpPointSceneList; + +namespace SohGui { +extern std::shared_ptr mSohMenu; + +using namespace UIWidgets; + +void SohMenu::AddSidebarEntry(std::string sectionName, std::string sidebarName, uint32_t columnCount) { + assert(!sectionName.empty()); + assert(!sidebarName.empty()); + menuEntries.at(sectionName).sidebars.emplace(sidebarName, SidebarEntry{ .columnCount = columnCount }); + menuEntries.at(sectionName).sidebarOrder.push_back(sidebarName); +} + +WidgetInfo& SohMenu::AddWidget(WidgetPath& pathInfo, std::string widgetName, WidgetType widgetType) { + assert(!widgetName.empty()); // Must be unique + assert(menuEntries.contains(pathInfo.sectionName)); // Section/header must already exist + assert(menuEntries.at(pathInfo.sectionName).sidebars.contains(pathInfo.sidebarName)); // Sidebar must already exist + std::unordered_map& sidebar = menuEntries.at(pathInfo.sectionName).sidebars; + uint8_t column = pathInfo.column; + if (sidebar.contains(pathInfo.sidebarName)) { + while (sidebar.at(pathInfo.sidebarName).columnWidgets.size() < column + 1) { + sidebar.at(pathInfo.sidebarName).columnWidgets.push_back({}); + } + } + SidebarEntry& entry = sidebar.at(pathInfo.sidebarName); + entry.columnWidgets.at(column).push_back({ .name = widgetName, .type = widgetType }); + WidgetInfo& widget = entry.columnWidgets.at(column).back(); + switch (widgetType) { + case WIDGET_CHECKBOX: + case WIDGET_CVAR_CHECKBOX: + widget.options = std::make_shared(); + break; + case WIDGET_SLIDER_FLOAT: + case WIDGET_CVAR_SLIDER_FLOAT: + widget.options = std::make_shared(); + break; + case WIDGET_SLIDER_INT: + case WIDGET_CVAR_SLIDER_INT: + widget.options = std::make_shared(); + break; + case WIDGET_COMBOBOX: + case WIDGET_CVAR_COMBOBOX: + case WIDGET_AUDIO_BACKEND: + case WIDGET_VIDEO_BACKEND: + widget.options = std::make_shared(); + break; + case WIDGET_BUTTON: + widget.options = std::make_shared(); + break; + case WIDGET_WINDOW_BUTTON: + widget.options = std::make_shared(); + break; + case WIDGET_COLOR_24: + case WIDGET_COLOR_32: + break; + case WIDGET_SEPARATOR_TEXT: + case WIDGET_TEXT: + widget.options = std::make_shared(); + break; + case WIDGET_SEARCH: + case WIDGET_SEPARATOR: + default: + widget.options = std::make_shared(); + } + return widget; +} + +SohMenu::SohMenu(const std::string& consoleVariable, const std::string& name) + : Menu(consoleVariable, name, 0, UIWidgets::Colors::LightBlue) { +} + +void SohMenu::InitElement() { + Ship::Menu::InitElement(); + AddMenuSettings(); + AddMenuEnhancements(); + AddMenuRandomizer(); +#ifdef ENABLE_REMOTE_CONTROL + AddMenuNetwork(); +#endif + AddMenuDevTools(); + + if (CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0)) { + InsertSidebarSearch(); + } + + for (auto& initFunc : MenuInit::GetInitFuncs()) { + initFunc(); + } + + disabledMap = { + { DISABLE_FOR_NO_VSYNC, + { [](disabledInfo& info) -> bool { + return !Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync(); + }, + "Disabling VSync not supported" } }, + { DISABLE_FOR_NO_WINDOWED_FULLSCREEN, + { [](disabledInfo& info) -> bool { + return !Ship::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen(); + }, + "Windowed Fullscreen not supported" } }, + { DISABLE_FOR_NO_MULTI_VIEWPORT, + { [](disabledInfo& info) -> bool { + return !Ship::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports(); + }, + "Multi-viewports not supported" } }, + { DISABLE_FOR_NOT_DIRECTX, + { [](disabledInfo& info) -> bool { + return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() != + Ship::WindowBackend::FAST3D_DXGI_DX11; + }, + "Available Only on DirectX" } }, + { DISABLE_FOR_DIRECTX, + { [](disabledInfo& info) -> bool { + return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == + Ship::WindowBackend::FAST3D_DXGI_DX11; + }, + "Not Available on DirectX" } }, + { DISABLE_FOR_MATCH_REFRESH_RATE_ON, + { [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_SETTING("MatchRefreshRate"), 0); }, + "Match Refresh Rate is Enabled" } }, + { DISABLE_FOR_ADVANCED_RESOLUTION_ON, + { [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); }, + "Advanced Resolution Enabled" } }, + { DISABLE_FOR_VERTICAL_RES_TOGGLE_ON, + { [](disabledInfo& info) -> bool { + return CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0); + }, + "Vertical Resolution Toggle Enabled" } }, + { DISABLE_FOR_LOW_RES_MODE_ON, + { [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_LOW_RES_MODE, 0); }, "N64 Mode Enabled" } }, + { DISABLE_FOR_NULL_PLAY_STATE, + { [](disabledInfo& info) -> bool { return gPlayState == NULL; }, "Save Not Loaded" } }, + { DISABLE_FOR_DEBUG_MODE_OFF, + { [](disabledInfo& info) -> bool { return !CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0); }, + "Debug Mode is Disabled" } }, + { DISABLE_FOR_FRAME_ADVANCE_OFF, + { [](disabledInfo& info) -> bool { return !(gPlayState != nullptr && gPlayState->frameAdvCtx.enabled); }, + "Frame Advance is Disabled" } }, + { DISABLE_FOR_ADVANCED_RESOLUTION_OFF, + { [](disabledInfo& info) -> bool { return !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); }, + "Advanced Resolution is Disabled" } }, + { DISABLE_FOR_VERTICAL_RESOLUTION_OFF, + { [](disabledInfo& info) -> bool { return !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0); }, + "Vertical Resolution Toggle is Off" } }, + }; +} + +void SohMenu::UpdateElement() { + Ship::Menu::UpdateElement(); +} + +void SohMenu::Draw() { + Ship::Menu::Draw(); +} + +void SohMenu::DrawElement() { + Ship::Menu::DrawElement(); +} +} // namespace SohGui diff --git a/soh/soh/SohGui/SohMenu.h b/soh/soh/SohGui/SohMenu.h new file mode 100644 index 000000000..dacb98936 --- /dev/null +++ b/soh/soh/SohGui/SohMenu.h @@ -0,0 +1,234 @@ +#ifndef SOHMENU_H +#define SOHMENU_H + +#include +#include "UIWidgets.hpp" +#include "Menu.h" +#include "graphic/Fast3D/gfx_rendering_api.h" +#include "soh/cvar_prefixes.h" +#include "soh/Enhancements/enhancementTypes.h" +#include "soh/Enhancements/presets.h" + +extern "C" { +#include "z64.h" +#include "functions.h" +#include "variables.h" +#include "macros.h" +extern PlayState* gPlayState; +} + +#ifdef __cplusplus +extern "C" { +#endif + void enableBetaQuest(); + void disableBetaQuest(); +#ifdef __cplusplus +} +#endif + +namespace SohGui { + +static const std::unordered_map menuThemeOptions = { + { UIWidgets::Colors::Red, "Red" }, + { UIWidgets::Colors::DarkRed, "Dark Red" }, + { UIWidgets::Colors::Orange, "Orange" }, + { UIWidgets::Colors::Green, "Green" }, + { UIWidgets::Colors::DarkGreen, "Dark Green" }, + { UIWidgets::Colors::LightBlue, "Light Blue" }, + { UIWidgets::Colors::Blue, "Blue" }, + { UIWidgets::Colors::DarkBlue, "Dark Blue" }, + { UIWidgets::Colors::Indigo, "Indigo" }, + { UIWidgets::Colors::Violet, "Violet" }, + { UIWidgets::Colors::Purple, "Purple" }, + { UIWidgets::Colors::Brown, "Brown" }, + { UIWidgets::Colors::Gray, "Gray" }, + { UIWidgets::Colors::DarkGray, "Dark Gray" }, +}; + +static const std::unordered_map textureFilteringMap = { + { FILTER_THREE_POINT, "Three-Point" }, + { FILTER_LINEAR, "Linear" }, + { FILTER_NONE, "None" }, +}; + +static const std::unordered_map logLevels = { + { DEBUG_LOG_TRACE, "Trace" }, { DEBUG_LOG_DEBUG, "Debug" }, { DEBUG_LOG_INFO, "Info" }, + { DEBUG_LOG_WARN, "Warn" }, { DEBUG_LOG_ERROR, "Error" }, { DEBUG_LOG_CRITICAL, "Critical" }, + { DEBUG_LOG_OFF, "Off" }, +}; + +static const std::unordered_map notificationPosition = { + { 0, "Top Left" }, { 1, "Top Right" }, { 2, "Bottom Left" }, { 3, "Bottom Right" }, { 4, "Hidden" }, +}; + +static const std::unordered_map dekuStickCheat = { + { DEKU_STICK_NORMAL, "Normal" }, + { DEKU_STICK_UNBREAKABLE, "Unbreakable" }, + { DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE, "Unbreakable + Always on Fire" } +}; + +static const std::unordered_map skipForcedDialogOptions = { + { FORCED_DIALOG_SKIP_NONE, "None" }, + { FORCED_DIALOG_SKIP_NAVI, "Navi" }, + { FORCED_DIALOG_SKIP_NPC, "NPCs" }, + { FORCED_DIALOG_SKIP_ALL, "All" } +}; + +static const std::unordered_map skipGetItemAnimationOptions = { + { SGIA_DISABLED, "Disabled" }, + { SGIA_JUNK, "Junk Items" }, + { SGIA_ALL, "All Items" } +}; + +static const std::unordered_map chestStyleMatchesContentsOptions = { + { CSMC_DISABLED, "Disabled" }, + { CSMC_BOTH, "Both" }, + { CSMC_TEXTURE, "Texture Only" }, + { CSMC_SIZE, "Size Only" } +}; + +static const std::unordered_map timeTravelOptions = { + { TIME_TRAVEL_DISABLED, "Disabled" }, + { TIME_TRAVEL_OOT, "Ocarina of Time" }, + { TIME_TRAVEL_ANY, "Any Ocarina" } +}; + +static const std::unordered_map sleepingWaterfallOptions = { + { WATERFALL_ALWAYS, "Always" }, + { WATERFALL_ONCE, "Once" }, + { WATERFALL_NEVER, "Never" } +}; + +static const std::unordered_map allPowers = { + { DAMAGE_VANILLA, "Vanilla (1x)" }, + { DAMAGE_DOUBLE, "Double (2x)" }, + { DAMAGE_QUADRUPLE, "Quadruple (4x)" }, + { DAMAGE_OCTUPLE, "Octuple (8x)" }, + { DAMAGE_FOOLISH, "Foolish (16x)" }, + { DAMAGE_RIDICULOUS, "Ridiculous (32x)" }, + { DAMAGE_MERCILESS, "Merciless (64x)" }, + { DAMAGE_TORTURE, "Pure Torture (128x)" }, + { DAMAGE_OHKO, "OHKO (256x)" } +}; + +static const std::unordered_map subPowers = { + { DAMAGE_VANILLA, "Vanilla (1x)" }, + { DAMAGE_DOUBLE, "Double (2x)" }, + { DAMAGE_QUADRUPLE, "Quadruple (4x)" }, + { DAMAGE_OCTUPLE, "Octuple (8x)" }, + { DAMAGE_FOOLISH, "Foolish (16x)" }, + { DAMAGE_RIDICULOUS, "Ridiculous (32x)" }, + { DAMAGE_MERCILESS, "Merciless (64x)" }, + { DAMAGE_TORTURE, "Pure Torture (128x)" }, +}; + +static const std::unordered_map subSubPowers = { + { DAMAGE_VANILLA, "Vanilla (1x)" }, + { DAMAGE_DOUBLE, "Double (2x)" }, + { DAMAGE_QUADRUPLE, "Quadruple (4x)" }, + { DAMAGE_OCTUPLE, "Octuple (8x)" }, + { DAMAGE_FOOLISH, "Foolish (16x)" }, + { DAMAGE_RIDICULOUS, "Ridiculous (32x)" }, + { DAMAGE_MERCILESS, "Merciless (64x)" }, +}; + +static const std::unordered_map bonkDamageValues = { + { BONK_DAMAGE_NONE, "No Damage" }, + { BONK_DAMAGE_QUARTER_HEART, "0.25 Hearts" }, + { BONK_DAMAGE_HALF_HEART, "0.5 Hearts" }, + { BONK_DAMAGE_1_HEART, "1 Heart" }, + { BONK_DAMAGE_2_HEARTS, "2 Hearts" }, + { BONK_DAMAGE_4_HEARTS, "4 Hearts" }, + { BONK_DAMAGE_8_HEARTS, "8 Hearts" }, + { BONK_DAMAGE_OHKO, "OHKO" } +}; + +static const std::unordered_map cursorAnywhereValues = { + { PAUSE_ANY_CURSOR_RANDO_ONLY, "Only in Rando" }, + { PAUSE_ANY_CURSOR_ALWAYS_ON, "Always" }, + { PAUSE_ANY_CURSOR_ALWAYS_OFF, "Never" } +}; + +static const std::unordered_map swordToggleModes = { + { SWORD_TOGGLE_NONE, "None" }, + { SWORD_TOGGLE_CHILD, "Child Toggle" }, + { SWORD_TOGGLE_BOTH_AGES, "Both Ages" } +}; + +static const std::unordered_map zFightingOptions = { + { ZFIGHT_FIX_DISABLED, "Disabled" }, + { ZFIGHT_FIX_CONSISTENT_VANISH, "Consistent Vanish" }, + { ZFIGHT_FIX_NO_VANISH, "No Vanish" } +}; + +static const std::unordered_map mirroredWorldModes = { + { MIRRORED_WORLD_OFF, "Disabled" }, + { MIRRORED_WORLD_ALWAYS, "Always" }, + { MIRRORED_WORLD_RANDOM, "Random" }, + { MIRRORED_WORLD_RANDOM_SEEDED, "Random (Seeded)" }, + { MIRRORED_WORLD_DUNGEONS_ALL, "Dungeons" }, + { MIRRORED_WORLD_DUNGEONS_VANILLA, "Dungeons (Vanilla)" }, + { MIRRORED_WORLD_DUNGEONS_MQ, "Dungeons (MQ)" }, + { MIRRORED_WORLD_DUNGEONS_RANDOM, "Dungeons Random" }, + { MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED, "Dungeons Random (Seeded)"} +}; + +static const std::unordered_map enemyRandomizerModes = { + { ENEMY_RANDOMIZER_OFF, "Disabled" }, + { ENEMY_RANDOMIZER_RANDOM, "Random" }, + { ENEMY_RANDOMIZER_RANDOM_SEEDED, "Random (Seeded)"} +}; + +static const std::unordered_map debugSaveFileModes = { + { 0, "Off" }, { 1, "Vanilla" }, { 2, "Maxed" }, +}; + +static const std::unordered_map bootSequenceLabels = { + { BOOTSEQUENCE_DEFAULT, "Default" }, + { BOOTSEQUENCE_AUTHENTIC, "Authentic" }, + { BOOTSEQUENCE_FILESELECT, "File Select" } +}; + +static const std::unordered_map enhancementPresetList = { + { ENHANCEMENT_PRESET_DEFAULT, "Default" }, + { ENHANCEMENT_PRESET_VANILLA_PLUS, "Vanilla Plus" }, + { ENHANCEMENT_PRESET_ENHANCED, "Enhanced" }, + { ENHANCEMENT_PRESET_RANDOMIZER, "Randomizer" } +}; + +static const char* itemCountMessageCVars[3] = { + CVAR_ENHANCEMENT("InjectItemCounts.GoldSkulltula"), + CVAR_ENHANCEMENT("InjectItemCounts.HeartPiece"), + CVAR_ENHANCEMENT("InjectItemCounts.HeartContainer"), +}; + +static const char* itemCountMessageOptions[ARRAY_COUNT(itemCountMessageCVars)] = { + "Gold Skulltula Tokens", + "Pieces of Heart", + "Heart Containers", +}; + +class SohMenu : public Ship::Menu { + public: + SohMenu(const std::string& consoleVariable, const std::string& name); + + void InitElement() override; + void DrawElement() override; + void UpdateElement() override; + void Draw() override; + + void AddSidebarEntry(std::string sectionName, std::string sidbarName, uint32_t columnCount); + WidgetInfo& AddWidget(WidgetPath& pathInfo, std::string widgetName, WidgetType widgetType); + void AddMenuSettings(); + void AddMenuEnhancements(); + void AddMenuDevTools(); + void AddMenuRandomizer(); + void AddMenuNetwork(); + + private: + char mGitCommitHashTruncated[8]; + bool mIsTaggedVersion; +}; +} // namespace SohGui + +#endif // SOHMENU_H diff --git a/soh/soh/SohGui/SohMenuBar.cpp b/soh/soh/SohGui/SohMenuBar.cpp index 5fd7324fb..ed63fea7b 100644 --- a/soh/soh/SohGui/SohMenuBar.cpp +++ b/soh/soh/SohGui/SohMenuBar.cpp @@ -39,12 +39,10 @@ #include "soh/Enhancements/randomizer/randomizer_entrance_tracker.h" #include "soh/Enhancements/randomizer/randomizer_item_tracker.h" #include "soh/Enhancements/randomizer/randomizer_settings_window.h" -#include "soh/Enhancements/resolution-editor/ResolutionEditor.h" #include "soh/Enhancements/enemyrandomizer.h" #include "soh/Enhancements/timesplits/TimeSplits.h" #include "soh/Enhancements/randomizer/Plandomizer.h" #include "soh/Enhancements/TimeDisplay/TimeDisplay.h" -#include "soh/AboutWindow.h" // FA icons are kind of wonky, if they worked how I expected them to the "+ 2.0f" wouldn't be needed, but // they don't work how I expect them to so I added that because it looked good when I eyeballed it @@ -70,8 +68,6 @@ static std::unordered_map windowBackendNames = { Ship::WindowBackend::FAST3D_SDL_METAL, "Metal" }, }; -static const char* imguiScaleOptions[4] = { "Small", "Normal", "Large", "X-Large" }; - static const char* filters[3] = { #ifdef __WIIU__ "", @@ -81,55 +77,6 @@ static const char* imguiScaleOptions[4] = { "Small", "Normal", "Large", "X-Large "Linear", "None" }; - static const char* chestStyleMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" }; - static const char* skipGetItemAnimationOptions[3] = { "Disabled", "Junk Items", "All Items" }; - static const char* skipForcedDialogOptions[4] = { "None", "Navi Only", "NPCs Only", "All" }; - static const char* sleepingWaterfallOptions[3] = { "Always", "Once", "Never" }; - static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" }; - static const char* mirroredWorldModes[9] = { - "Disabled", "Always", "Random", "Random (Seeded)", "Dungeons", - "Dungeons (Vanilla)", "Dungeons (MQ)", "Dungeons Random", "Dungeons Random (Seeded)", - }; - static const char* enemyRandomizerModes[3] = { "Disabled", "Random", "Random (Seeded)" }; - static const char* allPowers[9] = { - "Vanilla (1x)", - "Double (2x)", - "Quadruple (4x)", - "Octuple (8x)", - "Foolish (16x)", - "Ridiculous (32x)", - "Merciless (64x)", - "Pure Torture (128x)", - "OHKO (256x)" }; - static const char* subPowers[8] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6], allPowers[7] }; - static const char* subSubPowers[7] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6] }; - static const char* zFightingOptions[3] = { "Disabled", "Consistent Vanish", "No Vanish" }; - static const char* bootSequenceLabels[3] = { "Default", "Authentic", "File Select" }; - static const char* DebugSaveFileModes[3] = { "Off", "Vanilla", "Maxed" }; - static const char* DekuStickCheat[3] = { "Normal", "Unbreakable", "Unbreakable + Always on Fire" }; - static const char* bonkDamageValues[8] = { - "No Damage", - "0.25 Heart", - "0.5 Heart", - "1 Heart", - "2 Hearts", - "4 Hearts", - "8 Hearts", - "OHKO" - }; - static const char* timeTravelOptions[3] = { "Disabled", "Ocarina of Time", "Any Ocarina" }; - static const char* swordToggleModes[3] = { "Disabled", "Child Toggle", "Both Ages (May lead to unintended behaviour)"}; - static const char* itemCountMessageCVars[3] = { - CVAR_ENHANCEMENT("InjectItemCounts.GoldSkulltula"), - CVAR_ENHANCEMENT("InjectItemCounts.HeartPiece"), - CVAR_ENHANCEMENT("InjectItemCounts.HeartContainer"), - }; - static const char* itemCountMessageOptions[ARRAY_COUNT(itemCountMessageCVars)] = { - "Gold Skulltula Tokens", - "Pieces of Heart", - "Heart Containers", - }; - extern "C" SaveContext gSaveContext; namespace SohGui { @@ -137,2058 +84,25 @@ namespace SohGui { std::unordered_map availableWindowBackendsMap; Ship::WindowBackend configWindowBackend; -void UpdateWindowBackendObjects() { - Ship::WindowBackend runningWindowBackend = Ship::Context::GetInstance()->GetWindow()->GetWindowBackend(); - int32_t configWindowBackendId = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1); - if (Ship::Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(configWindowBackendId)) { - configWindowBackend = static_cast(configWindowBackendId); - } else { - configWindowBackend = runningWindowBackend; - } - - auto availableWindowBackends = Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends(); - for (auto& backend : *availableWindowBackends) { - availableWindowBackendsMap[backend] = windowBackendNames[backend]; - } -} - -void DrawMenuBarIcon() { - static bool gameIconLoaded = false; - if (!gameIconLoaded) { - Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Game_Icon", "textures/icons/gIcon.png"); - gameIconLoaded = true; - } - - if (Ship::Context::GetInstance()->GetWindow()->GetGui()->HasTextureByName("Game_Icon")) { -#ifdef __SWITCH__ - ImVec2 iconSize = ImVec2(20.0f, 20.0f); - float posScale = 1.0f; -#elif defined(__WIIU__) - ImVec2 iconSize = ImVec2(16.0f * 2, 16.0f * 2); - float posScale = 2.0f; -#else - ImVec2 iconSize = ImVec2(16.0f, 16.0f); - float posScale = 1.0f; -#endif - ImGui::SetCursorPos(ImVec2(5, 2.5f) * posScale); - ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Game_Icon"), iconSize); - ImGui::SameLine(); - ImGui::SetCursorPos(ImVec2(25, 0) * posScale); - } -} - -extern std::shared_ptr mAboutWindow; - -void DrawShipMenu() { - if (ImGui::BeginMenu("Ship")) { - if (mAboutWindow) { - if (ImGui::MenuItem("About...")) { - mAboutWindow->Show(); - } - } - - UIWidgets::Spacer(0); - - if (ImGui::MenuItem("Hide Menu Bar", -#if !defined(__SWITCH__) && !defined(__WIIU__) - "F1" -#else - "[-]" -#endif - )) { - Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenuBar()->ToggleVisibility(); - } - UIWidgets::Spacer(0); -#if !defined(__SWITCH__) && !defined(__WIIU__) - if (ImGui::MenuItem("Toggle Fullscreen", "F11")) { - Ship::Context::GetInstance()->GetWindow()->ToggleFullscreen(); - } - UIWidgets::Spacer(0); -#endif - if (ImGui::MenuItem("Reset", -#ifdef __APPLE__ - "Command-R" -#elif !defined(__SWITCH__) && !defined(__WIIU__) - "Ctrl+R" -#else - "" -#endif - )) { - std::reinterpret_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset"); - } -#if !defined(__SWITCH__) && !defined(__WIIU__) - UIWidgets::Spacer(0); - if (ImGui::MenuItem("Open App Files Folder")) { - std::string filesPath = Ship::Context::GetInstance()->GetAppDirectoryPath(); - SDL_OpenURL(std::string("file:///" + std::filesystem::absolute(filesPath).string()).c_str()); - } - UIWidgets::Spacer(0); - - if (ImGui::MenuItem("Quit")) { - Ship::Context::GetInstance()->GetWindow()->Close(); - } -#endif - ImGui::EndMenu(); - } -} - -extern std::shared_ptr mInputEditorWindow; extern std::shared_ptr mGfxDebuggerWindow; -extern std::shared_ptr mInputViewer; -extern std::shared_ptr mInputViewerSettings; -extern std::shared_ptr mAdvancedResolutionSettingsWindow; void DrawSettingsMenu() { - if (ImGui::BeginMenu("Settings")) - { - if (ImGui::BeginMenu("Audio")) { - UIWidgets::PaddedEnhancementSliderInt("Master Volume: %d %%", "##Master_Vol", CVAR_SETTING("Volume.Master"), 0, 100, "", 100, true, false, true); - if (UIWidgets::PaddedEnhancementSliderInt("Main Music Volume: %d %%", "##Main_Music_Vol", CVAR_SETTING("Volume.MainMusic"), 0, 100, "", 100, true, false, true)) { - Audio_SetGameVolume(SEQ_PLAYER_BGM_MAIN, ((float)CVarGetInteger(CVAR_SETTING("Volume.MainMusic"), 100) / 100.0f)); - } - if (UIWidgets::PaddedEnhancementSliderInt("Sub Music Volume: %d %%", "##Sub_Music_Vol", CVAR_SETTING("Volume.SubMusic"), 0, 100, "", 100, true, false, true)) { - Audio_SetGameVolume(SEQ_PLAYER_BGM_SUB, ((float)CVarGetInteger(CVAR_SETTING("Volume.SubMusic"), 100) / 100.0f)); - } - if (UIWidgets::PaddedEnhancementSliderInt("Fanfare Volume: %d %%", "##Fanfare_Vol", CVAR_SETTING("Volume.Fanfare"), 0, 100, "", 100, true, false, true)) { - Audio_SetGameVolume(SEQ_PLAYER_FANFARE, ((float)CVarGetInteger(CVAR_SETTING("Volume.Fanfare"), 100) / 100.0f)); - } - if (UIWidgets::PaddedEnhancementSliderInt("Sound Effects Volume: %d %%", "##Sound_Effect_Vol", CVAR_SETTING("Volume.SFX"), 0, 100, "", 100, true, false, true)) { - Audio_SetGameVolume(SEQ_PLAYER_SFX, ((float)CVarGetInteger(CVAR_SETTING("Volume.SFX"), 100) / 100.0f)); - } - - static std::unordered_map audioBackendNames = { - { Ship::AudioBackend::WASAPI, "Windows Audio Session API" }, - { Ship::AudioBackend::SDL, "SDL" } - }; - - ImGui::Text("Audio API (Needs reload)"); - auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetCurrentAudioBackend(); - - if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - if (ImGui::BeginCombo("##AApi", audioBackendNames[currentAudioBackend])) { - for (uint8_t i = 0; i < Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size(); i++) { - auto backend = Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->data()[i]; - if (ImGui::Selectable(audioBackendNames[backend], backend == currentAudioBackend)) { - Ship::Context::GetInstance()->GetAudio()->SetCurrentAudioBackend(backend); - } - } - ImGui::EndCombo(); - } - if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) { - UIWidgets::ReEnableComponent(""); - } - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Controller")) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2 (12.0f, 6.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); - if (mInputEditorWindow) { - if (ImGui::Button(GetWindowButtonText("Controller Mapping", CVarGetInteger(CVAR_WINDOW("ControllerConfiguration"), 0)).c_str(), ImVec2 (-1.0f, 0.0f))) { - mInputEditorWindow->ToggleVisibility(); - } - } - UIWidgets::PaddedSeparator(); - ImGui::PopStyleColor(1); - ImGui::PopStyleVar(3); - #ifndef __SWITCH__ - UIWidgets::EnhancementCheckbox("Menubar Controller Navigation", CVAR_IMGUI_CONTROLLER_NAV); - UIWidgets::Tooltip("Allows controller navigation of the SOH menu bar (Settings, Enhancements,...)\nCAUTION: This will disable game inputs while the menubar is visible.\n\nD-pad to move between items, A to select, and X to grab focus on the menu bar"); - UIWidgets::PaddedSeparator(); - #endif - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2 (12.0f, 6.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); - if (mInputViewer) { - if (ImGui::Button(GetWindowButtonText("Input Viewer", CVarGetInteger(CVAR_WINDOW("InputViewer"), 0)).c_str(), ImVec2 (-1.0f, 0.0f))) { - mInputViewer->ToggleVisibility(); - } - } - if (mInputViewerSettings) { - if (ImGui::Button(GetWindowButtonText("Input Viewer Settings", CVarGetInteger(CVAR_WINDOW("InputViewerSettings"), 0)).c_str(), ImVec2 (-1.0f, 0.0f))) { - mInputViewerSettings->ToggleVisibility(); - } - } - ImGui::PopStyleColor(1); - ImGui::PopStyleVar(3); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Graphics")) { - #ifndef __APPLE__ - const bool disabled_resolutionSlider = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0) && - CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); - if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %.1f %%", "##IMul", CVAR_INTERNAL_RESOLUTION, 0.5f, - 2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) { - Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat(CVAR_INTERNAL_RESOLUTION, 1)); - } - 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)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); - UIWidgets::Spacer(0); - if (ImGui::Button(GetWindowButtonText("Advanced Resolution", CVarGetInteger(CVAR_WINDOW("AdvancedResolutionEditor"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mAdvancedResolutionSettingsWindow->ToggleVisibility(); - } - ImGui::PopStyleColor(1); - ImGui::PopStyleVar(3); - } - #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(CVAR_MSAA_VALUE, 1) == 1) ? "Anti-aliasing (MSAA): Off" : "Anti-aliasing (MSAA): %d", - "##IMSAA", CVAR_MSAA_VALUE, 1, 8, "", 1, true, true, false)) { - Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger(CVAR_MSAA_VALUE, 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; - if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) { - maxFps = 360; - } else { - maxFps = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); - } - int currentFps = fmax(fmin(OTRGlobals::Instance->GetInterpolationFPS(), maxFps), minFps); - #ifdef __WIIU__ - UIWidgets::Spacer(0); - // only support divisors of 60 on the Wii U - if (currentFps > 60) { - currentFps = 60; - } else { - currentFps = 60 / (60 / currentFps); - } - - int fpsSlider = 1; - if (currentFps == 20) { - ImGui::Text("FPS: Original (20)"); - } else { - ImGui::Text("FPS: %d", currentFps); - if (currentFps == 30) { - fpsSlider = 2; - } else { // currentFps == 60 - fpsSlider = 3; - } - } - if (CVarGetInteger(CVAR_SETTING("MatchRefreshRate"), 0)) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - if (ImGui::Button(" - ##WiiUFPS")) { - fpsSlider--; - } - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - - UIWidgets::Spacer(0); - - ImGui::PushItemWidth(std::min((ImGui::GetContentRegionAvail().x - 60.0f), 260.0f)); - ImGui::SliderInt("##WiiUFPSSlider", &fpsSlider, 1, 3, "", ImGuiSliderFlags_AlwaysClamp); - ImGui::PopItemWidth(); - - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - if (ImGui::Button(" + ##WiiUFPS")) { - fpsSlider++; - } - - if (CVarGetInteger(CVAR_SETTING("MatchRefreshRate"), 0)) { - UIWidgets::ReEnableComponent(""); - } - if (fpsSlider > 3) { - fpsSlider = 3; - } else if (fpsSlider < 1) { - fpsSlider = 1; - } - - if (fpsSlider == 1) { - currentFps = 20; - } else if (fpsSlider == 2) { - currentFps = 30; - } else if (fpsSlider == 3) { - currentFps = 60; - } - CVarSetInteger(CVAR_SETTING("InterpolationFPS"), currentFps); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - #else - bool matchingRefreshRate = - CVarGetInteger(CVAR_SETTING("MatchRefreshRate"), 0) && Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() != Ship::WindowBackend::FAST3D_DXGI_DX11; - UIWidgets::PaddedEnhancementSliderInt( - (currentFps == 20) ? "Frame Rate: Original (20 fps)" : "Frame Rate: %d fps", - "##FPSInterpolation", CVAR_SETTING("InterpolationFPS"), minFps, maxFps, "", 20, true, true, false, matchingRefreshRate); - #endif - if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) { - UIWidgets::Tooltip( - "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.\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 (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) { - UIWidgets::Spacer(0); - if (ImGui::Button("Match Frame Rate to Refresh Rate")) { - int hz = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); - if (hz >= 20 && hz <= 360) { - CVarSetInteger(CVAR_SETTING("InterpolationFPS"), hz); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - } - } else { - UIWidgets::PaddedEnhancementCheckbox("Match Frame Rate to Refresh Rate", CVAR_SETTING("MatchRefreshRate"), true, false); - } - UIWidgets::Tooltip("Matches interpolation value to the game window's current refresh rate."); - - 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)"); - if (UIWidgets::EnhancementCombobox(CVAR_SETTING("ImGuiScale"), imguiScaleOptions, 1)) { - OTRGlobals::Instance->ScaleImGui(); - } - UIWidgets::Tooltip("Changes the scaling of the ImGui menu elements."); - - UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); - - ImGui::Text("Renderer API (Needs reload)"); - - if (availableWindowBackendsMap.size() <= 1) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - if (ImGui::BeginCombo("##RApi", availableWindowBackendsMap[configWindowBackend])) { - for (auto backend : availableWindowBackendsMap) { - if (ImGui::Selectable(backend.second, backend.first == configWindowBackend)) { - Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", static_cast(backend.first)); - Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name", backend.second); - Ship::Context::GetInstance()->GetConfig()->Save(); - UpdateWindowBackendObjects(); - } - } - ImGui::EndCombo(); - } - if (availableWindowBackendsMap.size() <= 1) { - UIWidgets::ReEnableComponent(""); - } - - if (Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync()) { - UIWidgets::PaddedEnhancementCheckbox("Enable Vsync", CVAR_VSYNC_ENABLED, true, false); - UIWidgets::Tooltip("Activate vertical sync, to prevent screen tearing."); - } - - if (Ship::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen()) { - UIWidgets::PaddedEnhancementCheckbox("Windowed fullscreen", CVAR_SDL_WINDOWED_FULLSCREEN, true, false); - } - - if (Ship::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports()) { - UIWidgets::PaddedEnhancementCheckbox("Allow multi-windows (Needs reload)", CVAR_ENABLE_MULTI_VIEWPORTS, 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 Filtering (Needs reload)"); - UIWidgets::EnhancementCombobox(CVAR_TEXTURE_FILTER, 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::PaddedSeparator(true, true, 3.0f, 3.0f); - - // Draw LUS settings menu (such as Overlays Text Font) - Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->DrawSettings(); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Languages")) { - UIWidgets::PaddedEnhancementCheckbox("Translate Title Screen", CVAR_SETTING("TitleScreenTranslation")); - if (UIWidgets::EnhancementRadioButton("English", CVAR_SETTING("Languages"), LANGUAGE_ENG)) { - GameInteractor::Instance->ExecuteHooks(); - } - if (UIWidgets::EnhancementRadioButton("German", CVAR_SETTING("Languages"), LANGUAGE_GER)) { - GameInteractor::Instance->ExecuteHooks(); - } - if (UIWidgets::EnhancementRadioButton("French", CVAR_SETTING("Languages"), LANGUAGE_FRA)) { - GameInteractor::Instance->ExecuteHooks(); - } - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Accessibility")) { - #if defined(_WIN32) || defined(__APPLE__) - UIWidgets::PaddedEnhancementCheckbox("Text to Speech", CVAR_SETTING("A11yTTS")); - UIWidgets::Tooltip("Enables text to speech for in game dialog"); - #endif - UIWidgets::PaddedEnhancementCheckbox("Disable Idle Camera Re-Centering", CVAR_SETTING("A11yDisableIdleCam")); - UIWidgets::Tooltip("Disables the automatic re-centering of the camera when idle."); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Notifications")) { - static const char* notificationPosition[] = { - "Top Left", - "Top Right", - "Bottom Left", - "Bottom Right", - "Hidden", - }; - - ImGui::Text("Position"); - UIWidgets::EnhancementCombobox(CVAR_SETTING("Notifications.Position"), notificationPosition, 0); - UIWidgets::EnhancementSliderFloat("Duration: %.1f seconds", "##NotificationDuration", CVAR_SETTING("Notifications.Duration"), 3.0f, 30.0f, "", 10.0f, false, true, false); - UIWidgets::EnhancementSliderFloat("BG Opacity: %.1f %%", "##NotificaitonBgOpacity", CVAR_SETTING("Notifications.BgOpacity"), 0.0f, 1.0f, "", 0.5f, true, true, false); - UIWidgets::EnhancementSliderFloat("Size: %.1f", "##NotificaitonSize", CVAR_SETTING("Notifications.Size"), 1.0f, 20.0f, "", 1.8f, false, true, false); - - UIWidgets::Spacer(0); - - if (ImGui::Button("Test Notification", ImVec2(-1.0f, 0.0f))) { - Notification::Emit({ - .message = (gPlayState != NULL ? SohUtils::GetSceneName(gPlayState->sceneNum) : "Hyrule") + " looks beautiful today!", - }); - } - - ImGui::EndMenu(); - } - - ImGui::EndMenu(); - } -} - -extern std::shared_ptr mAudioEditorWindow; -extern std::shared_ptr mCosmeticsEditorWindow; -extern std::shared_ptr mGameplayStatsWindow; -extern std::shared_ptr mTimeSplitWindow; -extern std::shared_ptr mTimeDisplayWindow; - -void DrawEnhancementsMenu() { - if (ImGui::BeginMenu("Enhancements")) - { - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); - - DrawPresetSelector(PRESET_TYPE_ENHANCEMENTS); - - UIWidgets::PaddedSeparator(); - - if (ImGui::BeginMenu("Gameplay")) - { - if (ImGui::BeginMenu("Time Savers")) - { - ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 8.0f); - ImGui::BeginTable("##timeSaversMenu", 2, ImGuiTableFlags_SizingFixedFit); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableNextColumn(); - UIWidgets::Spacer(0); - ImGui::Text("Speed-ups:"); - UIWidgets::PaddedSeparator(); - bool allSkipsChecked = - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO); - bool someSkipsChecked = - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO) || - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO); - - ImGuiContext* g = ImGui::GetCurrentContext(); - ImGuiItemFlags backup_item_flags = g->CurrentItemFlags; - if (!allSkipsChecked && someSkipsChecked) g->CurrentItemFlags |= ImGuiItemFlags_MixedValue; - if (ImGui::Checkbox("All", &allSkipsChecked)) { - int32_t newValue = allSkipsChecked ? 1 : 0; - - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), newValue); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), newValue); - - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - g->CurrentItemFlags = backup_item_flags; - UIWidgets::PaddedEnhancementCheckbox("Skip Intro", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Skip Entrance Cutscenes", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Skip Story Cutscenes", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Skip Song Cutscenes", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Skip Boss Introductions", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Quick Boss Deaths", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Skip One Point Cutscenes (Chests, Door Unlocks, etc)", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Skip Owl Interactions", CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Skip Misc Interactions", CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Disable Title Card", CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO); - UIWidgets::PaddedEnhancementCheckbox("Exclude Glitch-Aiding Cutscenes", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, 0); - UIWidgets::Tooltip("Don't skip cutscenes that are associated with useful glitches, currently this is only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS and the Box Skip One Point in Jabu"); - UIWidgets::PaddedEnhancementCheckbox("Skip Child Stealth", CVAR_ENHANCEMENT("TimeSavers.SkipChildStealth"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, false); - UIWidgets::Tooltip("The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards."); - UIWidgets::PaddedEnhancementCheckbox("Skip Tower Escape", CVAR_ENHANCEMENT("TimeSavers.SkipTowerEscape"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, false); - UIWidgets::Tooltip("Skip the tower escape sequence between Ganondorf and Ganon."); - - UIWidgets::PaddedText("Skip Get Item Animations", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), skipGetItemAnimationOptions, SGIA_DISABLED); - if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED) != SGIA_DISABLED) { - UIWidgets::EnhancementSliderFloat("Item Scale: %f", "##ItemScale", CVAR_ENHANCEMENT("TimeSavers.SkipGetItemAnimationScale"), 5.0f, 15.0f, "", 10.0f, false); - UIWidgets::Tooltip("The size of the item when it is picked up"); - } - - UIWidgets::PaddedText("Skip Forced Dialog", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("TimeSavers.SkipForcedDialog"), skipForcedDialogOptions, FORCED_DIALOG_SKIP_NONE); - UIWidgets::Tooltip("Prevent forced conversations with Navi or other NPCs"); - - UIWidgets::PaddedEnhancementSliderInt("Text Speed: %dx", "##TEXTSPEED", CVAR_ENHANCEMENT("TextSpeed"), 1, 5, "", 1, true, false, true); - UIWidgets::PaddedEnhancementCheckbox("Skip Text", CVAR_ENHANCEMENT("SkipText"), false, true); - UIWidgets::Tooltip("Holding down B skips text"); - UIWidgets::PaddedEnhancementSliderInt("Slow Text Speed: %dx", "##SLOWTEXTSPEED", CVAR_ENHANCEMENT("SlowTextSpeed"), 1, 5, "", 1, true, false, true); - UIWidgets::Tooltip("Changes the speed of sections of text that normally are paced slower than the text surrounding it."); - if (ImGui::Button("Match Normal Text")) { - CVarSetInteger(CVAR_ENHANCEMENT("SlowTextSpeed"), CVarGetInteger(CVAR_ENHANCEMENT("TextSpeed"), 1)); - } - UIWidgets::Tooltip("Makes the speed of slow text match the normal text speed above."); - UIWidgets::PaddedEnhancementSliderFloat("King Zora Speed: %.2fx", "##MWEEPSPEED", CVAR_ENHANCEMENT("MweepSpeed"), 0.1f, 5.0f, "", 1.0f, false, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Vine/Ladder Climb speed +%d", "##CLIMBSPEED", CVAR_ENHANCEMENT("ClimbSpeed"), 0, 12, "", 0, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Block pushing speed +%d", "##BLOCKSPEED", CVAR_ENHANCEMENT("FasterBlockPush"), 0, 5, "", 0, true, false, true); - UIWidgets::PaddedEnhancementSliderInt("Crawl speed %dx", "##CRAWLSPEED", CVAR_ENHANCEMENT("CrawlSpeed"), 1, 4, "", 1, true, false, true); - UIWidgets::PaddedEnhancementCheckbox("Faster Heavy Block Lift", CVAR_ENHANCEMENT("FasterHeavyBlockLift"), false, false); - UIWidgets::Tooltip("Speeds up lifting silver rocks and obelisks"); - UIWidgets::PaddedEnhancementCheckbox("Skip Pickup Messages", CVAR_ENHANCEMENT("FastDrops"), true, false); - UIWidgets::Tooltip("Skip pickup messages for new consumable items and bottle swipes"); - UIWidgets::PaddedEnhancementCheckbox("Fast Ocarina Playback", CVAR_ENHANCEMENT("FastOcarinaPlayback"), true, false); - UIWidgets::Tooltip("Skip the part where the Ocarina playback is called when you play a song"); - UIWidgets::PaddedEnhancementCheckbox("Skip Magic Arrow Equip Animation", CVAR_ENHANCEMENT("SkipArrowAnimation"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Skip save confirmation", CVAR_ENHANCEMENT("SkipSaveConfirmation"), true, false); - UIWidgets::Tooltip("Skip the \"Game saved.\" confirmation screen"); - UIWidgets::PaddedEnhancementCheckbox("Faster Farore's Wind", CVAR_ENHANCEMENT("FastFarores"), true, false); - UIWidgets::Tooltip("Greatly decreases cast time of Farore's Wind magic spell."); - - ImGui::TableNextColumn(); - UIWidgets::Spacer(0); - ImGui::Text("Changes:"); - UIWidgets::PaddedSeparator(); - - UIWidgets::PaddedEnhancementSliderInt("Biggoron Forge Time: %d days", "##FORGETIME", CVAR_ENHANCEMENT("ForgeTime"), 0, 3, "", 3, true, false, true); - UIWidgets::Tooltip("Allows you to change the number of days it takes for Biggoron to forge the Biggoron Sword"); - UIWidgets::PaddedEnhancementCheckbox("Remember Save Location", CVAR_ENHANCEMENT("RememberSaveLocation"), false, false); - UIWidgets::Tooltip("When loading a save, places Link at the last entrance he went through.\n" - "This doesn't work if the save was made in grottos/fairy fountains or dungeons."); - UIWidgets::PaddedEnhancementCheckbox("Navi Timer Resets", CVAR_ENHANCEMENT("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", CVAR_ENHANCEMENT("SkulltulaFreeze"), true, false); - UIWidgets::Tooltip("Stops the game from freezing the player when picking up Gold Skulltulas"); - UIWidgets::PaddedEnhancementCheckbox("Nighttime GS Always Spawn", CVAR_ENHANCEMENT("NightGSAlwaysSpawn"), true, false); - UIWidgets::Tooltip("Nighttime Skulltulas will spawn during both day and night."); - UIWidgets::PaddedEnhancementCheckbox("Dampe Appears All Night", CVAR_ENHANCEMENT("DampeAllNight"), true, false); - UIWidgets::Tooltip("Makes Dampe appear anytime during the night, not just his usual working hours."); - UIWidgets::PaddedEnhancementCheckbox("Fast Chests", CVAR_ENHANCEMENT("FastChests"), true, false); - UIWidgets::Tooltip("Kick open every chest"); - UIWidgets::PaddedText("Chest size & texture matches contents", true, false); - if (UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), chestStyleMatchesContentsOptions, CSMC_DISABLED)) { - if (CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), CSMC_DISABLED) == CSMC_DISABLED) { - CVarSetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); - } - } - UIWidgets::Tooltip( - "Chest sizes and textures are changed to help identify the item inside.\n" - " - Major items: Large gold chests\n" - " - Lesser items: Large brown chests\n" - " - Junk items: Small brown chests\n" - " - Small keys: Small silver chest\n" - " - Boss keys: Vanilla size and texture\n" - " - Skulltula Tokens: Small skulltula chest\n" - "\n" - "NOTE: Textures will not apply if you are using a mod pack with a custom chest model." - ); - if (CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), CSMC_DISABLED) != CSMC_DISABLED) { - UIWidgets::PaddedEnhancementCheckbox("Chests of Agony", CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), true, false); - UIWidgets::Tooltip("Only change the size/texture of chests if you have the Stone of Agony."); - } - UIWidgets::PaddedEnhancementCheckbox("Ask to Equip New Items", CVAR_ENHANCEMENT("AskToEquip"), true, false); - UIWidgets::Tooltip("Adds a prompt to equip newly-obtained swords, shields and tunics"); - UIWidgets::PaddedEnhancementCheckbox("Better Owl", CVAR_ENHANCEMENT("BetterOwl"), true, false); - UIWidgets::Tooltip("The default response to Kaepora Gaebora is always that you understood what he said"); - UIWidgets::PaddedEnhancementCheckbox("Exit Market at Night", CVAR_ENHANCEMENT("MarketSneak"), true, false); - UIWidgets::Tooltip("Allows exiting Hyrule Castle Market Town to Hyrule Field at night by speaking to the guard next to the gate."); - bool randoLockedOverworldDoors = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LOCK_OVERWORLD_DOORS); - UIWidgets::PaddedEnhancementCheckbox("Shops and Games Always Open", CVAR_ENHANCEMENT("OpenAllHours"), true, false, randoLockedOverworldDoors, - "This is not compatible with the Locked Overworld Doors Randomizer option", UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Shops and minigames are open both day and night. Requires scene reload to take effect."); - UIWidgets::PaddedEnhancementCheckbox("Link as default file name", CVAR_ENHANCEMENT("LinkDefaultName"), true, false); - UIWidgets::Tooltip("Allows you to have \"Link\" as a premade file name"); - UIWidgets::PaddedEnhancementCheckbox("Quit Fishing At Door", CVAR_ENHANCEMENT("QuitFishingAtDoor"), 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(CVAR_ENHANCEMENT("TimeTravel"), timeTravelOptions, 0); - UIWidgets::Tooltip("Allows Link to freely change age by playing the Song of Time.\n" - "Time Blocks can still be used properly.\n\n" - "Requirements:\n" - "- Obtained the Ocarina of Time (depends on selection)\n" - "- Obtained the Song of Time\n" - "- Obtained the Master Sword\n" - "- Not within range of Time Block\n" - "- Not within range of Ocarina playing spots"); - UIWidgets::PaddedEnhancementCheckbox("Pause Warp", CVAR_ENHANCEMENT("PauseWarp"), true, false); - UIWidgets::Tooltip("Selection of warp song in pause menu initiates warp. Disables song playback."); - UIWidgets::PaddedEnhancementCheckbox("Skip water take breath animation", CVAR_ENHANCEMENT("SkipSwimDeepEndAnim"), true, false); - UIWidgets::Tooltip("Skips Link's taking breath animation after coming up from water. This setting does not interfere with getting items from underwater."); - bool forceSkipScarecrow = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_SCARECROWS_SONG); - static const char* forceSkipScarecrowText = "This setting is forcefully enabled because a savefile\nwith \"Skip Scarecrow Song\" is loaded"; - UIWidgets::PaddedEnhancementCheckbox("Skip Scarecrow Song", CVAR_ENHANCEMENT("InstantScarecrow"), true, false, - forceSkipScarecrow, forceSkipScarecrowText, UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song."); - bool forceSleepingWaterfallEnhancement = - IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SLEEPING_WATERFALL) == RO_WATERFALL_OPEN; - uint8_t forceSleepingWaterfallValue = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SLEEPING_WATERFALL) + 1; - static const char* forceSleepingWaterfallText = - "This setting is forcefully enabled because a randomizer savefile with \"Sleeping Waterfall: Open\" is loaded."; - UIWidgets::PaddedText("Play Zelda's Lullaby to open Sleeping Waterfall", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), - sleepingWaterfallOptions, 0, forceSleepingWaterfallEnhancement, - forceSleepingWaterfallText, forceSleepingWaterfallValue); - UIWidgets::Tooltip( - "Always: Link must always play Zelda's Lullaby to open " - "the waterfall entrance to Zora's Domain.\n" - "Once: Link only needs to play Zelda's Lullaby once to " - "open the waterfall; after that, it stays open permanently.\n" - "Never: Link never needs to play Zelda's Lullaby to open the " - "waterfall; he only needs to have learned it and have an ocarina." - ); - - ImGui::EndTable(); - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Items")) - { - UIWidgets::PaddedEnhancementCheckbox("Equip Items on D-pad", CVAR_ENHANCEMENT("DpadEquips"), true, false); - 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"); - UIWidgets::PaddedEnhancementCheckbox("Instant Putaway", CVAR_ENHANCEMENT("InstantPutaway"), true, false); - UIWidgets::Tooltip("Allow Link to put items away without having to wait around"); - UIWidgets::PaddedEnhancementCheckbox("Instant Boomerang Recall", CVAR_ENHANCEMENT("FastBoomerang"), true, false); - UIWidgets::Tooltip("Instantly return the boomerang to Link by pressing its item button while it's in the air"); - UIWidgets::PaddedEnhancementCheckbox("Prevent Dropped Ocarina Inputs", CVAR_ENHANCEMENT("DpadNoDropOcarinaInput"), true, false); - UIWidgets::Tooltip("Prevent dropping inputs when playing the ocarina quickly"); - UIWidgets::PaddedText("Bunny Hood Effect", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("MMBunnyHood"), bunnyHoodOptions, BUNNY_HOOD_VANILLA); - UIWidgets::Tooltip( - "Wearing the Bunny Hood grants a speed increase like in Majora's Mask. The longer jump option is not accounted for in randomizer logic.\n\n" - "Also disables NPC's reactions to wearing the Bunny Hood." - ); - UIWidgets::PaddedEnhancementCheckbox("Masks Equippable as Adult", CVAR_ENHANCEMENT("AdultMasks"), true, false); - UIWidgets::Tooltip("Allows masks to be equipped normally from the pause menu as adult."); - UIWidgets::PaddedEnhancementCheckbox("Persistent masks", CVAR_ENHANCEMENT("PersistentMasks"), true, false); - UIWidgets::Tooltip( - "Stops masks from automatically unequipping on certain situations:\n" - "- When entering a new scene\n" - "- When not in any C button or the D-Pad\n" - "- When saving and quitting\n" - "- When dying\n" - "- When traveling thru time (if \"Masks Equippable as Adult\" is activated)" - ); - UIWidgets::PaddedEnhancementCheckbox("Mask Select in Inventory", CVAR_ENHANCEMENT("MaskSelect"), true, false); - UIWidgets::Tooltip("After completing the mask trading sub-quest, press A and any direction on the mask slot to change masks"); - UIWidgets::PaddedEnhancementCheckbox("Nuts explode bombs", CVAR_ENHANCEMENT("NutsExplodeBombs"), true, false); - UIWidgets::Tooltip("Makes nuts explode bombs, similar to how they interact with bombchus. This does not affect bombflowers."); - UIWidgets::PaddedEnhancementCheckbox("Equip Multiple Arrows at Once", CVAR_ENHANCEMENT("SeparateArrows"), true, false); - UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots. (Note this will disable the behaviour of the 'Equip Dupe' glitch)"); - UIWidgets::PaddedEnhancementCheckbox("Bow as Child/Slingshot as Adult", CVAR_ENHANCEMENT("BowSlingshotAmmoFix"), true, false); - UIWidgets::Tooltip("Allows child to use bow with arrows.\nAllows adult to use slingshot with seeds.\n\nRequires glitches or 'Timeless Equipment' cheat to equip."); - UIWidgets::PaddedEnhancementCheckbox("Better Farore's Wind", CVAR_ENHANCEMENT("BetterFarore"), true, false); - UIWidgets::Tooltip("Helps FW persist between ages, gives child and adult separate FW points, and can be used in more places."); - UIWidgets::PaddedEnhancementCheckbox("Remove Explosive Limit", CVAR_ENHANCEMENT("RemoveExplosiveLimit"), true, false); - UIWidgets::Tooltip("Removes the cap of 3 active explosives being deployed at once."); - UIWidgets::PaddedEnhancementCheckbox("Static Explosion Radius", CVAR_ENHANCEMENT("StaticExplosionRadius"), true, false); - UIWidgets::Tooltip("Explosions are now a static size, like in Majora's Mask and OoT3D. Makes bombchu hovering much easier."); - UIWidgets::PaddedEnhancementCheckbox("Prevent Bombchus Forcing First-Person", CVAR_ENHANCEMENT("DisableFirstPersonChus"), true, false); - UIWidgets::Tooltip("Prevent bombchus from forcing the camera into first-person mode when released."); - UIWidgets::PaddedEnhancementCheckbox("Better Bombchu Shopping", CVAR_ENHANCEMENT("BetterBombchuShopping"), true, false, - IS_RANDO, "This setting is forcefully enabled when you are playing a randomizer.", UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Bombchus do not sell out when bought, and a 10 pack of bombchus costs 99 rupees instead of 100." - "\n" - "Toggling while inside the shop will not change prices or restock any SOLD OUTs"); - UIWidgets::PaddedEnhancementCheckbox("Aiming reticle for the bow/slingshot", CVAR_ENHANCEMENT("BowReticle"), true, false); - UIWidgets::Tooltip("Aiming with a bow or slingshot will display a reticle as with the hookshot when the projectile is ready to fire."); - if (UIWidgets::PaddedEnhancementCheckbox("Aim boomerang in first-person mode", CVAR_ENHANCEMENT("BoomerangFirstPerson"), true, false)) { - if (!CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) { - CVarSetInteger(CVAR_ENHANCEMENT("BoomerangReticle"), 0); - } - } - UIWidgets::Tooltip( - "Change aiming for the boomerang from third person to first person to see past Link's head"); - if (CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) { - UIWidgets::PaddedEnhancementCheckbox("Aiming reticle for boomerang", CVAR_ENHANCEMENT("BoomerangReticle"), true, false); - UIWidgets::Tooltip("Aiming with the boomerang will display a reticle as with the hookshot"); - } - if (UIWidgets::PaddedEnhancementCheckbox("Allow strength equipment to be toggled", CVAR_ENHANCEMENT("ToggleStrength"), true, false)) { - if (!CVarGetInteger(CVAR_ENHANCEMENT("ToggleStrength"), 0)) { - CVarSetInteger(CVAR_ENHANCEMENT("StrengthDisabled"), 0); - } - } - UIWidgets::Tooltip("Allows strength to be toggled on and off by pressing A on the strength upgrade in the equipment subscreen of the pause menu (This allows performing some glitches that require the player to not have strength)."); - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Item Count Messages")) { - int numOptions = ARRAY_COUNT(itemCountMessageCVars); - bool allItemCountsChecked = std::all_of(itemCountMessageCVars, itemCountMessageCVars + numOptions, - [](const char* cvar) { return CVarGetInteger(cvar, 0); }); - bool someItemCountsChecked = std::any_of(itemCountMessageCVars, itemCountMessageCVars + numOptions, - [](const char* cvar) { return CVarGetInteger(cvar, 0); }); - - ImGuiContext* g = ImGui::GetCurrentContext(); - ImGuiItemFlags backup_item_flags = g->CurrentItemFlags; - if (!allItemCountsChecked && someItemCountsChecked) g->CurrentItemFlags |= ImGuiItemFlags_MixedValue; - if (ImGui::Checkbox("All", &allItemCountsChecked)) { - int32_t newValue = allItemCountsChecked ? 1 : 0; - - std::for_each(itemCountMessageCVars, itemCountMessageCVars + numOptions, - [newValue](const char* cvar) { CVarSetInteger(cvar, newValue); }); - - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - g->CurrentItemFlags = backup_item_flags; - - for (int i = 0; i < numOptions; i++) { - UIWidgets::PaddedEnhancementCheckbox(itemCountMessageOptions[i], itemCountMessageCVars[i], true, false); - } - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Difficulty Options")) - { - if (ImGui::BeginMenu("Shooting Gallery")) { - UIWidgets::EnhancementCheckbox("Customize Behavior", CVAR_ENHANCEMENT("CustomizeShootingGallery")); - UIWidgets::Tooltip("Turn on/off changes to the shooting gallery behavior"); - bool disabled = !CVarGetInteger(CVAR_ENHANCEMENT("CustomizeShootingGallery"), 0); - static const char* disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off"; - UIWidgets::PaddedEnhancementCheckbox("Instant Win", CVAR_ENHANCEMENT("InstantShootingGalleryWin"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Skips the shooting gallery minigame"); - UIWidgets::PaddedEnhancementCheckbox("No Rupee Randomization", CVAR_ENHANCEMENT("ConstantAdultGallery"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Forces the rupee order to not be randomized as adult, making it the same as chlid"); - UIWidgets::PaddedEnhancementSliderInt("Child Starting Ammunition: %d", "##cShootingGalleryAmmunition", CVAR_ENHANCEMENT("ShootingGalleryAmmoChild"), 10, 30, "", 15, true, true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("The ammunition at the start of the shooting gallery minigame as a child"); - UIWidgets::PaddedEnhancementSliderInt("Adult Starting Ammunition: %d", "##aShootingGalleryAmmunition", CVAR_ENHANCEMENT("ShootingGalleryAmmoAdult"), 10, 30, "", 15, true, true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("The ammunition at the start of the shooting gallery minigame as an adult"); - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Bombchu Bowling")) { - UIWidgets::EnhancementCheckbox("Customize Behavior", CVAR_ENHANCEMENT("CustomizeBombchuBowling")); - UIWidgets::Tooltip("Turn on/off changes to the bombchu bowling behavior"); - bool disabled = CVarGetInteger(CVAR_ENHANCEMENT("CustomizeBombchuBowling"), 0) == 0; - static const char* disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off"; - UIWidgets::PaddedEnhancementCheckbox("Remove Small Cucco", CVAR_ENHANCEMENT("BombchuBowlingNoSmallCucco"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Prevents the small cucco from appearing in the bombchu bowling minigame"); - UIWidgets::PaddedEnhancementCheckbox("Remove Big Cucco", CVAR_ENHANCEMENT("BombchuBowlingNoBigCucco"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Prevents the big cucco from appearing in the bombchu bowling minigame"); - UIWidgets::PaddedEnhancementSliderInt("Bombchu Count: %d", "##cBombchuBowlingAmmunition", CVAR_ENHANCEMENT("BombchuBowlingAmmo"), 3, 20, "", 10, true, true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("The number of bombchus available at the start of the bombchu bowling minigame"); - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - if (ImGui::BeginMenu("Fishing")) { - UIWidgets::EnhancementCheckbox("Customize Behavior", CVAR_ENHANCEMENT("CustomizeFishing")); - UIWidgets::Tooltip("Turn on/off changes to the fishing behavior"); - bool disabled = !CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0); - static const char* disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off"; - UIWidgets::PaddedEnhancementCheckbox("Instant Fishing", CVAR_ENHANCEMENT("InstantFishing"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("All fish will be caught instantly"); - UIWidgets::PaddedEnhancementCheckbox("Guarantee Bite", CVAR_ENHANCEMENT("GuaranteeFishingBite"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("When a line is stable, guarantee bite. Otherwise use default logic"); - UIWidgets::PaddedEnhancementCheckbox("Fish Never Escape", CVAR_ENHANCEMENT("FishNeverEscape"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Once a hook has been set, fish will never let go while being reeled in."); - UIWidgets::PaddedEnhancementCheckbox("Loaches Always Appear", CVAR_ENHANCEMENT("LoachesAlwaysAppear"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Loaches will always appear in the fishing pond instead of every four visits."); - UIWidgets::PaddedEnhancementCheckbox("Skip Keep Confirmation", CVAR_ENHANCEMENT("SkipKeepConfirmation"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("The pond owner will not ask to confirm if you want to keep a smaller fish."); - UIWidgets::PaddedEnhancementSliderInt("Child Minimum Weight: %d", "##cMinimumWeight", CVAR_ENHANCEMENT("MinimumFishWeightChild"), 3, 10, "", 10, true, true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("The minimum weight for the unique fishing reward as a child"); - UIWidgets::PaddedEnhancementSliderInt("Adult Minimum Weight: %d", "##aMinimumWeight", CVAR_ENHANCEMENT("MinimumFishWeightAdult"), 6, 13, "", 13, true, true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("The minimum weight for the unique fishing reward as an adult"); - UIWidgets::PaddedEnhancementCheckbox("All fish are Hyrule Loaches", CVAR_ENHANCEMENT("AllHyruleLoaches"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Every fish in the fishing pond will always be a Hyrule Loach\n\nNote: This requires reloading the area"); - ImGui::EndMenu(); - } - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Lost Woods Ocarina Game")) { - UIWidgets::EnhancementCheckbox("Customize Behavior", CVAR_ENHANCEMENT("CustomizeOcarinaGame")); - UIWidgets::Tooltip("Turn on/off changes to the lost woods ocarina game behavior"); - bool disabled = !CVarGetInteger(CVAR_ENHANCEMENT("CustomizeOcarinaGame"), 0); - static const char* disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off"; - UIWidgets::PaddedEnhancementCheckbox("Instant Win", CVAR_ENHANCEMENT("InstantOcarinaGameWin"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Skips the lost woods ocarina game"); - UIWidgets::PaddedEnhancementSliderInt("Note Play Speed: %dx", "##OcarinaGameNoteSpeed", CVAR_ENHANCEMENT("OcarinaGame.NoteSpeed"), 1, 5, "", 1, true, true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Adjust the speed that the skull kids play notes"); - UIWidgets::PaddedEnhancementCheckbox("Unlimited Playback Time", CVAR_ENHANCEMENT("OcarinaUnlimitedFailTime"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Removes the timer to play back the song"); - UIWidgets::PaddedEnhancementSliderInt("Number of Starting Notes: %d", "##OcarinaGameStartingNotes", CVAR_ENHANCEMENT("OcarinaGame.StartingNotes"), 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(CVAR_ENHANCEMENT("OcarinaGame.StartingNotes"), 3); - UIWidgets::PaddedEnhancementSliderInt("Round One Notes: %d", "##OcarinaGameRoundOne", - CVAR_ENHANCEMENT("OcarinaGame.RoundOneNotes"), 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", - CVAR_ENHANCEMENT("OcarinaGame.RoundTwoNotes"), 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", - CVAR_ENHANCEMENT("OcarinaGame.RoundThreeNotes"), 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); - - if (ImGui::BeginMenu("Frogs Ocarina Game")) { - UIWidgets::EnhancementCheckbox("Customize Behavior", CVAR_ENHANCEMENT("CustomizeFrogsOcarinaGame")); - UIWidgets::Tooltip("Turn on/off changes to the frogs ocarina game behavior"); - bool disabled = !CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFrogsOcarinaGame"), 0); - static const char* disabledTooltip = - "This option is disabled because \"Customize Behavior\" is turned off"; - UIWidgets::PaddedEnhancementCheckbox("Instant Win", CVAR_ENHANCEMENT("InstantFrogsGameWin"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Skips the frogs ocarina game"); - UIWidgets::PaddedEnhancementCheckbox("Unlimited Playback Time", CVAR_ENHANCEMENT("FrogsUnlimitedFailTime"), true, false, disabled, disabledTooltip); - UIWidgets::Tooltip("Removes the timer to play back the song"); - bool disabledFrog = 0; - static const char* disabledFrogTooltip = - "This option is disabled because \"Customize Behavior\" is turned off or \"Unlimited Playback Time\" is on"; - if (!CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFrogsOcarinaGame"), 0) || CVarGetInteger(CVAR_ENHANCEMENT("FrogsUnlimitedFailTime"), 0)) { - disabledFrog = 1; - } else { - disabledFrog = 0; - } - UIWidgets::PaddedEnhancementSliderInt("Modify note timer: %dx", "##FrogsFailTimer", CVAR_ENHANCEMENT("FrogsModifyFailTime"), 1, 5, "", 1, true, true, false, - disabledFrog, disabledFrogTooltip); - UIWidgets::Tooltip("Adjusts the time allowed for playback before failing"); - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - UIWidgets::PaddedEnhancementCheckbox("Delete File On Death", CVAR_ENHANCEMENT("DeleteFileOnDeath"), true, false); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); - UIWidgets::Tooltip("Dying will delete your file\n\n " ICON_FA_EXCLAMATION_TRIANGLE " WARNING " ICON_FA_EXCLAMATION_TRIANGLE "\nTHIS IS NOT REVERSABLE\nUSE AT YOUR OWN RISK!"); - ImGui::PopStyleColor(); - if (UIWidgets::PaddedEnhancementCheckbox("Permanent heart loss", CVAR_ENHANCEMENT("PermanentHeartLoss"), true, false)) { - UpdatePermanentHeartLossState(); - } - UIWidgets::Tooltip("When you lose 4 quarters of a heart you will permanently lose that heart container.\n\nDisabling this after the fact will restore your heart containers."); - ImGui::Text("Damage Multiplier"); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("DamageMult"), allPowers, 0); - UIWidgets::Tooltip( - "Modifies all sources of damage not affected by other sliders\n" - "2x: Can survive all common attacks from the start of the game\n" - "4x: Dies in 1 hit to any substantial attack from the start of the game\n" - "8x: Can only survive trivial damage from the start of the game\n" - "16x: Can survive all common attacks with max health without double defense\n" - "32x: Can survive all common attacks with max health and double defense\n" - "64x: Can survive trivial damage with max health without double defense\n" - "128x: Can survive trivial damage with max health and double defense\n" - "256x: Cannot survive damage" - ); - UIWidgets::PaddedText("Fall Damage Multiplier", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("FallDamageMult"), subPowers, 0); - UIWidgets::Tooltip( - "Modifies all fall damage\n" - "2x: Can survive all fall damage from the start of the game\n" - "4x: Can only survive short fall damage from the start of the game\n" - "8x: Cannot survive any fall damage from the start of the game\n" - "16x: Can survive all fall damage with max health without double defense\n" - "32x: Can survive all fall damage with max health and double defense\n" - "64x: Can survive short fall damage with double defense\n" - "128x: Cannot survive fall damage" - ); - UIWidgets::PaddedText("Void Damage Multiplier", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("VoidDamageMult"), subSubPowers, 0); - UIWidgets::Tooltip( - "Modifies damage taken after falling into a void\n" - "2x: Can survive void damage from the start of the game\n" - "4x: Cannot survive void damage from the start of the game\n" - "8x: Can survive void damage twice with max health without double defense\n" - "16x: Can survive void damage with max health without double defense\n" - "32x: Can survive void damage with max health and double defense\n" - "64x: Cannot survive void damage" - ); - UIWidgets::PaddedText("Bonk Damage Multiplier", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("BonkDamageMult"), bonkDamageValues, BONK_DAMAGE_NONE); - UIWidgets::Tooltip("Modifies damage taken after bonking."); - UIWidgets::PaddedEnhancementCheckbox("Spawn with full health", CVAR_ENHANCEMENT("FullHealthSpawn"), true, false); - UIWidgets::Tooltip("Respawn with full health instead of 3 Hearts"); - UIWidgets::PaddedEnhancementCheckbox("No Random Drops", CVAR_ENHANCEMENT("NoRandomDrops"), true, false); - UIWidgets::Tooltip("Disables random drops, except from the Goron Pot, Dampe, and bosses"); - bool forceEnableBombchuDrops = IS_RANDO && - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_ENABLE_BOMBCHU_DROPS) == 1; - static const char* forceEnableBombchuDropsText = - "This setting is forcefully enabled because a savefile\nwith \"Enable Bombchu Drops\" is loaded."; - UIWidgets::PaddedEnhancementCheckbox("Enable Bombchu Drops", CVAR_ENHANCEMENT("EnableBombchuDrops"), true, false, - forceEnableBombchuDrops, forceEnableBombchuDropsText, UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Bombchus will sometimes drop in place of bombs"); - UIWidgets::PaddedEnhancementCheckbox("Trees Drop Sticks", CVAR_ENHANCEMENT("TreesDropSticks"), true, false); - UIWidgets::Tooltip("Bonking into trees will have a chance to drop up to 3 sticks. Must already have obtained sticks."); - UIWidgets::PaddedEnhancementCheckbox("No Heart Drops", CVAR_ENHANCEMENT("NoHeartDrops"), true, false); - UIWidgets::Tooltip("Disables heart drops, but not heart placements, like from a Deku Scrub running off\nThis simulates Hero Mode from other games in the series"); - if (UIWidgets::PaddedEnhancementCheckbox("Hyper Bosses", CVAR_ENHANCEMENT("HyperBosses"), true, false)) { - UpdateHyperBossesState(); - } - UIWidgets::Tooltip("All major bosses move and act twice as fast."); - if (UIWidgets::PaddedEnhancementCheckbox("Hyper Enemies", CVAR_ENHANCEMENT("HyperEnemies"), true, false)) { - UpdateHyperEnemiesState(); - } - UIWidgets::Tooltip("All regular enemies and mini-bosses move and act twice as fast."); - UIWidgets::PaddedEnhancementCheckbox("Always Win Goron Pot", CVAR_ENHANCEMENT("GoronPot"), true, false); - UIWidgets::Tooltip("Always get the heart piece/purple rupee from the spinning Goron pot"); - UIWidgets::PaddedEnhancementCheckbox("Always Win Dampe Digging Game", CVAR_ENHANCEMENT("DampeWin"), true, false); - UIWidgets::Tooltip("Always win the heart piece/purple rupee on the first dig in Dampe's grave digging game, just like in rando\nIn a rando file, this is unconditionally enabled"); - UIWidgets::PaddedEnhancementCheckbox("All Dogs are Richard", CVAR_ENHANCEMENT("AllDogsRichard"), true, false); - UIWidgets::Tooltip("All dogs can be traded in and will count as Richard."); - UIWidgets::PaddedEnhancementSliderInt("Cuccos Stay Put Multiplier: %dx", "##CuccoStayDurationMultiplier", CVAR_ENHANCEMENT("CuccoStayDurationMult"), 1, 5, "", 1, true, true, false); - UIWidgets::Tooltip("Cuccos will stay in place longer after putting them down, by a multiple of the value of the slider."); - UIWidgets::PaddedEnhancementSliderInt("Leever Spawn Rate: %d seconds", "##LeeverSpawnRate", CVAR_ENHANCEMENT("LeeverSpawnRate"), 0, 10, "", 0, true, true, false); - UIWidgets::Tooltip("The time between leever groups spawning."); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Reduced Clutter")) - { - UIWidgets::EnhancementCheckbox("Mute Low HP Alarm", CVAR_ENHANCEMENT("LowHpAlarm")); - UIWidgets::Tooltip("Disable the low HP beeping sound"); - UIWidgets::PaddedEnhancementCheckbox("Minimal UI", CVAR_ENHANCEMENT("MinimalUI"), true, false); - UIWidgets::Tooltip("Hides most of the UI when not needed\nNote: Doesn't activate until after loading a new scene"); - UIWidgets::PaddedEnhancementCheckbox("Disable Navi Call Audio", CVAR_ENHANCEMENT("DisableNaviCallAudio"), true, false); - UIWidgets::Tooltip("Disables the voice audio when Navi calls you"); - UIWidgets::PaddedEnhancementCheckbox("Disable Hot/Underwater Warning Text", CVAR_ENHANCEMENT("DisableTunicWarningText"), true, false); - UIWidgets::Tooltip("Disables warning text when you don't have on the Goron/Zora Tunic in Hot/Underwater conditions."); - UIWidgets::PaddedEnhancementCheckbox("Remember Minimap State Between Areas", CVAR_ENHANCEMENT("RememberMapToggleState")); - UIWidgets::Tooltip("Preserves the minimap visibility state when going between areas rather than defaulting it to \"on\" when going through loading zones."); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - UIWidgets::EnhancementCheckbox("Visual Stone of Agony", CVAR_ENHANCEMENT("VisualAgony")); - UIWidgets::Tooltip("Displays an icon and plays a sound when Stone of Agony should be activated, for those without rumble"); - static const char* cursorOnAnySlot[3] = { "Only in Rando", "Always", "Never" }; - UIWidgets::PaddedText("Allow the cursor to be on any slot", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("PauseAnyCursor"), 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::PaddedEnhancementCheckbox("Assignable Tunics and Boots", CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), true, false); - UIWidgets::Tooltip("Allows equipping the tunic and boots to c-buttons"); - UIWidgets::PaddedEnhancementCheckbox("Equipment Toggle", CVAR_ENHANCEMENT("EquipmentCanBeRemoved"), true, false); - UIWidgets::Tooltip("Allows equipment to be removed by toggling it off on\nthe equipment subscreen."); - if (CVarGetInteger(CVAR_ENHANCEMENT("EquipmentCanBeRemoved"), 0)) { - UIWidgets::PaddedText("Sword Toggle Options", true, false); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("SwordToggle"), swordToggleModes, SWORD_TOGGLE_NONE); - UIWidgets::Tooltip( - "Introduces Options for unequipping Link's sword\n\n" - "None: Only Biggoron's Sword/Giant's Knife can be toggled. Doing so will equip the Master Sword.\n\n" - "Child Toggle: This will allow for completely unequipping any sword as child link.\n\n" - "Both Ages: Any sword can be unequipped as either age. This may lead to swordless glitches as Adult.\n" - ); - } - - UIWidgets::PaddedEnhancementCheckbox("Link's Cow in Both Time Periods", CVAR_ENHANCEMENT("CowOfTime"), true, false); - UIWidgets::Tooltip("Allows the Lon Lon Ranch obstacle course reward to be shared across time periods"); - UIWidgets::PaddedEnhancementCheckbox("Enable visible guard vision", CVAR_ENHANCEMENT("GuardVision"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Enable passage of time on file select", CVAR_ENHANCEMENT("TimeFlowFileSelect"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Pull grave during the day", CVAR_ENHANCEMENT("DayGravePull"), true, false); - UIWidgets::Tooltip("Allows graves to be pulled when child during the day"); - UIWidgets::PaddedEnhancementCheckbox("Dogs follow you everywhere", CVAR_ENHANCEMENT("DogFollowsEverywhere"), true, false); - UIWidgets::Tooltip("Allows dogs to follow you anywhere you go, even if you leave the market"); - UIWidgets::PaddedEnhancementCheckbox("Don't require input for Credits sequence", CVAR_ENHANCEMENT("NoInputForCredits"), true, false); - UIWidgets::Tooltip("Removes the input requirement on textboxes after defeating Ganon, allowing Credits sequence to continue to progress"); - UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", CVAR_ENHANCEMENT("NaviOnL"), true, false); - UIWidgets::Tooltip("Speak to Navi with L but enter first-person camera with C-Up"); - - // Blue Fire Arrows - bool forceEnableBlueFireArrows = IS_RANDO && - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BLUE_FIRE_ARROWS); - static const char* forceEnableBlueFireArrowsText = - "This setting is forcefully enabled because a savefile\nwith \"Blue Fire Arrows\" is loaded."; - UIWidgets::PaddedEnhancementCheckbox("Blue Fire Arrows", CVAR_ENHANCEMENT("BlueFireArrows"), true, false, - forceEnableBlueFireArrows, forceEnableBlueFireArrowsText, UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Allows Ice Arrows to melt red ice.\nMay require a room reload if toggled during gameplay."); - - // Sunlight Arrows - bool forceEnableSunLightArrows = IS_RANDO && - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SUNLIGHT_ARROWS); - static const char* forceEnableSunLightArrowsText = - "This setting is forcefully enabled because a savefile\nwith \"Sunlight Arrows\" is loaded."; - UIWidgets::PaddedEnhancementCheckbox("Sunlight Arrows", CVAR_ENHANCEMENT("SunlightArrows"), true, false, - forceEnableSunLightArrows, forceEnableSunLightArrowsText, UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Allows Light Arrows to activate sun switches.\nMay require a room reload if toggled during gameplay."); - - UIWidgets::PaddedEnhancementCheckbox("Disable Crit wiggle", CVAR_ENHANCEMENT("DisableCritWiggle"), true, false); - UIWidgets::Tooltip("Disable random camera wiggle at low health"); - UIWidgets::PaddedEnhancementCheckbox("Enemy Health Bars", CVAR_ENHANCEMENT("EnemyHealthBar"), true, false); - UIWidgets::Tooltip("Renders a health bar for enemies when Z-Targeted"); - - UIWidgets::PaddedEnhancementCheckbox("Targetable Hookshot Reticle", CVAR_ENHANCEMENT("HookshotableReticle"), true, false); - UIWidgets::Tooltip("Use a different color when aiming at hookshotable collision"); - - UIWidgets::PaddedEnhancementCheckbox("Faster Rupee Accumulator", CVAR_ENHANCEMENT("TimeSavers.FasterRupeeAccumulator"), true, false); - UIWidgets::Tooltip("Causes your wallet to fill and empty faster when you gain or lose money."); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Graphics")) - { - if (ImGui::BeginMenu("Mods")) { - UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", CVAR_ENHANCEMENT("AltAssets"), false, false); - UIWidgets::Tooltip("Toggle between standard assets and alternate assets. Usually mods will indicate if this setting has to be used or not."); - UIWidgets::PaddedEnhancementCheckbox("Disable Bomb Billboarding", CVAR_ENHANCEMENT("DisableBombBillboarding"), true, false); - UIWidgets::Tooltip("Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to replace bombs with 3D objects."); - UIWidgets::PaddedEnhancementCheckbox("Disable Grotto Fixed Rotation", CVAR_ENHANCEMENT("DisableGrottoRotation"), true, false); - UIWidgets::Tooltip("Disables grottos rotating with the camera. To be used in conjunction with mods that want to replace grottos with 3D objects."); - UIWidgets::PaddedEnhancementCheckbox("Invisible Bunny Hood", CVAR_ENHANCEMENT("HideBunnyHood"), true, false); - UIWidgets::Tooltip("Turns Bunny Hood invisible while still maintaining its effects."); - UIWidgets::PaddedEnhancementCheckbox("Disable HUD Heart animations", CVAR_ENHANCEMENT("NoHUDHeartAnimation"), true, false); - UIWidgets::Tooltip("Disables the beating animation of the hearts on the HUD."); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - UIWidgets::PaddedEnhancementCheckbox("Animated Link in Pause Menu", CVAR_ENHANCEMENT("PauseMenuAnimatedLink"), false, false); - UIWidgets::PaddedEnhancementCheckbox("Disable LOD", CVAR_ENHANCEMENT("DisableLOD"), true, false); - UIWidgets::Tooltip( - "Turns off the Level of Detail setting, making models use their higher-poly variants at any distance"); - if (UIWidgets::EnhancementSliderInt("Increase Actor Draw Distance: %dx", "##IncreaseActorDrawDistance", - CVAR_ENHANCEMENT("DisableDrawDistance"), 1, 5, "", 1, true, false)) { - if (CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1) { - CVarSetInteger(CVAR_ENHANCEMENT("DisableKokiriDrawDistance"), 0); - } - } - UIWidgets::Tooltip("Increases the range in which actors/objects are drawn"); - if (CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) > 1) { - UIWidgets::PaddedEnhancementCheckbox("Kokiri Draw Distance", - CVAR_ENHANCEMENT("DisableKokiriDrawDistance"), true, false); - UIWidgets::Tooltip("The Kokiri are mystical beings that fade into view when approached\nEnabling this " - "will remove their draw distance"); - } - UIWidgets::PaddedEnhancementCheckbox("Widescreen Actor Culling", CVAR_ENHANCEMENT("WidescreenActorCulling"), - true, false); - UIWidgets::Tooltip("Adjusts the horizontal culling plane to account for widescreen resolutions"); - UIWidgets::PaddedEnhancementCheckbox( - "Cull Glitch Useful Actors", CVAR_ENHANCEMENT("ExtendedCullingExcludeGlitchActors"), true, false, - !CVarGetInteger(CVAR_ENHANCEMENT("WidescreenActorCulling"), 0) && - CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1, - "Requires Actor Draw Distance to be increased or Widescreen Actor Culling enabled"); - UIWidgets::Tooltip( - "Exclude actors that are useful for glitches from the extended culling ranges.\n" - "Some actors may still draw in the extended ranges, but will not \"update\" so that certain " - "glitches that leverage the original culling requirements will still work.\n" - "\n" - "The following actors are excluded:\n" - "- White clothed Gerudos\n" - "- King Zora\n" - "- Gossip Stones\n" - "- Boulders\n" - "- Blue Warps\n" - "- Darunia\n" - "- Gold Skulltulas"); - if (UIWidgets::PaddedEnhancementCheckbox("Show Age-Dependent Equipment", CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), true, - false)) { - UpdatePatchHand(); - } - UIWidgets::Tooltip("Makes all equipment visible, regardless of Age."); - if (CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0) == 1) { - UIWidgets::PaddedEnhancementCheckbox("Scale Adult Equipment as Child", CVAR_ENHANCEMENT("ScaleAdultEquipmentAsChild"), 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", CVAR_LOW_RES_MODE, 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", CVAR_ENHANCEMENT("DrawLineupTick"), true, false); - UIWidgets::Tooltip("Displays a tick in the top center of the screen to help with glitch line-ups in SoH, as traditional UI based line-ups do not work outside of 4:3"); - UIWidgets::PaddedEnhancementCheckbox("Enable 3D Dropped items/projectiles", CVAR_ENHANCEMENT("NewDrops"), true, false); - UIWidgets::Tooltip("Change most 2D items and projectiles on the overworld to their 3D versions"); - UIWidgets::PaddedEnhancementCheckbox("Disable Black Bar Letterboxes", CVAR_ENHANCEMENT("DisableBlackBars"), true, false); - UIWidgets::Tooltip("Disables Black Bar Letterboxes during cutscenes and Z-targeting\nNote: there may be minor visual glitches that were covered up by the black bars\nPlease disable this setting before reporting a bug"); - UIWidgets::PaddedEnhancementCheckbox("Dynamic Wallet Icon", CVAR_ENHANCEMENT("DynamicWalletIcon"), true, false); - UIWidgets::Tooltip("Changes the rupee in the wallet icon to match the wallet size you currently have"); - UIWidgets::PaddedEnhancementCheckbox("Always show dungeon entrances", CVAR_ENHANCEMENT("AlwaysShowDungeonMinimapIcon"), true, false); - UIWidgets::Tooltip("Always shows dungeon entrance icons on the minimap"); - UIWidgets::PaddedEnhancementCheckbox("Show Gauntlets in First Person", CVAR_ENHANCEMENT("FirstPersonGauntlets"), true, false); - UIWidgets::Tooltip("Renders Gauntlets when using the Bow and Hookshot like in OOT3D"); - if (UIWidgets::PaddedEnhancementCheckbox("Color Temple of Time's Medallions", CVAR_ENHANCEMENT("ToTMedallionsColors"), true, false)) { - 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", CVAR_ENHANCEMENT("ShowDoorLocksOnBothSides"), true, false); - UIWidgets::PaddedText("Fix Vanishing Paths", true, false); - if (UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix"), zFightingOptions, ZFIGHT_FIX_DISABLED) && gPlayState != NULL) { - UpdateDirtPathFixState(gPlayState->sceneNum); - } - UIWidgets::Tooltip("Disabled: Paths vanish more the higher the resolution (Z-fighting is based on resolution)\n" - "Consistent: Certain paths vanish the same way in all resolutions\n" - "No Vanish: Paths do not vanish, Link seems to sink in to some paths\n" - "This might affect other decal effects\n"); - UIWidgets::PaddedEnhancementSliderInt("Text Spacing: %d", "##TEXTSPACING", CVAR_ENHANCEMENT("TextSpacing"), 4, 6, "", 6, true, true, true); - UIWidgets::Tooltip("Space between text characters (useful for HD font textures)"); - UIWidgets::PaddedEnhancementCheckbox("More info in file select", CVAR_ENHANCEMENT("FileSelectMoreInfo"), true, false); - UIWidgets::Tooltip("Shows what items you have collected in the file select screen, like in N64 randomizer"); - UIWidgets::PaddedEnhancementCheckbox("Better ammo rendering in pause menu", CVAR_ENHANCEMENT("BetterAmmoRendering"), true, false); - UIWidgets::Tooltip("Ammo counts in the pause menu will work correctly regardless of the position of items in the inventory"); - UIWidgets::PaddedEnhancementCheckbox("Remove spin attack darkness", CVAR_ENHANCEMENT("RemoveSpinAttackDarkness"), true, false); - UIWidgets::Tooltip("Remove the darkness that appears when charging a spin attack"); - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Fixes")) - { - UIWidgets::EnhancementCheckbox("Fix L&R Pause menu", CVAR_ENHANCEMENT("FixMenuLR")); - UIWidgets::Tooltip("Makes the L and R buttons in the pause menu the same color"); - UIWidgets::PaddedEnhancementCheckbox("Fix L&Z Page switch in Pause menu", CVAR_ENHANCEMENT("NGCKaleidoSwitcher"), true, false); - UIWidgets::Tooltip("Makes L and R switch pages like on the GameCube\nZ opens the Debug Menu instead"); - UIWidgets::PaddedEnhancementCheckbox("Fix Dungeon entrances", CVAR_ENHANCEMENT("FixDungeonMinimapIcon"), true, false); - UIWidgets::Tooltip("Removes the dungeon entrance icon on the top-left corner of the screen when no dungeon is present on the current map"); - UIWidgets::PaddedEnhancementCheckbox("Fix Two Handed idle animations", CVAR_ENHANCEMENT("TwoHandedIdle"), true, false); - UIWidgets::Tooltip("Re-enables the two-handed idle animation, a seemingly finished animation that was disabled on accident in the original game"); - UIWidgets::PaddedEnhancementCheckbox("Fix the Gravedigging Tour Glitch", CVAR_ENHANCEMENT("GravediggingTourFix"), true, false, SaveManager::Instance->IsRandoFile(), - "This setting is always enabled in randomizer files", UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Fixes a bug where the Gravedigging Tour Heart Piece disappears if the area reloads"); - UIWidgets::PaddedEnhancementCheckbox( - "Fix Deku Nut upgrade", CVAR_ENHANCEMENT("DekuNutUpgradeFix"), true, false, IS_RANDO, - "This setting is forcefully enabled when you are playing a randomizer.", - UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable after receiving the Poacher's Saw"); - UIWidgets::PaddedEnhancementCheckbox("Fix Navi text HUD position", CVAR_ENHANCEMENT("NaviTextFix"), true, false); - UIWidgets::Tooltip("Correctly centers the Navi text prompt on the HUD's C-Up button"); - UIWidgets::PaddedEnhancementCheckbox("Fix Anubis fireballs", CVAR_ENHANCEMENT("AnubisFix"), true, false); - UIWidgets::Tooltip("Make Anubis fireballs do fire damage when reflected back at them with the Mirror Shield"); - if (UIWidgets::PaddedEnhancementCheckbox("Fix Megaton Hammer crouch stab", CVAR_ENHANCEMENT("CrouchStabHammerFix"), true, false)) { - if (!CVarGetInteger(CVAR_ENHANCEMENT("CrouchStabHammerFix"), 0)) { - CVarClear(CVAR_ENHANCEMENT("CrouchStabFix")); - } - } - UIWidgets::Tooltip("Make the Megaton Hammer's crouch stab able to destroy rocks without first swinging it normally"); - if (CVarGetInteger(CVAR_ENHANCEMENT("CrouchStabHammerFix"), 0)) { - UIWidgets::PaddedEnhancementCheckbox("Remove power crouch stab", CVAR_ENHANCEMENT("CrouchStabFix"), true, false); - UIWidgets::Tooltip("Make crouch stabbing always do the same damage as a regular slash"); - } - UIWidgets::PaddedEnhancementCheckbox("Fix credits timing", CVAR_ENHANCEMENT("CreditsFix"), true, false); - UIWidgets::Tooltip("Extend certain credits scenes so the music lines up properly with the visuals"); - UIWidgets::PaddedEnhancementCheckbox("Fix Gerudo Warrior's clothing colors", CVAR_ENHANCEMENT("GerudoWarriorClothingFix"), true, false); - UIWidgets::Tooltip("Prevent the Gerudo Warrior's clothes changing color when changing Link's tunic or using bombs in front of her"); - UIWidgets::PaddedEnhancementCheckbox("Fix Camera Drift", CVAR_ENHANCEMENT("FixCameraDrift"), true, false); - UIWidgets::Tooltip("Fixes camera slightly drifting to the left when standing still due to a math error"); - UIWidgets::PaddedEnhancementCheckbox("Fix Camera Swing", CVAR_ENHANCEMENT("FixCameraSwing"), true, false); - UIWidgets::Tooltip("Fixes camera getting stuck on collision when standing still, also fixes slight shift back in camera when stop moving"); - UIWidgets::PaddedEnhancementCheckbox("Fix Hanging Ledge Swing Rate", CVAR_ENHANCEMENT("FixHangingLedgeSwingRate"), true, false); - UIWidgets::Tooltip("Fixes camera swing rate when player falls off a ledge and camera swings around"); - UIWidgets::PaddedEnhancementCheckbox("Fix Missing Jingle after 5 Silver Rupees", CVAR_ENHANCEMENT("SilverRupeeJingleExtend"), true, false); - UIWidgets::Tooltip( - "Adds 5 higher pitches for the Silver Rupee Jingle for the rooms with more than 5 Silver Rupees. " - "Currently only relevant in Master Quest."); - if (UIWidgets::PaddedEnhancementCheckbox("Fix out of bounds textures", CVAR_ENHANCEMENT("FixTexturesOOB"), true, false)) { - ApplyAuthenticGfxPatches(); - } - UIWidgets::Tooltip("Fixes authentic out of bounds texture reads, instead loading textures with the correct size"); - UIWidgets::PaddedEnhancementCheckbox("Fix Poacher's Saw Softlock", CVAR_ENHANCEMENT("FixSawSoftlock"), true, false, CVarGetInteger(CVAR_ENHANCEMENT("SkipText"), 0), - "This is disabled because it is forced on when Skip Text is enabled.", UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Prevents the Poacher's Saw softlock from mashing through the text, or with Skip Text enabled."); - UIWidgets::PaddedEnhancementCheckbox("Fix enemies not spawning near water", CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"), true, false); - UIWidgets::Tooltip("Causes respawning enemies, like stalchildren, to appear on land near bodies of water. " - "Fixes an incorrect calculation that acted like water underneath ground was above it."); - UIWidgets::PaddedEnhancementCheckbox("Fix Bush Item Drops", CVAR_ENHANCEMENT("BushDropFix"), true, false); - UIWidgets::Tooltip("Fixes the bushes to drop items correctly rather than spawning undefined items."); - UIWidgets::PaddedEnhancementCheckbox("Fix falling from vine edges", CVAR_ENHANCEMENT("FixVineFall"), true, false); - UIWidgets::Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges."); - UIWidgets::PaddedEnhancementCheckbox("Fix Link's eyes open while sleeping", CVAR_ENHANCEMENT("FixEyesOpenWhileSleeping"), true, false); - UIWidgets::Tooltip("Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping."); - UIWidgets::PaddedEnhancementCheckbox("Fix Darunia dancing too fast", CVAR_ENHANCEMENT("FixDaruniaDanceSpeed"), - true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip("Fixes Darunia's dancing speed so he dances to the beat of Saria's Song, like in vanilla."); - UIWidgets::PaddedEnhancementCheckbox("Fix raised Floor Switches", CVAR_ENHANCEMENT("FixFloorSwitches"), true, false); - UIWidgets::Tooltip("Fixes the two raised floor switches, the one in Forest Temple Basement and the one at the top of Fire Temple. \n" - "This will lower them, making activating them easier"); - UIWidgets::PaddedEnhancementCheckbox("Fix Zora hint dialogue", CVAR_ENHANCEMENT("FixZoraHintDialogue"), true, false); - UIWidgets::Tooltip("Fixes one Zora's dialogue giving a hint about bringing Ruto's Letter to King Zora to properly occur before moving King Zora rather than after"); - if (UIWidgets::PaddedEnhancementCheckbox("Fix hand holding Hammer", CVAR_ENHANCEMENT("FixHammerHand"), true, false)) { - UpdatePatchHand(); - } - UIWidgets::Tooltip("Fixes Adult Link having a backwards left hand when holding the Megaton Hammer."); - if (UIWidgets::PaddedEnhancementCheckbox( - "Fix Broken Giant's Knife bug", CVAR_ENHANCEMENT("FixBrokenGiantsKnife"), true, false, IS_RANDO, - "This setting is forcefully enabled when you are playing a randomizer.", - UIWidgets::CheckboxGraphics::Checkmark)) { - bool hasGiantsKnife = CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BIGGORON); - bool hasBrokenKnife = CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BROKENGIANTKNIFE); - bool knifeIsBroken = gSaveContext.swordHealth == 0.0f; - - if (hasGiantsKnife && (hasBrokenKnife != knifeIsBroken)) { - func_800849EC(gPlayState); - } - } - UIWidgets::Tooltip("Fixes the Broken Giant's Knife flag not being reset when Medigoron fixes it"); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Restoration")) - { - UIWidgets::PaddedEnhancementCheckbox("Red Ganon blood", CVAR_ENHANCEMENT("RedGanonBlood"), true, false); - UIWidgets::Tooltip("Restore the original red blood from NTSC 1.0/1.1. Disable for green blood"); - UIWidgets::PaddedEnhancementCheckbox("Fish while hovering", CVAR_ENHANCEMENT("HoverFishing"), true, false); - UIWidgets::Tooltip("Restore a bug from NTSC 1.0 that allows casting the Fishing Rod while using the Hover Boots"); - UIWidgets::PaddedEnhancementCheckbox("N64 Weird Frames", CVAR_ENHANCEMENT("N64WeirdFrames"), true, false); - UIWidgets::Tooltip("Restores N64 Weird Frames allowing weirdshots to behave the same as N64"); - UIWidgets::PaddedEnhancementCheckbox("Bombchus out of bounds", CVAR_ENHANCEMENT("BombchusOOB"), true, false); - UIWidgets::Tooltip("Allows bombchus to explode out of bounds\nSimilar to GameCube and Wii VC"); - UIWidgets::PaddedEnhancementCheckbox("Quick Putaway", CVAR_ENHANCEMENT("QuickPutaway"), true, false); - UIWidgets::Tooltip("Restore a bug from NTSC 1.0 that allows putting away an item without an animation and performing Putaway Ocarina Items"); - UIWidgets::PaddedEnhancementCheckbox("Restore old Gold Skulltula cutscene", CVAR_ENHANCEMENT("GSCutscene"), true, false); - UIWidgets::Tooltip("Restore pre-release behavior where defeating a Gold Skulltula will play a cutscene showing it die."); - UIWidgets::PaddedEnhancementCheckbox("Quick Bongo Kill", CVAR_ENHANCEMENT("QuickBongoKill"), true, false); - UIWidgets::Tooltip("Restore a bug from NTSC 1.0 that allows bypassing Bongo Bongo's intro cutscene to quickly kill him"); - UIWidgets::PaddedEnhancementCheckbox("Original RBA Values", CVAR_ENHANCEMENT("RestoreRBAValues"), true, false); - UIWidgets::Tooltip("Restores the original outcomes when performing Reverse Bottle Adventure."); - UIWidgets::PaddedEnhancementCheckbox("Early Eyeball Frog", CVAR_ENHANCEMENT("EarlyEyeballFrog"), true, false); - UIWidgets::Tooltip("Restores a bug from NTSC 1.0/1.1 that allows you to obtain the eyeball frog from King Zora instead of the Zora Tunic by holding shield."); - UIWidgets::PaddedEnhancementCheckbox("Pulsate boss icon", CVAR_ENHANCEMENT("PulsateBossIcon"), true, false); - UIWidgets::Tooltip("Restores an unfinished feature to pulsate the boss room icon when you are in the boss room."); - UIWidgets::PaddedEnhancementSliderInt("Pause Buffer Input Window: %d", "##PauseBufferWindow", CVAR_ENHANCEMENT("PauseBufferWindow"), 0, 40, "", 0, true, true, false); - UIWidgets::PaddedEnhancementCheckbox("Include held inputs at start of Buffer Input Window", CVAR_ENHANCEMENT("IncludeHeldInputsBufferWindow"), true, false); - UIWidgets::Tooltip("Typically, inputs that are held prior to the buffer window are not included in the buffer. This setting changes that behavior to include them. This may cause some inputs to be re-triggered undesireably, for instance Z-Targetting something you might not want to."); - UIWidgets::PaddedEnhancementSliderInt("Simulated Input Lag: %d frames", "##SimulatedInputLag", CVAR_SIMULATED_INPUT_LAG, 0, 6, "", 0, true, true, false); - UIWidgets::Tooltip("Buffers your inputs to be executed a specified amount of frames later"); - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Extra Modes")) { - UIWidgets::PaddedText("Mirrored World", true, false); - if (UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("MirroredWorldMode"), mirroredWorldModes, MIRRORED_WORLD_OFF) && gPlayState != NULL) { - UpdateMirrorModeState(gPlayState->sceneNum); - } - UIWidgets::Tooltip( - "Mirrors the world horizontally\n\n" - "- Always: Always mirror the world\n" - "- Random: Randomly decide to mirror the world on each scene change\n" - "- Random (Seeded): Scenes are mirrored based on the current randomizer seed/file\n" - "- Dungeons: Mirror the world in Dungeons\n" - "- Dungeons (Vanilla): Mirror the world in vanilla Dungeons\n" - "- Dungeons (MQ): Mirror the world in MQ Dungeons\n" - "- Dungeons Random: Randomly decide to mirror the world in Dungeons\n" - "- Dungeons Random (Seeded): Dungeons are mirrored based on the current randomizer seed/file\n" - ); - - UIWidgets::PaddedText("Enemy Randomizer", true, false); - if (UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("RandomizedEnemies"), enemyRandomizerModes, ENEMY_RANDOMIZER_OFF)) { - GetSelectedEnemies(); - } - UIWidgets::Tooltip( - "Replaces fixed enemies throughout the game with a random enemy. Bosses, mini-bosses and a few specific regular enemies are excluded.\n" - "Enemies that need more than Deku Nuts + either Deku Sticks or a sword to kill are excluded from spawning in \"clear enemy\" rooms.\n\n" - "- Random: Enemies are randomized every time you load a room\n" - "- Random (Seeded): Enemies are randomized based on the current randomizer seed/file\n" - ); - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0) == ENEMY_RANDOMIZER_RANDOM) { - ImGui::Separator(); - if (ImGui::BeginMenu("Enemy List")) { - UIWidgets::PaddedEnhancementCheckbox("Select All Enemies", CVAR_ENHANCEMENT("RandomizedEnemyList.All"), true, false); - bool disabledEnemyList = CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0); - ImGui::Separator(); - - ImGui::BeginDisabled(CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)); - - ImGui::BeginTable("Enemy Table", 2); - ImGui::TableNextColumn(); - for (int i = 0; i < RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE; i++) { - if (UIWidgets::PaddedEnhancementCheckbox(enemyNameList[i], enemyCVarList[i], true, false, disabledEnemyList, - "These options are disabled because \"Select All Enemies\" is enabled.", - UIWidgets::CheckboxGraphics::Checkmark)) { - GetSelectedEnemies(); - } - if (i == RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE / 2) { - ImGui::TableNextColumn(); - } - } - ImGui::EndTable(); - ImGui::EndDisabled(); - - ImGui::EndMenu(); - } - }; - ImGui::Separator(); - - UIWidgets::PaddedEnhancementCheckbox("Randomized Enemy Sizes", CVAR_ENHANCEMENT("RandomizedEnemySizes"), true, false); - UIWidgets::Tooltip("Enemies and Bosses spawn with random sizes."); - - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemySizes"), 0)) { - UIWidgets::EnhancementCheckbox("Scale Health with Size", CVAR_ENHANCEMENT("EnemySizeScalesHealth")); - UIWidgets::Tooltip("Scales normal enemies health with their randomized size. *This will NOT affect bosses*"); - } - - UIWidgets::PaddedEnhancementCheckbox("Ivan the Fairy (Coop Mode)", CVAR_ENHANCEMENT("IvanCoopModeEnabled"), true, false); - UIWidgets::Tooltip("Enables Ivan the Fairy upon the next map change. Player 2 can control Ivan and " - "press the C-Buttons to use items and mess with Player 1!"); - - UIWidgets::PaddedEnhancementCheckbox("Rupee Dash Mode", CVAR_ENHANCEMENT("RupeeDash"), true, false); - UIWidgets::Tooltip("Rupees reduced over time, Link suffers damage when the count hits 0."); - - if (CVarGetInteger(CVAR_ENHANCEMENT("RupeeDash"), 0)) { - UIWidgets::PaddedEnhancementSliderInt( - "Rupee Dash Interval: %d", "##DashInterval", CVAR_ENHANCEMENT("RupeeDashInterval"), 1, 10, "", 5, true, true, false, - !CVarGetInteger(CVAR_ENHANCEMENT("RupeeDash"), 0), - "This option is disabled because \"Rupee Dash Mode\" is turned off"); - UIWidgets::Tooltip("Interval between Rupee reduction in Rupee Dash Mode"); - } - - UIWidgets::PaddedEnhancementCheckbox("Shadow Tag Mode", CVAR_ENHANCEMENT("ShadowTag"), true, false); - UIWidgets::Tooltip("A wallmaster follows Link everywhere, don't get caught!"); - - UIWidgets::PaddedEnhancementCheckbox("Additional Traps", CVAR_ENHANCEMENT("ExtraTraps.Enabled"), true, false); - UIWidgets::Tooltip("Enables additional Trap variants."); - - if (CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0)) { - UIWidgets::PaddedSeparator(); - if (ImGui::BeginMenu("Trap Options")) { - ImGui::Text("Tier 1 Traps:"); - UIWidgets::Spacer(0); - UIWidgets::PaddedEnhancementCheckbox("Freeze Traps", CVAR_ENHANCEMENT("ExtraTraps.Ice"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Burn Traps", CVAR_ENHANCEMENT("ExtraTraps.Burn"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Shock Traps", CVAR_ENHANCEMENT("ExtraTraps.Shock"), true, false); - - UIWidgets::PaddedSeparator(); - ImGui::Text("Tier 2 Traps:"); - UIWidgets::Spacer(0); - UIWidgets::PaddedEnhancementCheckbox("Knockback Traps", CVAR_ENHANCEMENT("ExtraTraps.Knockback"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Speed Traps", CVAR_ENHANCEMENT("ExtraTraps.Speed"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Bomb Traps", CVAR_ENHANCEMENT("ExtraTraps.Bomb"), true, false); - - UIWidgets::PaddedSeparator(); - ImGui::Text("Tier 3 Traps:"); - UIWidgets::Spacer(0); - UIWidgets::PaddedEnhancementCheckbox("Void Traps", CVAR_ENHANCEMENT("ExtraTraps.Void"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Ammo Traps", CVAR_ENHANCEMENT("ExtraTraps.Ammo"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Death Traps", CVAR_ENHANCEMENT("ExtraTraps.Kill"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Teleport Traps", CVAR_ENHANCEMENT("ExtraTraps.Teleport"), true, false); - - ImGui::EndMenu(); - } - } - - UIWidgets::Spacer(0); - if (UIWidgets::PaddedEnhancementCheckbox("Hurt Container Mode", CVAR_ENHANCEMENT("HurtContainer"), true, false)) { - UpdateHurtContainerModeState(CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 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(); - } - - UIWidgets::PaddedSeparator(); - - UIWidgets::EnhancementCheckbox("Autosave", CVAR_ENHANCEMENT("Autosave")); - UIWidgets::Tooltip("Save the game automatically on a 3 minute interval and when soft-resetting the game.\n\n" - "The interval autosave will wait if the game is paused in any way (dialogue, pause screen up, cutscenes)."); - - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); - - UIWidgets::PaddedText("Boot Sequence", false, true); - UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("BootSequence"), bootSequenceLabels, BOOTSEQUENCE_DEFAULT); - UIWidgets::Tooltip("Configure what happens when starting or resetting the game\n\n" - "Default: LUS logo -> N64 logo\n" - "Authentic: N64 logo only\n" - "File Select: Skip to file select menu" - ); - - UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f); - - ImGui::EndDisabled(); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); - - if (mCosmeticsEditorWindow) { - if (ImGui::Button(GetWindowButtonText("Cosmetics Editor", CVarGetInteger(CVAR_WINDOW("CosmeticsEditor"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mCosmeticsEditorWindow->ToggleVisibility(); - } - } - - if (mAudioEditorWindow) { - if (ImGui::Button(GetWindowButtonText("Audio Editor", CVarGetInteger(CVAR_WINDOW("AudioEditor"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mAudioEditorWindow->ToggleVisibility(); - } - } - - if (mGameplayStatsWindow) { - if (ImGui::Button(GetWindowButtonText("Gameplay Stats", CVarGetInteger(CVAR_WINDOW("GameplayStats"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mGameplayStatsWindow->ToggleVisibility(); - } - } - - if (mTimeSplitWindow) { - if (ImGui::Button(GetWindowButtonText("Time Splits", CVarGetInteger(CVAR_WINDOW("TimeSplitEnabled"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mTimeSplitWindow->ToggleVisibility(); - } - } - - if (mTimeDisplayWindow) { - if (ImGui::Button(GetWindowButtonText("Additional Timers", CVarGetInteger(CVAR_WINDOW("TimeDisplayEnabled"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mTimeDisplayWindow->ToggleVisibility(); - } - } - if (mTimeDisplayWindow->IsVisible()) { - ImGui::SeparatorText("Timer Display Options"); - - if (!gPlayState) { - ImGui::Text("Additional Timer options\n" - "available when a file is\n" - "loaded..."); - } else { - if (UIWidgets::PaddedEnhancementSliderFloat("Font Scale: %.2fx", "##FontScale", CVAR_ENHANCEMENT("TimeDisplay.FontScale"), - 1.0f, 5.0f, "", 1.0f, false, true, false, true)) { - TimeDisplayInitSettings(); - } - if (UIWidgets::PaddedEnhancementCheckbox("Hide Background", CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG"), - false, false)) { - TimeDisplayInitSettings(); - } - ImGui::Separator(); - for (auto& timer : timeDisplayList) { - if (UIWidgets::PaddedEnhancementCheckbox(timer.timeLabel.c_str(), timer.timeEnable, false, false)) { - TimeDisplayUpdateDisplayOptions(); - } - } - } - } - ImGui::PopStyleVar(3); - ImGui::PopStyleColor(1); - - #ifdef __SWITCH__ - UIWidgets::Spacer(0); - ImGui::Text("Switch performance mode"); - if (UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("SwitchPerfMode"), SWITCH_CPU_PROFILES, (int)Ship::SwitchProfiles::STOCK)) { - SPDLOG_INFO("Profile:: %s", SWITCH_CPU_PROFILES[CVarGetInteger(CVAR_ENHANCEMENT("SwitchPerfMode"), (int)Ship::SwitchProfiles::STOCK)]); - Ship::Switch::ApplyOverclock(); - } - #endif - - ImGui::EndMenu(); - } -} - -void DrawCheatsMenu() { - if (ImGui::BeginMenu("Cheats")) - { - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 8.0f); - ImGui::BeginTable("##cheatsMenu", 2, ImGuiTableFlags_SizingFixedFit); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableNextColumn(); - UIWidgets::Spacer(2.0f); - ImGui::Text("Inventory:"); - UIWidgets::PaddedSeparator(); - - UIWidgets::PaddedEnhancementCheckbox("Super Tunic", CVAR_CHEAT("SuperTunic"), true, false); - UIWidgets::Tooltip("Makes every tunic have the effects of every other tunic"); - UIWidgets::PaddedEnhancementCheckbox("Easy ISG", CVAR_CHEAT("EasyISG"), true, false); - UIWidgets::Tooltip("Passive Infinite Sword Glitch\nIt makes your sword's swing effect and hitbox stay active indefinitely"); - UIWidgets::PaddedEnhancementCheckbox("Easy QPA", CVAR_CHEAT("EasyQPA"), true, false); - UIWidgets::Tooltip("Gives you the glitched damage value of the quick put away glitch."); - UIWidgets::PaddedEnhancementCheckbox("Timeless Equipment", CVAR_CHEAT("TimelessEquipment"), true, false); - UIWidgets::Tooltip("Allows any item to be equipped, regardless of age\nAlso allows Child to use Adult strength upgrades"); - UIWidgets::PaddedEnhancementCheckbox("Unrestricted Items", CVAR_CHEAT("NoRestrictItems"), true, false); - UIWidgets::Tooltip("Allows you to use any item at any location"); - UIWidgets::PaddedEnhancementCheckbox("Fireproof Deku Shield", CVAR_CHEAT("FireproofDekuShield"), true, false); - UIWidgets::Tooltip("Prevents the Deku Shield from burning on contact with fire"); - UIWidgets::PaddedEnhancementCheckbox("Shield with Two-Handed Weapons", CVAR_CHEAT("ShieldTwoHanded"), true, false); - UIWidgets::Tooltip("This allows you to put up your shield with any two-handed weapon in hand except for Deku Sticks"); - UIWidgets::Spacer(2.0f); - ImGui::Text("Deku Sticks:"); - UIWidgets::EnhancementCombobox(CVAR_CHEAT("DekuStick"), DekuStickCheat, DEKU_STICK_NORMAL); - UIWidgets::Spacer(2.0f); - UIWidgets::EnhancementSliderFloat("Bomb Timer Multiplier: %.2fx", "##gBombTimerMultiplier", CVAR_CHEAT("BombTimerMultiplier"), 0.1f, 5.0f, "", 1.0f, false); - UIWidgets::PaddedEnhancementCheckbox("Hookshot Everything", CVAR_CHEAT("HookshotEverything"), true, false); - UIWidgets::Tooltip("Makes every surface in the game hookshot-able"); - UIWidgets::Spacer(0); - UIWidgets::EnhancementSliderFloat("Hookshot Reach Multiplier: %.2fx", "##gCheatHookshotReachMultiplier", CVAR_CHEAT("HookshotReachMultiplier"), 1.0f, 5.0f, "", 1.0f, false); - UIWidgets::Spacer(2.0f); - if (ImGui::Button("Change Age")) { - SwitchAge(); - } - UIWidgets::Tooltip("Switches Link's age and reloads the area."); - UIWidgets::Spacer(2.0f); - if (ImGui::Button("Clear Cutscene Pointer")) { - GameInteractor::RawAction::ClearCutscenePointer(); - } - UIWidgets::Tooltip("Clears the cutscene pointer to a value safe for wrong warps."); - - ImGui::TableNextColumn(); - UIWidgets::Spacer(2.0f); - - if (ImGui::BeginMenu("Infinite...")) { - UIWidgets::EnhancementCheckbox("Money", CVAR_CHEAT("InfiniteMoney")); - UIWidgets::PaddedEnhancementCheckbox("Health", CVAR_CHEAT("InfiniteHealth"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Ammo", CVAR_CHEAT("InfiniteAmmo"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Magic", CVAR_CHEAT("InfiniteMagic"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Nayru's Love", CVAR_CHEAT("InfiniteNayru"), true, false); - UIWidgets::PaddedEnhancementCheckbox("Epona Boost", CVAR_CHEAT("InfiniteEponaBoost"), true, false); - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(0); - - if (ImGui::BeginMenu("Save States")) { - ImGui::TextColored({ 0.85f, 0.85f, 0.0f, 1.0f }, " " ICON_FA_EXCLAMATION_TRIANGLE); - ImGui::SameLine(); - ImGui::TextColored({ 0.85f, 0.35f, 0.0f, 1.0f }, " WARNING!!!! "); - ImGui::SameLine(); - ImGui::TextColored({ 0.85f, 0.85f, 0.0f, 1.0f }, ICON_FA_EXCLAMATION_TRIANGLE); - UIWidgets::PaddedText("These are NOT like emulator states.", true, false); - UIWidgets::PaddedText("They do not save your game progress, and", true, false); - UIWidgets::PaddedText("they WILL break across transitions and", true, false); - UIWidgets::PaddedText("load zones (like doors). Support for", true, false); - UIWidgets::PaddedText("related issues will not be provided.", true, false); - if (UIWidgets::PaddedEnhancementCheckbox("I promise I have read the warning", CVAR_CHEAT("SaveStatePromise"), true, - false)) { - CVarSetInteger(CVAR_CHEAT("SaveStatesEnabled"), 0); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - if (CVarGetInteger(CVAR_CHEAT("SaveStatePromise"), 0) == 1) { - UIWidgets::PaddedEnhancementCheckbox("I understand, enable save states", CVAR_CHEAT("SaveStatesEnabled"), true, - false); - UIWidgets::Tooltip("F5 to save, F6 to change slots, F7 to load"); - } - - ImGui::EndMenu(); - } - - UIWidgets::Spacer(2.0f); - ImGui::Text("Behavior:"); - UIWidgets::PaddedSeparator(); - - UIWidgets::PaddedEnhancementCheckbox("No Clip", CVAR_CHEAT("NoClip"), true, false); - UIWidgets::Tooltip("Allows you to walk through walls"); - UIWidgets::PaddedEnhancementCheckbox("Climb Everything", CVAR_CHEAT("ClimbEverything"), true, false); - UIWidgets::Tooltip("Makes every surface in the game climbable"); - UIWidgets::PaddedEnhancementCheckbox("Moon Jump on L", CVAR_CHEAT("MoonJumpOnL"), true, false); - UIWidgets::Tooltip("Holding L makes you float into the air"); - UIWidgets::PaddedEnhancementCheckbox("New Easy Frame Advancing", CVAR_CHEAT("EasyFrameAdvance"), true, false); - UIWidgets::Tooltip("Continue holding START button when unpausing to only advance a single frame and then re-pause."); - UIWidgets::PaddedEnhancementCheckbox("Drops Don't Despawn", CVAR_CHEAT("DropsDontDie"), true, false); - UIWidgets::Tooltip("Drops from enemies, grass, etc. don't disappear after a set amount of time"); - UIWidgets::PaddedEnhancementCheckbox("Fish Don't despawn", CVAR_CHEAT("NoFishDespawn"), true, false); - UIWidgets::Tooltip("Prevents fish from automatically despawning after a while when dropped"); - UIWidgets::PaddedEnhancementCheckbox("Bugs Don't despawn", CVAR_CHEAT("NoBugsDespawn"), true, false); - UIWidgets::Tooltip("Prevents bugs from automatically despawning after a while when dropped"); - UIWidgets::PaddedEnhancementCheckbox("Freeze Time", CVAR_CHEAT("FreezeTime"), true, false); - UIWidgets::Tooltip("Freezes the time of day"); - UIWidgets::PaddedEnhancementCheckbox("Time Sync", CVAR_CHEAT("TimeSync"), true, false); - UIWidgets::Tooltip("This syncs the ingame time with the real world time"); - UIWidgets::PaddedEnhancementCheckbox("No ReDead/Gibdo Freeze", CVAR_CHEAT("NoRedeadFreeze"), true, false); - UIWidgets::Tooltip("Prevents ReDeads and Gibdos from being able to freeze you with their scream"); - UIWidgets::PaddedEnhancementCheckbox("Keese/Guay don't target you", CVAR_CHEAT("NoKeeseGuayTarget"), true, false); - UIWidgets::Tooltip("Keese and Guay no longer target you and simply ignore you as if you were wearing the skull mask"); - { - static int32_t betaQuestEnabled = CVarGetInteger(CVAR_CHEAT("EnableBetaQuest"), 0); - static int32_t lastBetaQuestEnabled = betaQuestEnabled; - static int32_t betaQuestWorld = CVarGetInteger(CVAR_CHEAT("BetaQuestWorld"), 0xFFEF); - static int32_t lastBetaQuestWorld = betaQuestWorld; - - if (!isBetaQuestEnabled) { - UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - UIWidgets::PaddedEnhancementCheckbox("Enable Beta Quest", CVAR_CHEAT("EnableBetaQuest"), true, false); - UIWidgets::Tooltip("Turns on OoT Beta Quest. *WARNING* This will reset your game."); - betaQuestEnabled = CVarGetInteger(CVAR_CHEAT("EnableBetaQuest"), 0); - if (betaQuestEnabled) { - if (betaQuestEnabled != lastBetaQuestEnabled) { - betaQuestWorld = 0; - } - - ImGui::Text("Beta Quest World: %d", betaQuestWorld); - - if (ImGui::Button(" - ##BetaQuest")) { - betaQuestWorld--; - } - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - - ImGui::SliderInt("##BetaQuest", &betaQuestWorld, 0, 8, "", ImGuiSliderFlags_AlwaysClamp); - UIWidgets::Tooltip("Set the Beta Quest world to explore. *WARNING* Changing this will reset your game.\nCtrl+Click to type in a value."); - - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - if (ImGui::Button(" + ##BetaQuest")) { - betaQuestWorld++; - } - - if (betaQuestWorld > 8) { - betaQuestWorld = 8; - } - else if (betaQuestWorld < 0) { - betaQuestWorld = 0; - } - } - else { - lastBetaQuestWorld = betaQuestWorld = 0xFFEF; - CVarClear(CVAR_CHEAT("BetaQuestWorld")); - } - if (betaQuestEnabled != lastBetaQuestEnabled || betaQuestWorld != lastBetaQuestWorld) - { - // Reset the game if the beta quest state or world changed because beta quest happens on redirecting the title screen cutscene. - lastBetaQuestEnabled = betaQuestEnabled; - lastBetaQuestWorld = betaQuestWorld; - CVarSetInteger(CVAR_CHEAT("EnableBetaQuest"), betaQuestEnabled); - CVarSetInteger(CVAR_CHEAT("BetaQuestWorld"), betaQuestWorld); - - std::reinterpret_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset"); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - - if (!isBetaQuestEnabled) { - UIWidgets::ReEnableComponent(""); - } - } - - ImGui::EndTable(); - ImGui::EndDisabled(); - ImGui::EndMenu(); - } -} - -extern std::shared_ptr mStatsWindow; -extern std::shared_ptr mConsoleWindow; -extern std::shared_ptr mSaveEditorWindow; -extern std::shared_ptr mHookDebuggerWindow; -extern std::shared_ptr mColViewerWindow; -extern std::shared_ptr mActorViewerWindow; -extern std::shared_ptr mDLViewerWindow; -extern std::shared_ptr mValueViewerWindow; -extern std::shared_ptr mMessageViewerWindow; - -void DrawDeveloperToolsMenu() { - if (ImGui::BeginMenu("Developer Tools")) { - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); - - UIWidgets::EnhancementCheckbox("OoT Debug Mode", CVAR_DEVELOPER_TOOLS("DebugEnabled")); - UIWidgets::Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right, and open the debug menu with L on the pause screen"); - if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0)) { - UIWidgets::EnhancementCheckbox("OoT Registry Editor", CVAR_DEVELOPER_TOOLS("RegEditEnabled")); - UIWidgets::Tooltip("Enables the registry editor"); - ImGui::Text("Debug Save File Mode:"); - UIWidgets::EnhancementCombobox(CVAR_DEVELOPER_TOOLS("DebugSaveFileMode"), DebugSaveFileModes, 1); - UIWidgets::Tooltip( - "Changes the behaviour of debug file select creation (creating a save file on slot 1 with debug mode on)\n" - "- Off: The debug save file will be a normal savefile\n" - "- Vanilla: The debug save file will be the debug save file from the original game\n" - "- Maxed: The debug save file will be a save file with all of the items & upgrades" - ); - } - UIWidgets::PaddedEnhancementCheckbox("OoT Skulltula Debug", CVAR_DEVELOPER_TOOLS("SkulltulaDebugEnabled"), true, false); - UIWidgets::Tooltip("Enables Skulltula Debug, when moving the cursor in the menu above various map icons (boss key, compass, map screen locations, etc) will set the GS bits in that area.\nUSE WITH CAUTION AS IT DOES NOT UPDATE THE GS COUNT."); - UIWidgets::PaddedEnhancementCheckbox("Better Debug Warp Screen", CVAR_DEVELOPER_TOOLS("BetterDebugWarpScreen"), true, false); - UIWidgets::Tooltip("Optimized debug warp screen, with the added ability to chose entrances and time of day"); - UIWidgets::PaddedEnhancementCheckbox("Debug Warp Screen Translation", CVAR_DEVELOPER_TOOLS("DebugWarpScreenTranslation"), true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip("Translate the Debug Warp Screen based on the game language"); - UIWidgets::PaddedEnhancementCheckbox("Resource logging", CVAR_DEVELOPER_TOOLS("ResourceLogging"), true, false); - UIWidgets::Tooltip("Logs some resources as XML when they're loaded in binary format"); - if (gPlayState != NULL) { - UIWidgets::PaddedSeparator(); - ImGui::Checkbox("Frame Advance##frameAdvance", (bool*)&gPlayState->frameAdvCtx.enabled); - if (gPlayState->frameAdvCtx.enabled) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); - if (ImGui::Button("Advance 1", ImVec2(ImGui::GetContentRegionAvail().x / 2.0f, 0.0f))) { - CVarSetInteger(CVAR_GENERAL("FrameAdvance"), 1); - } - ImGui::SameLine(); - ImGui::Button("Advance (Hold)"); - if (ImGui::IsItemActive()) { - CVarSetInteger(CVAR_GENERAL("FrameAdvance"), 1); - } - ImGui::PopStyleVar(3); - ImGui::PopStyleColor(1); - } - } - UIWidgets::PaddedSeparator(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); - if (mStatsWindow) { - if (ImGui::Button(GetWindowButtonText("Stats", CVarGetInteger(CVAR_STATS_WINDOW_OPEN, 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mStatsWindow->ToggleVisibility(); - } - UIWidgets::Tooltip("Shows the stats window, with your FPS and frametimes, and the OS you're playing on"); - } - UIWidgets::Spacer(0); - if (mConsoleWindow) { - if (ImGui::Button(GetWindowButtonText("Console", CVarGetInteger(CVAR_CONSOLE_WINDOW_OPEN, 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mConsoleWindow->ToggleVisibility(); - } - UIWidgets::Tooltip("Enables the console window, allowing you to input commands, type help for some examples"); - } - UIWidgets::Spacer(0); - if (mSaveEditorWindow) { - if (ImGui::Button(GetWindowButtonText("Save Editor", CVarGetInteger(CVAR_WINDOW("SaveEditor"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mSaveEditorWindow->ToggleVisibility(); - } - } - UIWidgets::Spacer(0); - if (mHookDebuggerWindow) { - if (ImGui::Button(GetWindowButtonText("Hook Debugger", CVarGetInteger(CVAR_WINDOW("HookDebugger"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mHookDebuggerWindow->ToggleVisibility(); - } - } - UIWidgets::Spacer(0); - if (mColViewerWindow) { - if (ImGui::Button(GetWindowButtonText("Collision Viewer", CVarGetInteger(CVAR_WINDOW("CollisionViewer"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mColViewerWindow->ToggleVisibility(); - } - } - UIWidgets::Spacer(0); - if (mActorViewerWindow) { - if (ImGui::Button(GetWindowButtonText("Actor Viewer", CVarGetInteger(CVAR_WINDOW("ActorViewer"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mActorViewerWindow->ToggleVisibility(); - } - } - UIWidgets::Spacer(0); - if (mDLViewerWindow) { - if (ImGui::Button(GetWindowButtonText("Display List Viewer", CVarGetInteger(CVAR_WINDOW("DLViewer"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mDLViewerWindow->ToggleVisibility(); - } - } - UIWidgets::Spacer(0); - if (mValueViewerWindow) { - if (ImGui::Button(GetWindowButtonText("Value Viewer", CVarGetInteger(CVAR_WINDOW("ValueViewer"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mValueViewerWindow->ToggleVisibility(); - } - } - UIWidgets::Spacer(0); - if (mMessageViewerWindow) { - if (ImGui::Button(GetWindowButtonText("Message Viewer", CVarGetInteger(CVAR_WINDOW("MessageViewer"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mMessageViewerWindow->ToggleVisibility(); - } - } - UIWidgets::Spacer(0); - if (mGfxDebuggerWindow) { - if (ImGui::Button(GetWindowButtonText("Gfx Debugger", CVarGetInteger(CVAR_WINDOW("GfxDebugger"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - mGfxDebuggerWindow->ToggleVisibility(); - } - } - - ImGui::PopStyleVar(3); - ImGui::PopStyleColor(1); - - ImGui::EndDisabled(); - - ImGui::EndMenu(); - } -} - -#ifdef ENABLE_REMOTE_CONTROL -void DrawRemoteControlMenu() { - if (ImGui::BeginMenu("Network")) { - Sail::Instance->DrawMenu(); - CrowdControl::Instance->DrawMenu(); - ImGui::EndMenu(); - } -} -#endif - -extern std::shared_ptr mRandomizerSettingsWindow; -extern std::shared_ptr mPlandomizerWindow; -extern std::shared_ptr mItemTrackerWindow; -extern std::shared_ptr mItemTrackerSettingsWindow; -extern std::shared_ptr mEntranceTrackerWindow; -extern std::shared_ptr mEntranceTrackerSettingsWindow; -extern std::shared_ptr mCheckTrackerWindow; -extern std::shared_ptr mCheckTrackerSettingsWindow; -extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); - -void DrawRandomizerMenu() { - if (ImGui::BeginMenu("Randomizer")) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); - - #ifdef __WIIU__ - static ImVec2 buttonSize(200.0f * 2.0f, 0.0f); - static ImVec2 buttonWithOptionsSize(170.0f * 2.0f, 0.0f); - static ImVec2 optionsButtonSize(25.0f * 2.0f, 0.0f); - static float separationToOptionsButton = 5.0f * 2.0f; - #else - static ImVec2 buttonSize(200.0f, 0.0f); - static ImVec2 buttonWithOptionsSize(170.0f, 0.0f); - static ImVec2 optionsButtonSize(25.0f, 0.0f); - static float separationToOptionsButton = 5.0f; - #endif - - if (mRandomizerSettingsWindow) { - if (ImGui::Button(GetWindowButtonText("Randomizer Settings", CVarGetInteger(CVAR_WINDOW("RandomizerSettings"), 0)).c_str(), buttonSize)) { - mRandomizerSettingsWindow->ToggleVisibility(); - } - } - - UIWidgets::Spacer(0); - - if (mPlandomizerWindow) { - if (ImGui::Button(GetWindowButtonText("Plandomizer Editor", CVarGetInteger(CVAR_WINDOW("PlandomizerWindow"), 0)).c_str(), buttonSize)) { - mPlandomizerWindow->ToggleVisibility(); - } - } - - UIWidgets::Spacer(0); - - if (mItemTrackerWindow) { - if (ImGui::Button(GetWindowButtonText("Item Tracker", CVarGetInteger(CVAR_WINDOW("ItemTracker"), 0)).c_str(), buttonWithOptionsSize)) { - mItemTrackerWindow->ToggleVisibility(); - } - } - - ImGui::SameLine(0, 0); - ImVec2 cursor = ImGui::GetCursorPos(); - ImGui::SetCursorPos(ImVec2(cursor.x + separationToOptionsButton, cursor.y)); - - if (mItemTrackerSettingsWindow) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(FA_ICON_BUTTON_FRAME_PADDING_X(ICON_FA_COG), 6.0f)); - if (ImGui::Button(ICON_FA_COG "##ItemTrackerSettings", optionsButtonSize)) { - mItemTrackerSettingsWindow->ToggleVisibility(); - } - ImGui::PopStyleVar(); - } - - UIWidgets::Spacer(0); - if (mEntranceTrackerWindow) { - if (ImGui::Button(GetWindowButtonText("Entrance Tracker", CVarGetInteger(CVAR_WINDOW("EntranceTracker"), 0)).c_str(), buttonWithOptionsSize)) { - mEntranceTrackerWindow->ToggleVisibility(); - } - } - - ImGui::SameLine(0, 0); - cursor = ImGui::GetCursorPos(); - ImGui::SetCursorPos(ImVec2(cursor.x + separationToOptionsButton, cursor.y)); - - if (mEntranceTrackerSettingsWindow) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(FA_ICON_BUTTON_FRAME_PADDING_X(ICON_FA_COG), 6.0f)); - if (ImGui::Button(ICON_FA_COG "##EntranceTrackerSettings", optionsButtonSize)) { - mEntranceTrackerSettingsWindow->ToggleVisibility(); - } - ImGui::PopStyleVar(); - } - - UIWidgets::Spacer(0); - - if (mCheckTrackerWindow) { - if (ImGui::Button(GetWindowButtonText("Check Tracker", CVarGetInteger(CVAR_WINDOW("CheckTracker"), 0)).c_str(), buttonWithOptionsSize)) { - mCheckTrackerWindow->ToggleVisibility(); - } - } - - ImGui::SameLine(0, 0); - cursor = ImGui::GetCursorPos(); - ImGui::SetCursorPos(ImVec2(cursor.x + separationToOptionsButton, cursor.y)); - - if (mCheckTrackerSettingsWindow) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(FA_ICON_BUTTON_FRAME_PADDING_X(ICON_FA_COG), 6.0f)); - if (ImGui::Button(ICON_FA_COG "##CheckTrackerSettings", optionsButtonSize)) { - mCheckTrackerSettingsWindow->ToggleVisibility(); - } - ImGui::PopStyleVar(); - } - - ImGui::PopStyleVar(3); - ImGui::PopStyleColor(1); - - UIWidgets::PaddedSeparator(); - - if (ImGui::BeginMenu("Rando Enhancements")) - { - UIWidgets::EnhancementCheckbox("Rando-Relevant Navi Hints", CVAR_RANDOMIZER_ENHANCEMENT("RandoRelevantNavi"), false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip( - "Replace Navi's overworld quest hints with rando-related gameplay hints." - ); - UIWidgets::PaddedEnhancementCheckbox("Random Rupee Names", CVAR_RANDOMIZER_ENHANCEMENT("RandomizeRupeeNames"), true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true); - UIWidgets::Tooltip( - "When obtaining rupees, randomize what the rupee is called in the textbox." - ); - - UIWidgets::PaddedEnhancementCheckbox("Use Custom Key Models", CVAR_RANDOMIZER_ENHANCEMENT("CustomKeyModels"), true, false); - UIWidgets::Tooltip("Use Custom graphics for dungeon keys, Big and Small, so that they can be easily told apart"); - - bool disableCompassColors = !DUNGEON_ITEMS_CAN_BE_OUTSIDE_DUNGEON(RSK_SHUFFLE_MAPANDCOMPASS); - - static const char* disableCompassColorsText = - "This setting is disabled because a savefile is loaded without the compass\n" - "shuffle settings set to \"Any Dungeon\", \"Overworld\" or \"Anywhere\""; - - if (UIWidgets::PaddedEnhancementCheckbox("Compass Colors Match Dungeon", CVAR_RANDOMIZER_ENHANCEMENT("MatchCompassColors"), true, false, - disableCompassColors, disableCompassColorsText, UIWidgets::CheckboxGraphics::Cross, true)) { - PatchCompasses(); - } - UIWidgets::Tooltip( - "Matches the color of compasses to the dungeon they belong to. " - "This helps identify compasses from afar and adds a little bit of flair.\n\nThis only " - "applies to seeds with compasses shuffled to \"Any Dungeon\", \"Overworld\", or \"Anywhere\"."); - - UIWidgets::PaddedEnhancementCheckbox("Quest Item Fanfares", CVAR_RANDOMIZER_ENHANCEMENT("QuestItemFanfares"), true, false); - UIWidgets::Tooltip( - "Play unique fanfares when obtaining quest items " - "(medallions/stones/songs). Note that these fanfares are longer than usual." - ); - UIWidgets::PaddedEnhancementCheckbox("Mysterious Shuffled Items", CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), true, false); - UIWidgets::Tooltip( - "Displays a \"Mystery Item\" model in place of any freestanding/GS/shop items that were shuffled, " - "and replaces item names for them and scrubs and merchants, regardless of hint settings, " - "so you never know what you're getting."); - UIWidgets::PaddedEnhancementCheckbox("Simpler Boss Soul Models", - CVAR_RANDOMIZER_ENHANCEMENT("SimplerBossSoulModels"), true, false); - UIWidgets::Tooltip( - "When shuffling boss souls, they'll appear as a simpler model instead of showing the boss' models." - "This might make boss souls more distinguishable from a distance, and can help with performance." - ); - ImGui::EndMenu(); - } - - ImGui::EndMenu(); - } } void SohMenuBar::InitElement() { - UpdateWindowBackendObjects(); + } void SohMenuBar::DrawElement() { if (ImGui::BeginMenuBar()) { - DrawMenuBarIcon(); - static ImVec2 sWindowPadding(8.0f, 8.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, sWindowPadding); - DrawShipMenu(); - - ImGui::SetCursorPosY(0.0f); - DrawSettingsMenu(); ImGui::SetCursorPosY(0.0f); - DrawEnhancementsMenu(); - - ImGui::SetCursorPosY(0.0f); - - DrawCheatsMenu(); - - ImGui::SetCursorPosY(0.0f); - - DrawDeveloperToolsMenu(); - - ImGui::SetCursorPosY(0.0f); - - #ifdef ENABLE_REMOTE_CONTROL - DrawRemoteControlMenu(); - - ImGui::SetCursorPosY(0.0f); - #endif - - DrawRandomizerMenu(); - ImGui::PopStyleVar(1); ImGui::EndMenuBar(); } diff --git a/soh/soh/SohGui/SohMenuDevTools.cpp b/soh/soh/SohGui/SohMenuDevTools.cpp new file mode 100644 index 000000000..53d866a8e --- /dev/null +++ b/soh/soh/SohGui/SohMenuDevTools.cpp @@ -0,0 +1,170 @@ +#include "SohMenu.h" + +namespace SohGui { + +extern std::shared_ptr mSohMenu; +using namespace UIWidgets; + +void SohMenu::AddMenuDevTools() { + // Add Dev Tools Menu + AddMenuEntry("Dev Tools", CVAR_SETTING("Menu.DevToolsSidebarSection")); + + // General + AddSidebarEntry("Dev Tools", "General", 3); + WidgetPath path = { "Dev Tools", "General", SECTION_COLUMN_1 }; + + AddWidget(path, "Popout Menu", WIDGET_CVAR_CHECKBOX) + .CVar("gSettings.Menu.Popout") + .Options(CheckboxOptions().Tooltip("Changes the menu display from overlay to windowed.")); + AddWidget(path, "Debug Mode", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_DEVELOPER_TOOLS("DebugEnabled")) + .Options(CheckboxOptions().Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip " + "with L + D-pad Right, and open the debug menu with L on the pause screen.")); + AddWidget(path, "OoT Registry Editor", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_DEVELOPER_TOOLS("RegEditEnabled")) + .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0); }) + .Options(CheckboxOptions().Tooltip("Enables the registry editor.")); + AddWidget(path, "Debug Save File Mode", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_DEVELOPER_TOOLS("DebugSaveFileMode")) + .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0); }) + .Options(ComboboxOptions() + .Tooltip("Changes the behaviour of debug file select creation (creating a save file on slot 1 " + "with debug mode on)\n" + "- Off: The debug save file will be a normal savefile\n" + "- Vanilla: The debug save file will be the debug save file from the original game\n" + "- Maxed: The debug save file will be a save file with all of the items & upgrades") + .ComboMap(debugSaveFileModes)); + AddWidget(path, "OoT Skulltula Debug", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_DEVELOPER_TOOLS("SkulltulaDebugEnabled")) + .Options(CheckboxOptions().Tooltip("Enables Skulltula Debug, when moving the cursor in the menu above various " + "map icons (boss key, compass, map screen locations, etc) will set the GS " + "bits in that area.\nUSE WITH CAUTION AS IT DOES NOT UPDATE THE GS COUNT.")); + AddWidget(path, "Better Debug Warp Screen", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_DEVELOPER_TOOLS("BetterDebugWarpScreen")) + .Options(CheckboxOptions().Tooltip( + "Optimized debug warp screen, with the added ability to chose entrances and time of day")); + AddWidget(path, "Debug Warp Screen Translation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_DEVELOPER_TOOLS("DebugWarpScreenTranslation")) + .Options(CheckboxOptions() + .Tooltip("Translate the Debug Warp Screen based on the game language.") + .DefaultValue(true)); + AddWidget(path, "Resource logging", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_DEVELOPER_TOOLS("ResourceLogging")) + .Options(CheckboxOptions().Tooltip("Logs some resources as XML when they're loaded in binary format")); + + AddWidget(path, "Frame Advance", WIDGET_CHECKBOX) + .Options(CheckboxOptions().Tooltip( + "This allows you to advance through the game one frame at a time on command. " + "To advance a frame, hold Z and tap R on the second controller. Holding Z " + "and R will advance a frame every half second. You can also use the buttons below.")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NULL_PLAY_STATE).active || + mSohMenu->disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; + if (gPlayState != nullptr) { + info.valuePointer = (bool*)&gPlayState->frameAdvCtx.enabled; + } else { + info.valuePointer = (bool*)nullptr; + } + }); + AddWidget(path, "Advance 1", WIDGET_BUTTON) + .Options(ButtonOptions().Tooltip("Advance 1 frame.").Size(Sizes::Inline)) + .Callback([](WidgetInfo& info) { CVarSetInteger(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick"), 1); }) + .PreFunc([](WidgetInfo& info) { + info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_FRAME_ADVANCE_OFF).active || + mSohMenu->disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; + }); + AddWidget(path, "Advance (Hold)", WIDGET_BUTTON) + .Options(ButtonOptions().Tooltip("Advance frames while the button is held.").Size(Sizes::Inline)) + .PreFunc([](WidgetInfo& info) { + info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_FRAME_ADVANCE_OFF).active || + mSohMenu->disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; + }) + .PostFunc([](WidgetInfo& info) { + if (ImGui::IsItemActive()) { + CVarSetInteger(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick"), 1); + } + }) + .SameLine(true); + + // Stats + path.sidebarName = "Stats"; + AddSidebarEntry("Dev Tools", path.sidebarName, 1); + AddWidget(path, "Popout Stats Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("Stats")) + .WindowName("Stats") + .Options(WindowButtonOptions().Tooltip("Enables the separate Stats Window.")); + + // Console + path.sidebarName = "Console"; + AddSidebarEntry("Dev Tools", path.sidebarName, 1); + AddWidget(path, "Popout Console", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("Console")) + .WindowName("Console##SoH") + .Options(WindowButtonOptions().Tooltip("Enables the separate Console Window.")); + + // Save Editor + path.sidebarName = "Save Editor"; + AddSidebarEntry("Dev Tools", path.sidebarName, 1); + AddWidget(path, "Popout Save Editor", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("SaveEditor")) + .WindowName("Save Editor") + .Options(WindowButtonOptions().Tooltip("Enables the separate Save Editor Window.")); + + // Hook Debugger + path.sidebarName = "Hook Debugger"; + AddSidebarEntry("Dev Tools", path.sidebarName, 1); + AddWidget(path, "Popout Hook Debugger", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("HookDebugger")) + .WindowName("Hook Debugger") + .Options(WindowButtonOptions().Tooltip("Enables the separate Hook Debugger Window.")); + + // Collision Viewer + path.sidebarName = "Collision Viewer"; + AddSidebarEntry("Dev Tools", path.sidebarName, 2); + AddWidget(path, "Popout Collision Viewer", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("CollisionViewer")) + .WindowName("Collision Viewer") + .Options(WindowButtonOptions().Tooltip("Enables the separate Collision Viewer Window.")); + + // Actor Viewer + path.sidebarName = "Actor Viewer"; + AddSidebarEntry("Dev Tools", path.sidebarName, 2); + AddWidget(path, "Popout Actor Viewer", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("ActorViewer")) + .WindowName("Actor Viewer") + .Options(WindowButtonOptions().Tooltip("Enables the separate Actor Viewer Window.")); + + // Display List Viewer + path.sidebarName = "DList Viewer"; + AddSidebarEntry("Dev Tools", path.sidebarName, 2); + AddWidget(path, "Popout Display List Viewer", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("DisplayListViewer")) + .WindowName("Display List Viewer") + .Options(WindowButtonOptions().Tooltip("Enables the separate Display List Viewer Window.")); + + // Value Viewer + path.sidebarName = "Value Viewer"; + AddSidebarEntry("Dev Tools", path.sidebarName, 2); + AddWidget(path, "Popout Value Viewer", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("ValueViewer")) + .WindowName("Value Viewer") + .Options(WindowButtonOptions().Tooltip("Enables the separate Value Viewer Window.")); + + // Message Viewer + path.sidebarName = "Message Viewer"; + AddSidebarEntry("Dev Tools", path.sidebarName, 2); + AddWidget(path, "Popout Message Viewer", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("MessageViewer")) + .WindowName("Message Viewer") + .Options(WindowButtonOptions().Tooltip("Enables the separate Message Viewer Window.")); + + // Gfx Debugger + path.sidebarName = "Gfx Debugger"; + AddSidebarEntry("Dev Tools", path.sidebarName, 1); + AddWidget(path, "Popout Gfx Debugger", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("GfxDebugger")) + .WindowName("GfxDebugger##SoH") + .Options(WindowButtonOptions().Tooltip("Enables the separate Gfx Debugger Window.")); +} + +} // namespace SohGui diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp new file mode 100644 index 000000000..ad12c92aa --- /dev/null +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -0,0 +1,1822 @@ +#include "SohMenu.h" +#include +#include +#include +#include +#include +#include +#include + +static std::string comboboxTooltip = ""; +static int32_t enhancementPresetSelected = ENHANCEMENT_PRESET_DEFAULT; +bool isBetaQuestEnabled = false; +static std::unordered_map bunnyHoodEffectMap = {{ BUNNY_HOOD_VANILLA, "Vanilla" }, { BUNNY_HOOD_FAST, "Faster Run" }, { BUNNY_HOOD_FAST_AND_JUMP, "Faster + Longer Jump" }}; + +extern "C" { + void enableBetaQuest() { isBetaQuestEnabled = true; } + void disableBetaQuest() { isBetaQuestEnabled = false; } +} + +namespace SohGui { + +extern std::shared_ptr mSohMenu; +using namespace UIWidgets; + +void SohMenu::AddMenuEnhancements() { + // Add Enhancements Menu + AddMenuEntry("Enhancements", CVAR_SETTING("Menu.EnhancementsSidebarSection")); + + // Enhancements + WidgetPath path = { "Enhancements", "Presets", SECTION_COLUMN_1 }; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + + const PresetTypeDefinition presetTypeDef = presetTypes.at(PRESET_TYPE_ENHANCEMENTS); + for (auto iter = presetTypeDef.presets.begin(); iter != presetTypeDef.presets.end(); ++iter) { + if (iter->first != 0) comboboxTooltip += "\n\n"; + comboboxTooltip += std::string(iter->second.label) + " - " + std::string(iter->second.description); + } + AddWidget(path, "Enhancement Presets", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Select Preset", WIDGET_COMBOBOX) + .ValuePointer(&enhancementPresetSelected) + .Callback([](WidgetInfo& info) { + const std::string presetTypeCvar = CVAR_GENERAL("SelectedPresets.") + std::to_string(PRESET_TYPE_ENHANCEMENTS); + CVarSetInteger(presetTypeCvar.c_str(), *std::get(info.valuePointer)); + }) + .Options(ComboboxOptions() + .ComboMap(enhancementPresetList) + .DefaultIndex(ENHANCEMENT_PRESET_DEFAULT) + .Tooltip(comboboxTooltip.c_str()) + ); + AddWidget(path, "Apply Preset##Enhancemnts", WIDGET_BUTTON) + .Options(ButtonOptions().Size(UIWidgets::Sizes::Inline)) + .Callback([](WidgetInfo& info) { + const std::string presetTypeCvar = CVAR_GENERAL("SelectedPresets.") + std::to_string(PRESET_TYPE_ENHANCEMENTS); + const PresetTypeDefinition presetTypeDef = presetTypes.at(PRESET_TYPE_ENHANCEMENTS); + uint16_t selectedPresetId = CVarGetInteger(presetTypeCvar.c_str(), 0); + if(selectedPresetId >= presetTypeDef.presets.size()){ + selectedPresetId = 0; + } + const PresetDefinition selectedPresetDef = presetTypeDef.presets.at(selectedPresetId); + for(const char* block : presetTypeDef.blocksToClear) { + CVarClearBlock(block); + } + if (selectedPresetId != 0) { + applyPreset(selectedPresetDef.entries); + } + CVarSetInteger(presetTypeCvar.c_str(), selectedPresetId); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }); + + // Quality of Life + path.sidebarName = "Quality of Life"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Saving", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Autosave", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("Autosave")) + .Options(CheckboxOptions().Tooltip( + "Save the game automatically on a 3 minute interval and when soft-resetting the game. The interval " + "autosave will wait if the game is paused in any way (dialogue, pause screen up, cutscenes, " + "etc.).\n\n" + "The soft-reset save will *not* trigger in cutscene maps like the Chamber of Sages!")); + AddWidget(path, "Remember Save Location", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RememberSaveLocation")) + .Options(CheckboxOptions().Tooltip( + "When loading a save, places Link at the last entrance he went through.\n" + "This doesn't work if the save was made in grottos, fairy fountains, or dungeons.")); + + AddWidget(path, "Containers Match Contents", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Chest Size & Texture Matches Contents", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents")) + .Callback([](WidgetInfo& info) { + if (CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), CSMC_DISABLED) == CSMC_DISABLED) { + CVarSetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); + } + }) + .Options(ComboboxOptions() + .ComboMap(chestStyleMatchesContentsOptions) + .DefaultIndex(CSMC_DISABLED) + .Tooltip("Chest sizes and textures are changed to help identify the item inside.\n" + " - Major items: Large gold chests\n" + " - Lesser items: Large brown chests\n" + " - Junk items: Small brown chests\n" + " - Small keys: Small silver chests\n" + " - Boss keys: Vanilla size and texture\n" + " - Skulltula Tokens: Small skulltula chest\n" + "\n" + "NOTE: Textures will not apply if you are using a mod pack with a custom chest model.")); + AddWidget(path, "Chests of Agony", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchesContents"), CSMC_DISABLED); + }) + .Options(CheckboxOptions().Tooltip("Only change the size/texture of chests if you have the Stone of Agony.")); + + AddWidget(path, "Time of Day", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Nighttime GS Always Spawn", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NightGSAlwaysSpawn")) + .Options(CheckboxOptions().Tooltip("Nighttime Skulltulas will spawn during both day and night.")); + AddWidget(path, "Pull Grave During the Day", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DayGravePull")) + .Options(CheckboxOptions().Tooltip("Allows graves to be pulled when child during the day.")); + AddWidget(path, "Dampe Appears All Night", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DampeAllNight")) + .Options(CheckboxOptions().Tooltip( + "Makes Dampe appear anytime during the night, not just his usual working hours.")); + AddWidget(path, "Exit Market at Night", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("MarketSneak")) + .Options(CheckboxOptions().Tooltip( + "Allows exiting Hyrule Castle Market Town to Hyrule Field at night by speaking to the guard " + "next to the gate.")); + AddWidget(path, "Shops and Games Always Open", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("OpenAllHours")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_LOCK_OVERWORLD_DOORS).Is(RO_GENERIC_ON); + }) + .Options( + CheckboxOptions() + .Tooltip("Shops and Minigames are open both day and night. Requires a scene reload to take effect.") + .DisabledTooltip("This is not compatible with the Locked Overworld Doors Randomizer option.")); + + AddWidget(path, "Pause Menu", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Allow the Cursor to be on Any Slot", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("PauseAnyCursor")) + .Options( + ComboboxOptions() + .ComboMap(cursorAnywhereValues) + .DefaultIndex(PAUSE_ANY_CURSOR_RANDO_ONLY) + .Tooltip("Allows the cursor on the pause menu to be over any slot. Sometimes required in Randomizer " + "to select certain items.")); + AddWidget(path, "Pause Warp", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("PauseWarp")) + .Options(CheckboxOptions().Tooltip( + "Selection of warp song in pause menu initiates a warp. Disables song playback.")); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Controls", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Answer Navi Prompt with L Button", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NaviOnL")) + .Options(CheckboxOptions().Tooltip("Speak to Navi with L but enter First-Person Camera with C-Up")); + AddWidget(path, "Don't Require Input for Credits Sequence", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NoInputForCredits")) + .Options(CheckboxOptions().Tooltip( + "Removes the Input Requirement on Text boxes after defeating Ganon, allowing the Credits " + "Sequence to continue to progress.")); + AddWidget(path, "Include Held Inputs at the Start of Pause Buffer Input Window", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("IncludeHeldInputsBufferWindow")) + .Options(CheckboxOptions().Tooltip( + "Typically, inputs that are held prior to the buffer window are not included in the buffer. This " + "setting changes that behavior to include them. This may cause some inputs to be re-triggered " + "undesireably, for instance Z-Targetting something you might not want to.")); + AddWidget(path, "Pause Buffer Input Window: %d frames", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("PauseBufferWindow")) + .Options(IntSliderOptions() + .Min(0) + .Max(40) + .DefaultValue(0) + .Format("%d frames") + .Tooltip("Adds back in a delay after unpausing before the game resumes playing again, " + "where inputs can be held prematurely to be input immediately after the game resumes. " + "This essentially brings back behaviour from console releases which are lost on default " + "because SoH isn't limited to N64 hardware.")); + AddWidget(path, "Simulated Input Lag: %d frames", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_SIMULATED_INPUT_LAG) + .Options(IntSliderOptions() + .Min(0) + .Max(6) + .DefaultValue(0) + .Format("%d frames") + .Tooltip("Buffers your inputs to be executed a specified amount of frames later.")); + + AddWidget(path, "Item Count Messages", WIDGET_SEPARATOR_TEXT); + int numOptions = ARRAY_COUNT(itemCountMessageCVars); + bool allItemCountsChecked = false; + AddWidget(path, "All", WIDGET_CHECKBOX) + .ValuePointer(&allItemCountsChecked) + .PreFunc([](WidgetInfo& info) { + int numOptions = ARRAY_COUNT(itemCountMessageCVars); + *std::get(info.valuePointer) = std::all_of(itemCountMessageCVars, itemCountMessageCVars + numOptions, + [](const char* cvar) { return CVarGetInteger(cvar, 0); }); + }) + .Callback([](WidgetInfo& info) { + int32_t newValue = *std::get(info.valuePointer) ? 1 : 0; + int numOptions = ARRAY_COUNT(itemCountMessageCVars); + std::for_each(itemCountMessageCVars, itemCountMessageCVars + numOptions, + [newValue](const char* cvar) { CVarSetInteger(cvar, newValue); }); + + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }); + for (int i = 0; i < numOptions; i++) { + AddWidget(path, itemCountMessageOptions[i], WIDGET_CVAR_CHECKBOX).CVar(itemCountMessageCVars[i]); + } + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Misc", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Disable Crit Wiggle", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableCritWiggle")) + .Options(CheckboxOptions().Tooltip("Disable Random Camera Wiggle at Low Health.")); + AddWidget(path, "Better Owl", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BetterOwl")) + .Options(CheckboxOptions().Tooltip( + "The default response to Kaepora Gaebora is always that you understood what he said.")); + + AddWidget(path, "Convenience", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Quit Fishing at Door", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("QuitFishingAtDoor")) + .Options(CheckboxOptions().Tooltip( + "Fisherman asks if you want to quit at the door if you try to leave the Fishing Pond " + "while still holding the Fishing Rod.")); + AddWidget(path, "Instant Putaway", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InstantPutaway")) + .Options(CheckboxOptions().Tooltip("Allow Link to put items away without having to wait around.")); + AddWidget(path, "Navi Timer Resets on Scene Change", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ResetNaviTimer")) + .Options( + CheckboxOptions().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.")); + AddWidget(path, "Link's Cow in Both Time Periods", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CowOfTime")) + .Options(CheckboxOptions().Tooltip( + "Allows the Lon Lon Ranch Obstacle Course reward to be shared across time periods.")); + AddWidget(path, "Play Zelda's Lullaby to Open Sleeping Waterfall", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + IS_RANDO && + OTRGlobals::Instance->gRandoContext->GetOption(RSK_SLEEPING_WATERFALL).Is(RO_WATERFALL_OPEN); + info.options->disabledTooltip = "This setting is forcefully enabled because a randomizer savefile with " + "\"Sleeping Waterfall: Open\" is loaded."; + }) + .Options( + ComboboxOptions() + .ComboMap(sleepingWaterfallOptions) + .DefaultIndex(WATERFALL_ALWAYS) + .Tooltip( + "Always: Link must always play Zelda's Lullaby to open the waterfall entrance to Zora's Domain.\n" + "Once: Link only needs to play Zelda's Lullaby once to open the waterfall; after that, it stays " + "open permanently.\n" + "Never: Link never needs to play Zelda's Lullaby to open the waterfall. He only needs to have " + "learned it and have an Ocarina.")); + + // Skips & Speed-ups + path.sidebarName = "Skips & Speed-ups"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Cutscenes", WIDGET_SEPARATOR_TEXT); + bool allSkipsChecked = false; + AddWidget(path, "Skip All", WIDGET_CHECKBOX) + .ValuePointer(&allSkipsChecked) + .PreFunc([](WidgetInfo& info) { + *std::get(info.valuePointer) = + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO); + }) + .Callback([](WidgetInfo& info) { + int32_t newValue = *std::get(info.valuePointer) ? 1 : 0; + + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), newValue); + CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), newValue); + + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }); + AddWidget(path, "Skip Intro", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Skip Entrance Cutscenes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Skip Story Cutscenes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Skip Song Cutscenes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Skip Boss Introductions", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Quick Boss Deaths", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Skip One Point Cutscenes (Chests, Door Unlocks, etc.)", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Skip Owl Interactions", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Skip Misc Interactions", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Disable Title Card", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard")) + .Options(CheckboxOptions().DefaultValue(IS_RANDO)); + AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding")) + .Options(CheckboxOptions().Tooltip( + "Don't skip cutscenes that are associated wiht useful glitches. Currently, it is " + "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, and the Box Skip One " + "Point in Jabu.")); + + AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastDrops")) + .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for new Consumable Items and Bottle Swipes.")); + AddWidget(path, "Skip Forced Dialog", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipForcedDialog")) + .Options(ComboboxOptions() + .ComboMap(skipForcedDialogOptions) + .DefaultIndex(FORCED_DIALOG_SKIP_NONE) + .Tooltip("Prevent forced conversations with Navi and/or other NPCs.")); + AddWidget(path, "Skip Text", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SkipText")) + .Options(CheckboxOptions().Tooltip("Holding down B skips text.")); + AddWidget(path, "Text Speed: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("TextSpeed")) + .Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx")); + AddWidget(path, "Slow Text Speed: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("SlowTextSpeed")) + .Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx").Tooltip( + "Changes the speed of sections of text that normally are paced slower than the text surrounding it.")); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Animations", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Faster Heavy Block Lift", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FasterHeavyBlockLift")) + .Options(CheckboxOptions().Tooltip("Speeds up lifting Silver Rocks and Obelisks.")); + AddWidget(path, "Fast Chests", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastChests")) + .Options(CheckboxOptions().Tooltip("Makes Link always kick the chest to open it, instead of doing the longer " + "chest opening animation for major items.")); + AddWidget(path, "Skip Water Take Breath Animation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SkipSwimDeepEndAnim")) + .Options(CheckboxOptions().Tooltip("Skips Link's taking breath animation after coming up from water. " + "This setting does not interfere with getting items from underwater.")); + AddWidget(path, "Vine/Ladder Climb Speed +%d", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ClimbSpeed")) + .Options(IntSliderOptions().Min(0).Max(12).DefaultValue(0).Format("+%d")); + AddWidget(path, "Block Pushing Speed +%d", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("FasterBlockPush")) + .Options(IntSliderOptions().Min(0).Max(5).DefaultValue(0).Format("+%d")); + AddWidget(path, "Crawl Speed %dx", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("CrawlSpeed")) + .Options(IntSliderOptions().Min(1).Max(4).DefaultValue(1).Format("%dx")); + AddWidget(path, "King Zora Speed: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_ENHANCEMENT("MweepSpeed")) + .Options(FloatSliderOptions().Min(0.1f).Max(5.0f).DefaultValue(1.0f).Format("%.2fx")); + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Misc", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Skip Child Stealth", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipChildStealth")) + .Options(CheckboxOptions().Tooltip( + "The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards.")); + AddWidget(path, "Skip Tower Escape", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipTowerEscape")) + .Options(CheckboxOptions().Tooltip("Skip the tower escape sequence between Ganondorf and Ganon.")); + AddWidget(path, "Skip Scarecrow's Song", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InstantScarecrow")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_SKIP_SCARECROWS_SONG); + info.options->disabledTooltip = "This setting is forcefully enabled because a randomized " + "save file with the option \"Skip Scarecrow Song\" is currently loaded."; + }) + .Options(CheckboxOptions().Tooltip( + "Pierre appears when an Ocarina is pulled out. Requires learning the Scarecrow's Song first.")); + AddWidget(path, "Faster Rupee Accumulator", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FasterRupeeAccumulator")) + .Options(CheckboxOptions().Tooltip("Causes your Wallet to fill and empty faster when you gain or lose money.")); + AddWidget(path, "No Skulltula Freeze", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SkulltulaFreeze")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = IS_RANDO && GameInteractor::IsSaveLoaded(true); + info.options->disabledTooltip = + "This setting is disabled because a randomizer savefile is loaded. Please use the " + "\"Skip Get Item Animation\" option within the randomizer enhancements instead."; + }) + .Options(CheckboxOptions().Tooltip( + "Stops the game from freezing the player when picking up Gold Skulltula Tokens. Does not" + "apply in randomizer savefiles.")); + AddWidget(path, "Skip Save Confirmation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SkipSaveConfirmation")) + .Options(CheckboxOptions().Tooltip("Skip the \"Game Saved\" confirmation screen.")); + AddWidget(path, "Link as Default File Name", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LinkDefaultName")) + .Options(CheckboxOptions().Tooltip("Allows you to have \"Link\" as a premade file name.")); + AddWidget(path, "Biggoron Forge Time: %d days", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ForgeTime")) + .Options(IntSliderOptions().Min(0).Max(3).DefaultValue(3).Format("%d days").Tooltip( + "Allows you to change the number of days it takes for " + "Biggoron to forge the Biggoron's Sword.")); + + // Graphics + path.sidebarName = "Graphics"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Mods", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Use Alternate Assets", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("AltAssets")) + .Options(CheckboxOptions().Tooltip( + "Toggle between standard assets and alternate assets. Usually mods will indicate if " + "this setting has to be used or not." + )); + AddWidget(path, "Disable Bomb Billboarding", WIDGET_CVAR_CHECKBOX) + .CVar("DisableBombBillboarding") + .Options(CheckboxOptions().Tooltip( + "Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to " + "replace bombs with 3D objects." + )); + AddWidget(path, "Disable Grotto Fixed Rotation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableGrottoRotation")) + .Options(CheckboxOptions().Tooltip( + "Disables Grottos rotating with the Camera. To be used in conjuction with mods that want to " + "replace grottos with 3D objects." + )); + AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("TextSpacing")) + .Options(IntSliderOptions().Min(4).Max(6).DefaultValue(6).Tooltip( + "Space between text characters (useful for HD font textures).")); + + AddWidget(path, "Models & Textures", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Disable LOD", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableLOD")) + .Options(CheckboxOptions().Tooltip( + "Turns off the Level of Detail setting, making models use their Higher-Poly variants at any distance.")); + AddWidget(path, "Enemy Health Bars", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EnemyHealthBar")) + .Options(CheckboxOptions().Tooltip("Renders a health bar for Enemies when Z-Targeted.")); + AddWidget(path, "Enable 3D Dropped Items/Projectiles", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NewDrops")) + .Options(CheckboxOptions().Tooltip( + "Replaces most 2D items and projectiles on the overworld with their equivalent 3D models.")); + AddWidget(path, "Animated Link in Pause Menu", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("PauseMenuAnimatedLink")) + .Options(CheckboxOptions().Tooltip( + "Turns the Static Image of Link in the Pause Menu's Equipment Subsceen " + "into a model cycling through his idle animations." + )); + AddWidget(path, "Show Age-Dependent Equipment", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EquipmentAlwaysVisible")) + .Callback([](WidgetInfo& info) { + UpdatePatchHand(); + }) + .Options(CheckboxOptions().Tooltip( + "Makes all equipment visible, regardless of Age." + )); + AddWidget(path, "Scale Adult Equipment as Child", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ScaleAdultEquipmentAsChild")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0) == 0; + }) + .Options(CheckboxOptions().Tooltip( + "Scales all of the Adult Equipment, as well as moving some a bit, to fit on Child Link better. May " + "not work properly with some mods." + )); + AddWidget(path, "Show Gauntlets in First Person", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FirstPersonGauntlets")) + .Options(CheckboxOptions().Tooltip( + "Renders Guantlets when using the Bow and Hookshot like in OoT3D." + )); + AddWidget(path, "Show Chains on Both Sides of Locked Doors", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ShowDoorLocksOnBothSides")); + AddWidget(path, "Color Temple of Time's Medallions", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ToTMedallionsColors")) + .Callback([](WidgetInfo& info) { PatchToTMedallions(); }) + .Options(CheckboxOptions().Tooltip( + "When Medallions are collected, the Medallion imprints around the Master Sword Pedestal in the Temple " + "of Time will become colored-in.")); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "UI", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Minimal UI", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("MinimalUI")) + .Options(CheckboxOptions().Tooltip("Hides most of the UI when not needed.\n" + "NOTE: Doesn't activate until scene transition.")); + AddWidget(path, "Disable Hot/Underwater Warning Text", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableTunicWarningText")) + .Options(CheckboxOptions().Tooltip("Disables warning text when you don't have on the Goron/Zora Tunic " + "in Hot/Underwater conditions.")); + AddWidget(path, "Remember Minimap State Between Areas", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RememberMapToggleState")) + .Options(CheckboxOptions().Tooltip( + "Preverse the minimap visibility state when going between areas rather than default it to \"on\" " + "when going through loading zones.")); + AddWidget(path, "Visual Stone of Agony", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("VisualAgony")) + .Options(CheckboxOptions().Tooltip( + "Displays an icon and plays a sound when Stone of Agony should be activated, for those without rumble.")); + AddWidget(path, "Disable HUD Heart Animations", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NoHUDHeartAnimation")) + .Options(CheckboxOptions().Tooltip("Disables the Beating Animation of the Hearts on the HUD.")); + AddWidget(path, "Glitch Line-up Tick", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DrawLineupTick")) + .Options(CheckboxOptions().Tooltip( + "Displays a tick in the top center of the screen to help with glitch line-ups in SoH, since traditional " + "UI based line-ups do not work outside of 4:3")); + AddWidget(path, "Disable Black Bar Letterboxes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableBlackBars")) + .Options(CheckboxOptions().Tooltip( + "Disables Black Bar Letterboxes during cutscenes and Z-Targeting. NOTE: there may be minor visual " + "glitches that were covered up by the black bars. Please disable this setting before reporting a bug.")); + AddWidget(path, "Dynamic Wallet Icon", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DynamicWalletIcon")) + .Options(CheckboxOptions().Tooltip( + "Changes the Rupee in the Wallet icon to match the wallet size you currently have.")); + AddWidget(path, "Always Show Dungeon Entrances", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("AlwaysShowDungeonMinimapIcon")) + .Options(CheckboxOptions().Tooltip("Always shows dungeon entrance icons on the Minimap.")); + AddWidget(path, "More Info in File Select", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FileSelectMoreInfo")) + .Options(CheckboxOptions().Tooltip( + "Shows what items you have collected in the File Select screen, like in N64 Randomizer.")); + AddWidget(path, "Better Ammo Rendering in Pause Menu", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BetterAmmoRendering")) + .Options(CheckboxOptions().Tooltip( + "Ammo counts in the pause menu will work correctly regardless of the position of items in the Inventory.")); + AddWidget(path, "Enable Passage of Time on File Select", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeFlowFileSelect")) + .Options(CheckboxOptions().Tooltip( + "The skybox in the background of the File Select screen will go through the day and night cycle over time.")); + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Misc.", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "N64 Mode", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_LOW_RES_MODE) + .Options(CheckboxOptions().Tooltip( + "Sets the aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution." + )); + AddWidget(path, "Remove Spin Attack Darkness", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RemoveSpinAttackDarkness")) + .Options(CheckboxOptions().Tooltip( + "Remove the Darkness that appears when charging a Spin Attack" + )); + AddWidget(path, "Draw Distance", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Increase Actor Draw Distance: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("DisableDrawDistance")) + .Callback([](WidgetInfo& info) { + if (CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1) { + CVarSetInteger(CVAR_ENHANCEMENT("DisableKokiriDrawDistance"), 0); + } + }) + .Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx").Tooltip( + "Increases the range in which Actors/Objects are drawn.")); + AddWidget(path, "Kokiri Draw Distance", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableKokiriDrawDistance")) + .PreFunc( + [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) > 1; }) + .Options(CheckboxOptions().Tooltip( + "The Kokiri are mystical beings that fade into view when approached. Enabling this will remove their " + "draw distance.")); + AddWidget(path, "Widescreen Actor Culling", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("WidescreenActorCulling")) + .Options( + CheckboxOptions().Tooltip("Adjusts the Horizontal Culling Plane to account for Widescreen Resolutions.")); + AddWidget(path, "Cull Glitch Useful Actors", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtendedCullingExcludeGlitchActors")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = !CVarGetInteger(CVAR_ENHANCEMENT("WidescreenActorCulling"), 0) && + CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1; + info.options->disabledTooltip = + "Requires Actor Draw Distance to be increased or Widscreen Actor Culling to be enabled."; + }) + .Options(CheckboxOptions().Tooltip( + "Exclude Actors that are useful for Glitches from the extended culling ranges. Some actors may still draw " + "in the extended ranges, but will not \"update\" so that certain glitches that leverage the original " + "culling requirements will still work.\n\nThe following actors are excluded:\n" + " - White clothed Gerudos\n" + " - King Zora\n" + " - Gossip Stones\n" + " - Boulders\n" + " - Blue Warps\n" + " - Darunia\n" + " - Gold SKulltulas\n")); + + path.sidebarName = "Items"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Equipment", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Equip Items on Dpad", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DpadEquips")) + .Options(CheckboxOptions().Tooltip( + "Equip items and equipment on the D-Pad. If used with \"D-Pad on Pause Screen\", you must " + "hold C-Up to equip instead of navgiate.")); + AddWidget(path, "Assignable Tunics and Boots", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("AssignableTunicsAndBoots")) + .Options(CheckboxOptions().Tooltip("Allows equipping the Tunics and Boots to C-Buttons/D-Pad.")); + // TODO: Revist strength toggle, it's currently separate but should probably be locked behind the + // Equipment toggle settings or be absorbed by it completely. + AddWidget(path, "Equipment Toggle", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EquipmentCanBeRemoved")) + .Options(CheckboxOptions().Tooltip( + "Allows equipment to be removed by toggling it off on\n the equipment subscreen.")); + AddWidget(path, "Allow Strength Equipment to be Toggled", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ToggleStrength")) + .Callback([](WidgetInfo& info) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("ToggleStrength"), 0)) { + CVarSetInteger(CVAR_ENHANCEMENT("StrengthDisabled"), 0); + } + }) + .Options(CheckboxOptions().Tooltip( + "Allows Strength to be toggled on and off by pressing A on the Strength Upgrade " + "in the Equipment Subscreen of the Pause Menu. This allows performing some glitches " + "that require the player to not have Strength.")); + AddWidget(path, "Sword Toggle Options", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("SwordToggle")) + .PreFunc( + [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("EquipmentCanBeRemoved"), 0) == 0; }) + .Options(ComboboxOptions() + .ComboMap(swordToggleModes) + .DefaultIndex(SWORD_TOGGLE_NONE) + .Tooltip("Introduces Options for unequipping Link's sword\n\n" + "None: Only Biggoron's Sword/Giant's Knife can be toggled. Doing so will equip the " + "Master Sword.\n\n" + "Child Toggle: This will allow for completely unequipping any sword as child link.\n\n" + "Both Ages: Any sword can be unequipped as either age. This may lead to swordless " + "glitches as Adult.")); + AddWidget(path, "Ask to Equip New Items", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("AskToEquip")) + .Options(CheckboxOptions().Tooltip("Adds a prompt to equip newly-obtained Swords, Shields, and Tunics.")); + + AddWidget(path, "Ocarina", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Prevent Dropped Ocarina Inputs", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DpadNoDropOcarinaInput")) + .Options(CheckboxOptions().Tooltip("Prevent dropping inputs when playing the Ocarina too quickly.")); + AddWidget(path, "Fast Ocarina Playback", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastOcarinaPlayback")) + .Options(CheckboxOptions().Tooltip("Skip the part where the Ocarina Playback is called when you play a song.")); + AddWidget(path, "Time Travel with Song of Time", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("TimeTravel")) + .Options(ComboboxOptions() + .ComboMap(timeTravelOptions) + .DefaultIndex(0) + .Tooltip("Allows Link to freely change age by playing the Song of Time.\n" + "Time Blocks can still be used properly.\n\n" + "Requirements:\n" + " - Obtained the Ocarina of Time (depends on selection)\n" + " - Obtained the Song of Time\n" + " - Obtained the Master Sword\n" + " - Not within range of a Time Block\n" + " - Not within range of Ocarina Playing spots.")); + + AddWidget(path, "Masks", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Bunny Hood Effect", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("MMBunnyHood")) + .Options(ComboboxOptions().ComboMap(bunnyHoodEffectMap).Tooltip("Wearing the Bunny Hood grants a speed and jump boost like in Majora's Mask.\n" + "Can also be limited to only the speed boost.\n" + "The effects of either option are not accounted for in Randomizer logic.\n" + "Also disables NPC's reactions to wearing the Bunny Hood.")); + AddWidget(path, "Masks Equippable as Adult", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("AdultMasks")) + .Options(CheckboxOptions().Tooltip("Allows masks to be equipped normally from the pause menu as adult.")); + AddWidget(path, "Persistent Masks", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("PersistentMasks")) + .Options( + CheckboxOptions().Tooltip("Stops masks from automatically unequipping on certain situations:\n" + "- When entering a new scene\n" + "- When not in any C button or the D-Pad\n" + "- When saving and quitting\n" + "- When dying\n" + "- When traveling thru time (if \"Masks Equippable as Adult\" is activated).")); + AddWidget(path, "Invisible Bunny Hood", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("HideBunnyHood")) + .Options(CheckboxOptions().Tooltip("Turns Bunny Hood Invisible while still maintaining its effects.")); + AddWidget(path, "Mask Select in Inventory", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("MaskSelect")) + .Options(CheckboxOptions().Tooltip( + "After completing the mask trading sub-quest, press A and any direction on the mask " + "slog to change masks")); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Explosives", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Deku Nuts Explode Bombs", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NutsExplodeBombs")) + .Options(CheckboxOptions().Tooltip("Make Deku Nuts explode Bombs, similar to how they interact with Bombchus. " + "This does not affect Bombflowers.")); + AddWidget(path, "Remove Explosive Limit", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RemoveExplosiveLimit")) + .Options(CheckboxOptions().Tooltip("Removes the cap of 3 active explosives being deployed at once.")); + AddWidget(path, "Static Explosion Radius", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("StaticExplosionRadius")) + .Options(CheckboxOptions().Tooltip( + "Explosions are now a static size, like in Majora's Mask and OoT3D. Makes Bombchu " + "hovering much easier.")); + AddWidget(path, "Prevent Bombchus Forcing First-Person", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableFirstPersonChus")) + .Options(CheckboxOptions().Tooltip( + "Prevent Bombchus from forcing the camera into first-person mode when released.")); + AddWidget(path, "Better Bombchu Shopping", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BetterBombchuShopping")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = IS_RANDO && GameInteractor::IsSaveLoaded(true); + info.options->disabledTooltip = "This setting is forcefully enabled when you are playing a randomizer."; + }) + .Options( + CheckboxOptions().Tooltip("Bombchus do not sell out when bought, and a 10 pack of Bombchus costs 99 rupees " + "instead of 100.")); + + AddWidget(path, "Bow / Slingshot", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Equip Multiple Arrows at Once", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SeparateArrows")) + .Options(CheckboxOptions().Tooltip( + "Allow the Bow and Magic Arrows to be equipped at the same time on different slots. " + "NOTE: This will disable the behavior of the 'Equip Dupe' glitch.")); + AddWidget(path, "Skip Magic Arrow Equip Animation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SkipArrowAnimation")); + // TODO: See if a Callback could be registered to avoid the need to reload scenes for the next two options. + AddWidget(path, "Blue Fire Arrows", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BlueFireArrows")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + OTRGlobals::Instance->gRandoContext->GetOption(RSK_BLUE_FIRE_ARROWS).Is(RO_GENERIC_ON); + info.options->disabledTooltip = "This setting is forcefully enabled because a randomized savefile with " + "\"Blue Fire Arrows\" is currently loaded."; + }) + .Options(CheckboxOptions().Tooltip( + "Allows Ice Arrows to melt Red Ice. May require a room reload if toggled during gameplay.")); + AddWidget(path, "Sunlight Arrows", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SunlightArrows")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + OTRGlobals::Instance->gRandoContext->GetOption(RSK_SUNLIGHT_ARROWS).Is(RO_GENERIC_ON); + info.options->disabledTooltip = "This setting is forcefully enabled because a randomized savefile with " + "\"Sunlight Arrows\" enabled is currently loaded."; + }) + .Options(CheckboxOptions().Tooltip( + "Allows Light Arrows to activate Sun Switches. May require a room reload if toggled during gameplay.")); + AddWidget(path, "Bow and Child/Slingshot as Adult", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BowSlingshotAmmoFix")) + .Options(CheckboxOptions().Tooltip("Allows Child to use a Bow with Arrows.\n" + "Allows Adult to use a Slingshot with Seeds.\n\n" + "Requires glitches or the 'Timeless Equipment' cheat to equip.")); + AddWidget(path, "Aiming Reticle for the Bow/Slingshot", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BowReticle")) + .Options(CheckboxOptions().Tooltip("Aiming with a Bow or Slingshot will display a reticle as with the Hookshot " + "when the projectile is ready to fire.")); + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Hookshot", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Targetable Hookshot Reticle", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("HookshotableReticle")) + .Options(CheckboxOptions().Tooltip( + "Makes the Hookshot Reticle use a different color when aiming at Hookshotable Collision.")); + + AddWidget(path, "Boomerang", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Instant Boomerang Recall", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastBoomerang")) + .Options(CheckboxOptions().Tooltip("Instantly return the boomerang to Link by pressing its item button while " + "it's in the air.")); + AddWidget(path, "Aim Boomerang in First-Person Mode", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BoomerangFirstPerson")) + .Callback([](WidgetInfo& info) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) { + CVarSetInteger(CVAR_ENHANCEMENT("BoomerangReticle"), 0); + } + }) + .Options(CheckboxOptions().Tooltip( + "Change aiming for the Boomerang from Third-Person to First-Person to see past Link's head.")); + AddWidget(path, "Aiming Reticle for Boomerang", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BoomerangFirstPerson")) + .PreFunc( + [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0) != 0; }) + .Options(CheckboxOptions().Tooltip("Aiming with the Boomerang will display a reticle as with the Hookshot.")); + + AddWidget(path, "Magic Spells", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Better Farore's Wind", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BetterFarore")) + .Options(CheckboxOptions().Tooltip( + "Helps FW persist between ages, gives Child and Adult separate FW points, and can " + "be used in more places.")); + AddWidget(path, "Faster Farore's Wind", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastFarores")) + .Options(CheckboxOptions().Tooltip("Greatly decreases cast time of Farore's Wind magic spell.")); + + // Fixes + path.sidebarName = "Fixes"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + AddWidget(path, "Gameplay Fixes", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fix the Gravedigging Tour Glitch", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("GravediggingTourFix")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = IS_RANDO && GameInteractor::IsSaveLoaded(true); + info.options->disabledTooltip = "This setting is always enabled in randomized save files."; + }) + .Options(CheckboxOptions().Tooltip( + "Fixes a bug where the Gravedigging Tour Heart Piece disappears if the area reloads.")); + AddWidget(path, "Fix Raised Floor Switches", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixFloorSwitches")) + .Options(CheckboxOptions().Tooltip( + "Fixes the two raised floor switches, the one in Forest Temple Basement and the one at the top of Fire " + "Temple. This will lower them, making activating them easier.")); + AddWidget(path, "Fix Zora Hint Dialogue", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixZoraHintDialogue")) + .Options(CheckboxOptions().Tooltip( + "Fixes one Zora's dialogue giving a hint about bringing Ruto's Letter to King Zora to properly occur " + "before moving King Zora rather than after.")); + AddWidget(path, "Fix Falling from Vine Edges", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixVineFall")) + .Options( + CheckboxOptions().Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges.")); + AddWidget(path, "Fix Bush Item Drops", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BushDropFix")) + .Options(CheckboxOptions().Tooltip( + "Fixes the bushes to drop items correctly rather than spawning undefined items.")); + AddWidget(path, "Fix Enemies not Spawning Near Water", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes")) + .Options(CheckboxOptions().Tooltip( + "Causes respanwing enemies, like Stalchildren, to appear on land near bodies of water. " + "Fixes an incorrect calculation that acted like water underneath ground was above it.")); + AddWidget(path, "Fix Poacher's Saw Softlock", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixSawSoftlock")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("SkipText"), 0) == 1; + info.options->disabledTooltip = "This option is forced on when Skip Text is enabled."; + }) + .Options(CheckboxOptions().Tooltip( + "Prevents the Poacher's Saw softlock from mashing through the text, or with Skip Text enabled.")); + AddWidget(path, "Fix Anubis Fireballs", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("AnubisFix")) + .Options(CheckboxOptions().Tooltip( + "Make Anubis Fireballs do Fire damage when reflected back at them with the Mirror Shield.")); + + AddWidget(path, "Item-related Fixes", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fix Deku Nut Upgrade", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DekuNutUpgradeFix")) + .Options(CheckboxOptions().Tooltip("Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable " + "after receiving the Poacher's Saw.")); + AddWidget(path, "Fix Megaton Hammer Crouch Stab", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CrouchStabHammerFix")) + .Callback([](WidgetInfo& info) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("CrouchStabHammerFix"), 0)) { + CVarClear(CVAR_ENHANCEMENT("CrouchStabFix")); + } + }) + .Options(CheckboxOptions().Tooltip( + "Make the Megaton Hammer's crouch stab able to destroy rocks without first swinging it normally.")); + AddWidget(path, "Remove Power Crouch Stab", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CrouchStabFix")) + .PreFunc( + [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("CrouchStabHammerFix"), 0) == 0; }) + .Options(CheckboxOptions().Tooltip("Make crouch stabbing always do the same damage as a regular slash.")); + AddWidget(path, "Fix Broken Giant's Knife Bug", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixGrokenGiantsKnife")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = IS_RANDO && GameInteractor::IsSaveLoaded(true); + info.options->disabledTooltip = "This setting is forcefully enabled when you are playing a Randomizer."; + }) + .Callback([](WidgetInfo& info) { + bool hasGiantsKnife = CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BIGGORON); + bool hasBrokenKnife = CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BROKENGIANTKNIFE); + bool knifeIsBroken = gSaveContext.swordHealth == 0.0f; + + if (hasGiantsKnife && (hasBrokenKnife != knifeIsBroken)) { + func_800849EC(gPlayState); + } + }) + .Options( + CheckboxOptions().Tooltip("Fixes the Broken Giant's Knife flag not being reset when Medigoron fixes it.")); + + AddWidget(path, "Camera Fixes", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fix Camera Drift", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixCameraDrift")) + .Options(CheckboxOptions().Tooltip( + "Fixes camera slightly drifting to the left when standing still due to a math error.")); + AddWidget(path, "Fix Camera Swing", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixCameraSwing")) + .Options(CheckboxOptions().Tooltip( + "Fixes camera getting stuck on collision when standing still. Also fixes slight shift " + "back in camera when Link stops moving.")); + AddWidget(path, "Fix Hanging Ledge Swing Rate", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixHangingLedgeSwingRate")) + .Options(CheckboxOptions().Tooltip( + "Fixes camera swing rate when the player falls off a ledge and the camera swings around.")); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Graphical Fixes", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fix L&R Pause Menu", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixMenuLR")) + .Options(CheckboxOptions().Tooltip("Makes the L and R buttons in the pause menu the same color")); + AddWidget(path, "Fix Dungeon Entrances", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixDungeonMinimapIcon")) + .Options(CheckboxOptions().Tooltip( + "Removes the Dungeon Entrance icon on the top-left corner of the screen when no dungeon is present on the " + "current map.")); + AddWidget(path, "Fix Two-Handled Idle Animations", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TwoHandedIdle")) + .Options(CheckboxOptions().Tooltip( + "Re-Enables the two-handed idle animation, a seemingly finished animation that was disabled on accident " + "in the original game.")); + AddWidget(path, "Fix Navi Text HUD Position", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NaviTextFix")) + .Options(CheckboxOptions().Tooltip("Correctly centers the Navi text prompt on the HUD's C-Up button.")); + AddWidget(path, "Fix Gerudo Warrior's Clothing Colors", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("GerudoWarriorClothingFix")) + .Options(CheckboxOptions().Tooltip( + "Prevent the Gerudo Warrior's clothes changing color when changing Link's tunic or " + "using Bombs in front of her.")); + AddWidget(path, "Fix Out of Bounds Textures", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixTexturesOOB")) + .Callback([](WidgetInfo& info) { ApplyAuthenticGfxPatches(); }) + .Options(CheckboxOptions().Tooltip( + "Fixes authentic out of bounds texture reads, instead loading textures with the correct size.")); + AddWidget(path, "Fix Link's Eyes Open while Sleeping", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixEyesOpenWhileSleeping")) + .Options(CheckboxOptions().Tooltip( + "Fixes Link's eyes being open in the openeing cutscene when he is supposed to be sleeping.")); + AddWidget(path, "Fix Hand Holding Hammer", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixHammerHand")) + .Callback([](WidgetInfo& info) { UpdatePatchHand(); }) + .Options(CheckboxOptions().Tooltip( + "Fixes Adult Link having a backwards Left hand when holding the Megaton Hammer.")); + AddWidget(path, "Fix Vanishing Paths", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix")) + .Callback([](WidgetInfo& info) { + if (gPlayState != NULL) { + UpdateDirtPathFixState(gPlayState->sceneNum); + } + }) + .Options( + ComboboxOptions() + .ComboMap(zFightingOptions) + .DefaultIndex(ZFIGHT_FIX_DISABLED) + .Tooltip("Disabled: Paths vanish more the higher the resolution (Z-fighting is based on resolution)\n" + "Consistent: Certain paths vanish the same way in all resolutions\n" + "No Vanish: Paths do not vanish, Link seems to sink in to some paths\n" + "This might affect other decal effects\n")); + + AddWidget(path, "Audio Fixes", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fix Missing Jingle after 5 Silver Rupees", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SilverRupeeJingleExtend")) + .Options(CheckboxOptions().Tooltip( + "Adds 5 higher pitches for the Silver Rupee Jingle for the rooms with more than 5 Silver Rupees. " + "Only relevant for playthroughs involving Master Quest Dungeons.")); + + AddWidget(path, "Desync Fixes", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fix Darunia Dancing too Fast", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FixDaruniaDanceSpeed")) + .Options(CheckboxOptions().Tooltip( + "Fixes Darunia's dancing speed so he dances to the beat of Saria's Song, like in the Original Game.")); + AddWidget(path, "Fix Credits Timing", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CreditsFix")) + .Options(CheckboxOptions().Tooltip( + "Extend certain credits scenes so the music lines up properly with the visuals.")); + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Graphical Restorations", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Red Ganon Blood", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RedGanonBlood")) + .Options( + CheckboxOptions().Tooltip("Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.")); + AddWidget(path, "Restore Old Gold Skulltula Cutscene", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("GSCutscene")) + .Options(CheckboxOptions().Tooltip( + "Restore pre-release behavior where defeating a Gold Skulltula will play a cutscene showing it die.")); + AddWidget(path, "Pulsate Boss Icon", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("Pulsate Boss Icon")) + .Options(CheckboxOptions().Tooltip( + "Restores an unfinished feature to pulsate the boss room icon when you are in the boss room.")); + + AddWidget(path, "Glitch Restorations", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fish while Hovering", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("HoverFishing")) + .Options(CheckboxOptions().Tooltip( + "Restore a bug from NSTC 1.0 that allows casting the Fishing Rod while using the Hover Boots.")); + AddWidget(path, "N64 Weird Frames", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("N64WeirdFrames")) + .Options(CheckboxOptions().Tooltip("Restores N64 Weird Frames allwing weirdshots to behave the same as N64.")); + AddWidget(path, "Bombchus Out of Bounds", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BombchusOOB")) + .Options(CheckboxOptions().Tooltip("Allows Bombchus to explode out of bounds. Similar to Gamecube and Wii VC")); + AddWidget(path, "Quick Putaway", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("QuickPutaway")) + .Options(CheckboxOptions().Tooltip( + "Restore a bug from NTSC 1.0 that allows putting away an item without an animation and performing " + "Putaway Ocarina Items.")); + AddWidget(path, "Quick Bongo Kill", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("QuickBongoKill")) + .Options(CheckboxOptions().Tooltip( + "Restore a bug from NTSC 1.0 that allows bypassing Bongo Bongo's intro cutscene to quickly kill him.")); + AddWidget(path, "Original RBA Values", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RestoreRBAValues")) + .Options(CheckboxOptions().Tooltip("Restores the original outcomes when performing Reverse Bottle Adventure.")); + AddWidget(path, "Early Eyeball Frog", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EarlyEyeballFrog")) + .Options(CheckboxOptions().Tooltip( + "Restores a bug from NTSC 1.0/1.1 that allows you to obtain the eyeball frog from King Zora " + "instead of the Zora Tunic by Holding Shield.")); + + AddWidget(path, "Misc Restorations", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Fix L&Z Page Switch in Pause Menu", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NGCKaleidoSwitcher")) + .Options(CheckboxOptions().Tooltip( + "Makes L and R switch pages like on the Gamecube. Z opens the Debug Menu instead.")); + + // Difficulty Options + path.sidebarName = "Difficulty"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Health", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Permanent Heart Loss", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("PermanentHeartLoss")) + .Callback([](WidgetInfo& info) { + UpdatePermanentHeartLossState(); + }) + .Options(CheckboxOptions().Tooltip( + "When you lose 4 quarters of a heart you will permanently lose that Heart Container.\n\n" + "Disabling this after the fact will restore your Heart Containers." + )); + AddWidget(path, "Damage Multiplier", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("DamageMult")) + .Options(ComboboxOptions() + .ComboMap(allPowers) + .DefaultIndex(0) + .Tooltip( + "Modifies all sources of damage not affected by other sliders\n" + "2x: Can survive all common attacks from the start of the game\n" + "4x: Dies in 1 hit to any substantial attack from the start of the game\n" + "8x: Can only survive trivial damage from the start of the game\n" + "16x: Can survive all common attacks with max health without double defense\n" + "32x: Can survive all common attacks with max health and double defense\n" + "64x: Can survive trivial damage with max health without double defense\n" + "128x: Can survive trivial damage with max health and double defense\n" + "256x: Cannot survive damage" + ) + ); + AddWidget(path, "Fall Damage Multiplier", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("FallDamageMult")) + .Options(ComboboxOptions() + .ComboMap(subPowers) + .Tooltip( + "Modifies all fall damage\n" + "2x: Can survive all fall damage from the start of the game\n" + "4x: Can only survive short fall damage from the start of the game\n" + "8x: Cannot survive any fall damage from the start of the game\n" + "16x: Can survive all fall damage with max health without double defense\n" + "32x: Can survive all fall damage with max health and double defense\n" + "64x: Can survive short fall damage with double defense\n" + "128x: Cannot survive fall damage" + ) + ); + AddWidget(path, "Void Damage Multiplier", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("VoidDamageMult")) + .Options(ComboboxOptions() + .ComboMap(subSubPowers) + .DefaultIndex(0) + .Tooltip( + "Modifies damage taken after falling into a void\n" + "2x: Can survive void damage from the start of the game\n" + "4x: Cannot survive void damage from the start of the game\n" + "8x: Can survive void damage twice with max health without double defense\n" + "16x: Can survive void damage with max health without double defense\n" + "32x: Can survive void damage with max health and double defense\n" + "64x: Cannot survive void damage" + ) + ); + AddWidget(path, "Bonk Damage Multiplier", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("BonkDamageMult")) + .Options(ComboboxOptions() + .ComboMap(bonkDamageValues) + .DefaultIndex(BONK_DAMAGE_NONE) + .Tooltip("Modifies Damage taken after Bonking.") + ); + AddWidget(path, "Spawn with Full Health", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FullHealthSpawn")) + .Options(CheckboxOptions().Tooltip( + "Respawn with Full Health instead of 3 hearts." + )); + AddWidget(path, "No Heart Drops", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NoHeartDrops")) + .Options(CheckboxOptions().Tooltip( + "Disables Heart Drops, but not Heart Placements, like from a Deku Scrub running off.\n" + "This simulates Hero Mode from other games in the series." + )); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Drops", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "No Random Drops", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("NoRandomDrops")) + .Options(CheckboxOptions().Tooltip( + "Disables Random Drops, except from the Goron Pot, Dampe, and Bosses." + )); + AddWidget(path, "Enable Bombchu Drops", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EnableBombchuDrops")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + IS_RANDO && GameInteractor::IsSaveLoaded(true) && + OTRGlobals::Instance->gRandoContext->GetOption(RSK_ENABLE_BOMBCHU_DROPS).Is(RO_GENERIC_ON); + info.options->disabledTooltip = "This setting is forcefully enabled because a randomized savefile with " + "\"Enable Bombchu Drops\" is loaded."; + }) + .Options(CheckboxOptions().Tooltip( + "Bombchus will sometimes drop in place of Bombs." + )); + AddWidget(path, "Trees Drop Sticks", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TreesDropSticks")) + .Options(CheckboxOptions().Tooltip( + "Bonking into Trees will have a chance to drop up to 3 Sticks. Must have obtained sticks previously." + )); + + AddWidget(path, "Miscellaneous", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Delete File on Death", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DeleteFileOnDeath")) + .Options(CheckboxOptions().Tooltip( + "Dying will delete your file.\n\n" + ICON_FA_EXCLAMATION_TRIANGLE " WARNING " ICON_FA_EXCLAMATION_TRIANGLE + "\nTHIS IS NOT REVERSABLE\nUSE AT YOUR OWN RISK!" + )); + AddWidget(path, "Always Win Goron Pot", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("GoronPot")) + .Options(CheckboxOptions().Tooltip( + "Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot." + )); + AddWidget(path, "Always Win Dampe Digging Game", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DampeWin")) + .Options(CheckboxOptions().Tooltip( + "Always win the Heart Piece/Purple Rupee on the first dig in Dampe's Grave Digging game. " + "In a Randomizer file, this is always enabled." + )); + AddWidget(path, "All Dogs are Richard", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("AllDogsRichard")) + .Options(CheckboxOptions().Tooltip( + "All dogs can be traded in and will count as Richard." + )); + AddWidget(path, "Cuccos Stay Put Multiplier: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("CuccoStayDurationMult")) + .Options(IntSliderOptions() + .Min(1) + .Max(5) + .DefaultValue(1) + .Format("%dx") + .Tooltip( + "Cuccos will stay in place longer after putting them down, by a multiple of the value of the slider." + ) + ); + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Enemies", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Hyper Bosses", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("HyperBosses")) + .Callback([](WidgetInfo& info) { UpdateHyperBossesState(); }) + .Options(CheckboxOptions().Tooltip("All Major Bosses move and act twice as fast.")); + AddWidget(path, "Hyper Enemies", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("HyperEnemies")) + .Callback([](WidgetInfo& info) { UpdateHyperEnemiesState(); }) + .Options(CheckboxOptions().Tooltip("All Regular Enemies and Mini-Bosses move and act twice as fast.")); + AddWidget(path, "Enable Visual Guard Vision", WIDGET_CVAR_CHECKBOX).CVar(CVAR_ENHANCEMENT("GuardVision")); + AddWidget(path, "Leever Spawn Rate: %d seconds", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("LeeverSpawnRate")) + .Options(IntSliderOptions() + .Min(0) + .Max(10) + .DefaultValue(0) + .Format("%d seconds") + .Tooltip("The time between groups of Leevers spawning.")); + + // Minigames + path.sidebarName = "Minigames"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Shooting Gallery", WIDGET_SEPARATOR_TEXT); + auto shootingGalleryDisabledFunc = [](WidgetInfo& info) { + info.options->disabled = !CVarGetInteger(CVAR_ENHANCEMENT("CustomizeShootingGallery"), 0); + info.options->disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off."; + }; + AddWidget(path, "Customize Behavior##Shooting", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CustomizeShootingGallery")) + .Options(CheckboxOptions().Tooltip("Turn on/off changes to the shooting gallery behavior")); + AddWidget(path, "Instant Win", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InstantShootingGalleryWin")) + .PreFunc(shootingGalleryDisabledFunc) + .Options(CheckboxOptions().Tooltip("Skips the Shooting Gallery minigame")); + AddWidget(path, "No Rupee Randomization", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ConstantAdultGallery")) + .PreFunc(shootingGalleryDisabledFunc) + .Options(CheckboxOptions().Tooltip( + "Forces the rupee order to not be randomized as adult, making it the same as child.")); + AddWidget(path, "Child Starting Ammunition: %d seeds", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ShootingGalleryAmmoChild")) + .PreFunc(shootingGalleryDisabledFunc) + .Options(IntSliderOptions() + .Min(10) + .Max(30) + .DefaultValue(15) + .Format("%d seeds") + .Tooltip("The ammunition at the start of the Shooting Gallery minigame as Child.")); + AddWidget(path, "Adult Starting Ammunition: %d arrows", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ShootingGalleryAmmoAdult")) + .PreFunc(shootingGalleryDisabledFunc) + .Options(IntSliderOptions() + .Min(10) + .Max(30) + .DefaultValue(15) + .Format("%d arrows") + .Tooltip("The ammunition at the start of the Shooting Gallery minigame as Adult.")); + + AddWidget(path, "Bombchu Bowling", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Customize Behavior##Bowling", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CustomizeBombchuBowling")) + .Options(CheckboxOptions().Tooltip("Turn on/off changes to the Bombchu Bowling behavior.")); + auto bombchuBowlingDisabledFunc = [](WidgetInfo& info) { + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("CustomizeBombchuBowling"), 0) == 0; + info.options->disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off"; + }; + AddWidget(path, "Remove Small Cucco", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BombchuBowlingNoSmallCucco")) + .PreFunc(bombchuBowlingDisabledFunc) + .Options(CheckboxOptions().Tooltip("Prevents the small Cucco from appearing in the Bombchu Bowling minigame.")); + AddWidget(path, "Remove Big Cucco", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("BombchuBowlingNoBigCucco")) + .PreFunc(bombchuBowlingDisabledFunc) + .Options(CheckboxOptions().Tooltip("Prevents the big Cucco from appearing in the Bombchu Bowling minigame.")); + AddWidget(path, "Bombchu Count: %d bombchus", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("BombchuBowlingAmmo")) + .PreFunc(bombchuBowlingDisabledFunc) + .Options(IntSliderOptions() + .Min(3) + .Max(20) + .DefaultValue(10) + .Format("%d bombchus") + .Tooltip("The number of Bombchus available at the start of the Bombchu Bowling minigame.")); + AddWidget(path, "Frogs' Ocarina Game", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Customize Behavior##Frogs", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CustomizeFrogsOcarinaGame")) + .Options(CheckboxOptions().Tooltip("Turn on/off changes to the Frogs' Ocarina Game behavior.")); + auto frogsOcarinaGameDisabledFunc = [](WidgetInfo& info) { + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFrogsOcarinaGame"), 0) == 0; + info.options->disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off."; + }; + AddWidget(path, "Instant Win##Frogs", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InstantFrogsGameWin")) + .PreFunc(frogsOcarinaGameDisabledFunc) + .Options(CheckboxOptions().Tooltip("Skips the Frogs' Ocarina Game.")); + AddWidget(path, "Unlimited Playback Time##Frogs", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FrogsUnlimitedFailTime")) + .PreFunc(frogsOcarinaGameDisabledFunc) + .Options(CheckboxOptions().Tooltip("Removes the timer to play back the song.")); + AddWidget(path, "Modify Note Timer: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("FrogsModifyFailTime")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = !CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFrogsOcarinaGame"), 0) || + CVarGetInteger(CVAR_ENHANCEMENT("FrogsUnlimitedFailTime"), 0); + info.options->disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off or " + "\"Unlimited Playback Time\" is on"; + }) + .Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx").Tooltip( + "Adjusts the time allowed for playback before failing.")); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Lost Woods Ocarina Game", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Customize Behavior##LostWoods", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CustomizeOcarinaGame")) + .Options(CheckboxOptions().Tooltip("Turn on/off changes to the Lost Woods Ocarina Game behavior.")); + auto ocarinaMemoryGameDisabledFunc = [](WidgetInfo& info) { + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("CustomizeOcarinaGame"), 0) == 0; + info.options->disabledTooltip = "This options is disabled because \"Customize Behavior\" is turned off."; + }; + AddWidget(path, "Instant Win##LostWoods", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InstantOcarinaGameWin")) + .PreFunc(ocarinaMemoryGameDisabledFunc) + .Options(CheckboxOptions().Tooltip("Skips the Lost Woods Ocarina Memory Game.")); + AddWidget(path, "Note Play Speed: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("OcarinaGame.NoteSpeed")) + .PreFunc(ocarinaMemoryGameDisabledFunc) + .Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx").Tooltip( + "Adjust the speed that the Skull Kids play the notes.")); + AddWidget(path, "Unlimited Playback Time##LostWoods", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("OcarinaUnlimitedFailTime")) + .PreFunc(ocarinaMemoryGameDisabledFunc) + .Options(CheckboxOptions().Tooltip("Removes the timer to play back the song.")); + AddWidget(path, "Number of Starting Notes: %d notes", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("OcarinaGame.StartingNotes")) + .PreFunc(ocarinaMemoryGameDisabledFunc) + .Options(IntSliderOptions() + .Min(1) + .Max(8) + .DefaultValue(3) + .Format("%d notes") + .Tooltip("Adjust the number of notes the Skull Kids play to start the first round.")); + int roundMin = CVarGetInteger(CVAR_ENHANCEMENT("OcarinaGame.StartingNotes"), 3); + AddWidget(path, "Round One Notes: %d notes", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("OcarinaGame.RoundOneNotes")) + .PreFunc(ocarinaMemoryGameDisabledFunc) + .Options(IntSliderOptions() + .Min(roundMin) + .Max(8) + .DefaultValue(5) + .Format("%d notes") + .Tooltip("Adjust the number of notes you need to play to end the first round.")); + AddWidget(path, "Round Two Notes: %d notes", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("OcarinaGame.RoundTwoNotes")) + .PreFunc(ocarinaMemoryGameDisabledFunc) + .Options(IntSliderOptions() + .Min(roundMin) + .Max(8) + .DefaultValue(6) + .Format("%d notes") + .Tooltip("Adjust the number of notes you need to play to end the second round.")); + AddWidget(path, "Round Three Notes: %d notes", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("OcarinaGame.RoundThreeNotes")) + .PreFunc(ocarinaMemoryGameDisabledFunc) + .Options(IntSliderOptions() + .Min(roundMin) + .Max(8) + .DefaultValue(8) + .Format("%d notes") + .Tooltip("Adjust the number of notes you need to play to end the third round.")); + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Fishing", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Customize Behavior##Fishing", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("CustomizeFishing")) + .Options(CheckboxOptions().Tooltip("Turn on/off changes to the Fishing behavior")); + auto fishingDisabledFunc = [](WidgetInfo& info) { + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0) == 0; + info.options->disabledTooltip = "This option is disabled because \"Customize Behavior\" is turned off."; + }; + AddWidget(path, "Instant Fishing", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InstantFishing")) + .PreFunc(fishingDisabledFunc) + .Options(CheckboxOptions().Tooltip("All fish will be caught instantly.")); + AddWidget(path, "Guarantee Bite", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("GuaranteeFishingBite")) + .PreFunc(fishingDisabledFunc) + .Options(CheckboxOptions().Tooltip("When a line is stable, guarantee bite. Otherwise use Default logic.")); + AddWidget(path, "Fish never Escape", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FishNeverEscape")) + .PreFunc(fishingDisabledFunc) + .Options(CheckboxOptions().Tooltip("Once a hook as been set, Fish will never let go while being reeled in.")); + AddWidget(path, "Loaches always Appear", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LoachesAlwaysAppear")) + .PreFunc(fishingDisabledFunc) + .Options( + CheckboxOptions().Tooltip("Loaches will always appear in the fishing pond instead of every four visits.")); + AddWidget(path, "Skip Keep Confirmation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("SkipKeepConfirmation")) + .PreFunc(fishingDisabledFunc) + .Options( + CheckboxOptions().Tooltip("The Pond Owner will not ask to confirm if you want to keep a smaller Fish.")); + AddWidget(path, "Child Minimum Weight: %d lbs.", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("MinimumFishWeightChild")) + .PreFunc(fishingDisabledFunc) + .Options(IntSliderOptions().Min(3).Max(10).DefaultValue(10).Format("%d lbs.").Tooltip( + "The minimum weight for the unique Fishing Reward as a Child.")); + AddWidget(path, "Adult Minimum Weight: %d lbs.", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("MinimumFishWeightAdult")) + .PreFunc(fishingDisabledFunc) + .Options(IntSliderOptions().Min(6).Max(13).DefaultValue(13).Format("%d lbs.").Tooltip( + "The minimum weight for the unique fishing reward as an Adult.")); + AddWidget(path, "All Fish are Hyrule Loaches", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("AllHyruleLoaches")) + .PreFunc(fishingDisabledFunc) + .Options(IntSliderOptions().Tooltip("Every fish in the Fishing Pond will always be a Hyrule Loach.\n\n" + "NOTE: This requires reloading the area.")); + + // Extra Modes + path.sidebarName = "Extra Modes"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Mirrored World", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("MirroredWorldMode")) + .Callback([](WidgetInfo& info) { + if (gPlayState != NULL) { + UpdateMirrorModeState(gPlayState->sceneNum); + } + }) + .Options(ComboboxOptions() + .DefaultIndex(MIRRORED_WORLD_OFF) + .ComboMap(mirroredWorldModes) + .Tooltip( + "Mirrors the world horizontally\n\n" + " - Always: Always mirror the world\n" + " - Random: Randomly decide to mirror the world on each scene change\n" + " - Random (Seeded): Scenes are mirrored based on the current randomizer seed/file\n" + " - Dungeons: Mirror the world in Dungeons\n" + " - Dungeons (Vanilla): Mirror the world in vanilla Dungeons\n" + " - Dungeons (MQ): Mirror the world in MQ Dungeons\n" + " - Dungeons Random: Randomly decide to mirror the world in Dungeons\n" + " - Dungeons Random (Seeded): Dungeons are mirrored based on the current randomizer seed/file\n" + ) + ); + AddWidget(path, "Ivan the Fairy (Coop Mode)", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("IvanCoopModeEnabled")) + .Options(CheckboxOptions().Tooltip( + "Enables Ivan the Fairy upon the next map change. Player 2 can control Ivan and press the C-Buttons to " + "use items and mess with Player 1!" + )); + AddWidget(path, "Dogs Follow You Everywhere", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DogFollowsEverywhere")) + .Options(CheckboxOptions().Tooltip("Allows dogs to follow you anywhere you go, even if you leave the Market.")); + AddWidget(path, "Rupee Dash Mode", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RupeeDash")) + .Options(CheckboxOptions().Tooltip( + "Rupees reduce over time, Link suffers damage when the count hits 0." + )); + AddWidget(path, "Rupee Dash Interval %d seconds", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("RupeeDashInterval")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("RupeeDash"), 0) == 0; + }) + .Options(IntSliderOptions() + .Min(1) + .Max(10) + .DefaultValue(5) + .Format("%d seconds") + .Tooltip( + "Interval between Rupee reduction in Rupee Dash Mode." + ) + ); + AddWidget(path, "Shadow Tag Mode", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ShadowTag")) + .Options(CheckboxOptions().Tooltip( + "A Wallmaster follows Link everywhere, don't get caught!" + )); + AddWidget(path, "Hurt Container Mode", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("HurtContainer")) + .Callback([](WidgetInfo& info) { + UpdateHurtContainerModeState(CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)); + }) + .Options(CheckboxOptions().Tooltip( + "Changes Heart Piece and Heart Container functionality.\n\n" + " - Each Heart Container or full Heart Piece reduces Link's Hearts by 1.\n" + " - Can be enabled retroactively after a File has already started." + )); + AddWidget(path, "Additional Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Enabled")) + .Options(CheckboxOptions().Tooltip( + "Enables additional Trap variants." + )); + AddWidget(path, "Trap Options", WIDGET_SEPARATOR_TEXT) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Tier 1 Traps:", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Freeze Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Ice")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Burn Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Burn")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Shock Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Shock")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Tier 2 Traps:", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Knockback Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Knockback")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Speed Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Speed")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Bomb Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Bomb")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Tier 3 Traps:", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Void Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Void")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Ammo Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Ammo")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Death Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Kill")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + AddWidget(path, "Teleport Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Teleport")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; + }); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Enemy Randomizer", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("RandomizedEnemies")) + .Callback([](WidgetInfo& info) { + GetSelectedEnemies(); + }) + .Options(ComboboxOptions() + .DefaultIndex(ENEMY_RANDOMIZER_OFF) + .ComboMap(enemyRandomizerModes) + .Tooltip( + "Replaces fixed enemies throughout the game with a random enemy. Bosses, mini-bosses and a few specific regular enemies are excluded.\n" + "Enemies that need more than Deku Nuts + either Deku Sticks or a sword to kill are excluded from spawning in \"clear enemy\" rooms.\n\n" + "- Random: Enemies are randomized every time you load a room\n" + "- Random (Seeded): Enemies are randomized based on the current randomizer seed/file\n" + ) + ); + AddWidget(path, "Randomized Enemy Sizes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RandomizedEnemySizes")) + .Options(CheckboxOptions().Tooltip("Enemies and Bosses spawn with random sizes.")); + AddWidget(path, "Scale Health with Size", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EnemySizeScalesHealth")) + .PreFunc( + [](WidgetInfo& info) { info.options->disabled = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemySizes"), 0); }) + .Options(CheckboxOptions().Tooltip( + "Scales normal enemies Health with their randomized size. *This will NOT affect bosses*")); + AddWidget(path, "Enemy List", WIDGET_SEPARATOR_TEXT) + .PreFunc([](WidgetInfo& info) { + info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); + }); + AddWidget(path, "Select all Enemies", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RandomizedEnemyList.All")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); + }); + AddWidget(path, "Enemy List", WIDGET_SEPARATOR).PreFunc([](WidgetInfo& info) { + info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); + }); + for (int i = 0; i < RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE; i++) { + AddWidget(path, enemyNameList[i], WIDGET_CVAR_CHECKBOX) + .CVar(enemyCVarList[i]) + .Options(CheckboxOptions().DefaultValue(true)) + .PreFunc([](WidgetInfo& info) { + info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0); + info.options->disabledTooltip = "These options are disabled because \"Select All Enemies\" is enabled."; + }) + .Callback([](WidgetInfo& info) { + GetSelectedEnemies(); + }); + } + + // Cheats + path.sidebarName = "Cheats"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Infinite...", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Money", WIDGET_CVAR_CHECKBOX).CVar(CVAR_CHEAT("InfiniteMoney")); + AddWidget(path, "Health", WIDGET_CVAR_CHECKBOX).CVar(CVAR_CHEAT("InfiniteHealth")); + AddWidget(path, "Ammo", WIDGET_CVAR_CHECKBOX).CVar(CVAR_CHEAT("InfiniteAmmo")); + AddWidget(path, "Magic", WIDGET_CVAR_CHECKBOX).CVar(CVAR_CHEAT("InfiniteMagic")); + AddWidget(path, "Nayru's Love", WIDGET_CVAR_CHECKBOX).CVar(CVAR_CHEAT("InfiniteNayru")); + AddWidget(path, "Epona Boost", WIDGET_CVAR_CHECKBOX).CVar(CVAR_CHEAT("InfiniteEponaBoost")); + + AddWidget(path, "Items", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Timeless Equipment", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("TimelessEquipment")) + .Options(CheckboxOptions().Tooltip("Allows any item to be equipped, regardless of age.\n" + "Also allows Child to use Adult strength upgrades.")); + AddWidget(path, "Unrestricted Items", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("NoRestrictItems")) + .Options(CheckboxOptions().Tooltip("Allows you to use any item at any location")); + AddWidget(path, "Super Tunic", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("SuperTunic")) + .Options(CheckboxOptions().Tooltip("Makes every tunic have the effects of every other tunic.")); + AddWidget(path, "Fireproof Deku Shield", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("FireproofDekuShield")) + .Options(CheckboxOptions().Tooltip("Prevents the Deku Shield from burning on contact with fire.")); + AddWidget(path, "Shield with Two-Handed Weapons", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("ShieldTwoHanded")) + .Options(CheckboxOptions().Tooltip( + "This allows you to put up for shield with any two-handed weapon in hand except for Deku Sticks.")); + AddWidget(path, "Deku Sticks:", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_CHEAT("DekuStick")) + .Options(ComboboxOptions().ComboMap(dekuStickCheat).DefaultIndex(DEKU_STICK_NORMAL)); + AddWidget(path, "Bomb Timer Multiplier: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_CHEAT("BombTimerMultiplier")) + .Options(FloatSliderOptions().Format("%.2f").Min(0.1f).Max(5.0f).DefaultValue(1.0f)); + AddWidget(path, "Hookshot Everything", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("HookshotEverything")) + .Options(CheckboxOptions().Tooltip("Makes every surface in the game hookshot-able.")); + AddWidget(path, "Hookshot Reach Multiplier: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_CHEAT("HookshotReachMultiplier")) + .Options(FloatSliderOptions().Format("%.2f").Min(1.0f).Max(5.0f)); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Misc", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "No Clip", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("NoClip")) + .Options(CheckboxOptions().Tooltip("Allows you to walk through walls.")); + AddWidget(path, "Climb Everything", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("ClimbEverything")) + .Options(CheckboxOptions().Tooltip("Makes every surface in the game climbable.")); + AddWidget(path, "Moon Jump on L", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("MoonJumpOnL")) + .Options(CheckboxOptions().Tooltip("Holding L makes you float into the air.")); + AddWidget(path, "No ReDead/Gibdo Freeze", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("NoRedeadFreeze")) + .Options( + CheckboxOptions().Tooltip("Prevents ReDeads and Gibdos from being able to freeze you with their scream.")); + AddWidget(path, "Keese/Guay don't Target You", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("NoKeeseGuayTarget")) + .Options(CheckboxOptions().Tooltip( + "Keese and Guay no longer target you and simply ignore you as if you were wearing the " + "Skull Mask.")); + + AddWidget(path, "Glitch Aids", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Easy Frame Advancing with Pause", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("EasyFrameAdvance")) + .Options(CheckboxOptions().Tooltip( + "Continue holding START button when unpausing to only advance a single frame and then re-pause.")); + AddWidget(path, "Easy ISG", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("EasyISG")) + .Options(CheckboxOptions().Tooltip("Passive Infinite Sword Glitch\n" + "It makes your sword's swing effect and hitbox stay active indefinitely.")); + AddWidget(path, "Easy QPA", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("EasyQPA")) + .Options(CheckboxOptions().Tooltip("Gives you the glitched damage value of the quick put away glitch.")); + AddWidget(path, "Clear Cutscene Pointer", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { GameInteractor::RawAction::ClearCutscenePointer(); }) + .Options(ButtonOptions() + .Tooltip("Clears the cutscene pointer to a value safe for wrong warps.") + .Size(UIWidgets::Sizes::Inline)); + + AddWidget(path, "Despawn Timers", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Drops Don't Despawn", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("DropsDontDie")) + .Options( + CheckboxOptions().Tooltip("Drops from enemies, grass, etc. don't disappear after a set amount of time.")); + AddWidget(path, "Fish don't Despawn", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("NoFishDespawn")) + .Options(CheckboxOptions().Tooltip("Prevents fish from automatically despawning after a while when dropped.")); + AddWidget(path, "Bugs don't Despawn", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("NoBugsDespawn")) + .Options(CheckboxOptions().Tooltip("Prevents bugs from automatically despawning after a while when dropped.")); + + AddWidget(path, "Time of Day", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Freeze Time", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("FreezeTime")) + .Options(CheckboxOptions().Tooltip("Freezes the time of day")); + AddWidget(path, "Time Sync", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("TimeSync")) + .Options(CheckboxOptions().Tooltip("Syncs the in-game time with the real world time.")); + + AddWidget(path, "Instant Age Change", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Change Age", WIDGET_BUTTON) + .Options(ButtonOptions().Tooltip("Switches Link's age and reloads the area.").Size(UIWidgets::Sizes::Inline)) + .Callback([](WidgetInfo& info) { SwitchAge(); }); + + path.column = SECTION_COLUMN_3; + AddWidget(path, "Save States", WIDGET_SEPARATOR_TEXT); + AddWidget(path, ICON_FA_EXCLAMATION_TRIANGLE " WARNING!!!! " ICON_FA_EXCLAMATION_TRIANGLE, WIDGET_TEXT) + .Options(TextOptions().Color(Colors::Orange)); + AddWidget(path, + "These are NOT like emulator states. They do not save your game progress " + "and they WILL break across transitions and load zones (like doors). " + "Support for related issues will not be provided.", + WIDGET_TEXT); + AddWidget(path, "I promise I have read the warning", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("SaveStatePromise")) + .Callback([](WidgetInfo& info) { + CVarSetInteger(CVAR_CHEAT("SaveStatesEnabled"), 0); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }); + AddWidget(path, "I understand, enable save states", WIDGET_CVAR_CHECKBOX) + .PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_CHEAT("SaveStatePromise"), 0) == 0; }) + .CVar(CVAR_CHEAT("SaveStatesEnabled")) + .Options(CheckboxOptions().Tooltip("F5 to save, F6 to change slots, F7 to load")); + + AddWidget(path, "Beta Quest", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Enable Beta Quest", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_CHEAT("EnableBetaQuest")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = !isBetaQuestEnabled; + }) + .Callback([](WidgetInfo& info) { + if (CVarGetInteger(CVAR_CHEAT("EnableBetaQuest"), 0) == 0) { + CVarClear(CVAR_CHEAT("BetaQuestWorld")); + } else { + CVarSetInteger(CVAR_CHEAT("BetaQuestWorld"), 0); + } + std::reinterpret_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset"); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }) + .Options(CheckboxOptions().Tooltip( + "Turns on OoT Beta Quest. *WARNING* This will reset your game." + )); + AddWidget(path, "Beta Quest World: %d", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_CHEAT("BetaQuestWorld")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = info.isHidden = CVarGetInteger(CVAR_CHEAT("EnableBetaQuest"), 0) == 0; + }) + .Callback([](WidgetInfo& info) { + std::reinterpret_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset"); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }) + .Options(IntSliderOptions() + .DefaultValue(0) + .Min(0) + .Max(8) + .Tooltip( + "Set the Beta Quest world to explore. *WARNING* Changing this will reset your game.\n" + "Ctrl+Click to type in a value." + )); + + // Cosmetics Editor + path.sidebarName = "Cosmetics Editor"; + AddSidebarEntry("Enhancements", path.sidebarName, 1); + path.column = SECTION_COLUMN_1; + AddWidget(path, "Popout Cosmetics Editor Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("CosmeticsEditor")) + .WindowName("Cosmetics Editor") + .Options(WindowButtonOptions().Tooltip("Enables the separate Cosmetics Editor Window.")); + + + // Audio Editor + path.sidebarName = "Audio Editor"; + AddSidebarEntry("Enhancements", path.sidebarName, 1); + AddWidget(path, "Popout Audio Editor Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("AudioEditor")) + .WindowName("Audio Editor") + .Options(WindowButtonOptions().Tooltip("Enables the separate Audio Editor Window.")); + + + // Gameplay Stats + path.sidebarName = "Gameplay Stats"; + AddSidebarEntry("Enhancements", path.sidebarName, 2); + AddWidget(path, "Popout Gameplay Stats Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("GameplayStats")) + .WindowName("Gameplay Stats") + .Options(WindowButtonOptions().Tooltip("Enables the separate Gameplay Stats Window.")); + + + // Time Splits + path.sidebarName = "Time Splits"; + AddSidebarEntry("Enhancements", path.sidebarName, 1); + AddWidget(path, "Popout Time Splits Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("TimeSplits")) + .WindowName("Time Splits") + .Options(WindowButtonOptions().Tooltip("Enables the separate Time Splits Window.")); + + + // Timers + path.sidebarName = "Timers"; + AddSidebarEntry("Enhancements", path.sidebarName, 3); + AddWidget(path, "Toggle Timers Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("TimeDisplayEnabled")) + .WindowName("Additional Timers") + .Options(WindowButtonOptions().Tooltip("Enables the separate Additional Timers Window.")); + AddWidget(path, "Font Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_ENHANCEMENT("TimeDisplay.FontScale")) + .Callback([](WidgetInfo& info) { + TimeDisplayInitSettings(); + }) + .Options(FloatSliderOptions() + .Min(1.0f) + .Max(5.0f) + .DefaultValue(1.0f) + .Format("%.2fx") + ); + AddWidget(path, "Hide Background", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG")) + .Callback([](WidgetInfo& info) { + TimeDisplayInitSettings(); + }); + for (auto& timer : timeDisplayList) { + AddWidget(path, timer.timeLabel, WIDGET_CVAR_CHECKBOX) + .CVar(timer.timeEnable) + .Callback([](WidgetInfo& info) { + TimeDisplayUpdateDisplayOptions(); + }); + } +} + +} // namespace SohGui diff --git a/soh/soh/SohGui/SohMenuNetwork.cpp b/soh/soh/SohGui/SohMenuNetwork.cpp new file mode 100644 index 000000000..135f7e2be --- /dev/null +++ b/soh/soh/SohGui/SohMenuNetwork.cpp @@ -0,0 +1,148 @@ +#ifdef ENABLE_REMOTE_CONTROL +#include "SohMenu.h" +#include +#include +#include "SohGui.hpp" +#include +#include + +namespace SohGui { + +extern std::shared_ptr mSohMenu; +using namespace UIWidgets; + +void SohMenu::AddMenuNetwork() { + // Add Network Menu + AddMenuEntry("Network", CVAR_SETTING("Menu.NetworkSidebarSection")); + + // Sail + WidgetPath path = { "Network", "Sail", SECTION_COLUMN_1 }; + AddSidebarEntry("Network", path.sidebarName, 3); + + AddWidget(path, "Sail is a networking protocol designed to facilitate remote " + "control of the Ship of Harkinian client. It is intended to " + "be utilized alongside a Sail server, for which we provide a " + "few straightforward implementations on our GitHub. The current " + "implementations available allow integration with Twitch chat " + "and SAMMI Bot, feel free to contribute your own!\n" + "\n" + "Click this button to copy the link to the Sail Github " + "page to your clipboard.", WIDGET_TEXT); + AddWidget(path, ICON_FA_CLIPBOARD "##Sail", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + ImGui::SetClipboardText("https://github.com/HarbourMasters/sail"); + Notification::Emit({ + .message = "Copied to clipboard", + }); + }) + .Options(ButtonOptions() + .Tooltip("https://github.com/HarbourMasters/sail") + ); + AddWidget(path, "Host & Port", WIDGET_CUSTOM) + .CustomFunction([](WidgetInfo& info) { + ImGui::BeginDisabled(Sail::Instance->isEnabled); + ImGui::Text("%s", info.name.c_str()); + CVarInputString("##HostSail", CVAR_REMOTE_SAIL("Host"), InputOptions().Color(THEME_COLOR).PlaceholderText("127.0.0.1").DefaultValue("127.0.0.1").Size(ImVec2(ImGui::GetFontSize() * 15, 0)).LabelPosition(LabelPositions::None)); + ImGui::SameLine(); + ImGui::Text(":"); + ImGui::SameLine(); + CVarInputInt("##PortSail", CVAR_REMOTE_SAIL("Port"), InputOptions().Color(THEME_COLOR).PlaceholderText("43384").DefaultValue("43384").Size(ImVec2(ImGui::GetFontSize() * 5, 0)).LabelPosition(LabelPositions::None)); + ImGui::EndDisabled(); + }); + AddWidget(path, "Enable##Sail", WIDGET_BUTTON) + .PreFunc([](WidgetInfo& info) { + std::string host = CVarGetString(CVAR_REMOTE_SAIL("Host"), "127.0.0.1"); + uint16_t port = CVarGetInteger(CVAR_REMOTE_SAIL("Port"), 43384); + info.options->disabled = !(!SohUtils::IsStringEmpty(host) && port > 1024 && port < 65535); + if (Sail::Instance->isEnabled) { + info.name = "Disable##Sail"; + } else { + info.name = "Enable##Sail"; + } + }) + .Callback([](WidgetInfo& info) { + if (Sail::Instance->isEnabled) { + CVarClear(CVAR_REMOTE_SAIL("Enabled")); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + Sail::Instance->Disable(); + } else { + CVarSetInteger(CVAR_REMOTE_SAIL("Enabled"), 1); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + Sail::Instance->Enable(); + } + }); + AddWidget(path, "Connecting...##Sail", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { + info.isHidden = !Sail::Instance->isEnabled; + if (Sail::Instance->isConnected) { + info.name = "Connected##Sail"; + } else { + info.name = "Connecting...##Sail"; + } + }); + + path.sidebarName = "Crowd Control"; + AddSidebarEntry("Network", path.sidebarName, 3); + path.column = SECTION_COLUMN_1; + + AddWidget(path, "Crowd Control is a platform that allows viewers to interact " + "with a streamer's game in real time.\n" + "\n" + "Click the question mark to copy the link to the Crowd Control " + "website to your clipboard.", WIDGET_TEXT); + AddWidget(path, ICON_FA_CLIPBOARD "##CrowdControl", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + ImGui::SetClipboardText("https://crowdcontrol.live"); + Notification::Emit({ + .message = "Copied to clipboard", + }); + }) + .Options(ButtonOptions() + .Tooltip("https://crowdcontrol.live") + ); + AddWidget(path, "Host & Port", WIDGET_CUSTOM) + .CustomFunction([](WidgetInfo& info) { + ImGui::BeginDisabled(CrowdControl::Instance->isEnabled); + ImGui::Text("%s", info.name.c_str()); + CVarInputString("##HostCrowdControl", CVAR_REMOTE_CROWD_CONTROL("Host"), InputOptions().Color(THEME_COLOR).PlaceholderText("127.0.0.1").DefaultValue("127.0.0.1").Size(ImVec2(ImGui::GetFontSize() * 15, 0)).LabelPosition(LabelPositions::None)); + ImGui::SameLine(); + ImGui::Text(":"); + ImGui::SameLine(); + CVarInputInt("##PortCrowdControl", CVAR_REMOTE_CROWD_CONTROL("Port"), InputOptions().Color(THEME_COLOR).PlaceholderText("43384").DefaultValue("43384").Size(ImVec2(ImGui::GetFontSize() * 5, 0)).LabelPosition(LabelPositions::None)); + ImGui::EndDisabled(); + }); + AddWidget(path, "Enable##CrowdControl", WIDGET_BUTTON) + .PreFunc([](WidgetInfo& info) { + std::string host = CVarGetString(CVAR_REMOTE_CROWD_CONTROL("Host"), "127.0.0.1"); + uint16_t port = CVarGetInteger(CVAR_REMOTE_CROWD_CONTROL("Port"), 43384); + info.options->disabled = !(!SohUtils::IsStringEmpty(host) && port > 1024 && port < 65535); + if (CrowdControl::Instance->isEnabled) { + info.name = "Disable##CrowdControl"; + } else { + info.name = "Enable##CrowdControl"; + } + }) + .Callback([](WidgetInfo& info) { + if (CrowdControl::Instance->isEnabled) { + CVarClear(CVAR_REMOTE_CROWD_CONTROL("Enabled")); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + CrowdControl::Instance->Disable(); + } else { + CVarSetInteger(CVAR_REMOTE_CROWD_CONTROL("Enabled"), 1); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + CrowdControl::Instance->Enable(); + } + }); + AddWidget(path, "Connecting...##CrowdControl", WIDGET_TEXT) + .PreFunc([](WidgetInfo& info) { + info.isHidden = !CrowdControl::Instance->isEnabled; + if (CrowdControl::Instance->isConnected) { + info.name = "Connected##CrowdControl"; + } else { + info.name = "Connecting...##CrowdControl"; + } + }); +} + +} // namespace SohGui +#endif \ No newline at end of file diff --git a/soh/soh/SohGui/SohMenuRandomizer.cpp b/soh/soh/SohGui/SohMenuRandomizer.cpp new file mode 100644 index 000000000..0d3b52b23 --- /dev/null +++ b/soh/soh/SohGui/SohMenuRandomizer.cpp @@ -0,0 +1,151 @@ +#include "SohMenu.h" +#include + +namespace SohGui { + +extern std::shared_ptr mSohMenu; +using namespace UIWidgets; + +void SohMenu::AddMenuRandomizer() { + // Add Randomizer Menu + AddMenuEntry("Randomizer", CVAR_SETTING("Menu.RandomizerSidebarSection")); + + // Seed Settings + WidgetPath path = { "Randomizer", "Seed Settings", SECTION_COLUMN_1 }; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + AddWidget(path, "Popout Randomizer Settings Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("RandomizerSettings")) + .WindowName("Randomizer Settings") + .Options(WindowButtonOptions().Tooltip("Enables the separate Randomizer Settings Window.")); + + // Enhancements + path.sidebarName = "Enhancements"; + AddSidebarEntry("Randomizer", path.sidebarName, 3); + AddWidget(path, "Randomizer Enhancements", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Rando-Relevant Navi Hints", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("RandoRelevantNavi")) + .Options(CheckboxOptions().Tooltip( + "Replace Navi's overworld quest hints with rando-related gameplay hints." + ).DefaultValue(true)); + AddWidget(path, "Random Rupee Names", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("RandomizeRupeeNames")) + .Options(CheckboxOptions().Tooltip( + "When obtaining Rupees, randomize what the Rupee is called in the textbox." + ).DefaultValue(true)); + AddWidget(path, "Use Custom Key Models", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("CustomKeyModels")) + .Options(CheckboxOptions().Tooltip( + "Use Custom graphics for Dungeon Keys, Big and Small, so that they can be easily told apart." + ).DefaultValue(true)); + AddWidget(path, "Map & Compass Colors Match Dungeon", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("ColoredMapsAndCompasses")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = !( + OTRGlobals::Instance->gRandoContext->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).IsNot(RO_DUNGEON_ITEM_LOC_STARTWITH) && + OTRGlobals::Instance->gRandoContext->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).IsNot(RO_DUNGEON_ITEM_LOC_VANILLA) && + OTRGlobals::Instance->gRandoContext->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).IsNot(RO_DUNGEON_ITEM_LOC_OWN_DUNGEON) + ); + info.options->disabledTooltip = "This setting is disabled because a savefile is loaded without the map & compass\n" + "shuffle settings set to \"Any Dungeon\", \"Overworld\" or \"Anywhere\""; + }) + .Options(CheckboxOptions().Tooltip( + "Matches the color of maps & compasses to the dungeon they belong to. " + "This helps identify maps & compasses from afar and adds a little bit of flair.\n\nThis only " + "applies to seeds with maps & compasses shuffled to \"Any Dungeon\", \"Overworld\", or \"Anywhere\"." + ).DefaultValue(true)); + AddWidget(path, "Quest Item Fanfares", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("QuestItemFanfares")) + .Options(CheckboxOptions().Tooltip( + "Play unique fanfares when obtaining quest items (medallions/stones/songs). Note that these " + "fanfares can be longer than usual." + )); + AddWidget(path, "Mysterious Shuffled Items", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle")) + .Options(CheckboxOptions().Tooltip( + "Displays a \"Mystery Item\" model in place of any freestanding/GS/shop items that were shuffled, " + "and replaces item names for them and scrubs and merchants, regardless of hint settings, " + "so you never know what you're getting." + )); + AddWidget(path, "Simpler Boss Soul Models", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("SimplerBossSoulModels")) + .Options(CheckboxOptions().Tooltip( + "When shuffling boss souls, they'll appear as a simpler model instead of showing the boss' models." + "This might make boss souls more distinguishable from a distance, and can help with performance." + )); + AddWidget(path, "Skip Get Item Animations", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("TimeSavers.SkipGetItemAnimation")) + .Options(ComboboxOptions().ComboMap(skipGetItemAnimationOptions).DefaultIndex(SGIA_JUNK)); + AddWidget(path, "Item Scale: %.2f", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_RANDOMIZER_ENHANCEMENT("TimeSavers.SkipGetItemAnimationScale")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + !CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("TimeSavers.SkipGetItemAnimation"), SGIA_DISABLED); + info.options->disabledTooltip = "This slider only applies when using the \"Skip Get Item Animations\" option."; + }) + .Options(FloatSliderOptions() + .Min(5.0f) + .Max(15.0f) + .Format("%.2f") + .DefaultValue(10.0f) + .Tooltip( + "The size of the item when it is picked up" + )); + + // Plandomizer + path.sidebarName = "Plandomizer"; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + AddWidget(path, "Popout Plandomizer Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("PlandomizerEditor")) + .WindowName("Plandomizer Editor") + .Options(WindowButtonOptions().Tooltip("Enables the separate Randomizer Settings Window.")); + + // Item Tracker + path.sidebarName = "Item Tracker"; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + + AddWidget(path, "Item Tracker", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Toggle Item Tracker", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("ItemTracker")) + .WindowName("Item Tracker") + .Options(WindowButtonOptions().Tooltip("Toggles the Item Tracker.").EmbedWindow(false)); + + AddWidget(path, "Item Tracker Settings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Popout Item Tracker Settings", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("ItemTrackerSettings")) + .WindowName("Item Tracker Settings") + .Options(WindowButtonOptions().Tooltip("Enables the separate Item Tracker Settings Window.")); + + // Entrance Tracker + path.sidebarName = "Entrance Tracker"; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + + AddWidget(path, "Entrance Tracker", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Toggle Entrance Tracker", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("EntranceTracker")) + .WindowName("Entrance Tracker") + .Options(WindowButtonOptions().Tooltip("Toggles the Entrance Tracker.").EmbedWindow(false)); + + AddWidget(path, "Entrance Tracker Settings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Popout Entrance Tracker Settings", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("EntranceTrackerSettings")) + .WindowName("Entrance Tracker Settings") + .Options(WindowButtonOptions().Tooltip("Enables the separate Entrance Tracker Settings Window.")); + + // Check Tracker + path.sidebarName = "Check Tracker"; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + + AddWidget(path, "Check Tracker", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Toggle Check Tracker", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("CheckTracker")) + .WindowName("Check Tracker") + .Options(WindowButtonOptions().Tooltip("Toggles the Check Tracker.").EmbedWindow(false)); + + AddWidget(path, "Check Tracker Settings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Popout Check Tracker Settings", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("CheckTrackerSettings")) + .WindowName("Check Tracker Settings") + .Options(WindowButtonOptions().Tooltip("Enables the separate Check Tracker Settings Window.")); +} + +} // namespace SohGui diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp new file mode 100644 index 000000000..20ce9ff50 --- /dev/null +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -0,0 +1,393 @@ +#include "SohMenu.h" +#include "soh/Notification/Notification.h" +#include +#include "soh/ResourceManagerHelpers.h" +#include "UIWidgets.hpp" +#include + +extern "C" { +#include "include/z64audio.h" +#include "variables.h" +} + +namespace SohGui { + +extern std::shared_ptr mSohMenu; +using namespace UIWidgets; +static std::unordered_map languages = {{ LANGUAGE_ENG, "English" }, { LANGUAGE_GER, "German" }, { LANGUAGE_FRA, "French" }}; +static std::unordered_map imguiScaleOptions = {{ 0, "Small" }, { 1, "Normal" }, { 2, "Large" }, { 3, "X-Large" }}; + +const char* GetGameVersionString(uint32_t index) { + uint32_t gameVersion = ResourceMgr_GetGameVersion(index); + switch (gameVersion) { + case OOT_NTSC_US_10: + return "NTSC-U 1.0"; + case OOT_NTSC_US_11: + return "NTSC-U 1.1"; + case OOT_NTSC_US_12: + return "NTSC-U 1.2"; + case OOT_PAL_10: + return "PAL 1.0"; + case OOT_PAL_11: + return "PAL 1.1"; + case OOT_PAL_GC: + return "PAL GC"; + case OOT_PAL_MQ: + return "PAL MQ"; + case OOT_PAL_GC_DBG1: + case OOT_PAL_GC_DBG2: + return "PAL GC-D"; + case OOT_PAL_GC_MQ_DBG: + return "PAL MQ-D"; + case OOT_IQUE_CN: + return "IQUE CN"; + case OOT_IQUE_TW: + return "IQUE TW"; + default: + return "UNKNOWN"; + } +} + +void SohMenu::AddMenuSettings() { + // Add Settings Menu + AddMenuEntry("Settings", CVAR_SETTING("Menu.SettingsSidebarSection")); + AddSidebarEntry("Settings", "General", 3); + WidgetPath path = { "Settings", "General", SECTION_COLUMN_1 }; + + // General - Settings + AddWidget(path, "Menu Settings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Menu Theme", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_SETTING("Menu.Theme")) + .Options(ComboboxOptions() + .Tooltip("Changes the Theme of the Menu Widgets.") + .ComboMap(menuThemeOptions) + .DefaultIndex(Colors::LightBlue)); +#if not defined(__SWITCH__) and not defined(__WIIU__) + AddWidget(path, "Menu Controller Navigation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_IMGUI_CONTROLLER_NAV) + .Options(CheckboxOptions().Tooltip( + "Allows controller navigation of the port menu (Settings, Enhancements,...)\nCAUTION: " + "This will disable game inputs while the menu is visible.\n\nD-pad to move between " + "items, A to select, B to move up in scope.")); + AddWidget(path, "Menu Background Opacity", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_SETTING("Menu.BackgroundOpacity")) + .Options(FloatSliderOptions() + .DefaultValue(0.85f) + .IsPercentage() + .Tooltip("Sets the opacity of the background of the port menu.")); + + AddWidget(path, "General Settings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Cursor Always Visible", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("CursorVisibility")) + .Callback([](WidgetInfo& info) { + Ship::Context::GetInstance()->GetWindow()->SetForceCursorVisibility( + CVarGetInteger(CVAR_SETTING("CursorVisibility"), 0)); + }) + .Options(CheckboxOptions().Tooltip("Makes the cursor always visible, even in full screen.")); +#endif + AddWidget(path, "Search In Sidebar", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("Menu.SidebarSearch")) + .Callback([](WidgetInfo& info) { + if (CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0)) { + mSohMenu->InsertSidebarSearch(); + } else { + mSohMenu->RemoveSidebarSearch(); + } + }) + .Options(CheckboxOptions().Tooltip( + "Displays the Search menu as a sidebar entry in Settings instead of in the header.")); + AddWidget(path, "Search Input Autofocus", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("Menu.SearchAutofocus")) + .Options(CheckboxOptions().Tooltip( + "Search input box gets autofocus when visible. Does not affect using other widgets.")); + AddWidget(path, "Alt Assets Tab hotkey", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("Mods.AlternateAssetsHotkey")) + .Options( + CheckboxOptions().Tooltip("Allows pressing the Tab key to toggle alternate assets").DefaultValue(true)); + AddWidget(path, "Open App Files Folder", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + std::string filesPath = Ship::Context::GetInstance()->GetAppDirectoryPath(); + SDL_OpenURL(std::string("file:///" + std::filesystem::absolute(filesPath).string()).c_str()); + }) + .Options(ButtonOptions().Tooltip("Opens the folder that contains the save and mods folders, etc.")); + + AddWidget(path, "Boot", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Boot Sequence", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("BootSequence")) + .Options(ComboboxOptions() + .DefaultIndex(BOOTSEQUENCE_DEFAULT) + .LabelPosition(LabelPositions::Far) + .ComponentAlignment(ComponentAlignments::Right) + .ComboMap(bootSequenceLabels) + .Tooltip("Configure what happens when starting or resetting the game.\n\n" + "Default: LUS logo -> N64 logo\n" + "Authentic: N64 logo only\n" + "File Select: Skip to file select menu")); + + AddWidget(path, "Languages", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Translate Title Screen", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("TitleScreenTranslation")); + AddWidget(path, "Menu Language", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_SETTING("Languages")) + .Options(ComboboxOptions().LabelPosition(LabelPositions::Far).ComponentAlignment(ComponentAlignments::Right).ComboMap(languages).DefaultIndex(LANGUAGE_ENG)); + AddWidget(path, "Accessibility", WIDGET_SEPARATOR_TEXT); + #if defined(_WIN32) || defined(__APPLE__) + AddWidget(path, "Text to Speech", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("A11yTTS")) + .Options(CheckboxOptions().Tooltip("Enables text to speech for in game dialog")); + #endif + AddWidget(path, "Disable Idle Camera Re-Centering", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("A11yDisableIdleCam")) + .Options(CheckboxOptions().Tooltip("Disables the automatic re-centering of the camera when idle.")); + AddWidget(path, "EXPERIMENTAL", WIDGET_SEPARATOR_TEXT) + .Options(TextOptions().Color(Colors::Orange)); + AddWidget(path, "ImGui Menu Scaling", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_SETTING("ImGuiScale")) + .Options(ComboboxOptions().ComboMap(imguiScaleOptions).Tooltip("Changes the scaling of the ImGui menu elements.").DefaultIndex(1) + .ComponentAlignment(ComponentAlignments::Right).LabelPosition(LabelPositions::Far)) + .Callback([](WidgetInfo& info) { + OTRGlobals::Instance->ScaleImGui(); + }); + + // General - About + path.column = SECTION_COLUMN_2; + + AddWidget(path, "About", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Ship Of Harkinian", WIDGET_TEXT); + if (gGitCommitTag[0] != 0) { + AddWidget(path, gBuildVersion, WIDGET_TEXT); + } else { + AddWidget(path, ("Branch: " + std::string(gGitBranch)), WIDGET_TEXT); + AddWidget(path, ("Commit: " + std::string(gGitCommitHash)), WIDGET_TEXT); + } + for (uint32_t i = 0; i < ResourceMgr_GetNumGameVersions(); i++) { + AddWidget(path, GetGameVersionString(i), WIDGET_TEXT); + } + + // Audio Settings + path.sidebarName = "Audio"; + path.column = SECTION_COLUMN_1; + AddSidebarEntry("Settings", "Audio", 3); + + AddWidget(path, "Master Volume: %d %%", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_SETTING("Volume.Master")) + .Options(IntSliderOptions() + .Min(0) + .Max(100) + .DefaultValue(40) + .ShowButtons(true) + .Format("")); + AddWidget(path, "Main Music Volume: %d %%", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_SETTING("Volume.MainMusic")) + .Options(IntSliderOptions() + .Min(0) + .Max(100) + .DefaultValue(100) + .ShowButtons(true) + .Format("")) + .Callback([](WidgetInfo& info) { + Audio_SetGameVolume(SEQ_PLAYER_BGM_MAIN, ((float)CVarGetInteger(CVAR_SETTING("Volume.MainMusic"), 100) / 100.0f)); + }); + AddWidget(path, "Sub Music Volume: %d %%", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_SETTING("Volume.SubMusic")) + .Options(IntSliderOptions() + .Min(0) + .Max(100) + .DefaultValue(100) + .ShowButtons(true) + .Format("")) + .Callback([](WidgetInfo& info) { + Audio_SetGameVolume(SEQ_PLAYER_BGM_SUB, ((float)CVarGetInteger(CVAR_SETTING("Volume.SubMusic"), 100) / 100.0f)); + }); + AddWidget(path, "Fanfare Volume: %d %%", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_SETTING("Volume.Fanfare")) + .Options(IntSliderOptions() + .Min(0) + .Max(100) + .DefaultValue(100) + .ShowButtons(true) + .Format("")) + .Callback([](WidgetInfo& info) { + Audio_SetGameVolume(SEQ_PLAYER_FANFARE, ((float)CVarGetInteger(CVAR_SETTING("Volume.Fanfare"), 100) / 100.0f)); + }); + AddWidget(path, "Sound Effects Volume: %d %%", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_SETTING("Volume.SFX")) + .Options(IntSliderOptions() + .Min(0) + .Max(100) + .DefaultValue(100) + .ShowButtons(true) + .Format("")) + .Callback([](WidgetInfo& info) { + Audio_SetGameVolume(SEQ_PLAYER_SFX, ((float)CVarGetInteger(CVAR_SETTING("Volume.SFX"), 100) / 100.0f)); + }); + AddWidget(path, "Audio API (Needs reload)", WIDGET_AUDIO_BACKEND); + + // Graphics Settings + static int32_t maxFps = 360; + const char* 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\nA higher target " + "FPS than your monitor's refresh rate will waste resources, and might give a worse result."; + path.sidebarName = "Graphics"; + AddSidebarEntry("Settings", "Graphics", 3); + AddWidget(path, "Graphics Options", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Toggle Fullscreen", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("Fullscreen")) + .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->ToggleFullscreen(); }) + .Options(CheckboxOptions().Tooltip("Toggles Fullscreen On/Off.")); + AddWidget(path, "Internal Resolution", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_INTERNAL_RESOLUTION) + .Callback([](WidgetInfo& info) { + Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier( + CVarGetFloat(CVAR_INTERNAL_RESOLUTION, 1)); + }) + .PreFunc([](WidgetInfo& info) { + if (mSohMenu->disabledMap.at(DISABLE_FOR_ADVANCED_RESOLUTION_ON).active && + mSohMenu->disabledMap.at(DISABLE_FOR_VERTICAL_RES_TOGGLE_ON).active) { + info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_ON); + info.activeDisables.push_back(DISABLE_FOR_VERTICAL_RES_TOGGLE_ON); + } else if (mSohMenu->disabledMap.at(DISABLE_FOR_LOW_RES_MODE_ON).active) { + info.activeDisables.push_back(DISABLE_FOR_LOW_RES_MODE_ON); + } + }) + .Options( + FloatSliderOptions() + .Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective " + "form of anti-aliasing.") + .ShowButtons(false) + .IsPercentage() + .Min(0.5f) + .Max(2.0f)); +#ifndef __WIIU__ + AddWidget(path, "Anti-aliasing (MSAA)", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_MSAA_VALUE) + .Callback([](WidgetInfo& info) { + Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger(CVAR_MSAA_VALUE, 1)); + }) + .Options( + IntSliderOptions() + .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.") + .Min(1) + .Max(8) + .DefaultValue(1)); +#endif + auto fps = CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 20); + const char* fpsFormat = fps == 20 ? "Original (%d)" : "%d"; + AddWidget(path, "Current FPS", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_SETTING("InterpolationFPS")) + .Callback([](WidgetInfo& info) { + auto options = std::static_pointer_cast(info.options); + int32_t defaultValue = options->defaultValue; + if (CVarGetInteger(info.cVar, defaultValue) == defaultValue) { + options->format = "Original (%d)"; + } + else { + options->format = "%d"; + } + }) + .PreFunc([](WidgetInfo& info) { + if (mSohMenu->disabledMap.at(DISABLE_FOR_MATCH_REFRESH_RATE_ON).active) + info.activeDisables.push_back(DISABLE_FOR_MATCH_REFRESH_RATE_ON); + }) + .Options(IntSliderOptions().Tooltip(tooltip).Min(20).Max(maxFps).DefaultValue(20).Format(fpsFormat)); + AddWidget(path, "Match Refresh Rate", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("MatchRefreshRate")) + .Options(CheckboxOptions().Tooltip("Matches interpolation value to the refresh rate of your display.")); + AddWidget(path, "Renderer API (Needs reload)", WIDGET_VIDEO_BACKEND); + AddWidget(path, "Enable Vsync", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_VSYNC_ENABLED) + .PreFunc([](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_VSYNC).active; }) + .Options(CheckboxOptions().Tooltip("Removes tearing, but clamps your max FPS to your displays refresh rate.")); + AddWidget(path, "Windowed Fullscreen", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SDL_WINDOWED_FULLSCREEN) + .PreFunc([](WidgetInfo& info) { + info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_WINDOWED_FULLSCREEN).active; + }) + .Options(CheckboxOptions().Tooltip("Enables Windowed Fullscreen Mode.")); + AddWidget(path, "Allow multi-windows", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENABLE_MULTI_VIEWPORTS) + .PreFunc( + [](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_MULTI_VIEWPORT).active; }) + .Options(CheckboxOptions().Tooltip( + "Allows multiple windows to be opened at once. Requires a reload to take effect.")); + AddWidget(path, "Texture Filter (Needs reload)", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_TEXTURE_FILTER) + .Options(ComboboxOptions().Tooltip("Sets the applied Texture Filtering.").ComboMap(textureFilteringMap)); + + path.column = SECTION_COLUMN_2; + AddWidget(path, "Advanced Graphics Options", WIDGET_SEPARATOR_TEXT); + + // Controls + path.sidebarName = "Controls"; + path.column = SECTION_COLUMN_1; + AddSidebarEntry("Settings", "Controls", 2); + AddWidget(path, "Controller Bindings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Popout Bindings Window", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("ControllerConfiguration")) + .WindowName("Configure Controller") + .Options(WindowButtonOptions().Tooltip("Enables the separate Bindings Window.")); + + // Input Viewer + path.sidebarName = "Input Viewer"; + AddSidebarEntry("Settings", path.sidebarName, 3); + AddWidget(path, "Input Viewer", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Toggle Input Viewer", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("InputViewer")) + .WindowName("Input Viewer") + .Options(WindowButtonOptions().Tooltip("Toggles the Input Viewer.").EmbedWindow(false)); + + AddWidget(path, "Input Viewer Settings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Popout Input Viewer Settings", WIDGET_WINDOW_BUTTON) + .CVar(CVAR_WINDOW("InputViewerSettings")) + .WindowName("Input Viewer Settings") + .Options(WindowButtonOptions().Tooltip("Enables the separate Input Viewer Settings Window.")); + + // Notifications + path.sidebarName = "Notifications"; + path.column = SECTION_COLUMN_1; + AddSidebarEntry("Settings", path.sidebarName, 3); + AddWidget(path, "Position", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_SETTING("Notifications.Position")) + .Options(ComboboxOptions() + .Tooltip("Which corner of the screen notifications appear in.") + .ComboMap(notificationPosition) + .DefaultIndex(3)); + AddWidget(path, "Duration: %.0f seconds", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_SETTING("Notifications.Duration")) + .Options(FloatSliderOptions() + .Tooltip("How long notifications are displayed for.") + .Format("%.1f") + .Step(0.1f) + .Min(3.0f) + .Max(30.0f) + .DefaultValue(10.0f)); + AddWidget(path, "Background Opacity: %.0f%%", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_SETTING("Notifications.BgOpacity")) + .Options(FloatSliderOptions() + .Tooltip("How opaque the background of notifications is.") + .DefaultValue(0.5f) + .IsPercentage()); + AddWidget(path, "Size %.1f", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_SETTING("Notifications.Size")) + .Options(FloatSliderOptions() + .Tooltip("How large notifications are.") + .Format("%.1f") + .Step(0.1f) + .Min(1.0f) + .Max(5.0f) + .DefaultValue(1.8f)); + AddWidget(path, "Test Notification", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + Notification::Emit({ + .itemIcon = "__OTR__textures/icon_item_24_static/gQuestIconGoldSkulltulaTex", + .prefix = "This", + .message = "is a", + .suffix = "test.", + }); + }) + .Options(ButtonOptions().Tooltip("Displays a test notification.")); +} + +} // namespace SohGui diff --git a/soh/soh/SohGui/SohModals.cpp b/soh/soh/SohGui/SohModals.cpp index 642fedf15..e2025437f 100644 --- a/soh/soh/SohGui/SohModals.cpp +++ b/soh/soh/SohGui/SohModals.cpp @@ -5,6 +5,7 @@ #include #include #include "UIWidgets.hpp" +#include "SohGui.hpp" #include "soh/OTRGlobals.h" #include "z64.h" @@ -19,6 +20,8 @@ struct SohModal { }; std::vector modals; +bool closePopup = false; + void SohModalWindow::Draw() { if (!IsVisible()) { return; @@ -34,8 +37,14 @@ void SohModalWindow::DrawElement() { if (!ImGui::IsPopupOpen(curModal.title_.c_str())) { ImGui::OpenPopup(curModal.title_.c_str()); } + if (closePopup) { + ImGui::CloseCurrentPopup(); + modals.erase(modals.begin()); + closePopup = false; + } if (ImGui::BeginPopupModal(curModal.title_.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) { ImGui::Text("%s", curModal.message_.c_str()); + UIWidgets::PushStyleButton(THEME_COLOR); if (ImGui::Button(curModal.button1_.c_str())) { if (curModal.button1callback_ != nullptr) { curModal.button1callback_(); @@ -43,8 +52,10 @@ void SohModalWindow::DrawElement() { ImGui::CloseCurrentPopup(); modals.erase(modals.begin()); } - ImGui::SameLine(); + UIWidgets::PopStyleButton(); if (curModal.button2_ != "") { + ImGui::SameLine(); + UIWidgets::PushStyleButton(THEME_COLOR); if (ImGui::Button(curModal.button2_.c_str())) { if (curModal.button2callback_ != nullptr) { curModal.button2callback_(); @@ -52,6 +63,7 @@ void SohModalWindow::DrawElement() { ImGui::CloseCurrentPopup(); modals.erase(modals.begin()); } + UIWidgets::PopStyleButton(); } } ImGui::EndPopup(); @@ -61,3 +73,11 @@ void SohModalWindow::DrawElement() { void SohModalWindow::RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function button1callback, std::function button2callback) { modals.push_back({ title, message, button1, button2, button1callback, button2callback }); } + +bool SohModalWindow::IsPopupOpen(std::string title) { + return !modals.empty() && modals.at(0).title_ == title; +} + +void SohModalWindow::DismissPopup() { + closePopup = true; +} diff --git a/soh/soh/SohGui/SohModals.h b/soh/soh/SohGui/SohModals.h index f584b9540..9a1fed80c 100644 --- a/soh/soh/SohGui/SohModals.h +++ b/soh/soh/SohGui/SohModals.h @@ -13,4 +13,6 @@ class SohModalWindow : public Ship::GuiWindow { void DrawElement() override; void UpdateElement() override {}; void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function button1callback = nullptr, std::function button2callback = nullptr); + bool IsPopupOpen(std::string title); + void DismissPopup(); }; \ No newline at end of file diff --git a/soh/soh/SohGui/UIWidgets.cpp b/soh/soh/SohGui/UIWidgets.cpp index 6d9ca7a65..e2f7bf2dc 100644 --- a/soh/soh/SohGui/UIWidgets.cpp +++ b/soh/soh/SohGui/UIWidgets.cpp @@ -1,836 +1,1137 @@ -// -// UIWidgets.cpp -// soh -// -// Created by David Chavez on 25.08.22. -// - #include "UIWidgets.hpp" - -#include +#define IMGUI_DEFINE_MATH_OPERATORS #include +#include #include - +#include +#include +#include +#include #include -#include "soh/Enhancements/cosmetics/CosmeticsEditor.h" +#include namespace UIWidgets { - // MARK: - Layout Helper - - // Automatically adds newlines to break up text longer than a specified number of characters - // Manually included newlines will still be respected and reset the line length - // If line is midword when it hits the limit, text should break at the last encountered space - std::string WrappedText(const char* text, unsigned int charactersPerLine) { - std::string newText(text); - const size_t tipLength = newText.length(); - int lastSpace = -1; - int currentLineLength = 0; - for (unsigned int currentCharacter = 0; currentCharacter < tipLength; currentCharacter++) { - if (newText[currentCharacter] == '\n') { - currentLineLength = 0; - lastSpace = -1; - continue; - } else if (newText[currentCharacter] == ' ') { - lastSpace = currentCharacter; - } - - if ((currentLineLength >= charactersPerLine) && (lastSpace >= 0)) { - newText[lastSpace] = '\n'; - currentLineLength = currentCharacter - lastSpace - 1; - lastSpace = -1; - } - currentLineLength++; +// Automatically adds newlines to break up text longer than a specified number of characters +// Manually included newlines will still be respected and reset the line length +// If line is midword when it hits the limit, text should break at the last encountered space +std::string WrappedText(const char* text, unsigned int charactersPerLine) { + std::string newText(text); + const size_t tipLength = newText.length(); + int lastSpace = -1; + int currentLineLength = 0; + for (unsigned int currentCharacter = 0; currentCharacter < tipLength; currentCharacter++) { + if (newText[currentCharacter] == '\n') { + currentLineLength = 0; + lastSpace = -1; + continue; + } else if (newText[currentCharacter] == ' ') { + lastSpace = currentCharacter; } - return newText; + if ((currentLineLength >= charactersPerLine) && (lastSpace >= 0)) { + newText[lastSpace] = '\n'; + currentLineLength = currentCharacter - lastSpace - 1; + lastSpace = -1; + } + currentLineLength++; } - std::string WrappedText(const std::string& text, unsigned int charactersPerLine) { - return WrappedText(text.c_str(), charactersPerLine); + return newText; +} + +std::string WrappedText(const std::string& text, unsigned int charactersPerLine) { + return WrappedText(text.c_str(), charactersPerLine); +} + +void PaddedSeparator(bool padTop, bool padBottom, float extraVerticalTopPadding, float extraVerticalBottomPadding) { + if (padTop) { + Spacer(extraVerticalTopPadding); } - - void SetLastItemHoverText(const std::string& text) { - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::Text("%s", WrappedText(text, 60).c_str()); - ImGui::EndTooltip(); - } - } - - void SetLastItemHoverText(const char* text) { - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::Text("%s", WrappedText(text, 60).c_str()); - ImGui::EndTooltip(); - } - } - - // Adds a "?" next to the previous ImGui item with a custom tooltip - void InsertHelpHoverText(const std::string& text) { - ImGui::SameLine(); - ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?"); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::Text("%s", WrappedText(text, 60).c_str()); - ImGui::EndTooltip(); - } - } - - void InsertHelpHoverText(const char* text) { - ImGui::SameLine(); - ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?"); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::Text("%s", WrappedText(text, 60).c_str()); - ImGui::EndTooltip(); - } - } - - - // MARK: - UI Elements - - void Tooltip(const char* text) { - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s", WrappedText(text).c_str()); - } - } - - void Spacer(float height) { - ImGui::Dummy(ImVec2(0.0f, height)); - } - - void PaddedSeparator(bool padTop, bool padBottom, float extraVerticalTopPadding, float extraVerticalBottomPadding) { - if (padTop) { - Spacer(extraVerticalTopPadding); - } - ImGui::Separator(); - if (padBottom) { - Spacer(extraVerticalBottomPadding); - } - } - - void RenderCross(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz) { - float thickness = ImMax(sz / 5.0f, 1.0f); - sz -= thickness * 0.5f; - pos += ImVec2(thickness * 0.25f, thickness * 0.25f); - - draw_list->PathLineTo(ImVec2(pos.x, pos.y)); - draw_list->PathLineTo(ImVec2(pos.x + sz, pos.y + sz)); - draw_list->PathStroke(col, 0, thickness); - - draw_list->PathLineTo(ImVec2(pos.x + sz, pos.y)); - draw_list->PathLineTo(ImVec2(pos.x, pos.y + sz)); - draw_list->PathStroke(col, 0, thickness); - } - - bool CustomCheckbox(const char* label, bool* v, bool disabled, CheckboxGraphics disabledGraphic, bool renderCrossWhenOff) { - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->SkipItems) { - return false; - } - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); - - const float square_sz = ImGui::GetFrameHeight(); - const ImVec2 pos = window->DC.CursorPos; - const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); - ImGui::ItemSize(total_bb, style.FramePadding.y); - if (!ImGui::ItemAdd(total_bb, id)) { - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); - return false; - } - - bool hovered, held; - bool pressed = ImGui::ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) { - *v = !(*v); - ImGui::MarkItemEdited(id); - } - - const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); - ImGui::RenderNavHighlight(total_bb, id); - ImGui::RenderFrame(check_bb.Min, check_bb.Max, ImGui::GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); - ImU32 check_col = ImGui::GetColorU32(ImGuiCol_CheckMark); - ImU32 cross_col = ImGui::GetColorU32(ImVec4(0.50f, 0.50f, 0.50f, 1.00f)); - bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0; - if (mixed_value) { - // Undocumented tristate/mixed/indeterminate checkbox (#2644) - // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) - ImVec2 pad(ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)), ImMax(1.0f, IM_FLOOR(square_sz / 3.6f))); - window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); - } else if ((!disabled && *v) || (disabled && disabledGraphic == CheckboxGraphics::Checkmark)) { - const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); - ImGui::RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); - } else if ((!disabled && !*v && renderCrossWhenOff) || (disabled && disabledGraphic == CheckboxGraphics::Cross)) { - const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); - //RenderCross(window->DrawList, check_bb.Min + ImVec2(pad, pad), disabled ? cross_col : check_col, square_sz - pad * 2.0f); // Caused confusion as to status - } - - ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); - if (g.LogEnabled) { - ImGui::LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); - } - if (label_size.x > 0.0f) { - ImGui::RenderText(label_pos, label); - } - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); - return pressed; - } - - bool CustomCheckboxTristate(const char* label, int* v, bool disabled, CheckboxGraphics disabledGraphic) { - bool ret; - if (*v == 0) { - bool b = false; - ret = CustomCheckbox(label, &b, disabled, disabledGraphic, true); - if (ret) { - *v = 1; - } - } else if (*v == 1) { - ImGui::PushItemFlag(ImGuiItemFlags_MixedValue, true); - bool b = true; - ret = CustomCheckbox(label, &b, disabled, disabledGraphic, true); - if (ret) { - *v = 2; - } - ImGui::PopItemFlag(); - } else if (*v == 2) { - bool b = true; - ret = CustomCheckbox(label, &b, disabled, disabledGraphic, true); - if (ret) { - *v = 0; - } - } else { - SPDLOG_INFO("Invalid CheckBoxTristate value: {}", *v); - *v = 0; - return false; - } - return ret; - } - - void ReEnableComponent(const char* disabledTooltipText) { - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(disabledTooltipText, "") != 0) { - ImGui::SetTooltip("%s", disabledTooltipText); - } - // End of disable region of previous component - ImGui::PopStyleVar(1); - ImGui::PopItemFlag(); - } - - void DisableComponent(const float alpha) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha); - } - - bool EnhancementCheckbox(const char* text, const char* cvarName, bool disabled, const char* disabledTooltipText, CheckboxGraphics disabledGraphic, bool defaultValue) { - bool changed = false; - if (disabled) { - DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - bool val = (bool)CVarGetInteger(cvarName, defaultValue); - if (CustomCheckbox(text, &val, disabled, disabledGraphic)) { - CVarSetInteger(cvarName, val); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - ShipInit::Init(cvarName); - changed = true; - } - - if (disabled) { - ReEnableComponent(disabledTooltipText); - } - return changed; - } - - bool EnhancementCheckboxTristate(const char* text, const char* cvarName, bool disabled, const char* disabledTooltipText, CheckboxGraphics disabledGraphic, bool defaultValue) { - bool changed = false; - if (disabled) { - DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - int val = CVarGetInteger(cvarName, defaultValue); - if (CustomCheckboxTristate(text, &val, disabled, disabledGraphic)) { - CVarSetInteger(cvarName, val); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - changed = true; - } - - if (disabled) { - ReEnableComponent(disabledTooltipText); - } - return changed; - } - - bool PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop, bool padBottom, bool disabled, const char* disabledTooltipText, CheckboxGraphics disabledGraphic, bool defaultValue) { - ImGui::BeginGroup(); - if (padTop) Spacer(0); - - bool changed = EnhancementCheckbox(text, cvarName, disabled, disabledTooltipText, disabledGraphic, defaultValue); - - if (padBottom) Spacer(0); - ImGui::EndGroup(); - return changed; - } - - bool EnhancementCombobox(const char* cvarName, std::span comboArray, uint8_t defaultIndex, bool disabled, const char* disabledTooltipText, uint8_t disabledValue) { - bool changed = false; - if (defaultIndex <= 0) { - defaultIndex = 0; - } - - if (disabled) { - DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - uint8_t selected = CVarGetInteger(cvarName, defaultIndex); - std::string comboName = std::string("##") + std::string(cvarName); - if (ImGui::BeginCombo(comboName.c_str(), comboArray[selected])) { - for (uint8_t i = 0; i < comboArray.size(); i++) { - if (strlen(comboArray[i]) > 0) { - if (ImGui::Selectable(comboArray[i], i == selected)) { - CVarSetInteger(cvarName, i); - selected = i; - changed = true; - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - ShipInit::Init(cvarName); - } - } - } - ImGui::EndCombo(); - } - - if (disabled) { - ReEnableComponent(disabledTooltipText); - - if (disabledValue >= 0 && selected != disabledValue) { - CVarSetInteger(cvarName, disabledValue); - changed = true; - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - } - - return changed; - } - - bool LabeledRightAlignedEnhancementCombobox(const char* label, const char* cvarName, std::span comboArray, uint8_t defaultIndex, bool disabled, const char* disabledTooltipText, uint8_t disabledValue) { - ImGui::Text("%s", label); - s32 currentValue = CVarGetInteger(cvarName, defaultIndex); - -#ifdef __WIIU__ - ImGui::SameLine(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize(comboArray[currentValue]).x + 40.0f)); - ImGui::PushItemWidth(ImGui::CalcTextSize(comboArray[currentValue]).x + 60.0f); -#else - ImGui::SameLine(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize(comboArray[currentValue]).x + 20.0f)); - ImGui::PushItemWidth(ImGui::CalcTextSize(comboArray[currentValue]).x + 30.0f); -#endif - - bool changed = EnhancementCombobox(cvarName, comboArray, defaultIndex, disabled, disabledTooltipText, disabledValue); - - ImGui::PopItemWidth(); - return changed; - } - - void PaddedText(const char* text, bool padTop, bool padBottom) { - if (padTop) Spacer(0); - - ImGui::Text("%s", text); - - if (padBottom) Spacer(0); - } - - bool EnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue, bool PlusMinusButton, bool disabled, const char* disabledTooltipText) { - bool changed = false; - int val = CVarGetInteger(cvarName, defaultValue); - const int oldVal = val; - - if (disabled) { - DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - ImGui::Text(text, val); - Spacer(0); - - ImGui::BeginGroup(); - if (PlusMinusButton) { - std::string MinusBTNName = " - ##" + std::string(cvarName); - if (ImGui::Button(MinusBTNName.c_str())) { - val--; - changed = true; - } - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - } - - ImGui::PushItemWidth(std::min((ImGui::GetContentRegionAvail().x - (PlusMinusButton ? sliderButtonWidth : 0.0f)), maxSliderWidth)); - if (ImGui::SliderInt(id, &val, min, max, format, ImGuiSliderFlags_AlwaysClamp)) - { - changed = true; - } - ImGui::PopItemWidth(); - - if (PlusMinusButton) { - std::string PlusBTNName = " + ##" + std::string(cvarName); - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - if (ImGui::Button(PlusBTNName.c_str())) { - val++; - changed = true; - } - } - ImGui::EndGroup(); - - if (disabled) { - ReEnableComponent(disabledTooltipText); - } - - if (val < min) { - val = min; - changed = true; - } - - if (val > max) { - val = max; - changed = true; - } - - if (changed && (oldVal != val)) { - CVarSetInteger(cvarName, val); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - ShipInit::Init(cvarName); - } else { - changed = false; - } - - return changed; - } - - bool EnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton, bool disabled, const char* disabledTooltipText) { - bool changed = false; - float val = CVarGetFloat(cvarName, defaultValue); - const float oldVal = val; - if (disabled) { - DisableComponent(ImGui::GetStyle().Alpha * 0.5f); - } - - // Calculate how much precision to save based on the given range of the slider, limited to 6 decimal places - // Precision is also used when adding/subtracting using the +/- buttons - const float sliderWidth = std::min((ImGui::GetContentRegionAvail().x - 2.0f * (PlusMinusButton ? sliderButtonWidth : 0.0f)), maxSliderWidth); - const float diff = (max - min) / sliderWidth; - int ticks = 0; - float increment = 1.0f; - if (diff < 1.0f) { - ticks++; - increment = 0.1f; - } - if (diff < 0.1f) { - ticks++; - increment = 0.01f; - } - if (diff < 0.01f) { - ticks++; - increment = 0.001f; - } - if (diff < 0.001f) { - ticks++; - increment = 0.0001f; - } - if (diff < 0.0001f) { - ticks++; - increment = 0.00001f; - } - if (diff < 0.00001f) { - ticks++; - increment = 0.000001f; - } - - if (!isPercentage) { - ImGui::Text(text, val); - } else { - ImGui::Text(text, val * 100.0f); - } - Spacer(0); - - ImGui::BeginGroup(); - if (PlusMinusButton) { - std::string MinusBTNName = " - ##" + std::string(cvarName); - if (ImGui::Button(MinusBTNName.c_str())) { - val -= increment; - changed = true; - } - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - } - - ImGui::PushItemWidth(sliderWidth); - if (ImGui::SliderFloat(id, &val, min, max, format, ImGuiSliderFlags_AlwaysClamp)) { - changed = true; - } - ImGui::PopItemWidth(); - - if (PlusMinusButton) { - std::string PlusBTNName = " + ##" + std::string(cvarName); - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - if (ImGui::Button(PlusBTNName.c_str())) { - val += increment; - changed = true; - } - } - ImGui::EndGroup(); - - if (disabled) { - ReEnableComponent(disabledTooltipText); - } - - if (val < min) { - val = min; - changed = true; - } - - if (val > max) { - val = max; - changed = true; - } - - if (changed && !(abs(oldVal - val) < 0.000001f)) { - std::stringstream ss; - ss << std::setprecision(ticks + 1) << std::setiosflags(std::ios_base::fixed) << val; - val = std::stof(ss.str()); - CVarSetFloat(cvarName, val); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } else { - changed = false; - } - - return changed; - } - - bool PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue, bool PlusMinusButton, bool padTop, bool padBottom, bool disabled, const char* disabledTooltipText) { - bool changed = false; - ImGui::BeginGroup(); - if (padTop) Spacer(0); - - changed = EnhancementSliderInt(text, id, cvarName, min, max, format, defaultValue, PlusMinusButton, disabled, disabledTooltipText); - - if (padBottom) Spacer(0); - ImGui::EndGroup(); - return changed; - } - - bool PaddedEnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton, bool padTop, bool padBottom, bool disabled, const char* disabledTooltipText) { - bool changed = false; - ImGui::BeginGroup(); - if (padTop) Spacer(0); - - changed = EnhancementSliderFloat(text, id, cvarName, min, max, format, defaultValue, isPercentage, PlusMinusButton, disabled, disabledTooltipText); - - if (padBottom) Spacer(0); - ImGui::EndGroup(); - return changed; - } - - bool EnhancementRadioButton(const char* text, const char* cvarName, int id) { - /*Usage : - EnhancementRadioButton("My Visible Name",CVAR_GROUP("MyCVarName"), MyID); - First arg is the visible name of the Radio button - Second is the cvar name where MyID will be saved. - Note: the CVar name should be the same to each Buddies. - Example : - EnhancementRadioButton("English", CVAR_SETTING("Languages"), LANGUAGE_ENG); - EnhancementRadioButton("German", CVAR_SETTING("Languages"), LANGUAGE_GER); - EnhancementRadioButton("French", CVAR_SETTING("Languages"), LANGUAGE_FRA); - */ - std::string make_invisible = "##" + std::string(text) + std::string(cvarName); - - bool ret = false; - int val = CVarGetInteger(cvarName, 0); - if (ImGui::RadioButton(make_invisible.c_str(), id == val)) { - CVarSetInteger(cvarName, id); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - ret = true; - } - ImGui::SameLine(); - ImGui::Text("%s", text); - - return ret; - } - - bool DrawResetColorButton(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha) { - bool changed = false; - std::string Cvar_RBM = std::string(cvarName) + "RBM"; - std::string MakeInvisible = "Reset##" + std::string(cvarName) + "Reset"; - if (ImGui::Button(MakeInvisible.c_str())) { - colors->x = defaultcolors.x; - colors->y = defaultcolors.y; - colors->z = defaultcolors.z; - colors->w = has_alpha ? defaultcolors.w : 255.0f; - - Color_RGBA8 colorsRGBA; - colorsRGBA.r = defaultcolors.x; - colorsRGBA.g = defaultcolors.y; - colorsRGBA.b = defaultcolors.z; - colorsRGBA.a = has_alpha ? defaultcolors.w : 255.0f; - - CVarSetColor(cvarName, colorsRGBA); - CVarSetInteger(Cvar_RBM.c_str(), 0); //On click disable rainbow mode. - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - changed = true; - } - Tooltip("Revert colors to the game's original colors (GameCube version)\nOverwrites previously chosen color"); - return changed; - } - - bool DrawRandomizeColorButton(const char* cvarName, ImVec4* colors) { - bool changed = false; - Color_RGBA8 NewColors = {0,0,0,255}; - std::string Cvar_RBM = std::string(cvarName) + "RBM"; - std::string FullName = "Random##" + std::string(cvarName) + "Random"; - if (ImGui::Button(FullName.c_str())) { -#if defined(__SWITCH__) || defined(__WIIU__) - srand(time(NULL)); -#endif - ImVec4 color = GetRandomValue(); - colors->x = color.x; - colors->y = color.y; - colors->z = color.z; - NewColors.r = fmin(fmax(colors->x * 255, 0), 255); - NewColors.g = fmin(fmax(colors->y * 255, 0), 255); - NewColors.b = fmin(fmax(colors->z * 255, 0), 255); - CVarSetColor(cvarName, NewColors); - CVarSetInteger(Cvar_RBM.c_str(), 0); // On click disable rainbow mode. - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - changed = true; - } - Tooltip("Chooses a random color\nOverwrites previously chosen color"); - return changed; - } - - void DrawLockColorCheckbox(const char* cvarName) { - std::string Cvar_Lock = std::string(cvarName) + "Lock"; - s32 lock = CVarGetInteger(Cvar_Lock.c_str(), 0); - std::string FullName = "Lock##" + Cvar_Lock; - EnhancementCheckbox(FullName.c_str(), Cvar_Lock.c_str()); - Tooltip("Prevents this color from being changed upon selecting \"Randomize all\""); - } - - void RainbowColor(const char* cvarName, ImVec4* colors) { - std::string Cvar_RBM = std::string(cvarName) + "RBM"; - std::string MakeInvisible = "Rainbow##" + std::string(cvarName) + "Rainbow"; - - EnhancementCheckbox(MakeInvisible.c_str(), Cvar_RBM.c_str()); - Tooltip("Cycles through colors on a timer\nOverwrites previously chosen color"); - } - - void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha) { - Color_RGBA8 defaultColors; - defaultColors.r = default_colors.x; - defaultColors.g = default_colors.y; - defaultColors.b = default_colors.z; - defaultColors.a = default_colors.w; - - Color_RGBA8 cvarColor = CVarGetColor(cvarname, defaultColors); - - ColorArray.x = cvarColor.r / 255.0; - ColorArray.y = cvarColor.g / 255.0; - ColorArray.z = cvarColor.b / 255.0; - ColorArray.w = cvarColor.a / 255.0; - } - - bool EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow, bool has_alpha, bool TitleSameLine) { - bool changed = false; - LoadPickersColors(ColorRGBA, cvarName, default_colors, has_alpha); - - ImGuiColorEditFlags flags = ImGuiColorEditFlags_None; - - if (!TitleSameLine) { - ImGui::Text("%s", text); - flags = ImGuiColorEditFlags_NoLabel; - } - - ImGui::PushID(cvarName); - - if (!has_alpha) { - if (ImGui::ColorEdit3(text, (float*)&ColorRGBA, flags)) - { - Color_RGBA8 colors; - colors.r = ColorRGBA.x * 255.0; - colors.g = ColorRGBA.y * 255.0; - colors.b = ColorRGBA.z * 255.0; - colors.a = 255.0; - - CVarSetColor(cvarName, colors); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - changed = true; - } - } - else - { - flags |= ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreview; - if (ImGui::ColorEdit4(text, (float*)&ColorRGBA, flags)) - { - Color_RGBA8 colors; - colors.r = ColorRGBA.x * 255.0; - colors.g = ColorRGBA.y * 255.0; - colors.b = ColorRGBA.z * 255.0; - colors.a = ColorRGBA.w * 255.0; - - CVarSetColor(cvarName, colors); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - changed = true; - } - } - - ImGui::PopID(); - - //ImGui::SameLine(); // Removing that one to gain some width spacing on the HUD editor - ImGui::PushItemWidth(-FLT_MIN); - if (DrawResetColorButton(cvarName, &ColorRGBA, default_colors, has_alpha)) { - changed = true; - } - ImGui::SameLine(); - if (DrawRandomizeColorButton(cvarName, &ColorRGBA)) { - changed = true; - } - if (allow_rainbow) { - if (ImGui::GetContentRegionAvail().x > 185) { - ImGui::SameLine(); - } - RainbowColor(cvarName, &ColorRGBA); - } - DrawLockColorCheckbox(cvarName); - ImGui::NewLine(); - ImGui::PopItemWidth(); - - return changed; - } - - void DrawFlagArray32(const std::string& name, uint32_t& flags) { - ImGui::PushID(name.c_str()); - for (int32_t flagIndex = 0; flagIndex < 32; flagIndex++) { - if ((flagIndex % 8) != 0) { - ImGui::SameLine(); - } - ImGui::PushID(flagIndex); - uint32_t bitMask = 1 << flagIndex; - bool flag = (flags & bitMask) != 0; - if (ImGui::Checkbox("##check", &flag)) { - if (flag) { - flags |= bitMask; - } else { - flags &= ~bitMask; - } - } - ImGui::PopID(); - } - ImGui::PopID(); - } - - void DrawFlagArray16(const std::string& name, uint16_t& flags) { - ImGui::PushID(name.c_str()); - for (int16_t flagIndex = 0; flagIndex < 16; flagIndex++) { - if ((flagIndex % 8) != 0) { - ImGui::SameLine(); - } - ImGui::PushID(flagIndex); - uint16_t bitMask = 1 << flagIndex; - bool flag = (flags & bitMask) != 0; - if (ImGui::Checkbox("##check", &flag)) { - if (flag) { - flags |= bitMask; - } else { - flags &= ~bitMask; - } - } - ImGui::PopID(); - } - ImGui::PopID(); - } - - void DrawFlagArray8(const std::string& name, uint8_t& flags) { - ImGui::PushID(name.c_str()); - for (int8_t flagIndex = 0; flagIndex < 8; flagIndex++) { - if ((flagIndex % 8) != 0) { - ImGui::SameLine(); - } - ImGui::PushID(flagIndex); - uint8_t bitMask = 1 << flagIndex; - bool flag = (flags & bitMask) != 0; - if (ImGui::Checkbox("##check", &flag)) { - if (flag) { - flags |= bitMask; - } else { - flags &= ~bitMask; - } - } - ImGui::PopID(); - } - ImGui::PopID(); - } - - bool StateButtonEx(const char* str_id, const char* label, ImVec2 size, ImGuiButtonFlags flags) { - ImGuiContext& g = *GImGui; - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiStyle& style = g.Style; - const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); - - const ImGuiID id = window->GetID(str_id); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - const float default_size = ImGui::GetFrameHeight(); - ImGui::ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f); - if (!ImGui::ItemAdd(bb, id)) - return false; - - if (g.LastItemData.ItemFlags & ImGuiItemFlags_ButtonRepeat) { - ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); - } - - bool hovered, held; - bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags); - - if (g.LastItemData.ItemFlags & ImGuiItemFlags_ButtonRepeat) { - ImGui::PopItemFlag(); // ImGuiItemFlags_ButtonRepeat; - } - - // Render - const ImU32 bg_col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive - : hovered ? ImGuiCol_ButtonHovered - : ImGuiCol_Button); - //const ImU32 text_col = ImGui::GetColorU32(ImGuiCol_Text); - ImGui::RenderNavHighlight(bb, id); - ImGui::RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); - ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, {0.55f, 0.45f}, &bb); - /*ImGui::RenderArrow(window->DrawList, - bb.Min + - ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), - text_col, dir);*/ - - IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); - return pressed; - } - - bool StateButton(const char* str_id, const char* label) { - float sz = ImGui::GetFrameHeight(); - return StateButtonEx(str_id, label, ImVec2(sz, sz), ImGuiButtonFlags_None); - } - - // Reference: imgui-src/misc/cpp/imgui_stdlib.cpp - int InputTextResizeCallback(ImGuiInputTextCallbackData* data) { - std::string* value = (std::string*)data->UserData; - if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { - value->resize(data->BufTextLen); - data->Buf = (char*)value->c_str(); - } - return 0; - } - - bool InputString(const char* label, std::string* value) { - return ImGui::InputText(label, (char*)value->c_str(), value->capacity() + 1, ImGuiInputTextFlags_CallbackResize, InputTextResizeCallback, value); + ImGui::Separator(); + if (padBottom) { + Spacer(extraVerticalBottomPadding); } } + +void Tooltip(const char* text) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", WrappedText(text).c_str()); + } +} + +void PushStyleMenu(const ImVec4& color) { + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ColorValues.at(Colors::DarkGray)); + ImGui::PushStyleColor(ImGuiCol_Border, ColorValues.at(Colors::DarkGray)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 15.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 3.0f); +} + +void PushStyleMenu(Colors color) { + PushStyleMenu(ColorValues.at(color)); +} + +void PopStyleMenu() { + ImGui::PopStyleVar(2); + ImGui::PopStyleColor(4); +} + +bool BeginMenu(const char* label, Colors color) { + bool dirty = false; + PushStyleMenu(color); + ImGui::SetNextWindowSizeConstraints(ImVec2(200.0f, 0.0f), ImVec2(FLT_MAX, FLT_MAX)); + if (ImGui::BeginMenu(label)) { + dirty = true; + } + PopStyleMenu(); + return dirty; +} + +void PushStyleMenuItem(const ImVec4& color) { + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, color); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0f, 15.0f)); +} + +void PushStyleMenuItem(Colors color) { + PushStyleMenuItem(ColorValues.at(color)); +} + +void PopStyleMenuItem() { + ImGui::PopStyleVar(1); + ImGui::PopStyleColor(1); +} + +bool MenuItem(const char* label, const char* shortcut, Colors color) { + bool dirty = false; + PushStyleMenuItem(color); + if (ImGui::MenuItem(label, shortcut)) { + dirty = true; + } + PopStyleMenuItem(); + return dirty; +} + +void PushStyleButton(const ImVec4& color, const ImVec2 padding) { + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, padding); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f); +} + +void PushStyleButton(Colors color, ImVec2 padding) { + PushStyleButton(ColorValues.at(color), padding); +} + +void PopStyleButton() { + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(4); +} + +void PushStyleInput(const ImVec4& color) { + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f); +} + +void PushStyleInput(Colors color) { + PushStyleInput(ColorValues.at(color)); +} + +void PopStyleInput() { + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(7); +} + +void PushStyleHeader(const ImVec4& color) { + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(color.x, color.y, color.z, 0.6f)); +} + +void PushStyleHeader(Colors color) { + PushStyleHeader(ColorValues.at(color)); +} + +void PopStyleHeader() { + ImGui::PopStyleColor(3); +} + +bool Button(const char* label, const ButtonOptions& options) { + ImGui::BeginDisabled(options.disabled); + PushStyleButton(options.color, options.padding); + bool dirty = ImGui::Button(label, options.size); + PopStyleButton(); + ImGui::EndDisabled(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && + !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + return dirty; +} + +bool WindowButton(const char* label, const char* cvarName, std::shared_ptr windowPtr, + const WindowButtonOptions& options) { + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); + std::string buttonText = label; + bool dirty = false; + if (CVarGetInteger(cvarName, 0)) { + buttonText = ICON_FA_WINDOW_CLOSE " " + buttonText; + } else { + buttonText = ICON_FA_EXTERNAL_LINK_SQUARE " " + buttonText; + } + if (Button(buttonText.c_str(), {{ options.tooltip, options.disabled, options.disabledTooltip }, + options.size, options.padding, options.color })) { + windowPtr->ToggleVisibility(); + dirty = true; + } + ImGui::PopStyleVar(); + return dirty; +} + +void PushStyleCheckbox(const ImVec4& color) { + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f)); + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.0f, 1.0f, 1.0f, 0.7f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f); +} + +void PushStyleCheckbox(Colors color) { + PushStyleCheckbox(ColorValues.at(color)); +} + +void PopStyleCheckbox() { + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(5); +} + +void Spacer(float height) { + ImGui::Dummy(ImVec2(0.0f, height)); +} + +void Separator(bool padTop, bool padBottom, float extraVerticalTopPadding, float extraVerticalBottomPadding) { + if (padTop) { + Spacer(extraVerticalTopPadding); + } + ImGui::Separator(); + if (padBottom) { + Spacer(extraVerticalBottomPadding); + } +} + +// Adds a "?" next to the previous ImGui item with a custom tooltip +void InsertHelpHoverText(const std::string& text) { + ImGui::SameLine(); + ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::Text("%s", WrappedText(text, 60).c_str()); + ImGui::EndTooltip(); + } +} + +void InsertHelpHoverText(const char* text) { + ImGui::SameLine(); + ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::Text("%s", WrappedText(text, 60).c_str()); + ImGui::EndTooltip(); + } +} + +void RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // Hide anything after a '##' string + const char* text_display_end; + if (hide_text_after_hash) { + text_display_end = ImGui::FindRenderedTextEnd(text, text_end); + } else { + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + text_display_end = text_end; + } + + if (text != text_display_end) { + window->DrawList->AddText(g.Font, g.FontSize, pos, ImGui::GetColorU32(ImGuiCol_Text), text, text_display_end); + if (g.LogEnabled) + ImGui::LogRenderedText(&pos, text, text_display_end); + } +} + +bool Checkbox(const char* _label, bool* value, const CheckboxOptions& options) { + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGui::BeginDisabled(options.disabled); + + bool above = options.labelPosition == LabelPositions::Above; + bool lpFar = options.labelPosition == LabelPositions::Far; + bool right = options.alignment == ComponentAlignments::Right; + bool none = options.labelPosition == LabelPositions::None; + + std::string labelStr = (none ? "##" : ""); + labelStr.append(_label); + + const char* label = labelStr.c_str(); + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + const float square_sz = ImGui::GetFrameHeight(); + ImVec2 pos = window->DC.CursorPos; + + if (right) { + float labelOffsetX = (above ? 0 : (style.ItemInnerSpacing.x * 2.0f) + square_sz); + if (!lpFar) { + pos.x += ImGui::GetContentRegionAvail().x - (label_size.x + labelOffsetX); + } + } + float bbAboveX = lpFar ? ImGui::GetContentRegionAvail().x + : (label_size.x + (above ? 0 : (style.ItemInnerSpacing.x * 2.0f) + square_sz)); + float bbAboveY = label_size.y + (above ? square_sz : 0) + (style.FramePadding.y * 2.0f); + const ImRect total_bb(pos, pos + ImVec2(bbAboveX, bbAboveY)); + + ImGui::ItemSize(total_bb, style.FramePadding.y); + if (!ImGui::ItemAdd(total_bb, id)) { + ImGui::EndDisabled(); + return false; + } + bool hovered, held, pressed; + pressed = ImGui::ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) { + *value = !(*value); + ImGui::MarkItemEdited(id); + } + PushStyleCheckbox(options.color); + ImVec2 checkPos = pos; + ImVec2 labelPos = pos; + if (options.labelPosition == LabelPositions::Above) { + checkPos.y += label_size.y + (style.ItemInnerSpacing.y * 2.0f); + } else { + // Center with checkbox automatically + labelPos.y += ImGui::CalcTextSize("g").y / 8; + } + if (options.alignment == ComponentAlignments::Right) { + checkPos.x = total_bb.Max.x - square_sz; + } else { + float labelFarOffset = ImGui::GetContentRegionAvail().x - label_size.x; + float labelOffsetX = above ? 0 : (lpFar ? labelFarOffset : (style.ItemInnerSpacing.x * 2.0f) + square_sz); + labelPos.x += labelOffsetX; + } + const ImRect check_bb(checkPos, checkPos + ImVec2(square_sz, square_sz)); + ImGui::RenderNavHighlight(total_bb, id); + ImGui::RenderFrame(check_bb.Min, check_bb.Max, + ImGui::GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive + : hovered ? ImGuiCol_FrameBgHovered + : ImGuiCol_FrameBg), + true, style.FrameRounding); + ImU32 check_col = ImGui::GetColorU32(ImGuiCol_CheckMark); + bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0; + if (mixed_value) { + // Undocumented tristate/mixed/indeterminate checkbox (#2644) + // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all + // widgets (not just checkbox) + ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); + window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); + } else if (*value) { + const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); + ImGui::RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); + } + RenderText(labelPos, label, ImGui::FindRenderedTextEnd(label), true); + PopStyleCheckbox(); + ImGui::EndDisabled(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && + !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + return pressed; +} + +bool CVarCheckbox(const char* label, const char* cvarName, const CheckboxOptions& options) { + bool dirty = false; + bool value = (bool)CVarGetInteger(cvarName, options.defaultValue); + if (Checkbox(label, &value, options)) { + CVarSetInteger(cvarName, value); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; +} + +bool StateButton(const char* str_id, const char* label, ImVec2 size, ButtonOptions options, ImGuiButtonFlags flags) { + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) { + return false; + } + + const ImGuiStyle& style = g.Style; + const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + + const ImGuiID id = window->GetID(str_id); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + const float default_size = ImGui::GetFrameHeight(); + ImGui::ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f); + if (!ImGui::ItemAdd(bb, id)) + return false; + + if (g.LastItemData.ItemFlags & ImGuiItemFlags_ButtonRepeat) { + ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); + } + + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags); + + if (g.LastItemData.ItemFlags & ImGuiItemFlags_ButtonRepeat) { + ImGui::PopItemFlag(); // ImGuiItemFlags_ButtonRepeat; + } + PushStyleButton(options.color); + // Render + const ImU32 bg_col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive + : hovered ? ImGuiCol_ButtonHovered + : ImGuiCol_Button); + //const ImU32 text_col = ImGui::GetColorU32(ImGuiCol_Text); + ImGui::RenderNavHighlight(bb, id); + ImGui::RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); + ImGui::RenderTextClipped(bb.Min + (style.FramePadding * 0.35f), bb.Max - (style.FramePadding / 4), label, NULL, &label_size, style.ButtonTextAlign, &bb); + PopStyleButton(); + /*ImGui::RenderArrow(window->DrawList, + bb.Min + + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), + text_col, dir);*/ + + IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); + return pressed; +} + +float CalcComboWidth(const char* preview_value, ImGuiComboFlags flags) { + ImGuiContext& g = *GImGui; + + const ImGuiStyle& style = g.Style; + IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together + if (flags & ImGuiComboFlags_WidthFitPreview) + IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0); + + const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : ImGui::GetFrameHeight(); + const float preview_width = ImGui::CalcTextSize(preview_value, NULL, true).x; + float w = arrow_size + preview_width + (style.FramePadding.x * 2.0f); + return w; +} + +void PushStyleCombobox(const ImVec4& color) { + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f)); +} + +void PushStyleCombobox(Colors color) { + PushStyleCombobox(ColorValues.at(color)); +} + +void PopStyleCombobox() { + ImGui::PopStyleVar(4); + ImGui::PopStyleColor(9); +} + +void PushStyleTabs(const ImVec4& color) { + ImGui::PushStyleColor(ImGuiCol_Tab, ImVec4(color.x, color.y, color.z, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_TabHovered, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_TabActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f)); +} + +void PushStyleTabs(Colors color) { + PushStyleTabs(ColorValues.at(color)); +} + +void PopStyleTabs() { + ImGui::PopStyleColor(6); + ImGui::PopStyleVar(4); +} + +void PushStyleSlider(Colors color_) { + const ImVec4& color = ColorValues.at(color_); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(1.0, 1.0, 1.0, 0.4f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(1.0, 1.0, 1.0, 0.5f)); + ImGui::PushStyleVar(ImGuiStyleVar_GrabRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); +} + +void PopStyleSlider() { + ImGui::PopStyleVar(4); + ImGui::PopStyleColor(6); +} + +bool SliderInt(const char* label, int32_t* value, const IntSliderOptions& options) { + bool dirty = false; + std::string invisibleLabelStr = "##" + std::string(label); + const char* invisibleLabel = invisibleLabelStr.c_str(); + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleSlider(options.color); + float width = (options.size == ImVec2(0,0)) ? ImGui::GetContentRegionAvail().x : options.size.x; + if (options.labelPosition == LabelPositions::Near || options.labelPosition == LabelPositions::Far) { + width = width - (ImGui::CalcTextSize(label).x + ImGui::GetStyle().FramePadding.x); + } + ImGui::AlignTextToFramePadding(); + if (options.alignment == ComponentAlignments::Right) { + ImGui::Text(label, *value); + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + } else if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + } else if (options.labelPosition == LabelPositions::Far || options.labelPosition == LabelPositions::None) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + } + } else if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text(label, *value); + } + } + if (options.showButtons) { + if (Button("-", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value > options.min) { + *value -= options.step; + if (options.clamp) { + if (*value < options.min) { + *value = options.min; + } + } + dirty = true; + } + ImGui::SameLine(0, 3.0f); + ImGui::SetNextItemWidth(width - (ImGui::CalcTextSize("+").x + ImGui::GetStyle().FramePadding.x * 2 + 3) * 2); + } else { + ImGui::SetNextItemWidth(width); + } + if (ImGui::SliderScalar(invisibleLabel, ImGuiDataType_S32, value, &options.min, &options.max, options.format, + options.flags)) { + if (options.clamp) { + if (*value < options.min) { + *value = options.min; + } + if (*value > options.max) + *value = options.max; + } + dirty = true; + } + if (options.showButtons) { + ImGui::SameLine(0, 3.0f); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (Button("+", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value < options.max) { + *value += options.step; + if (options.clamp) { + if (*value > options.max) + *value = options.max; + } + dirty = true; + } + } + + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + ImGui::Text(label, *value); + } else if (options.labelPosition == LabelPositions::Far || options.labelPosition == LabelPositions::None) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x); + ImGui::Text(label, *value); + } + } + PopStyleSlider(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && + !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; +} + +bool CVarSliderInt(const char* label, const char* cvarName, const IntSliderOptions& options) { + bool dirty = false; + int32_t value = CVarGetInteger(cvarName, options.defaultValue); + if (SliderInt(label, &value, options)) { + CVarSetInteger(cvarName, value); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; +} + +void ClampFloat(float* value, float min, float max, float step) { + int factor = 1; + if (step < 1.0f) { + factor *= 10; + } + if (step < 0.1f) { + factor *= 10; + } + if (step < 0.01f) { + factor *= 10; + } + if (step < 0.001f) { + factor *= 10; + } + if (step < 0.0001f) { + factor *= 10; + } + if (step < 0.00001f) { + factor *= 10; + } + if (*value < min) { + *value = min; + } else if (*value > max) { + *value = max; + } else { + int trunc = (int)std::round(*value * factor); + *value = (float)trunc / factor; + } +} + +bool SliderFloat(const char* label, float* value, const FloatSliderOptions& options) { + bool dirty = false; + std::string invisibleLabelStr = "##" + std::string(label); + const char* invisibleLabel = invisibleLabelStr.c_str(); + float valueToDisplay = options.isPercentage ? *value * 100.0f : *value; + float maxToDisplay = options.isPercentage ? options.max * 100.0f : options.max; + float minToDisplay = options.isPercentage ? options.min * 100.0f : options.min; + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleSlider(options.color); + float labelSpacing = ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x; + float width = (options.size == ImVec2(0, 0)) ? ImGui::GetContentRegionAvail().x : options.size.x; + if (options.labelPosition == LabelPositions::Near || options.labelPosition == LabelPositions::Far) { + width = width - (ImGui::CalcTextSize(label).x + ImGui::GetStyle().FramePadding.x); + } + ImGui::AlignTextToFramePadding(); + if (options.alignment == ComponentAlignments::Right) { + ImGui::Text(label, *value); + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + } else if (options.labelPosition == LabelPositions::Near) { + width -= labelSpacing; + ImGui::SameLine(); + } else if (options.labelPosition == LabelPositions::Far || options.labelPosition == LabelPositions::None) { + width -= labelSpacing; + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + } + } else if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text(label, *value); + } + } + if (options.showButtons) { + if (Button("-", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value > options.min) { + *value -= options.step; + if (options.clamp) { + ClampFloat(value, options.min, options.max, options.step); + } + dirty = true; + } + ImGui::SameLine(0, 3.0f); + ImGui::SetNextItemWidth(width - (ImGui::CalcTextSize("+").x + ImGui::GetStyle().FramePadding.x * 2 + 3) * 2); + } else { + ImGui::SetNextItemWidth(width); + } + if (ImGui::SliderScalar(invisibleLabel, ImGuiDataType_Float, &valueToDisplay, &minToDisplay, &maxToDisplay, + options.format, options.flags)) { + *value = options.isPercentage ? valueToDisplay / 100.0f : valueToDisplay; + if (options.clamp) { + ClampFloat(value, options.min, options.max, options.step); + } + dirty = true; + } + if (options.showButtons) { + ImGui::SameLine(0, 3.0f); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (Button("+", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value < options.max) { + *value += options.step; + if (options.clamp) { + ClampFloat(value, options.min, options.max, options.step); + } + dirty = true; + } + } + + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + ImGui::Text(label, *value); + } else if (options.labelPosition == LabelPositions::Far || options.labelPosition == LabelPositions::None) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - labelSpacing); + ImGui::Text(label, *value); + } + } + PopStyleSlider(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && + !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; +} + +bool CVarSliderFloat(const char* label, const char* cvarName, const FloatSliderOptions& options) { + bool dirty = false; + float value = CVarGetFloat(cvarName, options.defaultValue); + if (SliderFloat(label, &value, options)) { + CVarSetFloat(cvarName, value); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; +} + +int InputTextResizeCallback(ImGuiInputTextCallbackData* data) { + std::string* value = (std::string*)data->UserData; + if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { + value->resize(data->BufTextLen); + data->Buf = (char*)value->c_str(); + } + return 0; +} + +bool InputString(const char* label, std::string* value, const InputOptions& options) { + bool dirty = false; + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleInput(options.color); + float width = (options.size == ImVec2(0, 0)) ? ImGui::GetContentRegionAvail().x : options.size.x; + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text(label, *value->c_str()); + } + } else if (options.alignment == ComponentAlignments::Right) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(width - ImGui::CalcTextSize(label).x); + ImGui::Text(label, *value->c_str()); + } + } + ImGui::SetNextItemWidth(width); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize; + if (options.secret) { + flags |= ImGuiInputTextFlags_Password; + } + flags |= options.addedFlags; + if (ImGui::InputText(label, (char*)value->c_str(), value->capacity() + 1, flags, InputTextResizeCallback, value)) { + dirty = true; + } + if (value->empty() && !options.placeholder.empty()) { + ImGui::SameLine(17.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "%s", options.placeholder.c_str()); + } + PopStyleInput(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && + !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; +} + +bool CVarInputString(const char* label, const char* cvarName, const InputOptions& options) { + bool dirty = false; + std::string value = CVarGetString(cvarName, options.defaultValue.c_str()); + if (InputString(label, &value, options)) { + CVarSetString(cvarName, value.c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; +} + +bool InputInt(const char* label, int32_t* value, const InputOptions& options) { + bool dirty = false; + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleInput(options.color); + float width = (options.size == ImVec2(0, 0)) ? ImGui::GetContentRegionAvail().x : options.size.x; + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text(label, *value); + } + } else if (options.alignment == ComponentAlignments::Right) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(width - ImGui::CalcTextSize(label).x); + ImGui::Text(label, *value); + } + } + ImGui::SetNextItemWidth(width); + if (ImGui::InputScalar(label, ImGuiDataType_S32, value, nullptr, nullptr, nullptr, options.addedFlags)) { + dirty = true; + } + if ((ImGui::GetItemStatusFlags() & ImGuiItemStatusFlags_Edited) && !options.placeholder.empty()) { + ImGui::SameLine(17.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "%s", options.placeholder.c_str()); + } + PopStyleInput(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && + !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; +} + +bool CVarInputInt(const char* label, const char* cvarName, const InputOptions& options) { + bool dirty = false; + int32_t defaultValue = std::stoi(options.defaultValue); + int32_t value = CVarGetInteger(cvarName, defaultValue); + if (InputInt(label, &value, options)) { + CVarSetInteger(cvarName, value); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; +} + +bool CVarColorPicker(const char* label, const char* cvarName, Color_RGBA8 defaultColor, bool hasAlpha, uint8_t modifiers, UIWidgets::Colors themeColor) { + std::string valueCVar = std::string(cvarName) + ".Value"; + std::string rainbowCVar = std::string(cvarName) + ".Rainbow"; + std::string lockedCVar = std::string(cvarName) + ".Locked"; + Color_RGBA8 color = CVarGetColor(valueCVar.c_str(), defaultColor); + ImVec4 colorVec = ImVec4(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); + bool changed = false; + bool showReset = modifiers & ColorPickerResetButton; + bool showRandom = modifiers & ColorPickerRandomButton; + bool showRainbow = modifiers & ColorPickerRainbowCheck; + bool showLock = modifiers & ColorPickerLockCheck; + bool locked = CVarGetInteger(lockedCVar.c_str(), 0); + ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoInputs; + ImGui::BeginDisabled(locked); + PushStyleCombobox(UIWidgets::Colors::DarkGray); + if (hasAlpha) { + changed = ImGui::ColorEdit4(label, (float*)&colorVec, flags | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreview); + } else { + changed = ImGui::ColorEdit3(label, (float*)&colorVec, flags | ImGuiColorEditFlags_NoAlpha); + } + PopStyleCombobox(); + ImGui::AlignTextToFramePadding(); + if (showReset) { + ImGui::SameLine(); + std::string uniqueTag = "Reset##" + std::string(label); + if (UIWidgets::Button(uniqueTag.c_str(), UIWidgets::ButtonOptions({{ .tooltip = "Resets this color to its default value" }} ).Color(themeColor).Size(UIWidgets::Sizes::Inline))) { + CVarClearBlock(valueCVar.c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + } + if (showRandom) { + ImGui::SameLine(); + std::string uniqueTag = "Random##" + std::string(label); + if (UIWidgets::Button(uniqueTag.c_str(), UIWidgets::ButtonOptions({{ .tooltip = "Generates a random color value to use" }}).Color(themeColor).Size(UIWidgets::Sizes::Inline))) { + colorVec = GetRandomValue(); + color.r = fmin(fmax(colorVec.x * 255, 0), 255); + color.g = fmin(fmax(colorVec.y * 255, 0), 255); + color.b = fmin(fmax(colorVec.z * 255, 0), 255); + CVarSetColor(valueCVar.c_str(), color); + CVarSetInteger(rainbowCVar.c_str(), 0); // On click disable rainbow mode. + ShipInit::Init(rainbowCVar.c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + } + if (showRainbow) { + ImGui::SameLine(); + std::string uniqueTag = "Rainbow##" + std::string(cvarName) + "Rainbow"; + + UIWidgets::CVarCheckbox(uniqueTag.c_str(), rainbowCVar.c_str(), UIWidgets::CheckboxOptions({{ .tooltip = "Cycles through colors on a timer\nOverwrites previously chosen color" }}).Color(themeColor)); + } + ImGui::EndDisabled(); + if (showLock) { + ImGui::SameLine(); + std::string uniqueTag = "Lock##" + std::string(cvarName) + "Locked"; + + UIWidgets::CVarCheckbox(uniqueTag.c_str(), lockedCVar.c_str(), UIWidgets::CheckboxOptions({{ .tooltip = "Prevents this color from being changed" }}).Color(themeColor)); + } + if (changed) { + color.r = (uint8_t)(colorVec.x * 255.0f); + color.g = (uint8_t)(colorVec.y * 255.0f); + color.b = (uint8_t)(colorVec.z * 255.0f); + color.a = (uint8_t)(colorVec.w * 255.0f); + CVarSetColor(valueCVar.c_str(), color); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(valueCVar.c_str()); + changed = true; + } + + return changed; +} + +bool RadioButton(const char* label, bool active, const RadioButtonsOptions& options) { + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + + const float square_sz = ImGui::GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; + const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); + const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ImGui::ItemSize(total_bb, style.FramePadding.y); + if (!ImGui::ItemAdd(total_bb, id)) + return false; + + ImVec2 center = check_bb.GetCenter(); + center.x = IM_ROUND(center.x); + center.y = IM_ROUND(center.y); + const float radius = (square_sz - 1.0f) * 0.5f; + + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + ImGui::MarkItemEdited(id); + + ImGui::RenderNavCursor(total_bb, id); + const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius); + window->DrawList->AddCircleFilled(center, radius, ImGui::GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment); + if (active) + { + const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius - pad, ImGui::GetColorU32(ImGuiCol_CheckMark)); + } + + if (style.FrameBorderSize > 0.0f) + { + window->DrawList->AddCircle(center + ImVec2(1, 1), radius, ImGui::GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize); + window->DrawList->AddCircle(center, radius, ImGui::GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize); + } + + ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); + if (g.LogEnabled) + ImGui::LogRenderedText(&label_pos, active ? "(x)" : "( )"); + if (label_size.x > 0.0f) + RenderText(label_pos, label, ImGui::FindRenderedTextEnd(label), true); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; +} + +bool CVarRadioButton(const char* text, const char* cvarName, int32_t id, const RadioButtonsOptions& options) { + std::string make_invisible = "##" + std::string(text) + std::string(cvarName); + + bool ret = false; + int val = CVarGetInteger(cvarName, 0); + PushStyleCheckbox(options.color); + if (ImGui::RadioButton(make_invisible.c_str(), id == val)) { + CVarSetInteger(cvarName, id); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ret = true; + } + ImGui::SameLine(); + ImGui::Text("%s", text); + PopStyleCheckbox(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + + return ret; +} + +void DrawFlagArray32(const std::string& name, uint32_t& flags, Colors color) { + ImGui::PushID(name.c_str()); + for (int32_t flagIndex = 0; flagIndex < 32; flagIndex++) { + if ((flagIndex % 8) != 0) { + ImGui::SameLine(); + } + ImGui::PushID(flagIndex); + uint32_t bitMask = 1 << flagIndex; + bool flag = (flags & bitMask) != 0; + PushStyleCheckbox(color); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)); + std::string id = fmt::format("##{}{}", name, flagIndex); + if (ImGui::Checkbox(id.c_str(), &flag)) { + if (flag) { + flags |= bitMask; + } else { + flags &= ~bitMask; + } + } + ImGui::PopStyleVar(); + PopStyleCheckbox(); + ImGui::PopID(); + } + ImGui::PopID(); +} + +void DrawFlagArray16(const std::string& name, uint16_t& flags, Colors color) { + ImGui::PushID(name.c_str()); + for (int16_t flagIndex = 0; flagIndex < 16; flagIndex++) { + if ((flagIndex % 8) != 0) { + ImGui::SameLine(); + } + ImGui::PushID(flagIndex); + uint16_t bitMask = 1 << flagIndex; + bool flag = (flags & bitMask) != 0; + PushStyleCheckbox(color); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)); + std::string id = fmt::format("##{}{}", name, flagIndex); + if (ImGui::Checkbox(id.c_str(), &flag)) { + if (flag) { + flags |= bitMask; + } else { + flags &= ~bitMask; + } + } + ImGui::PopStyleVar(); + PopStyleCheckbox(); + ImGui::PopID(); + } + ImGui::PopID(); +} + +void DrawFlagArray8(const std::string& name, uint8_t& flags, Colors color) { + ImGui::PushID(name.c_str()); + for (int8_t flagIndex = 0; flagIndex < 8; flagIndex++) { + if ((flagIndex % 8) != 0) { + ImGui::SameLine(); + } + ImGui::PushID(flagIndex); + uint8_t bitMask = 1 << flagIndex; + bool flag = (flags & bitMask) != 0; + PushStyleCheckbox(color); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)); + std::string id = fmt::format("##{}{}", name, flagIndex); + if (ImGui::Checkbox(id.c_str(), &flag)) { + if (flag) { + flags |= bitMask; + } else { + flags &= ~bitMask; + } + } + ImGui::PopStyleVar(); + PopStyleCheckbox(); + ImGui::PopID(); + } + ImGui::PopID(); +} + +void DrawFlagArray8Mask(const std::string& name, uint8_t& flags, Colors color) { + ImGui::PushID(name.c_str()); + for (int8_t flagIndex = 0; flagIndex < 8; flagIndex++) { + if ((flagIndex % 8) != 0) { + ImGui::SameLine(); + } + ImGui::PushID(flagIndex); + uint8_t bitMask = 1 << flagIndex; + bool flag = (flags & bitMask) != 0; + PushStyleCheckbox(color); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)); + std::string id = fmt::format("##{}{}", name, flagIndex); + if (ImGui::Checkbox(id.c_str(), &flag)) { + if (flag) { + flags |= bitMask; + } else { + flags &= ~bitMask; + } + } + ImGui::PopStyleVar(); + PopStyleCheckbox(); + ImGui::PopID(); + } + ImGui::PopID(); +} +} // namespace UIWidgets + +ImVec4 GetRandomValue() { +#if !defined(__SWITCH__) && !defined(__WIIU__) + std::random_device rd; + std::mt19937 rng(rd()); +#else + size_t seed = std::hash{}(std::to_string(rand())); + std::mt19937_64 rng(seed); +#endif + std::uniform_int_distribution dist(0, 255 - 1); + + ImVec4 NewColor; + NewColor.x = (float)(dist(rng)) / 255.0f; + NewColor.y = (float)(dist(rng)) / 255.0f; + NewColor.z = (float)(dist(rng)) / 255.0f; + return NewColor; +} + +Color_RGBA8 RGBA8FromVec(ImVec4 vec) { + Color_RGBA8 color = { vec.x * 255, vec.y * 255, vec.z * 255, vec.w * 255 }; + return color; +} + +ImVec4 VecFromRGBA8(Color_RGBA8 color) { + ImVec4 vec = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; + return vec; +} diff --git a/soh/soh/SohGui/UIWidgets.hpp b/soh/soh/SohGui/UIWidgets.hpp index 97a367270..325795023 100644 --- a/soh/soh/SohGui/UIWidgets.hpp +++ b/soh/soh/SohGui/UIWidgets.hpp @@ -1,16 +1,21 @@ -#ifndef UIWidgets_hpp -#define UIWidgets_hpp +#ifndef UIWidgets2_hpp +#define UIWidgets2_hpp #include -#include #include #include #include +#define IMGUI_DEFINE_MATH_OPERATORS #include +#include +#include +#include "soh/ShipUtils.h" #include "soh/ShipInit.hpp" namespace UIWidgets { + using SectionFunc = void(*)(); + struct TextFilters { static int FilterNumbers(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("1234567890", (char)data->EventChar)) { @@ -26,74 +31,889 @@ namespace UIWidgets { } return 1; } + }; + + std::string WrappedText(const char* text, unsigned int charactersPerLine = 80); + std::string WrappedText(const std::string& text, unsigned int charactersPerLine = 80); + void PaddedSeparator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f, float extraVerticalBottomPadding = 0.0f); + void Tooltip(const char* text); + + typedef enum ColorPickerModifiers { + ColorPickerResetButton = 1, + ColorPickerRandomButton = 2, + ColorPickerRainbowCheck = 4, + ColorPickerLockCheck = 8, + } ColorPickerModifiers; + + // mostly in order for colors usable by the menu without custom text color + enum Colors { + Red, + DarkRed, + Orange, + Green, + DarkGreen, + LightBlue, + Blue, + DarkBlue, + Indigo, + Violet, + Purple, + Brown, + Gray, + DarkGray, + // not suitable for menu theme use + Pink, + Yellow, + Cyan, + Black, + LightGray, + White, + NoColor + }; + + enum InputTypes { + String, + Scalar + }; + + const std::unordered_map ColorValues = { + { Colors::Pink, ImVec4(0.87f, 0.3f, 0.87f, 1.0f) }, + { Colors::Red, ImVec4(0.55f, 0.0f, 0.0f, 1.0f) }, + { Colors::DarkRed, ImVec4(0.3f, 0.0f, 0.0f, 1.0f) }, + { Colors::Orange, ImVec4(0.85f, 0.55f, 0.0f, 1.0f) }, + { Colors::Yellow, ImVec4(0.95f, 0.95f, 0.0f, 1.0f) }, + { Colors::Green, ImVec4(0.0f, 0.55f, 0.0f, 1.0f) }, + { Colors::DarkGreen, ImVec4(0.0f, 0.3f, 0.0f, 1.0f) }, + { Colors::Cyan, ImVec4(0.0f, 0.9f, 0.9f, 1.0f) }, + { Colors::LightBlue, ImVec4(0.0f, 0.24f, 0.8f, 1.0f) }, + { Colors::Blue, ImVec4(0.08f, 0.03f, 0.65f, 1.0f) }, + { Colors::DarkBlue, ImVec4(0.03f, 0.0f, 0.5f, 1.0f) }, + { Colors::Indigo, ImVec4(0.35f, 0.0f, 0.87f, 1.0f) }, + { Colors::Violet, ImVec4(0.5f, 0.0f, 0.9f, 1.0f) }, + { Colors::Purple, ImVec4(0.31f, 0.0f, 0.67f, 1.0f) }, + { Colors::Brown, ImVec4(0.37f, 0.18f, 0.0f, 1.0f) }, + { Colors::LightGray, ImVec4(0.75f, 0.75f, 0.75f, 1.0f) }, + { Colors::Gray, ImVec4(0.45f, 0.45f, 0.45f, 1.0f) }, + { Colors::DarkGray, ImVec4(0.15f, 0.15f, 0.15f, 1.0f) }, + { Colors::Black, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)}, + { Colors::White, ImVec4(1.0f, 1.0f, 1.0f, 1.0f) }, + { Colors::NoColor, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)}, + }; + + namespace Sizes { + const ImVec2 Inline = ImVec2(0.0f, 0.0f); + const ImVec2 Fill = ImVec2(-1.0f, 0.0f); + } + + enum LabelPositions { + Near, + Far, + Above, + None, + Within, + }; + + enum ComponentAlignments { + Left, + Right, + }; + + struct WidgetOptions{ + const char* tooltip = ""; + bool disabled = false; + const char* disabledTooltip = ""; + + WidgetOptions& Tooltip(const char* tooltip_) { + tooltip = tooltip_; + return *this; + } + WidgetOptions& Disabled(bool disabled_) { + disabled = disabled_; + return *this; + } + WidgetOptions& DisabledTooltip(const char* disabledTooltip_) { + disabledTooltip = disabledTooltip_; + return *this; + } + }; + + struct TextOptions : WidgetOptions { + Colors color = Colors::NoColor; + + TextOptions& Color(Colors color_) { + color = color_; + return *this; + } + }; + + struct ButtonOptions : WidgetOptions { + ImVec2 size = Sizes::Fill; + ImVec2 padding = ImVec2(10.0f, 8.0f); + Colors color = Colors::Gray; + + ButtonOptions& Size(ImVec2 size_) { + size = size_; + return *this; + } + ButtonOptions& Padding(ImVec2 padding_) { + padding = padding_; + return *this; + } + ButtonOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + ButtonOptions& Color(Colors color_) { + color = color_; + return *this; + } + }; + + struct WindowButtonOptions : WidgetOptions { + ImVec2 size = Sizes::Inline; + ImVec2 padding = ImVec2(10.0f, 8.0f); + Colors color = Colors::Gray; + bool showButton = true; + bool embedWindow = true; + + WindowButtonOptions& Size(ImVec2 size_) { + size = size_; + return *this; + } + WindowButtonOptions& Padding(ImVec2 padding_) { + padding = padding_; + return *this; + } + WindowButtonOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + WindowButtonOptions& Color(Colors color_) { + color = color_; + return *this; + } + WindowButtonOptions& ShowButton(bool showButton_) { + showButton = showButton_; + return *this; + } + WindowButtonOptions& EmbedWindow(bool embedWindow_) { + embedWindow = embedWindow_; + return *this; + } + }; + + struct CheckboxOptions : WidgetOptions { + bool defaultValue = false; // Only applicable to CVarCheckbox + ComponentAlignments alignment = ComponentAlignments::Left; + LabelPositions labelPosition = LabelPositions::Near; + Colors color = Colors::LightBlue; + + CheckboxOptions& DefaultValue(bool defaultValue_) { + defaultValue = defaultValue_; + return *this; + } + CheckboxOptions& ComponentAlignment(ComponentAlignments alignment_) { + alignment = alignment_; + return *this; + } + CheckboxOptions& LabelPosition(LabelPositions labelPosition_) { + labelPosition = labelPosition_; + return *this; + } + CheckboxOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + CheckboxOptions& Color(Colors color_) { + color = color_; + return *this; + } + CheckboxOptions& DisabledTooltip(const char* disabledTooltip_) { + WidgetOptions::disabledTooltip = disabledTooltip_; + return *this; + } + }; + + struct ComboboxOptions : WidgetOptions { + std::unordered_map comboMap = {}; + uint32_t defaultIndex = 0; // Only applicable to CVarCombobox + ComponentAlignments alignment = ComponentAlignments::Left; + LabelPositions labelPosition = LabelPositions::Above; + ImGuiComboFlags flags = 0; + Colors color = Colors::LightBlue; + + ComboboxOptions& ComboMap(std::unordered_map comboMap_) { + comboMap = comboMap_; + return *this; + } + ComboboxOptions& DefaultIndex(uint32_t defaultIndex_) { + defaultIndex = defaultIndex_; + return *this; + } + ComboboxOptions& ComponentAlignment(ComponentAlignments alignment_) { + alignment = alignment_; + return *this; + } + ComboboxOptions& LabelPosition(LabelPositions labelPosition_) { + labelPosition = labelPosition_; + return *this; + } + ComboboxOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + ComboboxOptions& Color(Colors color_) { + color = color_; + return *this; + } + }; + + struct IntSliderOptions : WidgetOptions { + bool showButtons = true; + const char* format = "%d"; + int32_t step = 1; + int32_t min = 1; + int32_t max = 10; + int32_t defaultValue = 1; + bool clamp = true; + ComponentAlignments alignment = ComponentAlignments::Left; + LabelPositions labelPosition = LabelPositions::Above; + Colors color = Colors::Gray; + ImGuiSliderFlags flags = 0; + ImVec2 size = {0,0}; + + IntSliderOptions& ShowButtons(bool showButtons_) { + showButtons = showButtons_; + return *this; + } + IntSliderOptions& Format(const char* format_) { + format = format_; + return *this; + } + IntSliderOptions& Step(int32_t step_) { + step = step_; + return *this; + } + IntSliderOptions& Min(int32_t min_) { + min = min_; + return *this; + } + IntSliderOptions& Max(int32_t max_) { + max = max_; + return *this; + } + IntSliderOptions& DefaultValue(int32_t defaultValue_) { + defaultValue = defaultValue_; + return *this; + } + IntSliderOptions& ComponentAlignment(ComponentAlignments alignment_) { + alignment = alignment_; + return *this; + } + IntSliderOptions& LabelPosition(LabelPositions labelPosition_) { + labelPosition = labelPosition_; + return *this; + } + IntSliderOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + IntSliderOptions& Color(Colors color_) { + color = color_; + return *this; + } + IntSliderOptions& Size(ImVec2 size_) { + size = size_; + return *this; + } + IntSliderOptions& Clamp(bool clamp_) { + clamp = clamp_; + return *this; + } + }; + + struct FloatSliderOptions : WidgetOptions { + bool showButtons = true; + const char* format = "%f"; + float step = 0.01f; + float min = 0.01f; + float max = 10.0f; + float defaultValue = 1.0f; + bool clamp = true; + bool isPercentage = false; // Multiplies visual value by 100 + ComponentAlignments alignment = ComponentAlignments::Left; + LabelPositions labelPosition = LabelPositions::Above; + Colors color = Colors::Gray; + ImGuiSliderFlags flags = 0; + ImVec2 size = {0,0}; + + FloatSliderOptions& ShowButtons(bool showButtons_) { + showButtons = showButtons_; + return *this; + } + FloatSliderOptions& Format(const char* format_) { + format = format_; + return *this; + } + FloatSliderOptions& Step(float step_) { + step = step_; + return *this; + } + FloatSliderOptions& Min(float min_) { + min = min_; + return *this; + } + FloatSliderOptions& Max(float max_) { + max = max_; + return *this; + } + FloatSliderOptions& DefaultValue(float defaultValue_) { + defaultValue = defaultValue_; + return *this; + } + FloatSliderOptions& ComponentAlignment(ComponentAlignments alignment_) { + alignment = alignment_; + return *this; + } + FloatSliderOptions& LabelPosition(LabelPositions labelPosition_) { + labelPosition = labelPosition_; + return *this; + } + FloatSliderOptions& IsPercentage(bool isPercentage_ = true) { + isPercentage = isPercentage_; + format = "%.0f%%"; + min = 0.0f; + max = 1.0f; + return *this; + } + FloatSliderOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + FloatSliderOptions& Color(Colors color_) { + color = color_; + return *this; + } + FloatSliderOptions& Size(ImVec2 size_) { + size = size_; + return *this; + } + FloatSliderOptions& Clamp(bool clamp_) { + clamp = clamp_; + return *this; + } + }; + + struct RadioButtonsOptions : WidgetOptions { + std::unordered_map buttonMap; + Colors color = Colors::LightBlue; + RadioButtonsOptions& ButtonMap(std::unordered_map buttonMap_) { + buttonMap = buttonMap_; + return *this; + } + RadioButtonsOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + RadioButtonsOptions& Color(Colors color_) { + color = color_; + return *this; + } }; - // MARK: - Enums + struct InputOptions : WidgetOptions { + ComponentAlignments alignment = ComponentAlignments::Left; + LabelPositions labelPosition = LabelPositions::Above; + Colors color = Colors::Gray; + ImVec2 size = {0,0}; + std::string placeholder = ""; + InputTypes type = InputTypes::String; + std::string defaultValue = ""; + bool secret = false; + ImGuiInputFlags addedFlags = 0; - enum class CheckboxGraphics { - Cross, - Checkmark, - None + InputOptions& Tooltip(const char* tooltip_) { + WidgetOptions::tooltip = tooltip_; + return *this; + } + InputOptions& Color(Colors color_) { + color = color_; + return *this; + } + InputOptions& Size(ImVec2 size_) { + size = size_; + return *this; + } + + InputOptions& LabelPosition(LabelPositions labelPosition_) { + labelPosition = labelPosition_; + return *this; + } + + InputOptions& PlaceholderText(std::string&& placeholder_) { + placeholder = std::move(placeholder_); + return *this; + } + + InputOptions& PlaceholderText(std::string& placeholder_) { + placeholder = placeholder_; + return *this; + } + + InputOptions& InputType(InputTypes type_) { + type = type_; + return *this; + } + + InputOptions& DefaultValue(std::string defaultValue_) { + defaultValue = defaultValue_; + return *this; + } + + InputOptions& IsSecret(bool secret_ = false) { + secret = secret_; + return *this; + } }; - constexpr float maxSliderWidth = 260.0f; -#ifdef __SWITCH__ - constexpr float sliderButtonWidth = 42.0f; -#elif defined(__WIIU__) - constexpr float sliderButtonWidth = 60.0f; -#else - constexpr float sliderButtonWidth = 30.0f; -#endif - std::string WrappedText(const char* text, unsigned int charactersPerLine = 60); - std::string WrappedText(const std::string& text, unsigned int charactersPerLine); + void PushStyleMenu(const ImVec4& color); + void PushStyleMenu(Colors color = Colors::LightBlue); + void PopStyleMenu(); + bool BeginMenu(const char* label, Colors color = Colors::LightBlue); - void SetLastItemHoverText(const std::string& text); - void SetLastItemHoverText(const char* text); + void PushStyleMenuItem(const ImVec4& color); + void PushStyleMenuItem(Colors color = Colors::LightBlue); + void PopStyleMenuItem(); + bool MenuItem(const char* label, const char* shortcut = NULL, Colors color = Colors::LightBlue); + + void PushStyleButton(const ImVec4& color, ImVec2 padding = ImVec2(10.0f, 8.0f)); + void PushStyleButton(Colors color = Colors::Gray, ImVec2 padding = ImVec2(10.0f, 8.0f)); + void PopStyleButton(); + bool Button(const char* label, const ButtonOptions& options = {}); + bool WindowButton(const char* label, const char* cvarName, std::shared_ptr windowPtr, const WindowButtonOptions& options = {}); + + void PushStyleCheckbox(const ImVec4& color); + void PushStyleCheckbox(Colors color = Colors::LightBlue); + void PopStyleCheckbox(); + void RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash); + bool Checkbox(const char* label, bool* v, const CheckboxOptions& options = {}); + bool CVarCheckbox(const char* label, const char* cvarName, const CheckboxOptions& options = {}); + + void PushStyleCombobox(const ImVec4& color); + void PushStyleCombobox(Colors color = Colors::LightBlue); + void PopStyleCombobox(); + + void PushStyleTabs(const ImVec4& color); + void PushStyleTabs(Colors color = Colors::LightBlue); + void PopStyleTabs(); + + void PushStyleInput(const ImVec4& color); + void PushStyleInput(Colors color = Colors::LightBlue); + void PopStyleInput(); + + void PushStyleHeader(const ImVec4& color); + void PushStyleHeader(Colors color = Colors::LightBlue); + void PopStyleHeader(); + + void Spacer(float height = 0.0f); + void Separator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f, + float extraVerticalBottomPadding = 0.0f); + + float CalcComboWidth(const char* preview_value, ImGuiComboFlags flags); + + template + bool Combobox(const char* label, T* value, const std::unordered_map& comboMap, const ComboboxOptions& options = {}) { + bool dirty = false; + float startX = ImGui::GetCursorPosX(); + std::string invisibleLabelStr = "##" + std::string(label); + const char* invisibleLabel = invisibleLabelStr.c_str(); + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleCombobox(options.color); + + const char* longest; + int length = 0; + for (auto& [index, string] : comboMap) { + int len = strlen(string); + if (len > length) { + longest = string; + length = len; + } + } + float comboWidth = CalcComboWidth(longest, options.flags); + + ImGui::AlignTextToFramePadding(); + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Right) { + ImGui::Text("%s", label); + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } else if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + } else if (options.labelPosition == LabelPositions::Far) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } + } else if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text("%s", label); + } + } + } + + ImGui::SetNextItemWidth(comboWidth); + if (ImGui::BeginCombo(invisibleLabel, comboMap.at(*value), options.flags)) { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f)); + for (const auto& pair : comboMap) { + if (strlen(pair.second) > 1) { + if (ImGui::Selectable(pair.second, pair.first == *value)) { + *value = pair.first; + dirty = true; + } + } + } + ImGui::PopStyleVar(); + ImGui::EndCombo(); + } + + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + ImGui::Text("%s", label); + } else if (options.labelPosition == LabelPositions::Far) { + float width = ImGui::CalcTextSize(comboMap.at(*value)).x + ImGui::GetStyle().FramePadding.x * 2; + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + ImGui::Text("%s", label); + } + } + } + PopStyleCombobox(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; + } + + template + bool Combobox(const char* label, T* value, const std::vector& comboVector, const ComboboxOptions& options = {}) { + bool dirty = false; + size_t currentValueIndex = static_cast(*value); + std::string invisibleLabelStr = "##" + std::string(label); + const char* invisibleLabel = invisibleLabelStr.c_str(); + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleCombobox(options.color); + + const char* longest; + int length = 0; + for (auto& string : comboVector) { + int len = strlen(string); + if (len > length) { + longest = string; + length = len; + } + } + float comboWidth = CalcComboWidth(longest, options.flags); + + ImGui::AlignTextToFramePadding(); + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Right) { + ImGui::Text("%s", label); + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } else if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + } else if (options.labelPosition == LabelPositions::Far) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } + } else if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text("%s", label); + } + } + } + + ImGui::SetNextItemWidth(comboWidth); + if (ImGui::BeginCombo(invisibleLabel, comboVector.at(currentValueIndex), options.flags)) { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f)); + for (size_t i = 0; i < comboVector.size(); ++i) { + auto newValue = static_cast(i); + if (strlen(comboVector.at(i)) > 1) { + if (ImGui::Selectable(comboVector.at(i), newValue == *value)) { + *value = newValue; + dirty = true; + } + } + } + ImGui::PopStyleVar(); + ImGui::EndCombo(); + } + + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + ImGui::Text("%s", label); + } else if (options.labelPosition == LabelPositions::Far) { + float width = ImGui::CalcTextSize(comboVector.at(*value)).x + ImGui::GetStyle().FramePadding.x * 2; + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + ImGui::Text("%s", label); + } + } + } + + PopStyleCombobox(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; + } + + template + bool Combobox(const char* label, T* value, const std::vector& comboVector, const ComboboxOptions& options = {}) { + bool dirty = false; + size_t currentValueIndex = static_cast(*value); + std::string invisibleLabelStr = "##" + std::string(label); + const char* invisibleLabel = invisibleLabelStr.c_str(); + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleCombobox(options.color); + + const char* longest; + int length = 0; + for (auto& string : comboVector) { + int len = string.length(); + if (len > length) { + longest = string.c_str(); + length = len; + } + } + float comboWidth = CalcComboWidth(longest, options.flags); + + ImGui::AlignTextToFramePadding(); + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Right) { + ImGui::Text("%s", label); + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } else if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + } else if (options.labelPosition == LabelPositions::Far) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } + } else if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text("%s", label); + } + } + } + + ImGui::SetNextItemWidth(comboWidth); + if (ImGui::BeginCombo(invisibleLabel, comboVector.at(currentValueIndex).c_str(), options.flags)) { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f)); + for (size_t i = 0; i < comboVector.size(); ++i) { + auto newValue = static_cast(i); + if (comboVector.at(i).length() > 1) { + if (ImGui::Selectable(comboVector.at(i).c_str(), newValue == *value)) { + *value = newValue; + dirty = true; + } + } + } + ImGui::PopStyleVar(); + ImGui::EndCombo(); + } + + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + ImGui::Text("%s", label); + } else if (options.labelPosition == LabelPositions::Far) { + float width = ImGui::CalcTextSize(comboVector.at(*value).c_str()).x + ImGui::GetStyle().FramePadding.x * 2; + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + ImGui::Text("%s", label); + } + } + } + + PopStyleCombobox(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; + } + + template + bool Combobox(const char* label, T* value, const char* (&comboArray)[N], const ComboboxOptions& options = {}) { + bool dirty = false; + size_t currentValueIndex = static_cast(*value); + if (currentValueIndex >= N) { + currentValueIndex = 0; + } + std::string invisibleLabelStr = "##" + std::string(label); + const char* invisibleLabel = invisibleLabelStr.c_str(); + ImGui::PushID(label); + ImGui::BeginGroup(); + ImGui::BeginDisabled(options.disabled); + PushStyleCombobox(options.color); + + const char* longest; + int length = 0; + for (size_t i = 0; i < N; i++) { + int len = strlen(comboArray[i]); + if (len > length) { + longest = comboArray[i]; + length = len; + } + } + float comboWidth = CalcComboWidth(longest, options.flags); + + ImGui::AlignTextToFramePadding(); + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Right) { + ImGui::Text("%s", label); + if (options.labelPosition == LabelPositions::Above) { + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } else if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + } else if (options.labelPosition == LabelPositions::Far) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - comboWidth); + } + } else if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Above) { + ImGui::Text("%s", label); + } + } + } + + ImGui::SetNextItemWidth(comboWidth); + if (ImGui::BeginCombo(invisibleLabel, comboArray[currentValueIndex], options.flags)) { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f)); + for (size_t i = 0; i < N; ++i) { + auto newValue = static_cast(i); + if (strlen(comboArray[i]) > 1) { + if (ImGui::Selectable(comboArray[i], newValue == *value)) { + *value = newValue; + dirty = true; + } + } + } + ImGui::PopStyleVar(); + ImGui::EndCombo(); + } + + if (options.labelPosition != LabelPositions::None) { + if (options.alignment == ComponentAlignments::Left) { + if (options.labelPosition == LabelPositions::Near) { + ImGui::SameLine(); + ImGui::Text("%s", label); + } else if (options.labelPosition == LabelPositions::Far) { + float width = ImGui::CalcTextSize(comboArray[*value]).x + ImGui::GetStyle().FramePadding.x * 2; + ImGui::SameLine(ImGui::GetContentRegionAvail().x - width); + ImGui::Text("%s", label); + } + } + } + PopStyleCombobox(); + ImGui::EndDisabled(); + ImGui::EndGroup(); + if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); + } + ImGui::PopID(); + return dirty; + } + + template + bool CVarCombobox(const char* label, const char* cvarName, const std::unordered_map& comboMap, const ComboboxOptions& options = {}) { + bool dirty = false; + int32_t value = CVarGetInteger(cvarName, options.defaultIndex); + if (Combobox(label, &value, comboMap, options)) { + CVarSetInteger(cvarName, value); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; + } + + template + bool CVarCombobox(const char* label, const char* cvarName, const std::vector& comboVector, const ComboboxOptions& options = {}) { + bool dirty = false; + int32_t value = CVarGetInteger(cvarName, options.defaultIndex); + if (Combobox(label, &value, comboVector, options)) { + CVarSetInteger(cvarName, value); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; + } + + template + bool CVarCombobox(const char* label, const char* cvarName, const char* (&comboArray)[N], const ComboboxOptions& options = {}) { + bool dirty = false; + int32_t value = CVarGetInteger(cvarName, options.defaultIndex); + if (Combobox(label, &value, comboArray, options)) { + CVarSetInteger(cvarName, value); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); + dirty = true; + } + return dirty; + } + + void PushStyleSlider(Colors color = Colors::LightBlue); + void PopStyleSlider(); + bool SliderInt(const char* label, int32_t* value, const IntSliderOptions& options = {}); + bool CVarSliderInt(const char* label, const char* cvarName, const IntSliderOptions& options = {}); + bool SliderFloat(const char* label, float* value, const FloatSliderOptions& options = {}); + bool CVarSliderFloat(const char* label, const char* cvarName, const FloatSliderOptions& options = {}); + bool InputString(const char* label, std::string* value, const InputOptions& options = {}); + bool CVarInputString(const char* label, const char* cvarName, const InputOptions& options = {}); + bool InputInt(const char* label, int32_t* value, const InputOptions& options = {}); + bool CVarInputInt(const char* label, const char* cvarName, const InputOptions& options = {}); + bool CVarColorPicker(const char* label, const char* cvarName, Color_RGBA8 defaultColor, bool hasAlpha = false, uint8_t modifiers = 0, UIWidgets::Colors themeColor = UIWidgets::Colors::LightBlue); + bool RadioButton(const char* label, bool active); + bool CVarRadioButton(const char* text, const char* cvarName, int32_t id, const RadioButtonsOptions& options); + bool StateButton(const char* str_id, const char* label, ImVec2 size, UIWidgets::ButtonOptions options, ImGuiButtonFlags flags = ImGuiButtonFlags_None); + void DrawFlagArray32(const std::string& name, uint32_t& flags, Colors color = Colors::LightBlue); + void DrawFlagArray16(const std::string& name, uint16_t& flags, Colors color = Colors::LightBlue); + void DrawFlagArray8(const std::string& name, uint8_t& flags, Colors color = Colors::LightBlue); + void DrawFlagArray8Mask(const std::string& name, uint8_t& flags, Colors color = Colors::LightBlue); void InsertHelpHoverText(const std::string& text); void InsertHelpHoverText(const char* text); - - void Tooltip(const char* text); - void Spacer(float height); - void PaddedSeparator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f, float extraVerticalBottomPadding = 0.0f); - - void RenderCross(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); - bool CustomCheckbox(const char* label, bool* v, bool disabled, CheckboxGraphics disabledGraphic, bool renderCrossWhenOff = false); - bool CustomCheckboxTristate(const char* label, int* v, bool disabled, CheckboxGraphics disabledGraphic); - - void ReEnableComponent(const char* disabledTooltipText); - void DisableComponent(const float alpha); - - bool EnhancementCheckbox(const char* text, const char* cvarName, bool disabled = false, const char* disabledTooltipText = "", CheckboxGraphics disabledGraphic = CheckboxGraphics::Cross, bool defaultValue = false); - bool EnhancementCheckboxTristate(const char* text, const char* cvarName, bool disabled = false, const char* disabledTooltipText = "", CheckboxGraphics disabledGraphic = CheckboxGraphics::Cross, bool defaultValue = false); - bool PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop = true, bool padBottom = true, bool disabled = false, const char* disabledTooltipText = "", CheckboxGraphics disabledGraphic = CheckboxGraphics::Cross, bool defaultValue = false); - - bool EnhancementCombobox(const char* cvarName, std::span comboArray, uint8_t defaultIndex, bool disabled = false, const char* disabledTooltipText = "", uint8_t disabledValue = -1); - bool LabeledRightAlignedEnhancementCombobox(const char* label, const char* cvarName, std::span comboArray, uint8_t defaultIndex, bool disabled = false, const char* disabledTooltipText = "", uint8_t disabledValue = -1); - - void PaddedText(const char* text, bool padTop = true, bool padBottom = true); - - bool EnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue = 0, bool PlusMinusButton = true, bool disabled = false, const char* disabledTooltipText = ""); - bool PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue = 0, bool PlusMinusButton = true, bool padTop = true, bool padBottom = true, bool disabled = false, const char* disabledTooltipText = ""); - bool EnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton = true, bool disabled = false, const char* disabledTooltipText = ""); - bool PaddedEnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton = true, bool padTop = true, bool padBottom = true, bool disabled = false, const char* disabledTooltipText = ""); - - bool EnhancementRadioButton(const char* text, const char* cvarName, int id); - - bool DrawResetColorButton(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha); - bool DrawRandomizeColorButton(const char* cvarName, ImVec4* colors); - void DrawLockColorCheckbox(const char* cvarName); - void RainbowColor(const char* cvarName, ImVec4* colors); - - void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha); - bool EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow = true, bool has_alpha = false, bool TitleSameLine = false); - - void DrawFlagArray32(const std::string& name, uint32_t& flags); - void DrawFlagArray16(const std::string& name, uint16_t& flags); - void DrawFlagArray8(const std::string& name, uint8_t& flags); - bool StateButton(const char* str_id, const char* label); - bool InputString(const char* label, std::string* value); } +ImVec4 GetRandomValue(); + +Color_RGBA8 RGBA8FromVec(ImVec4 vec); +ImVec4 VecFromRGBA8(Color_RGBA8 color); #endif /* UIWidgets_hpp */ diff --git a/soh/soh/config/ConfigMigrators.h b/soh/soh/config/ConfigMigrators.h index 29cbb0774..f3c663f66 100644 --- a/soh/soh/config/ConfigMigrators.h +++ b/soh/soh/config/ConfigMigrators.h @@ -16,7 +16,7 @@ namespace SOH { std::vector version3Migrations = { { MigrationAction::Rename, "gSwitchAge", "gGeneral.SwitchAge" }, - { MigrationAction::Rename, "gFrameAdvance", "gGeneral.FrameAdvance" }, + { MigrationAction::Rename, "gFrameAdvance", "gDeveloperTools.FrameAdvanceTick" }, { MigrationAction::Rename, "gRandoGenerating", "gGeneral.RandoGenerating" }, { MigrationAction::Rename, "gNewSeedGenerated", "gGeneral.NewSeedGenerated" }, { MigrationAction::Rename, "gOnFileSelectNameEntry", "gGeneral.OnFileSelectNameEntry" }, @@ -42,14 +42,12 @@ namespace SOH { { MigrationAction::Rename, "gCheckTrackerSettingsEnabled", "gOpenWindows.CheckTrackerSettings" }, { MigrationAction::Rename, "gCollisionViewerEnabled", "gOpenWindows.CollisionViewer" }, { MigrationAction::Rename, "gCosmeticsEditorEnabled", "gOpenWindows.CosmeticsEditor" }, - { MigrationAction::Rename, "gDLViewerEnabled", "gOpenWindows.DLViewer" }, + { MigrationAction::Rename, "gDLViewerEnabled", "gOpenWindows.DisplayListViewer" }, { MigrationAction::Rename, "gEntranceTrackerEnabled", "gOpenWindows.EntranceTracker" }, { MigrationAction::Rename, "gGameplayStatsEnabled", "gOpenWindows.GameplayStats" }, { MigrationAction::Rename, "gItemTrackerEnabled", "gOpenWindows.ItemTracker" }, { MigrationAction::Rename, "gItemTrackerSettingsEnabled", "gOpenWindows.ItemTrackerSettings" }, { MigrationAction::Rename, "gMessageViewerEnabled", "gOpenWindows.MessageViewer" }, - { MigrationAction::Rename, "gOpenWindows.InputViewer", "gOpenWindows.InputViewer" }, - { MigrationAction::Rename, "gOpenWindows.InputViewerSettings", "gOpenWindows.InputViewerSettings" }, { MigrationAction::Rename, "gRandomizerSettingsEnabled", "gOpenWindows.RandomizerSettings" }, { MigrationAction::Rename, "gSaveEditorEnabled", "gOpenWindows.SaveEditor" }, { MigrationAction::Rename, "gValueViewer.WindowOpen", "gOpenWindows.ValueViewer" }, @@ -188,7 +186,7 @@ namespace SOH { { MigrationAction::Rename, "gDisableGrottoRotation", "gEnhancements.DisableGrottoRotation" }, { MigrationAction::Rename, "gDisableKokiriDrawDistance", "gEnhancements.DisableKokiriDrawDistance" }, { MigrationAction::Rename, "gDisableLOD", "gEnhancements.DisableLOD" }, - { MigrationAction::Rename, "gDisableNaviCallAudio", "gEnhancements.DisableNaviCallAudio" }, + { MigrationAction::Rename, "gDisableNaviCallAudio", "gAudioEditor.DisableNaviCallAudio" }, { MigrationAction::Rename, "gDisableTunicWarningText", "gEnhancements.DisableTunicWarningText" }, { MigrationAction::Rename, "gDogFollowsEverywhere", "gEnhancements.DogFollowsEverywhere" }, { MigrationAction::Rename, "gDpadNoDropOcarinaInput", "gEnhancements.DpadNoDropOcarinaInput" }, @@ -238,7 +236,7 @@ namespace SOH { { MigrationAction::Rename, "gInstantShootingGalleryWin", "gEnhancements.InstantShootingGalleryWin" }, { MigrationAction::Rename, "gIvanCoopModeEnabled", "gEnhancements.IvanCoopModeEnabled" }, { MigrationAction::Rename, "gLinkDefaultName", "gEnhancements.LinkDefaultName" }, - { MigrationAction::Rename, "gLowHpAlarm", "gEnhancements.LowHpAlarm" }, + { MigrationAction::Rename, "gLowHpAlarm", "gAudioEditor.LowHpAlarm" }, { MigrationAction::Rename, "gMMBunnyHood", "gEnhancements.MMBunnyHood" }, { MigrationAction::Rename, "gMarketSneak", "gEnhancements.MarketSneak" }, { MigrationAction::Rename, "gMaskSelect", "gEnhancements.MaskSelect" }, diff --git a/soh/soh/cvar_prefixes.h b/soh/soh/cvar_prefixes.h index 6ddde97bd..4b8d9f93a 100644 --- a/soh/soh/cvar_prefixes.h +++ b/soh/soh/cvar_prefixes.h @@ -7,11 +7,11 @@ #define CVAR_SETTING(var) CVAR_PREFIX_SETTING "." var #define CVAR_WINDOW(var) CVAR_PREFIX_WINDOW "." var #define CVAR_TRACKER(var) CVAR_PREFIX_TRACKER "." var -#define CVAR_TRACKER_ITEM(var) CVAR_TRACKER(".ItemTracker." var) -#define CVAR_TRACKER_CHECK(var) CVAR_TRACKER(".CheckTracker." var) -#define CVAR_TRACKER_ENTRANCE(var) CVAR_TRACKER(".EntranceTracker." var) +#define CVAR_TRACKER_ITEM(var) CVAR_TRACKER("ItemTracker." var) +#define CVAR_TRACKER_CHECK(var) CVAR_TRACKER("CheckTracker." var) +#define CVAR_TRACKER_ENTRANCE(var) CVAR_TRACKER("EntranceTracker." var) #define CVAR_DEVELOPER_TOOLS(var) CVAR_PREFIX_DEVELOPER_TOOLS "." var #define CVAR_GENERAL(var) CVAR_PREFIX_GENERAL "." var #define CVAR_REMOTE(var) CVAR_PREFIX_REMOTE "." var -#define CVAR_REMOTE_CROWD_CONTROL(var) CVAR_REMOTE(".CrowdControl." var) -#define CVAR_REMOTE_SAIL(var) CVAR_REMOTE(".Sail." var) \ No newline at end of file +#define CVAR_REMOTE_CROWD_CONTROL(var) CVAR_REMOTE("CrowdControl." var) +#define CVAR_REMOTE_SAIL(var) CVAR_REMOTE("Sail." var) \ No newline at end of file diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index 4eeaa16fd..7d3189107 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "Enhancements/randomizer/randomizerTypes.h" std::vector sceneNames = { @@ -341,18 +343,42 @@ std::array rcareaPrefixes = { }; const std::string& SohUtils::GetSceneName(int32_t scene) { + if (scene > sceneNames.size()) { + SPDLOG_WARN("Passed invalid scene id to SohUtils::GetSceneName: ({})", scene); + assert(false); + return ""; + } + return sceneNames[scene]; } const std::string& SohUtils::GetItemName(int32_t item) { + if (item > itemNames.size()) { + SPDLOG_WARN("Passed invalid item id to SohUtils::GetItemName: ({})", item); + assert(false); + return ""; + } + return itemNames[item]; } const std::string& SohUtils::GetQuestItemName(int32_t item) { + if (item > questItemNames.size()) { + SPDLOG_WARN("Passed invalid quest item id to SohUtils::GetQuestItemName: ({})", item); + assert(false); + return ""; + } + return questItemNames[item]; } const std::string& SohUtils::GetRandomizerCheckAreaPrefix(int32_t rcarea) { + if (rcarea > rcareaPrefixes.size()) { + SPDLOG_WARN("Passed invalid rcarea to SohUtils::GetRandomizerCheckAreaPrefix: ({})", rcarea); + assert(false); + return ""; + } + return rcareaPrefixes[rcarea]; } diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp index 022aeee37..f03db52ba 100644 --- a/soh/soh/z_scene_otr.cpp +++ b/soh/soh/z_scene_otr.cpp @@ -150,17 +150,13 @@ bool Scene_CommandObjectList(PlayState* play, SOH::ISceneCommand* cmd) { s32 i; s32 j; s32 k; - ObjectStatus* status; ObjectStatus* status2; - ObjectStatus* firstStatus; // s16* objectEntry = SEGMENTED_TO_VIRTUAL(cmd->objectList.segment); s16* objectEntry = (s16*)cmdObj->GetRawPointer(); void* nextPtr; k = 0; i = play->objectCtx.unk_09; - firstStatus = &play->objectCtx.status[0]; - status = &play->objectCtx.status[i]; // 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 diff --git a/soh/src/code/audio_playback.c b/soh/src/code/audio_playback.c index 430c463f8..7e334a919 100644 --- a/soh/src/code/audio_playback.c +++ b/soh/src/code/audio_playback.c @@ -95,7 +95,7 @@ void Audio_InitNoteSub(Note* note, NoteSubEu* sub, NoteSubAttributes* attrs) { vel = 0.0f > vel ? 0.0f : vel; vel = 1.0f < vel ? 1.0f : vel; - float master_vol = (float)CVarGetInteger(CVAR_SETTING("Volume.Master"), 100) / 100.0f; + float master_vol = (float)CVarGetInteger(CVAR_SETTING("Volume.Master"), 40) / 100.0f; sub->targetVolLeft = (s32)((vel * volLeft) * (0x1000 - 0.001f)) * master_vol; sub->targetVolRight = (s32)((vel * volRight) * (0x1000 - 0.001f)) * master_vol; diff --git a/soh/src/code/z_frame_advance.c b/soh/src/code/z_frame_advance.c index 6fcd76ae7..7f9f26fda 100644 --- a/soh/src/code/z_frame_advance.c +++ b/soh/src/code/z_frame_advance.c @@ -18,10 +18,11 @@ s32 FrameAdvance_Update(FrameAdvanceContext* frameAdvCtx, Input* input) { frameAdvCtx->enabled = !frameAdvCtx->enabled; } - if (!frameAdvCtx->enabled || CVarGetInteger(CVAR_GENERAL("FrameAdvance"), 0) || (CHECK_BTN_ALL(input->cur.button, BTN_Z) && + if (!frameAdvCtx->enabled || CVarGetInteger(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick"), 0) || + (CHECK_BTN_ALL(input->cur.button, BTN_Z) && (CHECK_BTN_ALL(input->press.button, BTN_R) || (CHECK_BTN_ALL(input->cur.button, BTN_R) && (++frameAdvCtx->timer >= 9))))) { - CVarClear(CVAR_GENERAL("FrameAdvance")); + CVarClear(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick")); frameAdvCtx->timer = 0; return true; } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 726e8d44d..0deab1a70 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -651,7 +651,7 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { if (interfaceCtx->unk_22A <= 0) { interfaceCtx->unk_22A = 0; interfaceCtx->unk_22C = 0; - if (CVarGetInteger(CVAR_ENHANCEMENT("LowHpAlarm"), 0) == 0 && !Player_InCsMode(play) && (play->pauseCtx.state == 0) && + if (CVarGetInteger(CVAR_AUDIO("LowHpAlarm"), 0) == 0 && !Player_InCsMode(play) && (play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0) && HealthMeter_IsCritical() && !Play_InCsMode(play)) { Sfx_PlaySfxCentered(NA_SE_SY_HITPOINT_ALARM); } diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index f424b9fcd..8c93b8a57 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2832,7 +2832,7 @@ void Interface_SetNaviCall(PlayState* play, u16 naviCallState) { if (((naviCallState == 0x1D) || (naviCallState == 0x1E)) && !interfaceCtx->naviCalling && (play->csCtx.state == CS_STATE_IDLE)) { - if (!CVarGetInteger(CVAR_ENHANCEMENT("DisableNaviCallAudio"), 0)) { + if (!CVarGetInteger(CVAR_AUDIO("DisableNaviCallAudio"), 0)) { // clang-format off if (naviCallState == 0x1E) { Audio_PlaySoundGeneral(NA_SE_VO_NAVY_CALL, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); } diff --git a/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c index 15c1950aa..d40d25499 100644 --- a/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c +++ b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c @@ -451,6 +451,10 @@ void EnDaiku_InitSubCamera(EnDaiku* this, PlayState* play) { this->subCamActive = true; this->escapeSubCamTimer = sEscapeSubCamParams[this->actor.params & 3].maxFramesActive; + if (!GameInteractor_Should(VB_PLAY_CARPENTER_FREE_CS, true, this)) { + return; + } + eyePosDeltaLocal.x = sEscapeSubCamParams[this->actor.params & 3].eyePosDeltaLocal.x; eyePosDeltaLocal.y = sEscapeSubCamParams[this->actor.params & 3].eyePosDeltaLocal.y; eyePosDeltaLocal.z = sEscapeSubCamParams[this->actor.params & 3].eyePosDeltaLocal.z; @@ -477,6 +481,10 @@ void EnDaiku_InitSubCamera(EnDaiku* this, PlayState* play) { void EnDaiku_UpdateSubCamera(EnDaiku* this, PlayState* play) { s32 pad; + if (!GameInteractor_Should(VB_PLAY_CARPENTER_FREE_CS, true, this)) { + return; + } + this->subCamAtTarget.x = this->actor.world.pos.x; this->subCamAtTarget.y = this->actor.world.pos.y + 60.0f; this->subCamAtTarget.z = this->actor.world.pos.z; @@ -493,8 +501,10 @@ void EnDaiku_EscapeSuccess(EnDaiku* this, PlayState* play) { Actor* gerudoGuard; Vec3f vec; - Play_ClearCamera(play, this->subCamId); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + if (GameInteractor_Should(VB_PLAY_CARPENTER_FREE_CS, true, this)) { + Play_ClearCamera(play, this->subCamId); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + } this->subCamActive = false; if (GET_EVENTCHKINF_CARPENTERS_FREE_ALL()) { diff --git a/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c index 24129fb2a..fb233cf0d 100644 --- a/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c +++ b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c @@ -152,7 +152,8 @@ void EnGe3_WaitTillCardGiven(EnGe3* this, PlayState* play) { } void EnGe3_GiveCard(EnGe3* this, PlayState* play) { - if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { + if (GameInteractor_Should(VB_END_GERUDO_MEMBERSHIP_TALK, + (Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play))) { Message_CloseTextbox(play); this->actor.flags &= ~ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; this->actionFunc = EnGe3_WaitTillCardGiven; diff --git a/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.c b/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.c index 5bef60ef9..41ababe85 100644 --- a/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.c +++ b/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.c @@ -32,13 +32,7 @@ void ObjRoomtimer_Init(Actor* thisx, PlayState* play) { ObjRoomtimer* this = (ObjRoomtimer*)thisx; s16 params = this->actor.params; - // Shabom room in Jabu Jabu has a lengthened timer in Enemy Randomizer. Flag doesn't match what the game - // expects. Instead set it back to the same flag as what it would be in vanilla. - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0) && play->sceneNum == SCENE_JABU_JABU && play->roomCtx.curRoom.num == 12) { - this->switchFlag = 30; - } else { - this->switchFlag = (params >> 10) & 0x3F; - } + this->switchFlag = (params >> 10) & 0x3F; this->actor.params = params & 0x3FF; params = this->actor.params;