Merge branch 'develop' of https://github.com/A-Green-Spoon/Shipwright into feature/frog-game-difficulty

This commit is contained in:
Eric Hoey 2024-04-10 18:35:22 -04:00
commit 43777c9259
390 changed files with 6700 additions and 5759 deletions

View File

@ -1 +1 @@
libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev ninja-build
libusb-dev libusb-1.0-0-dev libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev libzip-dev zipcmp zipmerge ziptool ninja-build

View File

@ -13,22 +13,37 @@ jobs:
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-soh-otr-ccache
key: ${{ runner.os }}-otr-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-otr-ccache-${{ github.ref }}
${{ runner.os }}-otr-ccache-
- name: Install dependencies
if: ${{ !vars.LINUX_RUNNER }}
run: |
sudo apt-get update
sudo apt-get install -y $(cat .github/workflows/apt-deps.txt)
- name: Cache build folders
uses: actions/cache@v4
with:
key: ${{ runner.os }}-otr-build-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-otr-build-${{ github.ref }}
${{ runner.os }}-otr-build-
path: |
build-cmake
SDL2-2.28.5
- name: Install latest SDL
if: ${{ !vars.LINUX_RUNNER }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz
tar -xzf SDL2-2.26.1.tar.gz
cd SDL2-2.26.1
./configure
if [ ! -d "SDL2-2.28.5" ]; then
wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz
tar -xzf SDL2-2.28.5.tar.gz
fi
cd SDL2-2.28.5
./configure --enable-hidapi-libusb
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
@ -37,7 +52,7 @@ jobs:
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release
cmake --build build-cmake --config Release --target GenerateSohOtr
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: soh.otr
path: soh.otr
@ -50,9 +65,12 @@ jobs:
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-ccache
key: ${{ runner.os }}-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-ccache-${{ github.ref }}
${{ runner.os }}-ccache-
- name: Install gtar wrapper
if: ${{ !vars.MAC_RUNNER }}
run: |
@ -74,18 +92,18 @@ jobs:
if [ -d /opt/local/ ]; then
echo "MacPorts already installed"
else
wget https://github.com/macports/macports-base/releases/download/v2.7.2/MacPorts-2.7.2-12-Monterey.pkg
sudo installer -pkg ./MacPorts-2.7.2-12-Monterey.pkg -target /
wget https://github.com/macports/macports-base/releases/download/v2.9.1/MacPorts-2.9.1-12-Monterey.pkg
sudo installer -pkg ./MacPorts-2.9.1-12-Monterey.pkg -target /
fi
echo "/opt/local/bin:/opt/local/sbin" >> $GITHUB_PATH
- name: Install dependencies
if: ${{ !vars.MAC_RUNNER }}
run: |
brew uninstall --ignore-dependencies libpng
brew uninstall --ignore-dependencies libpng libzip
sudo port install $(cat .github/workflows/macports-deps.txt)
brew install ninja
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
- name: Build SoH
@ -99,7 +117,7 @@ jobs:
mv _packages/*.dmg SoH.dmg
mv README.md readme.txt
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-mac
path: |
@ -128,33 +146,65 @@ jobs:
sudo apt-get update
sudo apt-get install -y $(cat .github/workflows/apt-deps.txt)
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ matrix.os }}-ccache
key: ${{ matrix.os }}-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ matrix.os }}-ccache-${{ github.ref }}
${{ matrix.os }}-ccache-
- name: Cache build folders
uses: actions/cache@v4
with:
key: ${{ matrix.os }}-build-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ matrix.os }}-build-${{ github.ref }}
${{ matrix.os }}-build-
path: |
SDL2-2.28.5
SDL2_net-2.2.0
- name: Install latest SDL
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz
tar -xzf SDL2-2.26.1.tar.gz
cd SDL2-2.26.1
./configure
if [ ! -d "SDL2-2.28.5" ]; then
wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz
tar -xzf SDL2-2.28.5.tar.gz
fi
cd SDL2-2.28.5
./configure --enable-hidapi-libusb
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Install latest libzip
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) }}
run: |
sudo apt-get remove libzip-dev zipcmp zipmerge ziptool
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
if [ ! -d "libzip-1.10.1" ]; then
wget https://libzip.org/download/libzip-1.10.1.tar.gz
tar -xzvf libzip-1.10.1.tar.gz
fi
cd libzip-1.10.1
mkdir build
cd build
cmake ..
make
sudo make install
- name: Install latest SDL_net
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz
tar -xzf SDL2_net-2.2.0.tar.gz
if [ ! -d "SDL2_net-2.2.0" ]; then
wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz
tar -xzf SDL2_net-2.2.0.tar.gz
fi
cd SDL2_net-2.2.0
./configure
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
- name: Build SoH
@ -170,7 +220,7 @@ jobs:
CC: gcc-${{ matrix.gcc }}
CXX: g++-${{ matrix.gcc }}
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-linux-${{ matrix.archive-suffix }}
path: |
@ -180,12 +230,23 @@ jobs:
needs: generate-soh-otr
runs-on: ${{ (vars.LINUX_RUNNER && fromJSON(vars.LINUX_RUNNER)) || 'ubuntu-latest' }}
container:
image: devkitpro/devkita64:latest
image: devkitpro/devkita64:20240120
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ninja-build
sudo apt-get remove -y cmake
wget https://github.com/Kitware/CMake/releases/download/v3.28.3/cmake-3.28.3-linux-x86_64.sh -O /tmp/cmake.sh
sudo sh /tmp/cmake.sh --prefix=/usr/local/ --exclude-subdir
wget https://libzip.org/download/libzip-1.10.1.tar.gz
tar -xzvf libzip-1.10.1.tar.gz
cd libzip-1.10.1
mkdir build
cd build
cmake -H.. -B. -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake
make
make install
- name: Fix dubious ownership error
if: ${{ vars.LINUX_RUNNER }}
run: git config --global --add safe.directory '*'
@ -193,9 +254,12 @@ jobs:
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-switch-ccache
key: ${{ runner.os }}-switch-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-switch-ccache-${{ github.ref }}
${{ runner.os }}-switch-ccache-
- name: Build SoH
run: |
cmake -H. -Bbuild-switch -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
@ -204,11 +268,11 @@ jobs:
mv build-switch/soh/*.nro soh.nro
mv README.md readme.txt
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-switch
path: |
@ -226,13 +290,27 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y ninja-build
sudo apt-get remove -y cmake
wget https://github.com/Kitware/CMake/releases/download/v3.28.3/cmake-3.28.3-linux-x86_64.sh -O /tmp/cmake.sh
sudo sh /tmp/cmake.sh --prefix=/usr/local/ --exclude-subdir
wget https://libzip.org/download/libzip-1.10.1.tar.gz
tar -xzvf libzip-1.10.1.tar.gz
cd libzip-1.10.1
mkdir build
cd build
cmake -H.. -B. -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake
make
make install
- uses: actions/checkout@v3
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-wiiu-ccache
key: ${{ runner.os }}-wiiu-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-wiiu-ccache-${{ github.ref }}
${{ runner.os }}-wiiu-ccache-
- name: Build SoH
run: |
cmake -H. -Bbuild-wiiu -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
@ -245,11 +323,11 @@ jobs:
DEVKITPRO: /opt/devkitpro
DEVKITPPC: /opt/devkitpro/devkitPPC
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-wiiu
path: |
@ -270,15 +348,25 @@ jobs:
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-ccache
- name: vcpkg
uses: johnwason/vcpkg-action@v5
variant: sccache
max-size: "1G"
key: ${{ runner.os }}-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-ccache-${{ github.ref }}
${{ runner.os }}-ccache-
- name: Cache build folder
uses: actions/cache@v4
with:
pkgs: zlib bzip2 libpng sdl2 sdl2-net glew glfw3
token: ${{ github.token }}
triplet: 'x64-windows-static'
save-always: true
key: ${{ runner.os }}-build-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-${{ github.ref }}
${{ runner.os }}-build-
path: |
build-windows
vcpkg
- name: Configure Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1
- name: Build SoH
@ -286,7 +374,7 @@ jobs:
VCPKG_ROOT: ${{github.workspace}}/vcpkg
run: |
set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH"
cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DBUILD_REMOTE_CONTROL=1
cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DBUILD_REMOTE_CONTROL=1
cmake --build build-windows --config Release --parallel 10
mkdir soh-windows
@ -299,12 +387,12 @@ jobs:
mv ./build-windows/gamecontrollerdb.txt ./soh-windows/gamecontrollerdb.txt
mv ./x64/Release/assets ./soh-windows
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
path: soh-windows
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-windows
path: soh-windows

View File

@ -1 +1 @@
libsdl2 +universal libsdl2_net +universal libpng +universal glew +universal
libsdl2 +universal libsdl2_net +universal libpng +universal glew +universal libzip +universal

View File

@ -0,0 +1,61 @@
name: test-builds-on-distros
on:
workflow_dispatch: # by request
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
matrix:
image: ["archlinux:base", "opensuse/tumbleweed:latest", "ubuntu:mantic", "debian:bookworm", "fedora:39"]
cc: ["gcc", "clang"]
include:
- cxx: g++
cc: gcc
- cxx: clang++
cc: clang
runs-on: ${{ (vars.LINUX_RUNNER && fromJSON(vars.LINUX_RUNNER)) || 'ubuntu-latest' }}
container:
image: ${{ matrix.image }}
steps:
- name: Install dependencies (pacman)
if: ${{ matrix.image == 'archlinux:base' }}
run: |
echo arch
echo pacman -S ${{ matrix.cc }} git cmake ninja lsb-release sdl2 libpng libzip sdl2_net boost
pacman -Syu --noconfirm
pacman -S --noconfirm ${{ matrix.cc }} git cmake ninja lsb-release sdl2 libpng libzip sdl2_net boost
- name: Install dependencies (dnf)
if: ${{ matrix.image == 'fedora:39' }}
run: |
echo fedora
echo dnf install ${{ matrix.cc }} ${{ (matrix.cxx == 'g++' && 'gcc-c++') || '' }} git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools boost-devel
dnf -y upgrade
dnf -y install ${{ matrix.cc }} ${{ (matrix.cxx == 'g++' && 'gcc-c++') || '' }} git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools boost-devel
- name: Install dependencies (apt)
if: ${{ matrix.image == 'ubuntu:mantic' || matrix.image == 'debian:bookworm' }}
run: |
echo debian based
echo apt-get install ${{ matrix.cc }} ${{ (matrix.cxx == 'g++' && 'g++') || '' }} git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool libboost-dev libopengl-dev
apt-get update
apt-get -y full-upgrade
apt-get -y install ${{ matrix.cc }} ${{ (matrix.cxx == 'g++' && 'g++') || '' }} git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool libboost-dev libopengl-dev
- name: Install dependencies (zypper)
if: ${{ matrix.image == 'opensuse/tumbleweed:latest' }}
run: |
echo openSUSE
echo zypper in ${{ matrix.cc }} ${{ (matrix.cxx == 'g++' && 'gcc-c++') || '' }} ${{ matrix.cc == 'clang' && 'libstdc++-devel' || '' }} git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools
zypper --non-interactive dup
zypper --non-interactive in ${{ matrix.cc }} ${{ (matrix.cxx == 'g++' && 'gcc-c++') || '' }} ${{ matrix.cc == 'clang' && 'libstdc++-devel' || '' }} git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools
- uses: actions/checkout@v3
with:
submodules: true
- name: Build SoH
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 -DBUILD_REMOTE_CONTROL=1
cmake --build build-cmake --config Release -j3
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}

View File

@ -8,5 +8,9 @@ if(MSVC)
set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
set_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc")
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi")
if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache")
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Z7")
else()
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi")
endif()
endif()

View File

@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
project(Ship VERSION 8.0.4 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Echo" CACHE STRING "")
project(Ship VERSION 8.0.5 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Foxtrot" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
@ -14,13 +14,17 @@ add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/utf-8>)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
include(CMake/automate-vcpkg.cmake)
include(CMake/automate-vcpkg.cmake)
set(VCPKG_TRIPLET x64-windows-static)
set(VCPKG_TARGET_TRIPLET x64-windows-static)
set(VCPKG_TRIPLET x64-windows-static)
set(VCPKG_TARGET_TRIPLET x64-windows-static)
vcpkg_bootstrap()
vcpkg_install_packages(zlib bzip2 libpng sdl2 sdl2-net glew glfw3)
vcpkg_bootstrap()
vcpkg_install_packages(zlib bzip2 libzip libpng sdl2 sdl2-net glew glfw3)
if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache")
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)
endif()
endif()
################################################################################

@ -1 +1 @@
Subproject commit 04b85b95fab07a394b62dcd28a502a3040f08e0c
Subproject commit e93bd2be062b13106fdb29d98cf4ada4d7ad6827

View File

@ -84,41 +84,66 @@ cd "build/x64"
```
## Linux
Requires `gcc >= 10, x11, curl, python3, sdl2 >= 2.0.22, libpng, glew >= 2.2, ninja, cmake, lld, pulseaudio-libs`
### Install dependencies
#### Debian/Ubuntu
```sh
# using gcc
apt-get install gcc g++ git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool libboost-dev libopengl-dev
**Important: For maximum performance make sure you have ninja build tools installed!**
# or using clang
apt-get install clang git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool libboost-dev libopengl-dev
```
#### Arch
```sh
# using gcc
pacman -S gcc git cmake ninja lsb-release sdl2 libpng libzip sdl2_net boost
_Note: If you're using Visual Studio Code, the [cpack plugin](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) makes it very easy to just press run and debug._
# or using clang
pacman -S clang git cmake ninja lsb-release sdl2 libpng libzip sdl2_net boost
```
#### Fedora
```sh
# using gcc
dnf install gcc gcc-c++ git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools boost-devel
# or using clang
dnf install clang git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools boost-devel
```
#### openSUSE
```sh
# using gcc
zypper in gcc gcc-c++ git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools
# or using clang
zypper in clang libstdc++-devel git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools
```
### Build
_Note: If you're using Visual Studio Code, the [CMake Tools plugin](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) makes it very easy to just press run and debug._
```bash
# Clone the repo
# Clone the repo and enter the directory
git clone https://github.com/HarbourMasters/Shipwright.git
cd Shipwright
# Clone the submodule libultraship
# Clone the submodules
git submodule update --init
# Copy the baserom to the OTRExporter folder
cp <path to your ROM> OTRExporter
# Generate Ninja project
cmake -H. -Bbuild-cmake -GNinja # -DCMAKE_BUILD_TYPE:STRING=Release (if you're packaging) -DPython3_EXECUTABLE=$(which python3) (if you are using non-standard Python installations such as PyEnv)
# Extract assets & generate OTR (run this anytime you need to regenerate OTR)
cmake --build build-cmake --target ExtractAssets
# Generate soh.otr
cmake --build build-cmake --target GenerateSohOtr
# Compile the project
cmake --build build-cmake # --config Release (if you're packaging)
# Now you can run the executable in ./build-cmake/soh/soh.elf
# To develop the project open the repository in VSCode (or your preferred editor)
# If you need to clean the project you can run
cmake --build build-cmake --target clean
# If you need to regenerate the asset headers to check them into source
cmake --build build-cmake --target ExtractAssetHeaders
# If you need a newer soh.otr only
cmake --build build-cmake --target GenerateSohOtr
```
### Generating a distributable
### Generate a distributable
After compiling the project you can generate a distributable by running of the following:
```bash
# Go to build folder
@ -129,6 +154,20 @@ cpack -G ZIP
cpack -G External (creates appimage)
```
### Additional CMake Targets
#### Clean
```bash
# If you need to clean the project you can run
cmake --build build-cmake --target clean
```
#### Regenerate Asset Headers
```bash
# If you need to regenerate the asset headers to check them into source
cp <path to your ROM> OTRExporter
cmake --build build-cmake --target ExtractAssetHeaders
```
## macOS
Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake` (can be installed via homebrew, macports, etc)

@ -1 +1 @@
Subproject commit 0833afad66e96d2ec4bbc410186d7247dc243ee2
Subproject commit c3a699403793c9ac97733179fe078d2e2f271ee1

View File

@ -8,5 +8,9 @@ if(MSVC)
set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
set_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc")
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi")
if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache")
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Z7")
else()
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi")
endif()
endif()

View File

@ -356,7 +356,13 @@ set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS})
if (BUILD_REMOTE_CONTROL)
find_package(SDL2_net)
set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
if(NOT SDL2_net_FOUND)
message(STATUS "SDL2_net not found (it's possible the version installed is too old). Disabling BUILD_REMOTE_CONTROL.")
set(BUILD_REMOTE_CONTROL 0)
else()
set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
endif()
endif()
target_include_directories(${PROJECT_NAME} PRIVATE assets
@ -623,6 +629,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
-Wno-parentheses
-Wno-narrowing
-Wno-missing-braces
-Wno-int-conversion
-Wno-implicit-int
$<$<COMPILE_LANGUAGE:C>:
-Werror-implicit-function-declaration
-Wno-incompatible-pointer-types

View File

@ -28,4 +28,10 @@
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- legacy -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to pm if pmv2 is not available -->
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

After

Width:  |  Height:  |  Size: 992 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1230,8 +1230,8 @@ Gfx* Gfx_EnvColor(GraphicsContext* gfxCtx, s32 r, s32 g, s32 b, s32 a);
void Gfx_SetupFrame(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b);
void func_80095974(GraphicsContext* gfxCtx);
void func_80095AA0(PlayState* play, Room* room, Input* arg2, UNK_TYPE arg3);
void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 height, u8 fmt, u8 siz, u16 mode0,
u16 tlutCount, f32 frameX, f32 frameY);
void Room_DrawBackground2D(Gfx** gfxP, void* tex, void* tlut, u16 width, u16 height, u8 fmt, u8 siz, u16 tlutMode,
u16 tlutCount, f32 offsetX, f32 offsetY);
void func_80096FD4(PlayState* play, Room* room);
u32 func_80096FE8(PlayState* play, RoomContext* roomCtx);
s32 func_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomNum);
@ -1828,8 +1828,8 @@ MtxF* Matrix_CheckFloats(MtxF* mf, char* file, s32 line);
void Matrix_SetTranslateScaleMtx2(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY,
f32 translateZ);
uintptr_t SysUcode_GetUCodeBoot(void);
uintptr_t SysUcode_GetUCodeBootSize(void);
uintptr_t SysUcode_GetUCode(void);
size_t SysUcode_GetUCodeBootSize(void);
uint32_t SysUcode_GetUCode(void);
uintptr_t SysUcode_GetUCodeData(void);
void func_800D2E30(UnkRumbleStruct* arg0);
void func_800D3140(UnkRumbleStruct* arg0);
@ -2467,6 +2467,10 @@ void Message_DrawText(PlayState* play, Gfx** gfxP);
void Interface_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH);
void Interface_RandoRestoreSwordless(void);
//Pause Warp
void PauseWarp_HandleSelection();
void PauseWarp_Execute();
// #endregion
#ifdef __cplusplus

View File

@ -345,7 +345,7 @@ typedef enum {
#define PLAYER_LIMB_BUF_COUNT LIMB_BUF_COUNT(PLAYER_LIMB_MAX)
typedef struct {
/* 0x00 */ f32 unk_00;
/* 0x00 */ f32 ceilingCheckHeight;
/* 0x04 */ f32 unk_04;
/* 0x08 */ f32 unk_08;
/* 0x0C */ f32 unk_0C;
@ -359,7 +359,7 @@ typedef struct {
/* 0x2C */ f32 unk_2C;
/* 0x30 */ f32 unk_30;
/* 0x34 */ f32 unk_34;
/* 0x38 */ f32 unk_38;
/* 0x38 */ f32 wallCheckRadius;
/* 0x3C */ f32 unk_3C;
/* 0x40 */ f32 unk_40;
/* 0x44 */ Vec3s unk_44;
@ -489,180 +489,187 @@ typedef s32 (*UpperActionFunc)(struct Player*, struct PlayState*);
typedef void (*PlayerFuncA74)(struct PlayState*, struct Player*);
typedef struct Player {
/* 0x0000 */ Actor actor;
/* 0x014C */ s8 currentTunic; // current tunic from `PlayerTunic`
/* 0x014D */ s8 currentSwordItemId;
/* 0x014E */ s8 currentShield; // current shield from `PlayerShield`
/* 0x014F */ s8 currentBoots; // current boots from `PlayerBoots`
/* 0x0150 */ s8 heldItemButton; // Button index for the item currently used
/* 0x0151 */ s8 heldItemAction; // Item action for the item currently used
/* 0x0152 */ u8 heldItemId; // Item id for the item currently used
/* 0x0153 */ s8 prevBoots; // previous boots from `PlayerBoots`
/* 0x0154 */ s8 itemAction; // the difference between this and heldItemAction is unclear
/* 0x0155 */ char unk_155[0x003];
/* 0x0158 */ u8 modelGroup;
/* 0x0159 */ u8 nextModelGroup;
/* 0x015A */ s8 itemChangeType;
/* 0x015B */ u8 modelAnimType;
/* 0x015C */ u8 leftHandType;
/* 0x015D */ u8 rightHandType;
/* 0x015E */ u8 sheathType;
/* 0x015F */ u8 currentMask; // current mask equipped from `PlayerMask`
/* 0x0160 */ Gfx** rightHandDLists;
/* 0x0164 */ Gfx** leftHandDLists;
/* 0x0168 */ Gfx** sheathDLists;
/* 0x016C */ Gfx** waistDLists;
/* 0x0170 */ u8 giObjectLoading;
/* 0x0000 */ Actor actor;
/* 0x014C */ s8 currentTunic; // current tunic from `PlayerTunic`
/* 0x014D */ s8 currentSwordItemId;
/* 0x014E */ s8 currentShield; // current shield from `PlayerShield`
/* 0x014F */ s8 currentBoots; // current boots from `PlayerBoots`
/* 0x0150 */ s8 heldItemButton; // Button index for the item currently used
/* 0x0151 */ s8 heldItemAction; // Item action for the item currently used
/* 0x0152 */ u8 heldItemId; // Item id for the item currently used
/* 0x0153 */ s8 prevBoots; // previous boots from `PlayerBoots`
/* 0x0154 */ s8 itemAction; // the difference between this and heldItemAction is unclear
/* 0x0155 */ char unk_155[0x003];
/* 0x0158 */ u8 modelGroup;
/* 0x0159 */ u8 nextModelGroup;
/* 0x015A */ s8 itemChangeType;
/* 0x015B */ u8 modelAnimType;
/* 0x015C */ u8 leftHandType;
/* 0x015D */ u8 rightHandType;
/* 0x015E */ u8 sheathType;
/* 0x015F */ u8 currentMask; // current mask equipped from `PlayerMask`
/* 0x0160 */ Gfx** rightHandDLists;
/* 0x0164 */ Gfx** leftHandDLists;
/* 0x0168 */ Gfx** sheathDLists;
/* 0x016C */ Gfx** waistDLists;
/* 0x0170 */ u8 giObjectLoading;
/* 0x0174 */ DmaRequest giObjectDmaRequest;
/* 0x0194 */ OSMesgQueue giObjectLoadQueue;
/* 0x01AC */ OSMesg giObjectLoadMsg;
/* 0x01B0 */ void* giObjectSegment; // also used for title card textures
/* 0x01B4 */ SkelAnime skelAnime;
/* 0x01F8 */ Vec3s jointTable[PLAYER_LIMB_BUF_COUNT];
/* 0x0288 */ Vec3s morphTable[PLAYER_LIMB_BUF_COUNT];
/* 0x0318 */ Vec3s blendTable[PLAYER_LIMB_BUF_COUNT];
/* 0x03A8 */ s16 unk_3A8[2];
/* 0x03AC */ Actor* heldActor;
/* 0x03B0 */ Vec3f leftHandPos;
/* 0x03BC */ Vec3s unk_3BC;
/* 0x03C4 */ Actor* unk_3C4;
/* 0x03C8 */ Vec3f unk_3C8;
/* 0x03D4 */ char unk_3D4[0x058];
/* 0x042C */ s8 doorType;
/* 0x042D */ s8 doorDirection;
/* 0x042E */ s16 doorTimer;
/* 0x0430 */ Actor* doorActor;
/* 0x0434 */ s16 getItemId; // Upstream TODO: Document why this is s16 while it's s8 upstream
/* 0x0436 */ u16 getItemDirection;
/* 0x0438 */ Actor* interactRangeActor;
/* 0x043C */ s8 mountSide;
/* 0x043D */ char unk_43D[0x003];
/* 0x0440 */ Actor* rideActor;
/* 0x0444 */ u8 csAction;
/* 0x0445 */ u8 prevCsAction;
/* 0x0446 */ u8 cueId;
/* 0x0447 */ u8 unk_447;
/* 0x0448 */ Actor* csActor;
/* 0x044C */ char unk_44C[0x004];
/* 0x0450 */ Vec3f unk_450;
/* 0x045C */ Vec3f unk_45C;
/* 0x0468 */ char unk_468[0x002];
/* 0x046A */ s16 doorBgCamIndex;
/* 0x046C */ s16 subCamId;
/* 0x046E */ char unk_46E[0x02A];
/* 0x01AC */ OSMesg giObjectLoadMsg;
/* 0x01B0 */ void* giObjectSegment; // also used for title card textures
/* 0x01B4 */ SkelAnime skelAnime;
/* 0x01F8 */ Vec3s jointTable[PLAYER_LIMB_BUF_COUNT];
/* 0x0288 */ Vec3s morphTable[PLAYER_LIMB_BUF_COUNT];
/* 0x0318 */ Vec3s blendTable[PLAYER_LIMB_BUF_COUNT];
/* 0x03A8 */ s16 unk_3A8[2];
/* 0x03AC */ Actor* heldActor;
/* 0x03B0 */ Vec3f leftHandPos;
/* 0x03BC */ Vec3s unk_3BC;
/* 0x03C4 */ Actor* unk_3C4;
/* 0x03C8 */ Vec3f unk_3C8;
/* 0x03D4 */ char unk_3D4[0x058];
/* 0x042C */ s8 doorType;
/* 0x042D */ s8 doorDirection;
/* 0x042E */ s16 doorTimer;
/* 0x0430 */ Actor* doorActor;
/* 0x0434 */ s16 getItemId; // Upstream TODO: Document why this is s16 while it's s8 upstream
/* 0x0436 */ u16 getItemDirection;
/* 0x0438 */ Actor* interactRangeActor;
/* 0x043C */ s8 mountSide;
/* 0x043D */ char unk_43D[0x003];
/* 0x0440 */ Actor* rideActor;
/* 0x0444 */ u8 csAction;
/* 0x0445 */ u8 prevCsAction;
/* 0x0446 */ u8 cueId;
/* 0x0447 */ u8 unk_447;
/* 0x0448 */ Actor* csActor; // Actor involved in a `csAction`. Typically the actor that invoked the cutscene.
/* 0x044C */ char unk_44C[0x004];
/* 0x0450 */ Vec3f unk_450;
/* 0x045C */ Vec3f unk_45C;
/* 0x0468 */ char unk_468[0x002];
/* 0x046A */ s16 doorBgCamIndex;
/* 0x046C */ s16 subCamId;
/* 0x046E */ char unk_46E[0x02A];
/* 0x0498 */ ColliderCylinder cylinder;
/* 0x04E4 */ ColliderQuad meleeWeaponQuads[2];
/* 0x05E4 */ ColliderQuad shieldQuad;
/* 0x0664 */ Actor* unk_664;
/* 0x0668 */ char unk_668[0x004];
/* 0x066C */ s32 unk_66C;
/* 0x0670 */ s32 meleeWeaponEffectIndex;
/* 0x0664 */ Actor* unk_664;
/* 0x0668 */ char unk_668[0x004];
/* 0x066C */ s32 unk_66C;
/* 0x0670 */ s32 meleeWeaponEffectIndex;
/* 0x0674 */ PlayerActionFunc actionFunc;
/* 0x0678 */ PlayerAgeProperties* ageProperties;
/* 0x067C */ u32 stateFlags1;
/* 0x0680 */ u32 stateFlags2;
/* 0x0684 */ Actor* unk_684;
/* 0x0688 */ Actor* boomerangActor;
/* 0x068C */ Actor* naviActor;
/* 0x0690 */ s16 naviTextId;
/* 0x0692 */ u8 stateFlags3;
/* 0x0693 */ s8 exchangeItemId;
/* 0x0694 */ Actor* targetActor;
/* 0x0698 */ f32 targetActorDistance;
/* 0x069C */ char unk_69C[0x004];
/* 0x06A0 */ f32 unk_6A0;
/* 0x06A4 */ f32 closestSecretDistSq;
/* 0x06A8 */ Actor* unk_6A8;
/* 0x06AC */ s8 unk_6AC;
/* 0x06AD */ u8 unk_6AD;
/* 0x06AE */ u16 unk_6AE;
/* 0x06B0 */ s16 unk_6B0;
/* 0x06B2 */ char unk_6B4[0x004];
/* 0x06B6 */ s16 unk_6B6;
/* 0x06B8 */ s16 unk_6B8;
/* 0x06BA */ s16 unk_6BA;
/* 0x06BC */ s16 unk_6BC;
/* 0x06BE */ s16 unk_6BE;
/* 0x06C0 */ s16 unk_6C0;
/* 0x06C2 */ s16 unk_6C2;
/* 0x06C4 */ f32 unk_6C4;
/* 0x06C8 */ SkelAnime upperSkelAnime;
/* 0x070C */ Vec3s upperJointTable[PLAYER_LIMB_BUF_COUNT];
/* 0x079C */ Vec3s upperMorphTable[PLAYER_LIMB_BUF_COUNT];
/* 0x067C */ u32 stateFlags1;
/* 0x0680 */ u32 stateFlags2;
/* 0x0684 */ Actor* unk_684;
/* 0x0688 */ Actor* boomerangActor;
/* 0x068C */ Actor* naviActor;
/* 0x0690 */ s16 naviTextId;
/* 0x0692 */ u8 stateFlags3;
/* 0x0693 */ s8 exchangeItemId;
/* 0x0694 */ Actor* targetActor;
/* 0x0698 */ f32 targetActorDistance;
/* 0x069C */ char unk_69C[0x004];
/* 0x06A0 */ f32 unk_6A0;
/* 0x06A4 */ f32 closestSecretDistSq;
/* 0x06A8 */ Actor* unk_6A8;
/* 0x06AC */ s8 unk_6AC;
/* 0x06AD */ u8 unk_6AD;
/* 0x06AE */ u16 unk_6AE;
/* 0x06B0 */ s16 unk_6B0;
/* 0x06B2 */ char unk_6B4[0x004];
/* 0x06B6 */ s16 unk_6B6;
/* 0x06B8 */ s16 unk_6B8;
/* 0x06BA */ s16 unk_6BA;
/* 0x06BC */ s16 unk_6BC;
/* 0x06BE */ s16 unk_6BE;
/* 0x06C0 */ s16 unk_6C0;
/* 0x06C2 */ s16 unk_6C2;
/* 0x06C4 */ f32 unk_6C4;
/* 0x06C8 */ SkelAnime upperSkelAnime;
/* 0x070C */ Vec3s upperJointTable[PLAYER_LIMB_BUF_COUNT];
/* 0x079C */ Vec3s upperMorphTable[PLAYER_LIMB_BUF_COUNT];
/* 0x082C */ UpperActionFunc upperActionFunc;
/* 0x0830 */ f32 upperAnimBlendWeight;
/* 0x0834 */ s16 unk_834;
/* 0x0836 */ s8 unk_836;
/* 0x0837 */ u8 unk_837;
/* 0x0838 */ f32 linearVelocity;
/* 0x083C */ s16 currentYaw;
/* 0x083E */ s16 targetYaw;
/* 0x0840 */ u16 underwaterTimer;
/* 0x0842 */ s8 meleeWeaponAnimation;
/* 0x0843 */ s8 meleeWeaponState;
/* 0x0844 */ s8 unk_844;
/* 0x0845 */ u8 unk_845;
/* 0x0846 */ u8 unk_846;
/* 0x0847 */ s8 unk_847[4];
/* 0x084B */ s8 unk_84B[4];
/* 0x084F */ s8 unk_84F;
/* 0x0850 */ s16 unk_850; // multipurpose timer
/* 0x0854 */ f32 unk_854;
/* 0x0858 */ f32 unk_858;
/* 0x085C */ f32 unk_85C; // stick length among other things
/* 0x0860 */ s16 unk_860; // stick flame timer among other things
/* 0x0862 */ s16 unk_862; // get item draw ID + 1
/* 0x0864 */ f32 unk_864;
/* 0x0868 */ f32 unk_868;
/* 0x086C */ f32 unk_86C;
/* 0x0870 */ f32 unk_870;
/* 0x0874 */ f32 unk_874;
/* 0x0878 */ f32 unk_878;
/* 0x087C */ s16 unk_87C;
/* 0x087E */ s16 unk_87E;
/* 0x0880 */ f32 unk_880;
/* 0x0884 */ f32 yDistToLedge; // y distance to ground above an interact wall. LEDGE_DIST_MAX if no ground is found
/* 0x0888 */ f32 distToInteractWall; // distance to the colliding wall plane
/* 0x088C */ u8 unk_88C;
/* 0x088D */ u8 unk_88D;
/* 0x088E */ u8 unk_88E;
/* 0x088F */ u8 unk_88F;
/* 0x0890 */ u8 unk_890;
/* 0x0891 */ u8 shockTimer;
/* 0x0892 */ u8 unk_892;
/* 0x0893 */ u8 hoverBootsTimer;
/* 0x0894 */ s16 fallStartHeight; // last truncated Y position before falling
/* 0x0896 */ s16 fallDistance; // truncated Y distance the player has fallen so far (positive is down)
/* 0x0898 */ s16 floorPitch; // angle of the floor slope in the direction of current world yaw (positive for ascending slope)
/* 0x089A */ s16 floorPitchAlt; // the calculation for this value is bugged and doesn't represent anything meaningful
/* 0x089C */ s16 unk_89C;
/* 0x089E */ u16 floorSfxOffset;
/* 0x08A0 */ u8 unk_8A0;
/* 0x08A1 */ u8 unk_8A1;
/* 0x08A2 */ s16 unk_8A2;
/* 0x08A4 */ f32 unk_8A4;
/* 0x08A8 */ f32 unk_8A8;
/* 0x08AC */ f32 pushedSpeed; // Pushing player, examples include water currents, floor conveyors, climbing sloped surfaces
/* 0x08B0 */ s16 pushedYaw; // Yaw direction of player being pushed
/* 0x0830 */ f32 upperAnimBlendWeight;
/* 0x0834 */ s16 unk_834;
/* 0x0836 */ s8 unk_836;
/* 0x0837 */ u8 unk_837;
/* 0x0838 */ f32 linearVelocity;
/* 0x083C */ s16 yaw; // General yaw value, used both for world and shape rotation. Current or target value depending on context.
/* 0x083E */ s16 zTargetYaw; // yaw relating to Z targeting/"parallel" mode
/* 0x0840 */ u16 underwaterTimer;
/* 0x0842 */ s8 meleeWeaponAnimation;
/* 0x0843 */ s8 meleeWeaponState;
/* 0x0844 */ s8 unk_844;
/* 0x0845 */ u8 unk_845;
/* 0x0846 */ u8 unk_846;
/* 0x0847 */ s8 unk_847[4];
/* 0x084B */ s8 unk_84B[4];
/* 0x084F */ union {
s8 actionVar1;
} av1; // "Action Variable 1": context dependent variable that has different meanings depending on what action is currently running
/* 0x0850 */ union {
s16 actionVar2;
} av2; // "Action Variable 2": context dependent variable that has different meanings depending on what action is currently running
/* 0x0854 */ f32 unk_854;
/* 0x0858 */ f32 unk_858;
/* 0x085C */ f32 unk_85C; // stick length among other things
/* 0x0860 */ s16 unk_860; // stick flame timer among other things
/* 0x0862 */ s16 unk_862; // get item draw ID + 1
/* 0x0864 */ f32 unk_864;
/* 0x0868 */ f32 unk_868;
/* 0x086C */ f32 unk_86C;
/* 0x0870 */ f32 unk_870;
/* 0x0874 */ f32 unk_874;
/* 0x0878 */ f32 unk_878;
/* 0x087C */ s16 unk_87C;
/* 0x087E */ s16 unk_87E;
/* 0x0880 */ f32 unk_880;
/* 0x0884 */ f32 yDistToLedge; // y distance to ground above an interact wall. LEDGE_DIST_MAX if no ground is found
/* 0x0888 */ f32 distToInteractWall; // xyz distance to the interact wall
/* 0x088C */ u8 ledgeClimbType;
/* 0x088D */ u8 ledgeClimbDelayTimer;
/* 0x088E */ u8 unk_88E;
/* 0x088F */ u8 unk_88F;
/* 0x0890 */ u8 unk_890;
/* 0x0891 */ u8 bodyShockTimer;
/* 0x0892 */ u8 unk_892;
/* 0x0893 */ u8 hoverBootsTimer;
/* 0x0894 */ s16 fallStartHeight; // last truncated Y position before falling
/* 0x0896 */ s16 fallDistance; // truncated Y distance the player has fallen so far (positive is down)
/* 0x0898 */ s16 floorPitch; // angle of the floor slope in the direction of current world yaw (positive for ascending slope)
/* 0x089A */ s16 floorPitchAlt; // the calculation for this value is bugged and doesn't represent anything meaningful
/* 0x089C */ s16 unk_89C;
/* 0x089E */ u16 floorSfxOffset;
/* 0x08A0 */ u8 unk_8A0;
/* 0x08A1 */ u8 unk_8A1;
/* 0x08A2 */ s16 unk_8A2;
/* 0x08A4 */ f32 unk_8A4;
/* 0x08A8 */ f32 unk_8A8;
/* 0x08AC */ f32 pushedSpeed; // Pushing player, examples include water currents, floor conveyors, climbing sloped surfaces
/* 0x08B0 */ s16 pushedYaw; // Yaw direction of player being pushed
/* 0x08B4 */ WeaponInfo meleeWeaponInfo[3];
/* 0x0908 */ Vec3f bodyPartsPos[PLAYER_BODYPART_MAX];
/* 0x09E0 */ MtxF mf_9E0;
/* 0x0A20 */ MtxF shieldMf;
/* 0x0A60 */ u8 isBurning;
/* 0x0A61 */ u8 flameTimers[PLAYER_BODYPART_MAX]; // one flame per body part
/* 0x0A73 */ u8 unk_A73;
/* 0x0908 */ Vec3f bodyPartsPos[PLAYER_BODYPART_MAX];
/* 0x09E0 */ MtxF mf_9E0;
/* 0x0A20 */ MtxF shieldMf;
/* 0x0A60 */ u8 bodyIsBurning;
/* 0x0A61 */ u8 bodyFlameTimers[PLAYER_BODYPART_MAX]; // one flame per body part
/* 0x0A73 */ u8 unk_A73;
/* 0x0A74 */ PlayerFuncA74 func_A74;
/* 0x0A78 */ s8 invincibilityTimer; // prevents damage when nonzero (positive = visible, counts towards zero each frame)
/* 0x0A79 */ u8 unk_A79;
/* 0x0A7A */ u8 unk_A7A;
/* 0x0A7B */ u8 unk_A7B;
/* 0x0A7C */ f32 unk_A7C;
/* 0x0A80 */ s16 unk_A80;
/* 0x0A82 */ u16 unk_A82;
/* 0x0A84 */ s16 unk_A84;
/* 0x0A86 */ s8 unk_A86;
/* 0x0A87 */ u8 unk_A87;
/* 0x0A88 */ Vec3f unk_A88; // previous body part 0 position
/* 0x0A78 */ s8 invincibilityTimer; // prevents damage when nonzero (positive = visible, counts towards zero each frame)
/* 0x0A79 */ u8 floorTypeTimer; // counts up every frame the current floor type is the same as the last frame
/* 0x0A7A */ u8 floorProperty;
/* 0x0A7B */ u8 prevFloorType;
/* 0x0A7C */ f32 prevControlStickMagnitude;
/* 0x0A80 */ s16 prevControlStickAngle;
/* 0x0A82 */ u16 prevFloorSfxOffset;
/* 0x0A84 */ s16 unk_A84;
/* 0x0A86 */ s8 unk_A86;
/* 0x0A87 */ u8 unk_A87;
/* 0x0A88 */ Vec3f unk_A88; // previous body part 0 position
// #region SOH [General]
// Upstream TODO: Rename these to be more obviously SoH specific
/* */ PendingFlag pendingFlag;
@ -670,7 +677,7 @@ typedef struct Player {
// #endregion
// #region SOH [Enhancements]
// Upstream TODO: Rename this to make it more obvious it is apart of an enhancement
/* */ u8 boomerangQuickRecall; // Has the player pressed the boomerang button while it's in the air still?
/* */ u8 boomerangQuickRecall; // Has the player pressed the boomerang button while it's in the air still?
// #endregion
u8 ivanFloating;
u8 ivanDamageMultiplier;

View File

@ -7,68 +7,6 @@ export RESPATH="${SNAME%/MacOS*}/Resources"
export LIBPATH="${SNAME%/MacOS*}/Frameworks"
export DYLD_FALLBACK_LIBRARY_PATH="$LIBPATH"
remap_hashes ()
{
# Remap v64 and n64 hashes to their z64 hash equivalent
# ZAPD will handle converting the data into z64 format
case "$ROMHASH" in
a9059b56e761c9034fbe02fe4c24985aaa835dac) # v64
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
;;
24708102dc504d3f375a37f4ae4e149c167dc515) # n64
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
;;
580dd0bd1b6d2c51cc20a764eece84dba558964c) # v64
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
;;
d6342c59007e57c1194661ec6880b2f078403f4e) # n64
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
;;
d0bdc2eb320668b4ba6893b9aefe4040a73123ff) # v64
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
;;
4946ab250f6ac9b32d76b21f309ebb8ebc8103d2) # n64
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
;;
663c34f1b2c05a09e5beffe4d0dcd440f7d49dc7) # v64
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
;;
24c73d378b0620a380ce5ef9f2b186c6c157a68b) # n64
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
;;
8ebf2e29313f44f2d49e5b4191971d09919e8e48) # v64
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
;;
4264bf7b875737b8fae77d52322a5099d051fc11) # n64
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
;;
973bc6fe56010a8d646166a1182a81b4f13b8cf9) # v64
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
;;
d327752c46edc70ff3668b9514083dbbee08927c) # v64
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
;;
ecdeb1747560834e079c22243febea7f6f26ba3b) # v64
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
;;
f19f8662ec7abee29484a272a6fda53e39efe0f1) # n64
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
;;
ab519ce04a33818ce2c39b3c514a751d807a494a) # v64
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
;;
c19a34f7646305e1755249fca2071e178bd7cd00) # n64
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
;;
25e8ae79ea0839ca5c984473f7460d8040c36f9c) # v64
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
;;
166c02770d67fcc3954c443eb400a6a3573d3fc0) # n64
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
;;
esac
}
if [ ! -e "$SHIP_HOME" ]; then mkdir "$SHIP_HOME"; fi
if [ ! -e "$SHIP_HOME"/mods ]; then
@ -76,178 +14,6 @@ if [ ! -e "$SHIP_HOME"/mods ]; then
touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt
fi
# If either OTR doesn't exist kick off the OTR gen process
if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then
# If no ROMs exist kick off the file selection prompts
while [ ! -e "$SHIP_HOME"/*.*64 ] && [ ! -e "$SHIP_HOME"/oot*.otr ]; do
SHOULD_PROMPT_FOR_ROM=1
while [ $SHOULD_PROMPT_FOR_ROM -eq 1 ]; do
SHOULD_PROMPT_FOR_ROM=0
# Use osascript to prompt the user to chose a file
DROPROM=`osascript <<-EOF
set romFile to choose file of type {"b64","n64","v64","z64"} with prompt "Please select your ROM:"
return POSIX path of romFile
EOF`
# If no rom was selected, the user cancelled, so exit
if [[ -z $DROPROM ]] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
echo "No ROM selected. Exiting..."
exit 1
elif [[ -z $DROPROM ]]; then
break;
fi
# If an invalid rom was selected, let the user know and ask to try again
ROMHASH="$(shasum "$DROPROM" | awk '{ print $1 }')"
remap_hashes
case "$ROMHASH" in
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
ROM_TYPE=0;;
0227d7c0074f2d0ac935631990da8ec5914597b4)
ROM_TYPE=0;;
328a1f1beba30ce5e178f031662019eb32c5f3b5)
ROM_TYPE=0;;
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
ROM_TYPE=0;;
f46239439f59a2a594ef83cf68ef65043b1bffe2)
ROM_TYPE=1;;
50bebedad9e0f10746a52b07239e47fa6c284d03)
ROM_TYPE=1;;
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
ROM_TYPE=1;;
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
ROM_TYPE=1;;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
ROM_TYPE=1;;
*)
TRY_AGAIN_RESULT=`osascript <<-EOF
set alertText to "Incompatible ROM hash"
set alertMessage to "Incompatible ROM provided, would you like to try again?"
return display alert alertText \
message alertMessage \
as critical \
buttons {"Cancel", "Try Again"}
EOF`
if [[ "$TRY_AGAIN_RESULT" == "button returned:Try Again" ]]; then
SHOULD_PROMPT_FOR_ROM=1
continue;
else
echo "No ROM selected. Exiting..."
exit 1
fi
esac
cp "$DROPROM" "$SHIP_HOME"
# Ask user if they would also like to select the other variant (MQ/Vanilla)
if [ $ROM_TYPE -eq 0 ] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
set alertText to "Success"
set alertMessage to "Would you also like to provide a Master Quest ROM?"
return display alert alertText \
message alertMessage \
buttons {"No", "Yes"}
EOF`
elif [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
set alertText to "Success"
set alertMessage to "Would you also like to provide a Vanilla (Non Master Quest) ROM?"
return display alert alertText \
message alertMessage \
buttons {"No", "Yes"}
EOF`
fi
if [[ "$UPLOAD_ANOTHER_RESULT" == "button returned:Yes" ]]; then
UPLOAD_ANOTHER_RESULT="button returned:No"
SHOULD_PROMPT_FOR_ROM=1
continue;
fi
break
done
done
# At this point we should now have 1 or more valid roms in $SHIP_HOME directory
# Prepare tmp dir
for ROMPATH in "$SHIP_HOME"/*.*64
do
ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
export ASSETDIR
cp -r "$RESPATH/assets" "$ASSETDIR"
mkdir -p "$ASSETDIR"/tmp
cp "$ROMPATH" "$ASSETDIR"/tmp/rom.z64
cd "$ASSETDIR" || return
# If an invalid rom was detected, let the user know
ROMHASH="$(shasum "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')"
remap_hashes
case "$ROMHASH" in
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
ROM=GC_NMQ_D
OTRNAME="oot.otr";;
0227d7c0074f2d0ac935631990da8ec5914597b4)
ROM=GC_NMQ_PAL_F
OTRNAME="oot.otr";;
328a1f1beba30ce5e178f031662019eb32c5f3b5)
ROM=N64_PAL_10
OTRNAME="oot.otr";;
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
ROM=N64_PAL_11
OTRNAME="oot.otr";;
f46239439f59a2a594ef83cf68ef65043b1bffe2)
ROM=GC_MQ_PAL_F
OTRNAME="oot-mq.otr";;
50bebedad9e0f10746a52b07239e47fa6c284d03)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
*)
osascript -e 'display notification "One or more invalid ROM provided" with title "Ship Of Harkinian"'
rm -r "$ASSETDIR"
cd "$SNAME"
continue;
esac
# Only generate OTR if we don't have on of this type yet
if [ -e "$SHIP_HOME"/"$OTRNAME" ]; then
rm -r "$ASSETDIR"
cd "$SNAME"
continue;
fi
osascript -e 'display notification "Generating OTR..." with title "Ship Of Harkinian"'
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --portVer "@CMAKE_PROJECT_VERSION@"
if [ -e "$ASSETDIR"/oot.otr ]; then
osascript -e 'display notification "OTR successfully generated" with title "Ship Of Harkinian"'
cp "$ASSETDIR"/oot.otr "$SHIP_HOME"/"$OTRNAME"
rm -r "$ASSETDIR"
cd "$SNAME"
fi
done
if [ ! -e "$SHIP_HOME"/oot*.otr ]; then
osascript -e 'display notification "OTR failed to generate" with title "Ship Of Harkinian"'
exit 1;
fi
fi
cd "$SNAME"
"$RESPATH"/soh-macos
exit

View File

@ -1,361 +0,0 @@
#include "GameControlEditor.h"
#include <string>
#include <list>
#include <unordered_map>
#include <utility>
#include <iterator>
#include <variables.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h>
#include <ImGui/imgui_internal.h>
#include <libultraship/bridge.h>
#include <libultraship/libultra/controller.h>
#include <Utils/StringHelper.h>
#include <libultraship/libultraship.h>
#include "macros.h"
#include "../../UIWidgets.hpp"
namespace GameControlEditor {
const ImGuiTableFlags PANEL_TABLE_FLAGS =
ImGuiTableFlags_BordersH |
ImGuiTableFlags_BordersV;
const ImGuiTableColumnFlags PANEL_TABLE_COLUMN_FLAGS =
ImGuiTableColumnFlags_IndentEnable |
ImGuiTableColumnFlags_NoSort;
namespace TableHelper {
void InitHeader(bool has_header = true) {
if (has_header) {
ImGui::TableHeadersRow();
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding(); //This is to adjust Vertical pos of item in a cell to be normlized.
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
}
void NextCol() {
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
}
void NextLine() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
}
}
void DrawHelpIcon(const std::string& helptext) {
// place the ? button to the most of the right side of the cell it is using.
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 22);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 15);
ImGui::SmallButton("?");
UIWidgets::Tooltip(helptext.c_str());
}
typedef uint32_t N64ButtonMask;
// Used together for an incomplete linked hash map implementation in order to
// map button masks to their names and original mapping on N64
static std::list<std::pair<N64ButtonMask, const char*>> buttons;
static std::unordered_map<N64ButtonMask, decltype(buttons)::iterator> buttonNames;
void addButtonName(N64ButtonMask mask, const char* name) {
buttons.push_back(std::make_pair(mask, name));
buttonNames[mask] = std::prev(buttons.end());
}
typedef struct {
const char* label;
const char* cVarName;
N64ButtonMask defaultBtn;
} CustomButtonMap;
// Ocarina button maps
static CustomButtonMap ocarinaD5 = {"D5", "gOcarinaD5BtnMap", BTN_CUP};
static CustomButtonMap ocarinaB4 = {"B4", "gOcarinaB4BtnMap", BTN_CLEFT};
static CustomButtonMap ocarinaA4 = {"A4", "gOcarinaA4BtnMap", BTN_CRIGHT};
static CustomButtonMap ocarinaF4 = {"F4", "gOcarinaF4BtnMap", BTN_CDOWN};
static CustomButtonMap ocarinaD4 = {"D4", "gOcarinaD4BtnMap", BTN_A};
static CustomButtonMap ocarinaSongDisable = {"Disable songs", "gOcarinaDisableBtnMap", BTN_L};
static CustomButtonMap ocarinaSharp = {"Pitch up", "gOcarinaSharpBtnMap", BTN_R};
static CustomButtonMap ocarinaFlat = {"Pitch down", "gOcarinaFlatBtnMap", BTN_Z};
void GameControlEditorWindow::InitElement() {
addButtonName(BTN_A, "A");
addButtonName(BTN_B, "B");
addButtonName(BTN_CUP, "C Up");
addButtonName(BTN_CDOWN, "C Down");
addButtonName(BTN_CLEFT, "C Left");
addButtonName(BTN_CRIGHT, "C Right");
addButtonName(BTN_L, "L");
addButtonName(BTN_Z, "Z");
addButtonName(BTN_R, "R");
addButtonName(BTN_START, "Start");
addButtonName(BTN_DUP, "D-pad up");
addButtonName(BTN_DDOWN, "D-pad down");
addButtonName(BTN_DLEFT, "D-pad left");
addButtonName(BTN_DRIGHT, "D-pad right");
addButtonName(0, "None");
}
// Draw a button mapping setting consisting of a padded label and button dropdown.
// excludedButtons indicates which buttons are unavailable to choose from.
void DrawMapping(CustomButtonMap& mapping, float labelWidth, N64ButtonMask excludedButtons) {
N64ButtonMask currentButton = CVarGetInteger(mapping.cVarName, mapping.defaultBtn);
const char* preview;
if (buttonNames.contains(currentButton)) {
preview = buttonNames[currentButton]->second;
} else {
preview = "Unknown";
}
UIWidgets::Spacer(0);
ImVec2 cursorPos = ImGui::GetCursorPos();
ImVec2 textSize = ImGui::CalcTextSize(mapping.label);
ImGui::SetCursorPosY(cursorPos.y + textSize.y / 4);
ImGui::SetCursorPosX(cursorPos.x + abs(textSize.x - labelWidth));
ImGui::Text("%s", mapping.label);
ImGui::SameLine();
ImGui::SetCursorPosY(cursorPos.y);
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::BeginCombo(StringHelper::Sprintf("##%s", mapping.cVarName).c_str(), preview)) {
for (auto i = buttons.begin(); i != buttons.end(); i++) {
if ((i->first & excludedButtons) != 0) {
continue;
}
if (ImGui::Selectable(i->second, i->first == currentButton)) {
CVarSetInteger(mapping.cVarName, i->first);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
}
ImGui::EndCombo();
}
UIWidgets::Spacer(0);
}
void DrawOcarinaControlPanel(GameControlEditorWindow* window) {
if (!ImGui::CollapsingHeader("Ocarina Controls")) {
return;
}
if (!ImGui::BeginTable("tableCustomOcarinaControls", 1, PANEL_TABLE_FLAGS)) {
return;
}
ImGui::TableSetupColumn("Custom Ocarina Controls", PANEL_TABLE_COLUMN_FLAGS | ImGuiTableColumnFlags_WidthStretch);
TableHelper::InitHeader(false);
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
UIWidgets::EnhancementCheckbox("Customize Ocarina Controls", "gCustomOcarinaControls");
if (CVarGetInteger("gCustomOcarinaControls", 0) == 1) {
if (ImGui::BeginTable("tableCustomMainOcarinaControls", 2, ImGuiTableFlags_SizingStretchProp)) {
float labelWidth;
N64ButtonMask disableMask = BTN_B;
if (CVarGetInteger("gDpadOcarina", 0)) {
disableMask |= BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT;
}
ImGui::TableSetupColumn("Notes##CustomOcarinaNotes", PANEL_TABLE_COLUMN_FLAGS);
ImGui::TableSetupColumn("Modifiers##CustomOcaranaModifiers", PANEL_TABLE_COLUMN_FLAGS);
TableHelper::InitHeader(false);
window->BeginGroupPanelPublic("Notes", ImGui::GetContentRegionAvail());
labelWidth = ImGui::CalcTextSize("D5").x + 10;
DrawMapping(ocarinaD5, labelWidth, disableMask);
DrawMapping(ocarinaB4, labelWidth, disableMask);
DrawMapping(ocarinaA4, labelWidth, disableMask);
DrawMapping(ocarinaF4, labelWidth, disableMask);
DrawMapping(ocarinaD4, labelWidth, disableMask);
ImGui::Dummy(ImVec2(0, 5));
float cursorY = ImGui::GetCursorPosY();
window->EndGroupPanelPublic(0);
TableHelper::NextCol();
window->BeginGroupPanelPublic("Modifiers", ImGui::GetContentRegionAvail());
labelWidth = ImGui::CalcTextSize(ocarinaSongDisable.label).x + 10;
DrawMapping(ocarinaSongDisable, labelWidth, disableMask);
DrawMapping(ocarinaSharp, labelWidth, disableMask);
DrawMapping(ocarinaFlat, labelWidth, disableMask);
window->EndGroupPanelPublic(cursorY - ImGui::GetCursorPosY() + 2);
ImGui::EndTable();
}
} else {
UIWidgets::Spacer(0);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
ImGui::TextWrapped("To modify the main ocarina controls, select the \"Customize Ocarina Controls\" checkbox.");
UIWidgets::Spacer(0);
}
window->BeginGroupPanelPublic("Alternate controls", ImGui::GetContentRegionAvail());
if (ImGui::BeginTable("tableOcarinaAlternateControls", 2, ImGuiTableFlags_SizingFixedSame)) {
ImGui::TableSetupColumn("D-pad", PANEL_TABLE_COLUMN_FLAGS);
ImGui::TableSetupColumn("Right stick", PANEL_TABLE_COLUMN_FLAGS);
TableHelper::InitHeader(false);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
UIWidgets::EnhancementCheckbox("Play with D-pad", "gDpadOcarina");
TableHelper::NextCol();
UIWidgets::EnhancementCheckbox("Play with camera stick", "gRStickOcarina");
UIWidgets::Spacer(0);
ImGui::EndTable();
}
window->EndGroupPanelPublic(0);
ImGui::EndTable();
}
void DrawCameraControlPanel(GameControlEditorWindow* window) {
if (!ImGui::CollapsingHeader("Camera Controls")) {
return;
}
UIWidgets::Spacer(0);
window->BeginGroupPanelPublic("Aiming/First-Person Camera", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Right Stick Aiming", "gRightStickAiming");
DrawHelpIcon("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming");
if (CVarGetInteger("gRightStickAiming", 0)) {
UIWidgets::PaddedEnhancementCheckbox("Allow moving while in first person mode", "gMoveWhileFirstPerson");
DrawHelpIcon("Changes the left stick to move the player while in first person mode");
}
UIWidgets::PaddedEnhancementCheckbox("Invert Aiming X Axis", "gInvertAimingXAxis");
DrawHelpIcon("Inverts the Camera X Axis in:\n-First-Person/C-Up view\n-Weapon Aiming");
UIWidgets::PaddedEnhancementCheckbox("Invert Aiming Y Axis", "gInvertAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Inverts the Camera Y Axis in:\n-First-Person/C-Up view\n-Weapon Aiming");
UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming Y Axis", "gInvertShieldAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Inverts the Shield Aiming Y Axis");
UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming X Axis", "gInvertShieldAimingXAxis");
DrawHelpIcon("Inverts the Shield Aiming X Axis");
UIWidgets::PaddedEnhancementCheckbox("Invert Z-Weapon Aiming Y Axis", "gInvertZAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Z-Weapon Aiming");
UIWidgets::PaddedEnhancementCheckbox("Disable Auto-Centering in First-Person View", "gDisableAutoCenterViewFirstPerson");
DrawHelpIcon("Prevents the C-Up view from auto-centering, allowing for Gyro Aiming");
if (UIWidgets::PaddedEnhancementCheckbox("Enable Custom Aiming/First-Person sensitivity", "gEnableFirstPersonSensitivity", true, false)) {
if (!CVarGetInteger("gEnableFirstPersonSensitivity", 0)) {
CVarClear("gFirstPersonCameraSensitivityX");
CVarClear("gFirstPersonCameraSensitivityY");
}
}
if (CVarGetInteger("gEnableFirstPersonSensitivity", 0)) {
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %.0f %%", "##FirstPersonSensitivity Horizontal",
"gFirstPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true);
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %.0f %%", "##FirstPersonSensitivity Vertical",
"gFirstPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true);
}
UIWidgets::Spacer(0);
window->EndGroupPanelPublic(0);
UIWidgets::Spacer(0);
window->BeginGroupPanelPublic("Third-Person Camera", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Free Camera", "gFreeCamera");
DrawHelpIcon("Enables free camera control\nNote: You must remap C buttons off of the right stick in the "
"controller config menu, and map the camera stick to the right stick.");
UIWidgets::PaddedEnhancementCheckbox("Invert Camera X Axis", "gInvertXAxis");
DrawHelpIcon("Inverts the Camera X Axis in:\n-Free camera");
UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free camera");
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %.0f %%", "##ThirdPersonSensitivity Horizontal",
"gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %.0f %%", "##ThirdPersonSensitivity Vertical",
"gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist",
"gFreeCameraDistMax", 100, 900, "", 185, true, false, true);
UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed",
"gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true);
window->EndGroupPanelPublic(0);
}
void DrawDpadControlPanel(GameControlEditorWindow* window) {
if (!ImGui::CollapsingHeader("D-Pad Controls")) {
return;
}
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
window->BeginGroupPanelPublic("D-Pad Options", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("D-pad Support on Pause Screen", "gDpadPause");
DrawHelpIcon("Navigate Pause with the D-pad\nIf used with D-pad as Equip Items, you must hold C-Up to equip instead of navigate\n"
"To make the cursor only move a single space no matter how long a direction is held, manually set gDpadHoldChange to 0");
UIWidgets::PaddedEnhancementCheckbox("D-pad Support in Text Boxes", "gDpadText");
DrawHelpIcon("Navigate choices in text boxes, shop item selection, and the file select / name entry screens with the D-pad\n"
"To make the cursor only move a single space during name entry no matter how long a direction is held, manually set gDpadHoldChange to 0");
UIWidgets::PaddedEnhancementCheckbox("D-pad as Equip Items", "gDpadEquips");
DrawHelpIcon("Equip items and equipment on the D-pad\nIf used with D-pad on Pause Screen, you must hold C-Up to equip instead of navigate");
window->EndGroupPanelPublic(0);
}
void DrawMiscControlPanel(GameControlEditorWindow* window) {
if (!ImGui::CollapsingHeader("Miscellaneous Controls")) {
return;
}
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
window->BeginGroupPanelPublic("Misc Controls", ImGui::GetContentRegionAvail());
UIWidgets::PaddedText("Allow the cursor to be on any slot");
static const char* cursorOnAnySlot[3] = { "Only in Rando", "Always", "Never" };
UIWidgets::EnhancementCombobox("gPauseAnyCursor", cursorOnAnySlot, PAUSE_ANY_CURSOR_RANDO_ONLY);
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
"certain items.");
UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::PaddedEnhancementCheckbox("Enable speed modifiers", "gEnableWalkModify", true, false);
DrawHelpIcon("Hold the assigned button to change the maximum walking or swimming speed\nTo change the assigned button, go into the Ports tabs above");
if (CVarGetInteger("gEnableWalkModify", 0)) {
UIWidgets::Spacer(5);
window->BeginGroupPanelPublic("Speed Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", "gWalkSpeedToggle", true, false);
window->BeginGroupPanelPublic("Walk Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Don't affect jump distance/velocity", "gWalkModifierDoesntChangeJump", true, false);
UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 1: %.0f %%", "##WalkMod1", "gWalkModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 2: %.0f %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
window->EndGroupPanelPublic(0);
window->BeginGroupPanelPublic("Swim Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 1: %.0f %%", "##SwimMod1", "gSwimModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 2: %.0f %%", "##SwimMod2", "gSwimModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
window->EndGroupPanelPublic(0);
window->EndGroupPanelPublic(0);
}
ImGui::EndDisabled();
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
window->EndGroupPanelPublic(0);
}
void GameControlEditorWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(465, 430), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Game Controls Configuration", &mIsVisible)) {
DrawOcarinaControlPanel(this);
DrawCameraControlPanel(this);
DrawDpadControlPanel(this);
DrawMiscControlPanel(this);
}
ImGui::End();
}
void GameControlEditorWindow::BeginGroupPanelPublic(const char* name, const ImVec2& size) {
BeginGroupPanel(name, size);
}
void GameControlEditorWindow::EndGroupPanelPublic(float minHeight) {
EndGroupPanel(minHeight);
}
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <libultraship/libultraship.h>
namespace GameControlEditor {
class GameControlEditorWindow : public LUS::GuiWindow {
public:
using LUS::GuiWindow::GuiWindow;
void BeginGroupPanelPublic(const char* name, const ImVec2& size);
void EndGroupPanelPublic(float minHeight);
void InitElement() override;
void DrawElement() override;
void UpdateElement() override {};
};
static int CurrentPort = 0;
static int BtnReading = -1;
} // namespace GameControlEditor

View File

@ -0,0 +1,519 @@
#include "InputViewer.h"
#include "public/bridge/consolevariablebridge.h"
#include "libultraship/libultra/controller.h"
#include "Context.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h>
#include <spdlog/spdlog.h>
#include <cmath>
#include "../../UIWidgets.hpp"
// 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 const char* buttonOutlineOptions[4] = { "Always Shown", "Shown Only While Not Pressed",
"Shown Only While Pressed", "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);
}
InputViewer::~InputViewer() {
SPDLOG_TRACE("destruct input viewer");
}
void InputViewer::RenderButton(std::string btnTexture, std::string btnOutlineTexture, int state, ImVec2 size,
int outlineMode) {
const ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetNextItemAllowOverlap();
// Render Outline based on settings
if (outlineMode == BUTTON_OUTLINE_ALWAYS_SHOWN || (outlineMode == BUTTON_OUTLINE_NOT_PRESSED && !state) ||
(outlineMode == BUTTON_OUTLINE_PRESSED && state)) {
ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(btnOutlineTexture), size,
ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255));
}
// Render button if pressed
if (state) {
ImGui::SetCursorPos(pos);
ImGui::SetNextItemAllowOverlap();
ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(btnTexture), size,
ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255));
}
}
void InputViewer::DrawElement() {
if (CVarGetInteger("gOpenWindows.InputViewer", 0)) {
static bool sButtonTexturesLoaded = false;
if (!sButtonTexturesLoaded) {
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage(
"Input-Viewer-Background", "textures/buttons/InputViewerBackground.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("A-Btn", "textures/buttons/ABtn.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("B-Btn", "textures/buttons/BBtn.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("L-Btn", "textures/buttons/LBtn.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("R-Btn", "textures/buttons/RBtn.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Z-Btn", "textures/buttons/ZBtn.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Start-Btn",
"textures/buttons/StartBtn.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Left", "textures/buttons/CLeft.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Right", "textures/buttons/CRight.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Up", "textures/buttons/CUp.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Down", "textures/buttons/CDown.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Analog-Stick",
"textures/buttons/AnalogStick.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Left",
"textures/buttons/DPadLeft.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Right",
"textures/buttons/DPadRight.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Up", "textures/buttons/DPadUp.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Down",
"textures/buttons/DPadDown.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Right-Stick",
"textures/buttons/RightStick.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("A-Btn Outline",
"textures/buttons/ABtnOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("B-Btn Outline",
"textures/buttons/BBtnOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("L-Btn Outline",
"textures/buttons/LBtnOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("R-Btn Outline",
"textures/buttons/RBtnOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Z-Btn Outline",
"textures/buttons/ZBtnOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Start-Btn Outline",
"textures/buttons/StartBtnOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Left Outline",
"textures/buttons/CLeftOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Right Outline",
"textures/buttons/CRightOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Up Outline",
"textures/buttons/CUpOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Down Outline",
"textures/buttons/CDownOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Analog-Stick Outline",
"textures/buttons/AnalogStickOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Left Outline",
"textures/buttons/DPadLeftOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Right Outline",
"textures/buttons/DPadRightOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Up Outline",
"textures/buttons/DPadUpOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Down Outline",
"textures/buttons/DPadDownOutline.png");
LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Right-Stick Outline",
"textures/buttons/RightStickOutline.png");
sButtonTexturesLoaded = true;
}
ImVec2 mainPos = ImGui::GetWindowPos();
ImVec2 size = ImGui::GetContentRegionAvail();
#ifdef __WIIU__
const float scale = CVarGetFloat("gInputViewer.Scale", 1.0f) * 2.0f;
#else
const float scale = CVarGetFloat("gInputViewer.Scale", 1.0f);
#endif
const int showAnalogAngles = CVarGetInteger("gInputViewer.AnalogAngles.Enabled", 0);
const int buttonOutlineMode = CVarGetInteger("gInputViewer.ButtonOutlineMode", BUTTON_OUTLINE_NOT_PRESSED);
ImVec2 bgSize = LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureSize("Input-Viewer-Background");
ImVec2 scaledBGSize = ImVec2(bgSize.x * scale, bgSize.y * scale);
ImGui::SetNextWindowSize(ImVec2(
scaledBGSize.x + 20,
scaledBGSize.y +
(showAnalogAngles ? ImGui::CalcTextSize("X").y : 0) * scale * CVarGetFloat("gInputViewer.AnalogAngles.Scale", 1.0f) + 20));
ImGui::SetNextWindowContentSize(
ImVec2(scaledBGSize.x, scaledBGSize.y + (showAnalogAngles ? 15 : 0) * scale *
CVarGetFloat("gInputViewer.AnalogAngles.Scale", 1.0f)));
ImGui::SetNextWindowPos(
ImVec2(mainPos.x + size.x - scaledBGSize.x - 30, mainPos.y + size.y - scaledBGSize.y - 30),
ImGuiCond_FirstUseEver);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
OSContPad* pads = LUS::Context::GetInstance()->GetControlDeck()->GetPads();
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoFocusOnAppearing;
if (!CVarGetInteger("gInputViewer.EnableDragging", 1)) {
windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove;
}
if (pads != nullptr && ImGui::Begin("Input Viewer", nullptr, windowFlags)) {
ImGui::SetCursorPos(ImVec2(10, 10));
const ImVec2 aPos = ImGui::GetCursorPos();
if (CVarGetInteger("gInputViewer.ShowBackground", 1)) {
ImGui::SetNextItemAllowOverlap();
// Background
ImGui::Image(
LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Input-Viewer-Background"),
scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255));
}
// A/B
if (CVarGetInteger("gInputViewer.BBtn", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("B-Btn", "B-Btn Outline", pads[0].button & BTN_B, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger("gInputViewer.ABtn", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("A-Btn", "A-Btn Outline", pads[0].button & BTN_A, scaledBGSize, buttonOutlineMode);
}
// C buttons
if (CVarGetInteger("gInputViewer.CUp", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("C-Up", "C-Up Outline", pads[0].button & BTN_CUP, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger("gInputViewer.CLeft", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("C-Left", "C-Left Outline", pads[0].button & BTN_CLEFT, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger("gInputViewer.CRight", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("C-Right", "C-Right Outline", pads[0].button & BTN_CRIGHT, scaledBGSize,
buttonOutlineMode);
}
if (CVarGetInteger("gInputViewer.CDown", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("C-Down", "C-Down Outline", pads[0].button & BTN_CDOWN, scaledBGSize, buttonOutlineMode);
}
// L/R/Z
if (CVarGetInteger("gInputViewer.LBtn", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("L-Btn", "L-Btn Outline", pads[0].button & BTN_L, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger("gInputViewer.RBtn", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("R-Btn", "R-Btn Outline", pads[0].button & BTN_R, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger("gInputViewer.ZBtn", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Z-Btn", "Z-Btn Outline", pads[0].button & BTN_Z, scaledBGSize, buttonOutlineMode);
}
// Start
if (CVarGetInteger("gInputViewer.StartBtn", 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Start-Btn", "Start-Btn Outline", pads[0].button & BTN_START, scaledBGSize,
buttonOutlineMode);
}
// Dpad
if (CVarGetInteger("gInputViewer.Dpad", 0)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Dpad-Left", "Dpad-Left Outline", pads[0].button & BTN_DLEFT, scaledBGSize,
buttonOutlineMode);
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Dpad-Right", "Dpad-Right Outline", pads[0].button & BTN_DRIGHT, scaledBGSize,
buttonOutlineMode);
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Dpad-Up", "Dpad-Up Outline", pads[0].button & BTN_DUP, scaledBGSize, buttonOutlineMode);
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Dpad-Down", "Dpad-Down Outline", pads[0].button & BTN_DDOWN, scaledBGSize,
buttonOutlineMode);
}
const bool analogStickIsInDeadzone = !pads[0].stick_x && !pads[0].stick_y;
const bool rightStickIsInDeadzone = !pads[0].right_stick_x && !pads[0].right_stick_y;
// Analog Stick
const int analogOutlineMode =
CVarGetInteger("gInputViewer.AnalogStick.OutlineMode", STICK_MODE_ALWAYS_SHOWN);
const float maxStickDistance = CVarGetInteger("gInputViewer.AnalogStick.Movement", 12);
if (analogOutlineMode == STICK_MODE_ALWAYS_SHOWN ||
(analogOutlineMode == STICK_MODE_HIDDEN_IN_DEADZONE && !analogStickIsInDeadzone)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
ImGui::Image(
LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Analog-Stick Outline"),
scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255));
}
const int analogStickMode =
CVarGetInteger("gInputViewer.AnalogStick.VisibilityMode", STICK_MODE_ALWAYS_SHOWN);
if (analogStickMode == STICK_MODE_ALWAYS_SHOWN ||
(analogStickMode == STICK_MODE_HIDDEN_IN_DEADZONE && !analogStickIsInDeadzone)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(
ImVec2(aPos.x + maxStickDistance * ((float)(pads[0].stick_x) / MAX_AXIS_RANGE) * scale,
aPos.y - maxStickDistance * ((float)(pads[0].stick_y) / MAX_AXIS_RANGE) * scale));
ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Analog-Stick"),
scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255));
}
// Right Stick
const float maxRightStickDistance = CVarGetInteger("gInputViewer.RightStick.Movement", 7);
const int rightOutlineMode =
CVarGetInteger("gInputViewer.RightStick.OutlineMode", STICK_MODE_ALWAYS_HIDDEN);
if (rightOutlineMode == STICK_MODE_ALWAYS_SHOWN ||
(rightOutlineMode == STICK_MODE_HIDDEN_IN_DEADZONE && !rightStickIsInDeadzone)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
ImGui::Image(
LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Right-Stick Outline"),
scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255));
}
const int rightStickMode =
CVarGetInteger("gInputViewer.RightStick.VisibilityMode", STICK_MODE_ALWAYS_HIDDEN);
if (rightStickMode == STICK_MODE_ALWAYS_SHOWN ||
(rightStickMode == STICK_MODE_HIDDEN_IN_DEADZONE && !rightStickIsInDeadzone)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(
ImVec2(aPos.x + maxRightStickDistance * ((float)(pads[0].right_stick_x) / MAX_AXIS_RANGE) * scale,
aPos.y - maxRightStickDistance * ((float)(pads[0].right_stick_y) / MAX_AXIS_RANGE) * scale));
ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Right-Stick"),
scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255));
}
// Analog stick angle text
if (showAnalogAngles) {
ImGui::SetCursorPos(ImVec2(aPos.x + 10 + CVarGetInteger("gInputViewer.AnalogAngles.Offset", 0) * scale,
scaledBGSize.y + aPos.y + 10));
// Scale font with input viewer scale
float oldFontScale = ImGui::GetFont()->Scale;
ImGui::GetFont()->Scale *= scale * CVarGetFloat("gInputViewer.AnalogAngles.Scale", 1.0f);
ImGui::PushFont(ImGui::GetFont());
// Calculate polar R coordinate from X and Y angles, squared to avoid sqrt
const float rSquared = pads[0].stick_x * pads[0].stick_x + pads[0].stick_y * pads[0].stick_y;
// ESS range
const int range1Min = CVarGetInteger("gInputViewer.AnalogAngles.Range1.Min", 8);
const int range1Max = CVarGetInteger("gInputViewer.AnalogAngles.Range1.Max", 27);
// Walking speed range
const int range2Min = CVarGetInteger("gInputViewer.AnalogAngles.Range2.Min", 27);
const int range2Max = CVarGetInteger("gInputViewer.AnalogAngles.Range2.Max", 62);
// Push color based on angle ranges
if (CVarGetInteger("gInputViewer.AnalogAngles.Range1.Enabled", 0) &&
(rSquared >= (range1Min * range1Min)) && (rSquared < (range1Max * range1Max))) {
ImGui::PushStyleColor(
ImGuiCol_Text,
color2Vec(CVarGetColor("gInputViewer.AnalogAngles.Range1.Color", vec2Color(range1Color))));
} else if (CVarGetInteger("gInputViewer.AnalogAngles.Range2.Enabled", 0) &&
(rSquared >= (range2Min * range2Min)) && (rSquared < (range2Max * range2Max))) {
ImGui::PushStyleColor(
ImGuiCol_Text,
color2Vec(CVarGetColor("gInputViewer.AnalogAngles.Range2.Color", vec2Color(range2Color))));
} else {
ImGui::PushStyleColor(ImGuiCol_Text, color2Vec(CVarGetColor("gInputViewer.AnalogAngles.TextColor",
vec2Color(textColor))));
}
// Render text
ImGui::Text("X: %-3d Y: %-3d", pads[0].stick_x, pads[0].stick_y);
// Restore original color
ImGui::PopStyleColor();
// Restore original font scale
ImGui::GetFont()->Scale = oldFontScale;
ImGui::PopFont();
}
ImGui::End();
}
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
}
InputViewerSettingsWindow::~InputViewerSettingsWindow() {
SPDLOG_TRACE("destruct input viewer settings window");
}
void InputViewerSettingsWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(450, 525), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Input Viewer Settings", &mIsVisible)) {
// gInputViewer.Scale
UIWidgets::EnhancementSliderFloat("Input Viewer Scale: %.2f", "##Input", "gInputViewer.Scale", 0.1f, 5.0f, "",
1.0f, false, true);
UIWidgets::Tooltip("Sets the on screen size of the input viewer");
// gInputViewer.EnableDragging
UIWidgets::EnhancementCheckbox("Enable Dragging", "gInputViewer.EnableDragging", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
UIWidgets::PaddedSeparator(true, true);
// gInputViewer.ShowBackground
UIWidgets::EnhancementCheckbox("Show Background Layer", "gInputViewer.ShowBackground", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
UIWidgets::PaddedSeparator(true, true);
if (ImGui::CollapsingHeader("Buttons")) {
// gInputViewer.ABtn
UIWidgets::EnhancementCheckbox("Show A-Button Layers", "gInputViewer.ABtn", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.BBtn
UIWidgets::EnhancementCheckbox("Show B-Button Layers", "gInputViewer.BBtn", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CUp
UIWidgets::EnhancementCheckbox("Show C-Up Layers", "gInputViewer.CUp", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CRight
UIWidgets::EnhancementCheckbox("Show C-Right Layers", "gInputViewer.CRight", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CDown
UIWidgets::EnhancementCheckbox("Show C-Down Layers", "gInputViewer.CDown", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CLeft
UIWidgets::EnhancementCheckbox("Show C-Left Layers", "gInputViewer.CLeft", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.LBtn
UIWidgets::EnhancementCheckbox("Show L-Button Layers", "gInputViewer.LBtn", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.RBtn
UIWidgets::EnhancementCheckbox("Show R-Button Layers", "gInputViewer.RBtn", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.ZBtn
UIWidgets::EnhancementCheckbox("Show Z-Button Layers", "gInputViewer.ZBtn", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.StartBtn
UIWidgets::EnhancementCheckbox("Show Start Button Layers", "gInputViewer.StartBtn", false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.Dpad
UIWidgets::EnhancementCheckbox("Show D-Pad Layers", "gInputViewer.Dpad", false, "",
UIWidgets::CheckboxGraphics::Checkmark, false);
// gInputViewer.ButtonOutlineMode
UIWidgets::PaddedText("Button Outlines/Backgrounds", true, false);
UIWidgets::EnhancementCombobox("gInputViewer.ButtonOutlineMode", buttonOutlineOptions,
BUTTON_OUTLINE_NOT_PRESSED);
UIWidgets::Tooltip(
"Sets the desired visibility behavior for the button outline/background layers. Useful for "
"custom input viewers.");
UIWidgets::PaddedSeparator(true, true);
}
if (ImGui::CollapsingHeader("Analog Stick")) {
// gInputViewer.AnalogStick.VisibilityMode
UIWidgets::PaddedText("Analog Stick Visibility", true, false);
UIWidgets::EnhancementCombobox("gInputViewer.AnalogStick.VisibilityMode", stickModeOptions,
STICK_MODE_ALWAYS_SHOWN);
UIWidgets::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("gInputViewer.AnalogStick.OutlineMode", stickModeOptions,
STICK_MODE_ALWAYS_SHOWN);
UIWidgets::Tooltip(
"Determines the conditions under which the analog stick outline/background texture is visible.");
// gInputViewer.AnalogStick.Movement
UIWidgets::EnhancementSliderInt("Analog Stick Movement: %dpx", "##AnalogMovement",
"gInputViewer.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.");
UIWidgets::PaddedSeparator(true, true);
}
if (ImGui::CollapsingHeader("Additional (\"Right\") Stick")) {
// gInputViewer.RightStick.VisibilityMode
UIWidgets::PaddedText("Right Stick Visibility", true, false);
UIWidgets::EnhancementCombobox("gInputViewer.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.");
// gInputViewer.RightStick.OutlineMode
UIWidgets::PaddedText("Right Stick Outline/Background Visibility", true, false);
UIWidgets::EnhancementCombobox("gInputViewer.RightStick.OutlineMode", stickModeOptions,
STICK_MODE_HIDDEN_IN_DEADZONE);
UIWidgets::Tooltip(
"Determines the conditions under which the right stick outline/background texture is visible.");
// gInputViewer.RightStick.Movement
UIWidgets::EnhancementSliderInt("Right Stick Movement: %dpx", "##RightMovement",
"gInputViewer.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.");
UIWidgets::PaddedSeparator(true, true);
}
if (ImGui::CollapsingHeader("Analog Angle Values")) {
// gAnalogAngles
UIWidgets::EnhancementCheckbox("Show Analog Stick Angle Values", "gInputViewer.AnalogAngles.Enabled");
UIWidgets::Tooltip("Displays analog stick angle values in the input viewer");
if (CVarGetInteger("gInputViewer.AnalogAngles.Enabled", 0)) {
// gInputViewer.AnalogAngles.TextColor
if (ImGui::ColorEdit4("Text Color", (float*)&textColor)) {
CVarSetColor("gInputViewer.AnalogAngles.TextColor", vec2Color(textColor));
}
// gAnalogAngleScale
UIWidgets::EnhancementSliderFloat("Angle Text Scale: %.2f%%", "##AnalogAngleScale",
"gInputViewer.AnalogAngles.Scale", 0.1f, 5.0f, "", 1.0f, true, true);
// gInputViewer.AnalogAngles.Offset
UIWidgets::EnhancementSliderInt("Angle Text Offset: %dpx", "##AnalogAngleOffset",
"gInputViewer.AnalogAngles.Offset", 0, 400, "", 0, true);
UIWidgets::PaddedSeparator(true, true);
// gInputViewer.AnalogAngles.Range1.Enabled
UIWidgets::EnhancementCheckbox("Highlight ESS Position", "gInputViewer.AnalogAngles.Range1.Enabled");
UIWidgets::Tooltip(
"Highlights the angle value text when the analog stick is in ESS position (on flat ground)");
if (CVarGetInteger("gInputViewer.AnalogAngles.Range1.Enabled", 0)) {
// gInputViewer.AnalogAngles.Range1.Color
if (ImGui::ColorEdit4("ESS Color", (float*)&range1Color)) {
CVarSetColor("gInputViewer.AnalogAngles.Range1.Color", vec2Color(range1Color));
}
}
UIWidgets::PaddedSeparator(true, true);
// gInputViewer.AnalogAngles.Range2.Enabled
UIWidgets::EnhancementCheckbox("Highlight Walking Speed Angles",
"gInputViewer.AnalogAngles.Range2.Enabled");
UIWidgets::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");
if (CVarGetInteger("gInputViewer.AnalogAngles.Range2.Enabled", 0)) {
// gInputViewer.AnalogAngles.Range2.Color
if (ImGui::ColorEdit4("Walking Speed Color", (float*)&range2Color)) {
CVarSetColor("gInputViewer.AnalogAngles.Range2.Color", vec2Color(range2Color));
}
}
}
}
ImGui::End();
}
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <libultraship/libultraship.h>
typedef enum {
BUTTON_OUTLINE_ALWAYS_SHOWN,
BUTTON_OUTLINE_NOT_PRESSED,
BUTTON_OUTLINE_PRESSED,
BUTTON_OUTLINE_ALWAYS_HIDDEN
} ButtonOutlineMode;
typedef enum {
STICK_MODE_ALWAYS_SHOWN,
STICK_MODE_HIDDEN_IN_DEADZONE,
STICK_MODE_ALWAYS_HIDDEN,
} StickMode;
class InputViewer : public LUS::GuiWindow {
public:
using LUS::GuiWindow::GuiWindow;
void InitElement() override {};
void DrawElement() override;
void UpdateElement() override {};
InputViewer();
~InputViewer();
void Draw();
private:
void RenderButton(std::string btn, std::string btnOutline, int state, ImVec2 size, int outlineMode);
};
class InputViewerSettingsWindow : public LUS::GuiWindow {
public:
using LUS::GuiWindow::GuiWindow;
void InitElement() override {};
void DrawElement() override;
void UpdateElement() override {};
InputViewerSettingsWindow();
~InputViewerSettingsWindow();
void Draw();
};

View File

@ -22,6 +22,22 @@ void SohInputEditorWindow::InitElement() {
mButtonsBitmasks = { BTN_A, BTN_B, BTN_START, BTN_L, BTN_R, BTN_Z, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT };
mDpadBitmasks = { BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
mModifierButtonsBitmasks = { BTN_MODIFIER1, BTN_MODIFIER2 };
addButtonName(BTN_A, "A");
addButtonName(BTN_B, "B");
addButtonName(BTN_CUP, "C Up");
addButtonName(BTN_CDOWN, "C Down");
addButtonName(BTN_CLEFT, "C Left");
addButtonName(BTN_CRIGHT, "C Right");
addButtonName(BTN_L, "L");
addButtonName(BTN_Z, "Z");
addButtonName(BTN_R, "R");
addButtonName(BTN_START, "Start");
addButtonName(BTN_DUP, "D-pad up");
addButtonName(BTN_DDOWN, "D-pad down");
addButtonName(BTN_DLEFT, "D-pad left");
addButtonName(BTN_DRIGHT, "D-pad right");
addButtonName(0, "None");
}
#define INPUT_EDITOR_WINDOW_GAME_INPUT_BLOCK_ID 95237929
@ -1017,14 +1033,6 @@ void SohInputEditorWindow::DrawAddLEDMappingButton(uint8_t port) {
}
}
void SohInputEditorWindow::DrawHelpIcon(const std::string& helptext) {
// place the ? button to the most of the right side of the cell it is using.
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - SCALE_IMGUI_SIZE(22));
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - SCALE_IMGUI_SIZE(15));
ImGui::SmallButton("?");
UIWidgets::Tooltip(helptext.c_str());
}
void SohInputEditorWindow::DrawLEDSection(uint8_t port) {
for (auto [id, mapping] :
LUS::Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetLED()->GetAllLEDMappings()) {
@ -1063,11 +1071,11 @@ void SohInputEditorWindow::DrawLEDSection(uint8_t port) {
};
UIWidgets::PaddedText("Source");
UIWidgets::EnhancementCombobox("gLedColorSource", ledSources, LED_SOURCE_TUNIC_ORIGINAL);
DrawHelpIcon("Health\n- Red when health critical (13-20% depending on max health)\n- Yellow when "
"health < 40%. Green otherwise.\n\n"
"Tunics: colors will mirror currently equipped tunic, whether original or the current "
"values in Cosmetics Editor.\n\n"
"Custom: single, solid color");
UIWidgets::Tooltip("Health\n- Red when health critical (13-20% depending on max health)\n- Yellow when "
"health < 40%. Green otherwise.\n\n"
"Tunics: colors will mirror currently equipped tunic, whether original or the current "
"values in Cosmetics Editor.\n\n"
"Custom: single, solid color");
if (CVarGetInteger("gLedColorSource", 1) == LED_SOURCE_CUSTOM) {
UIWidgets::Spacer(3);
auto port1Color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
@ -1087,12 +1095,12 @@ void SohInputEditorWindow::DrawLEDSection(uint8_t port) {
}
UIWidgets::PaddedEnhancementSliderFloat("Brightness: %.1f %%", "##LED_Brightness", "gLedBrightness", 0.0f,
1.0f, "", 1.0f, true, true);
DrawHelpIcon("Sets the brightness of controller LEDs. 0% brightness = LEDs off.");
UIWidgets::Tooltip("Sets the brightness of controller LEDs. 0% brightness = LEDs off.");
UIWidgets::PaddedEnhancementCheckbox(
"Critical Health Override", "gLedCriticalOverride", true, true,
CVarGetInteger("gLedColorSource", LED_SOURCE_TUNIC_ORIGINAL) == LED_SOURCE_HEALTH,
"Override redundant for health source.", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Shows red color when health is critical, otherwise displays according to color source.");
UIWidgets::Tooltip("Shows red color when health is critical, otherwise displays according to color source.");
}
ImGui::TreePop();
}
@ -1430,6 +1438,273 @@ void SohInputEditorWindow::DrawLEDDeviceIcons(uint8_t portIndex) {
}
}
const ImGuiTableFlags PANEL_TABLE_FLAGS =
ImGuiTableFlags_BordersH |
ImGuiTableFlags_BordersV;
const ImGuiTableColumnFlags PANEL_TABLE_COLUMN_FLAGS =
ImGuiTableColumnFlags_IndentEnable |
ImGuiTableColumnFlags_NoSort;
namespace TableHelper {
void InitHeader(bool has_header = true) {
if (has_header) {
ImGui::TableHeadersRow();
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding(); //This is to adjust Vertical pos of item in a cell to be normlized.
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
}
void NextCol() {
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
}
void NextLine() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
}
}
typedef uint32_t N64ButtonMask;
void SohInputEditorWindow::addButtonName(N64ButtonMask mask, const char* name) {
buttons.push_back(std::make_pair(mask, name));
buttonNames[mask] = std::prev(buttons.end());
}
// Ocarina button maps
static CustomButtonMap ocarinaD5 = {"D5", "gOcarinaD5BtnMap", BTN_CUP};
static CustomButtonMap ocarinaB4 = {"B4", "gOcarinaB4BtnMap", BTN_CLEFT};
static CustomButtonMap ocarinaA4 = {"A4", "gOcarinaA4BtnMap", BTN_CRIGHT};
static CustomButtonMap ocarinaF4 = {"F4", "gOcarinaF4BtnMap", BTN_CDOWN};
static CustomButtonMap ocarinaD4 = {"D4", "gOcarinaD4BtnMap", BTN_A};
static CustomButtonMap ocarinaSongDisable = {"Disable songs", "gOcarinaDisableBtnMap", BTN_L};
static CustomButtonMap ocarinaSharp = {"Pitch up", "gOcarinaSharpBtnMap", BTN_R};
static CustomButtonMap ocarinaFlat = {"Pitch down", "gOcarinaFlatBtnMap", BTN_Z};
// Draw a button mapping setting consisting of a padded label and button dropdown.
// excludedButtons indicates which buttons are unavailable to choose from.
void SohInputEditorWindow::DrawMapping(CustomButtonMap& mapping, float labelWidth, N64ButtonMask excludedButtons) {
N64ButtonMask currentButton = CVarGetInteger(mapping.cVarName, mapping.defaultBtn);
const char* preview;
if (buttonNames.contains(currentButton)) {
preview = buttonNames[currentButton]->second;
} else {
preview = "Unknown";
}
UIWidgets::Spacer(0);
ImVec2 cursorPos = ImGui::GetCursorPos();
ImVec2 textSize = ImGui::CalcTextSize(mapping.label);
ImGui::SetCursorPosY(cursorPos.y + textSize.y / 4);
ImGui::SetCursorPosX(cursorPos.x + abs(textSize.x - labelWidth));
ImGui::Text("%s", mapping.label);
ImGui::SameLine();
ImGui::SetCursorPosY(cursorPos.y);
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
if (ImGui::BeginCombo(StringHelper::Sprintf("##%s", mapping.cVarName).c_str(), preview)) {
for (auto i = buttons.begin(); i != buttons.end(); i++) {
if ((i->first & excludedButtons) != 0) {
continue;
}
if (ImGui::Selectable(i->second, i->first == currentButton)) {
CVarSetInteger(mapping.cVarName, i->first);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
}
ImGui::EndCombo();
}
UIWidgets::Spacer(0);
}
void SohInputEditorWindow::DrawOcarinaControlPanel() {
if (!ImGui::BeginTable("tableCustomOcarinaControls", 1, PANEL_TABLE_FLAGS)) {
return;
}
ImGui::TableSetupColumn("Custom Ocarina Controls", PANEL_TABLE_COLUMN_FLAGS | ImGuiTableColumnFlags_WidthStretch);
TableHelper::InitHeader(false);
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
UIWidgets::EnhancementCheckbox("Customize Ocarina Controls", "gCustomOcarinaControls");
if (CVarGetInteger("gCustomOcarinaControls", 0) == 1) {
if (ImGui::BeginTable("tableCustomMainOcarinaControls", 2, ImGuiTableFlags_SizingStretchProp)) {
float labelWidth;
N64ButtonMask disableMask = BTN_B;
if (CVarGetInteger("gDpadOcarina", 0)) {
disableMask |= BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT;
}
ImGui::TableSetupColumn("Notes##CustomOcarinaNotes", PANEL_TABLE_COLUMN_FLAGS);
ImGui::TableSetupColumn("Modifiers##CustomOcaranaModifiers", PANEL_TABLE_COLUMN_FLAGS);
TableHelper::InitHeader(false);
LUS::GuiWindow::BeginGroupPanel("Notes", ImGui::GetContentRegionAvail());
labelWidth = ImGui::CalcTextSize("D5").x + 10;
DrawMapping(ocarinaD5, labelWidth, disableMask);
DrawMapping(ocarinaB4, labelWidth, disableMask);
DrawMapping(ocarinaA4, labelWidth, disableMask);
DrawMapping(ocarinaF4, labelWidth, disableMask);
DrawMapping(ocarinaD4, labelWidth, disableMask);
ImGui::Dummy(ImVec2(0, 5));
float cursorY = ImGui::GetCursorPosY();
LUS::GuiWindow::EndGroupPanel(0);
TableHelper::NextCol();
LUS::GuiWindow::BeginGroupPanel("Modifiers", ImGui::GetContentRegionAvail());
labelWidth = ImGui::CalcTextSize(ocarinaSongDisable.label).x + 10;
DrawMapping(ocarinaSongDisable, labelWidth, disableMask);
DrawMapping(ocarinaSharp, labelWidth, disableMask);
DrawMapping(ocarinaFlat, labelWidth, disableMask);
LUS::GuiWindow::EndGroupPanel(cursorY - ImGui::GetCursorPosY() + 2);
ImGui::EndTable();
}
} else {
UIWidgets::Spacer(0);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
ImGui::TextWrapped("To modify the main ocarina controls, select the \"Customize Ocarina Controls\" checkbox.");
UIWidgets::Spacer(0);
}
LUS::GuiWindow::BeginGroupPanel("Alternate controls", ImGui::GetContentRegionAvail());
if (ImGui::BeginTable("tableOcarinaAlternateControls", 2, ImGuiTableFlags_SizingFixedSame)) {
ImGui::TableSetupColumn("D-pad", PANEL_TABLE_COLUMN_FLAGS);
ImGui::TableSetupColumn("Right stick", PANEL_TABLE_COLUMN_FLAGS);
TableHelper::InitHeader(false);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
UIWidgets::EnhancementCheckbox("Play with D-pad", "gDpadOcarina");
TableHelper::NextCol();
UIWidgets::EnhancementCheckbox("Play with camera stick", "gRStickOcarina");
UIWidgets::Spacer(0);
ImGui::EndTable();
}
LUS::GuiWindow::EndGroupPanel(0);
ImGui::EndTable();
}
void SohInputEditorWindow::DrawCameraControlPanel() {
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
LUS::GuiWindow::BeginGroupPanel("Aiming/First-Person Camera", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Right Stick Aiming", "gRightStickAiming");
UIWidgets::Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming");
if (CVarGetInteger("gRightStickAiming", 0)) {
UIWidgets::PaddedEnhancementCheckbox("Allow moving while in first person mode", "gMoveWhileFirstPerson");
UIWidgets::Tooltip("Changes the left stick to move the player while in first person mode");
}
UIWidgets::PaddedEnhancementCheckbox("Invert Aiming X Axis", "gInvertAimingXAxis");
UIWidgets::Tooltip("Inverts the Camera X Axis in:\n-First-Person/C-Up view\n-Weapon Aiming");
UIWidgets::PaddedEnhancementCheckbox("Invert Aiming Y Axis", "gInvertAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
UIWidgets::Tooltip("Inverts the Camera Y Axis in:\n-First-Person/C-Up view\n-Weapon Aiming");
UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming Y Axis", "gInvertShieldAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
UIWidgets::Tooltip("Inverts the Shield Aiming Y Axis");
UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming X Axis", "gInvertShieldAimingXAxis");
UIWidgets::Tooltip("Inverts the Shield Aiming X Axis");
UIWidgets::PaddedEnhancementCheckbox("Invert Z-Weapon Aiming Y Axis", "gInvertZAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
UIWidgets::Tooltip("Inverts the Camera Y Axis in:\n-Z-Weapon Aiming");
UIWidgets::PaddedEnhancementCheckbox("Disable Auto-Centering in First-Person View", "gDisableAutoCenterViewFirstPerson");
UIWidgets::Tooltip("Prevents the C-Up view from auto-centering, allowing for Gyro Aiming");
if (UIWidgets::PaddedEnhancementCheckbox("Enable Custom Aiming/First-Person sensitivity", "gEnableFirstPersonSensitivity", true, false)) {
if (!CVarGetInteger("gEnableFirstPersonSensitivity", 0)) {
CVarClear("gFirstPersonCameraSensitivityX");
CVarClear("gFirstPersonCameraSensitivityY");
}
}
if (CVarGetInteger("gEnableFirstPersonSensitivity", 0)) {
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %.0f %%", "##FirstPersonSensitivity Horizontal",
"gFirstPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true);
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %.0f %%", "##FirstPersonSensitivity Vertical",
"gFirstPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true);
}
UIWidgets::Spacer(0);
LUS::GuiWindow::EndGroupPanel(0);
UIWidgets::Spacer(0);
cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
LUS::GuiWindow::BeginGroupPanel("Third-Person Camera", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Free Camera", "gFreeCamera");
UIWidgets::Tooltip("Enables free camera control\nNote: You must remap C buttons off of the right stick in the "
"controller config menu, and map the camera stick to the right stick.");
UIWidgets::PaddedEnhancementCheckbox("Invert Camera X Axis", "gInvertXAxis");
UIWidgets::Tooltip("Inverts the Camera X Axis in:\n-Free camera");
UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
UIWidgets::Tooltip("Inverts the Camera Y Axis in:\n-Free camera");
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %.0f %%", "##ThirdPersonSensitivity Horizontal",
"gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %.0f %%", "##ThirdPersonSensitivity Vertical",
"gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist",
"gFreeCameraDistMax", 100, 900, "", 185, true, false, true);
UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed",
"gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true);
LUS::GuiWindow::EndGroupPanel(0);
}
void SohInputEditorWindow::DrawDpadControlPanel() {
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
LUS::GuiWindow::BeginGroupPanel("D-Pad Options", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("D-pad Support on Pause Screen", "gDpadPause");
UIWidgets::Tooltip("Navigate Pause with the D-pad\nIf used with D-pad as Equip Items, you must hold C-Up to equip instead of navigate\n"
"To make the cursor only move a single space no matter how long a direction is held, manually set gDpadHoldChange to 0");
UIWidgets::PaddedEnhancementCheckbox("D-pad Support in Text Boxes", "gDpadText");
UIWidgets::Tooltip("Navigate choices in text boxes, shop item selection, and the file select / name entry screens with the D-pad\n"
"To make the cursor only move a single space during name entry no matter how long a direction is held, manually set gDpadHoldChange to 0");
UIWidgets::PaddedEnhancementCheckbox("D-pad as Equip Items", "gDpadEquips");
UIWidgets::Tooltip("Equip items and equipment on the D-pad\nIf used with D-pad on Pause Screen, you must hold C-Up to equip instead of navigate");
LUS::GuiWindow::EndGroupPanel(0);
}
void SohInputEditorWindow::DrawMiscControlPanel() {
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
LUS::GuiWindow::BeginGroupPanel("Misc Controls", ImGui::GetContentRegionAvail());
UIWidgets::PaddedText("Allow the cursor to be on any slot");
static const char* cursorOnAnySlot[3] = { "Only in Rando", "Always", "Never" };
UIWidgets::EnhancementCombobox("gPauseAnyCursor", cursorOnAnySlot, PAUSE_ANY_CURSOR_RANDO_ONLY);
UIWidgets::Tooltip("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
"certain items.");
UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::PaddedEnhancementCheckbox("Enable speed modifiers", "gEnableWalkModify", true, false);
UIWidgets::Tooltip("Hold the assigned button to change the maximum walking or swimming speed");
if (CVarGetInteger("gEnableWalkModify", 0)) {
UIWidgets::Spacer(5);
LUS::GuiWindow::BeginGroupPanel("Speed Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", "gWalkSpeedToggle", true, false);
LUS::GuiWindow::BeginGroupPanel("Walk Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Don't affect jump distance/velocity", "gWalkModifierDoesntChangeJump", true, false);
UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 1: %.0f %%", "##WalkMod1", "gWalkModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Walk Modifier 2: %.0f %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
LUS::GuiWindow::EndGroupPanel(0);
LUS::GuiWindow::BeginGroupPanel("Swim Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 1: %.0f %%", "##SwimMod1", "gSwimModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Swim Modifier 2: %.0f %%", "##SwimMod2", "gSwimModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
LUS::GuiWindow::EndGroupPanel(0);
LUS::GuiWindow::EndGroupPanel(0);
}
ImGui::EndDisabled();
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
UIWidgets::Tooltip("Speak to Navi with L but enter first-person camera with C-Up");
LUS::GuiWindow::EndGroupPanel(0);
}
void SohInputEditorWindow::DrawLinkTab() {
uint8_t portIndex = 0;
if (ImGui::BeginTabItem(StringHelper::Sprintf("Link (P1)###port%d", portIndex).c_str())) {
@ -1516,6 +1791,46 @@ void SohInputEditorWindow::DrawLinkTab() {
DrawButtonDeviceIcons(portIndex, mModifierButtonsBitmasks);
}
if (ImGui::CollapsingHeader("Ocarina Controls")) {
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
DrawOcarinaControlPanel();
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
}
if (ImGui::CollapsingHeader("Camera Controls")) {
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
DrawCameraControlPanel();
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
}
if (ImGui::CollapsingHeader("D-Pad Controls")) {
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
DrawDpadControlPanel();
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
}
if (ImGui::CollapsingHeader("Miscellaneous Controls")) {
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
DrawMiscControlPanel();
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();

View File

@ -10,6 +10,15 @@
#include <string>
#include <vector>
#include <set>
#include <list>
typedef uint32_t N64ButtonMask;
typedef struct {
const char* label;
const char* cVarName;
N64ButtonMask defaultBtn;
} CustomButtonMap;
class SohInputEditorWindow : public LUS::GuiWindow {
public:
@ -51,6 +60,17 @@ class SohInputEditorWindow : public LUS::GuiWindow {
void DrawRemoveGyroMappingButton(uint8_t port, std::string id);
void DrawAddGyroMappingButton(uint8_t port);
// Used together for an incomplete linked hash map implementation in order to
// map button masks to their names and original mapping on N64
std::list<std::pair<N64ButtonMask, const char*>> buttons;
std::unordered_map<N64ButtonMask, decltype(buttons)::iterator> buttonNames;
void addButtonName(N64ButtonMask mask, const char* name);
void DrawMapping(CustomButtonMap& mapping, float labelWidth, N64ButtonMask excludedButtons);
void DrawOcarinaControlPanel();
void DrawCameraControlPanel();
void DrawDpadControlPanel();
void DrawMiscControlPanel();
int32_t mGameInputBlockTimer;
int32_t mMappingInputBlockTimer;
int32_t mRumbleTimer;
@ -84,6 +104,4 @@ class SohInputEditorWindow : public LUS::GuiWindow {
bool mInputEditorPopupOpen;
void DrawSetDefaultsButton(uint8_t portIndex);
void DrawClearAllButton(uint8_t portIndex);
void DrawHelpIcon(const std::string& helptext);
};

View File

@ -269,35 +269,35 @@ static std::map<std::string, CosmeticOption> cosmeticOptions = {
COSMETIC_OPTION("Hud_NameTagActorText", "Nametag Text", GROUP_HUD, ImVec4(255, 255, 255, 255), true, true, false),
COSMETIC_OPTION("Hud_NameTagActorBackground", "Nametag Background", GROUP_HUD, ImVec4(0, 0, 0, 80), true, false, true),
COSMETIC_OPTION("Kal_ItemSelA", "Item Select Color A", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, false),
COSMETIC_OPTION("Kal_ItemSelB", "Item Select Color B", GROUP_KALEIDO, ImVec4(70, 100, 130, 255), false, true, false),
COSMETIC_OPTION("Kal_ItemSelC", "Item Select Color C", GROUP_KALEIDO, ImVec4(70, 100, 130, 255), false, true, false),
COSMETIC_OPTION("Kal_ItemSelD", "Item Select Color D", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, false),
COSMETIC_OPTION("Kal_ItemSelA", "Item Select Color", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, false),
COSMETIC_OPTION("Kal_ItemSelB", "Item Select Color B", GROUP_KALEIDO, ImVec4(70, 100, 130, 255), false, true, true),
COSMETIC_OPTION("Kal_ItemSelC", "Item Select Color C", GROUP_KALEIDO, ImVec4(70, 100, 130, 255), false, true, true),
COSMETIC_OPTION("Kal_ItemSelD", "Item Select Color D", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, true),
COSMETIC_OPTION("Kal_EquipSelA", "Equip Select Color A", GROUP_KALEIDO, ImVec4(10, 50, 40, 255), false, true, false),
COSMETIC_OPTION("Kal_EquipSelB", "Equip Select Color B", GROUP_KALEIDO, ImVec4(90, 100, 60, 255), false, true, false),
COSMETIC_OPTION("Kal_EquipSelC", "Equip Select Color C", GROUP_KALEIDO, ImVec4(90, 100, 60, 255), false, true, false),
COSMETIC_OPTION("Kal_EquipSelD", "Equip Select Color D", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, false),
COSMETIC_OPTION("Kal_EquipSelA", "Equip Select Color", GROUP_KALEIDO, ImVec4(10, 50, 40, 255), false, true, false),
COSMETIC_OPTION("Kal_EquipSelB", "Equip Select Color B", GROUP_KALEIDO, ImVec4(90, 100, 60, 255), false, true, true),
COSMETIC_OPTION("Kal_EquipSelC", "Equip Select Color C", GROUP_KALEIDO, ImVec4(90, 100, 60, 255), false, true, true),
COSMETIC_OPTION("Kal_EquipSelD", "Equip Select Color D", GROUP_KALEIDO, ImVec4(10, 50, 80, 255), false, true, true),
COSMETIC_OPTION("Kal_MapSelDunA", "Map Dungeon Color A", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelDunB", "Map Dungeon Color B", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelDunC", "Map Dungeon Color C", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelDunD", "Map Dungeon Color D", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelDunA", "Map Dungeon Color", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, true),
COSMETIC_OPTION("Kal_MapSelDunB", "Map Dungeon Color B", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, true),
COSMETIC_OPTION("Kal_MapSelDunC", "Map Dungeon Color C", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, true),
COSMETIC_OPTION("Kal_MapSelDunD", "Map Dungeon Color D", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, true),
COSMETIC_OPTION("Kal_QuestStatusA", "Quest StatusColor A", GROUP_KALEIDO, ImVec4(80, 80, 50, 255), false, true, false),
COSMETIC_OPTION("Kal_QuestStatusB", "Quest StatusColor B", GROUP_KALEIDO, ImVec4(120, 120, 70, 255), false, true, false),
COSMETIC_OPTION("Kal_QuestStatusC", "Quest StatusColor C", GROUP_KALEIDO, ImVec4(120, 120, 70, 255), false, true, false),
COSMETIC_OPTION("Kal_QuestStatusD", "Quest StatusColor D", GROUP_KALEIDO, ImVec4(80, 80, 50, 255), false, true, false),
COSMETIC_OPTION("Kal_QuestStatusA", "Quest Status Color", GROUP_KALEIDO, ImVec4(80, 80, 50, 255), false, true, false),
COSMETIC_OPTION("Kal_QuestStatusB", "Quest Status Color B", GROUP_KALEIDO, ImVec4(120, 120, 70, 255), false, true, true),
COSMETIC_OPTION("Kal_QuestStatusC", "Quest Status Color C", GROUP_KALEIDO, ImVec4(120, 120, 70, 255), false, true, true),
COSMETIC_OPTION("Kal_QuestStatusD", "Quest Status Color D", GROUP_KALEIDO, ImVec4(80, 80, 50, 255), false, true, true),
COSMETIC_OPTION("Kal_MapSelectA", "Map Color A", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelectB", "Map Color B", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelectC", "Map Color C", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelectD", "Map Color D", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelectA", "Map Color", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, false),
COSMETIC_OPTION("Kal_MapSelectB", "Map Color B", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, true),
COSMETIC_OPTION("Kal_MapSelectC", "Map Color C", GROUP_KALEIDO, ImVec4(140, 60, 60, 255), false, true, true),
COSMETIC_OPTION("Kal_MapSelectD", "Map Color D", GROUP_KALEIDO, ImVec4(80, 40, 30, 255), false, true, true),
COSMETIC_OPTION("Kal_SaveA", "Save A", GROUP_KALEIDO, ImVec4(50, 50, 50, 255), false, true, false),
COSMETIC_OPTION("Kal_SaveB", "Save B", GROUP_KALEIDO, ImVec4(110, 110, 110, 255), false, true, false),
COSMETIC_OPTION("Kal_SaveC", "Save C", GROUP_KALEIDO, ImVec4(110, 110, 110, 255), false, true, false),
COSMETIC_OPTION("Kal_SaveD", "Save D", GROUP_KALEIDO, ImVec4(50, 50, 50, 255), false, true, false),
COSMETIC_OPTION("Kal_SaveA", "Save Color", GROUP_KALEIDO, ImVec4(50, 50, 50, 255), false, true, false),
COSMETIC_OPTION("Kal_SaveB", "Save Color B", GROUP_KALEIDO, ImVec4(110, 110, 110, 255), false, true, true),
COSMETIC_OPTION("Kal_SaveC", "Save Color C", GROUP_KALEIDO, ImVec4(110, 110, 110, 255), false, true, true),
COSMETIC_OPTION("Kal_SaveD", "Save Color D", GROUP_KALEIDO, ImVec4(50, 50, 50, 255), false, true, true),
COSMETIC_OPTION("Kal_NamePanel", "Name Panel", GROUP_KALEIDO, ImVec4(90,100,130,255), true, true, false),
@ -1065,12 +1065,16 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) {
if (manualChange || CVarGetInteger(npcGoldenSkulltula.rainbowCvar, 0)) {
static Color_RGBA8 defaultColor = {npcGoldenSkulltula.defaultColor.x, npcGoldenSkulltula.defaultColor.y, npcGoldenSkulltula.defaultColor.z, npcGoldenSkulltula.defaultColor.w};
Color_RGBA8 color = CVarGetColor(npcGoldenSkulltula.cvar, defaultColor);
PATCH_GFX(gGiSkulltulaTokenDL, "NPC_GoldenSkulltula1", npcGoldenSkulltula.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
PATCH_GFX(gGiSkulltulaTokenDL, "NPC_GoldenSkulltula2", npcGoldenSkulltula.changedCvar, 6, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255));
PATCH_GFX(gGiSkulltulaTokenFlameDL, "NPC_GoldenSkulltula3", npcGoldenSkulltula.changedCvar, 32, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
PATCH_GFX(gGiSkulltulaTokenFlameDL, "NPC_GoldenSkulltula4", npcGoldenSkulltula.changedCvar, 33, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255));
PATCH_GFX(object_st_DL_003FB0, "NPC_GoldenSkulltula5", npcGoldenSkulltula.changedCvar, 118, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
PATCH_GFX(object_st_DL_003FB0, "NPC_GoldenSkulltula6", npcGoldenSkulltula.changedCvar, 119, gsDPSetEnvColor(color.r / 4, color.g / 4, color.b / 4, 255));
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));
PATCH_GFX(gSkulltulaTokenFlameDL, "NPC_GoldenSkulltula4", npcGoldenSkulltula.changedCvar, 33, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255));
PATCH_GFX(gGiSkulltulaTokenDL, "NPC_GoldenSkulltula5", npcGoldenSkulltula.changedCvar, 5, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
PATCH_GFX(gGiSkulltulaTokenDL, "NPC_GoldenSkulltula6", npcGoldenSkulltula.changedCvar, 6, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255));
PATCH_GFX(gGiSkulltulaTokenFlameDL, "NPC_GoldenSkulltula7", npcGoldenSkulltula.changedCvar, 32, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
PATCH_GFX(gGiSkulltulaTokenFlameDL, "NPC_GoldenSkulltula8", npcGoldenSkulltula.changedCvar, 33, gsDPSetEnvColor(color.r / 2, color.g / 2, color.b / 2, 255));
PATCH_GFX(object_st_DL_003FB0, "NPC_GoldenSkulltula9", npcGoldenSkulltula.changedCvar, 118, gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
PATCH_GFX(object_st_DL_003FB0, "NPC_GoldenSkulltula10", npcGoldenSkulltula.changedCvar, 119, gsDPSetEnvColor(color.r / 4, color.g / 4, color.b / 4, 255));
}
static CosmeticOption& npcGerudo = cosmeticOptions.at("NPC_Gerudo");
@ -1181,6 +1185,43 @@ void DrawScaleSlider(const std::string CvarName,float DefaultValue){
//Disabled for now. feature not done and several fixes needed to be merged.
//UIWidgets::EnhancementSliderFloat("Scale : %dx", InvisibleLabel.c_str(), CvarLabel.c_str(), 0.1f, 3.0f,"",DefaultValue,true);
}
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) {
if (ImGui::CollapsingHeader(Header_Title)) {
if (ImGui::BeginTable(Table_ID, 1, FlagsTable)) {
ImGui::TableSetupColumn(Column_Title, FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider(Slider_Title, Slider_ID);
DrawPositionsRadioBoxes(Slider_ID);
DrawPositionSlider(Slider_ID, MinY, MaxY, MinX, MaxX);
DrawScaleSlider(Slider_ID, Default_Value);
ImGui::NewLine();
ImGui::EndTable();
}
}
}
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) {
if (ImGui::CollapsingHeader(Header_Title)) {
if (ImGui::BeginTable(Table_ID, 1, FlagsTable)) {
ImGui::TableSetupColumn(Column_Title, FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider(Slider_Title, Slider_ID);
DrawPositionsRadioBoxes(Slider_ID);
s16 Min_X_CU = 0;
s16 Max_X_CU = ImGui::GetWindowViewport()->Size.x/2;
if(CVarGetInteger(Int_Type,0) == 2){
Max_X_CU = 294;
} else if(CVarGetInteger(Int_Type,0) == 3){
Max_X_CU = ImGui::GetWindowViewport()->Size.x/2;
} else if(CVarGetInteger(Int_Type,0) == 4){
Min_X_CU = (ImGui::GetWindowViewport()->Size.x/2)*-1;
}
DrawPositionSlider(Slider_ID, 0, ImGui::GetWindowViewport()->Size.y/2, Min_X_CU, Max_X_CU);
DrawScaleSlider(Slider_ID, Slider_Scale_Value);
ImGui::NewLine();
ImGui::EndTable();
}
}
}
void Draw_Placements(){
if (ImGui::BeginTable("tableMargins", 1, FlagsTable)) {
ImGui::TableSetupColumn("General margins settings", FlagsCell, TablesCellsWidth);
@ -1247,126 +1288,13 @@ void Draw_Placements(){
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("B Button position")) {
if (ImGui::BeginTable("tablebbtn", 1, FlagsTable)) {
ImGui::TableSetupColumn("B Button settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("B Button", "gBBtn");
DrawPositionsRadioBoxes("gBBtn");
DrawPositionSlider("gBBtn", 0, ImGui::GetWindowViewport()->Size.y/4+50, -1, ImGui::GetWindowViewport()->Size.x-50);
DrawScaleSlider("gBBtn",0.95f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("A Button position")) {
if (ImGui::BeginTable("tableabtn", 1, FlagsTable)) {
ImGui::TableSetupColumn("A Button settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("A Button", "gABtn");
DrawPositionsRadioBoxes("gABtn");
DrawPositionSlider("gABtn", -10, ImGui::GetWindowViewport()->Size.y/4+50, -20, ImGui::GetWindowViewport()->Size.x-50);
DrawScaleSlider("gABtn",0.95f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Start Button position")) {
if (ImGui::BeginTable("tablestartbtn", 1, FlagsTable)) {
ImGui::TableSetupColumn("Start Button settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Start Button", "gStartBtn");
DrawPositionsRadioBoxes("gStartBtn");
DrawPositionSlider("gStartBtn", 0, ImGui::GetWindowViewport()->Size.y/2, 0, ImGui::GetWindowViewport()->Size.x/2+70);
DrawScaleSlider("gStartBtn",0.75f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("C Button Up position")) {
if (ImGui::BeginTable("tablecubtn", 1, FlagsTable)) {
ImGui::TableSetupColumn("C Button Up settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("C Button Up", "gCBtnU");
DrawPositionsRadioBoxes("gCBtnU");
s16 Min_X_CU = 0;
s16 Max_X_CU = ImGui::GetWindowViewport()->Size.x/2;
if(CVarGetInteger("gCBtnUPosType",0) == 2){
Max_X_CU = 294;
} else if(CVarGetInteger("gCBtnUPosType",0) == 3){
Max_X_CU = ImGui::GetWindowViewport()->Size.x/2;
} else if(CVarGetInteger("gCBtnUPosType",0) == 4){
Min_X_CU = (ImGui::GetWindowViewport()->Size.x/2)*-1;
}
DrawPositionSlider("gCBtnU", 0, ImGui::GetWindowViewport()->Size.y/2, Min_X_CU, Max_X_CU);
DrawScaleSlider("gCBtnU",0.5f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("C Button Down position")) {
if (ImGui::BeginTable("tablecdbtn", 1, FlagsTable)) {
ImGui::TableSetupColumn("C Button Down settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("C Button Down", "gCBtnD");
DrawPositionsRadioBoxes("gCBtnD");
s16 Min_X_CD = 0;
s16 Max_X_CD = ImGui::GetWindowViewport()->Size.x/2;
if(CVarGetInteger("gCBtnDPosType",0) == 2){
Max_X_CD = 294;
} else if(CVarGetInteger("gCBtnDPosType",0) == 3){
Max_X_CD = ImGui::GetWindowViewport()->Size.x/2;
} else if(CVarGetInteger("gCBtnDPosType",0) == 4){
Min_X_CD = (ImGui::GetWindowViewport()->Size.x/2)*-1;
}
DrawPositionSlider("gCBtnD", 0, ImGui::GetWindowViewport()->Size.y/2, Min_X_CD, Max_X_CD);
DrawScaleSlider("gCBtnD",0.87f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("C Button Left position")) {
if (ImGui::BeginTable("tableclbtn", 1, FlagsTable)) {
ImGui::TableSetupColumn("C Button Left settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("C Button Left", "gCBtnL");
DrawPositionsRadioBoxes("gCBtnL");
s16 Min_X_CL = 0;
s16 Max_X_CL = ImGui::GetWindowViewport()->Size.x/2;
if(CVarGetInteger("gCBtnLPosType",0) == 2){
Max_X_CL = 294;
} else if(CVarGetInteger("gCBtnLPosType",0) == 3){
Max_X_CL = ImGui::GetWindowViewport()->Size.x/2;
} else if(CVarGetInteger("gCBtnLPosType",0) == 4){
Min_X_CL = (ImGui::GetWindowViewport()->Size.x/2)*-1;
}
DrawPositionSlider("gCBtnL", 0, ImGui::GetWindowViewport()->Size.y/2, Min_X_CL, Max_X_CL);
DrawScaleSlider("gCBtnL",0.87f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("C Button Right position")) {
if (ImGui::BeginTable("tablecrnbtn", 1, FlagsTable)) {
ImGui::TableSetupColumn("C Button Right settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("C Button Right", "gCBtnR");
DrawPositionsRadioBoxes("gCBtnR");
s16 Min_X_CR = 0;
s16 Max_X_CR = ImGui::GetWindowViewport()->Size.x/2;
if(CVarGetInteger("gCBtnRPosType",0) == 2){
Max_X_CR = 294;
} else if(CVarGetInteger("gCBtnRPosType",0) == 3){
Max_X_CR = ImGui::GetWindowViewport()->Size.x/2;
} else if(CVarGetInteger("gCBtnRPosType",0) == 4){
Min_X_CR = (ImGui::GetWindowViewport()->Size.x/2)*-1;
}
DrawPositionSlider("gCBtnR", 0, ImGui::GetWindowViewport()->Size.y/2, Min_X_CR, Max_X_CR);
DrawScaleSlider("gCBtnR",0.87f);
ImGui::NewLine();
ImGui::EndTable();
}
}
Draw_Table_Dropdown("B Button position", "tablebbtn", "B Button settings", "B Button", "gBBtn", 0, ImGui::GetWindowViewport()->Size.y/4+50, -1, ImGui::GetWindowViewport()->Size.x-50, 0.95f);
Draw_Table_Dropdown("A Button position", "tableabtn", "A Button settings", "A Button", "gABtn", -10, ImGui::GetWindowViewport()->Size.y/4+50, -20, ImGui::GetWindowViewport()->Size.x-50, 0.95f);
Draw_Table_Dropdown("Start Button position", "tablestartbtn", "Start Button settings", "Start Button", "gStartBtn", 0, ImGui::GetWindowViewport()->Size.y/2, 0, ImGui::GetWindowViewport()->Size.x/2+70, 0.75f);
C_Button_Dropdown("C Button Up position", "tablecubtn", "C Button Up settings", "C Button Up", "gCBtnU", "gCBtnUPosType", 0.5f);
C_Button_Dropdown("C Button Down position", "tablecdbtn", "C Button Down settings", "C Button Down", "gCBtnD", "gCBtnDPosType", 0.87f);
C_Button_Dropdown("C Button Left position", "tableclbtn", "C Button Left settings", "C Button Left", "gCBtnL", "gCBtnLPosType", 0.87f);
C_Button_Dropdown("C Button Right position", "tablecrbtn", "C Button Right settings", "C Button Right", "gCBtnR", "gCBtnRPosType", 0.87f);
if (CVarGetInteger("gDpadEquips",0) && ImGui::CollapsingHeader("DPad items position")) {
if (ImGui::BeginTable("tabledpaditems", 1, FlagsTable)) {
ImGui::TableSetupColumn("DPad items settings", FlagsCell, TablesCellsWidth);
@ -1386,115 +1314,15 @@ void Draw_Placements(){
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Minimaps position")) {
if (ImGui::BeginTable("tableminimapspos", 1, FlagsTable)) {
ImGui::TableSetupColumn("minimaps settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Minimap", "gMinimap");
DrawPositionsRadioBoxes("gMinimap", false);
DrawPositionSlider("gMinimap", (ImGui::GetWindowViewport()->Size.y/3)*-1, ImGui::GetWindowViewport()->Size.y/3, ImGui::GetWindowViewport()->Size.x*-1, ImGui::GetWindowViewport()->Size.x/2);
DrawScaleSlider("gMinimap",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Small Keys counter position")) {
if (ImGui::BeginTable("tablesmolekeys", 1, FlagsTable)) {
ImGui::TableSetupColumn("Small Keys counter settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Small Keys counter", "gSKC");
DrawPositionsRadioBoxes("gSKC");
DrawPositionSlider("gSKC", 0, ImGui::GetWindowViewport()->Size.y/3, -1, ImGui::GetWindowViewport()->Size.x/2);
DrawScaleSlider("gSKC",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Rupee counter position")) {
if (ImGui::BeginTable("tablerupeecount", 1, FlagsTable)) {
ImGui::TableSetupColumn("Rupee counter settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Rupee counter", "gRC");
DrawPositionsRadioBoxes("gRC");
DrawPositionSlider("gRC", -2, ImGui::GetWindowViewport()->Size.y/3, -3, ImGui::GetWindowViewport()->Size.x/2);
DrawScaleSlider("gRC",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Carrots position")) {
if (ImGui::BeginTable("tableCarrots", 1, FlagsTable)) {
ImGui::TableSetupColumn("Carrots settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Carrots", "gCarrots");
DrawPositionsRadioBoxes("gCarrots");
DrawPositionSlider("gCarrots", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2+25);
DrawScaleSlider("gCarrots",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Timers position")) {
if (ImGui::BeginTable("tabletimers", 1, FlagsTable)) {
ImGui::TableSetupColumn("Timers settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Timers", "gTimers");
DrawPositionsRadioBoxes("gTimers");
DrawPositionSlider("gTimers", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2-50);
DrawScaleSlider("gTimers",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Archery Scores position")) {
if (ImGui::BeginTable("tablearchery", 1, FlagsTable)) {
ImGui::TableSetupColumn("Archery Scores settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Archery scores", "gAS");
DrawPositionsRadioBoxes("gAS", false);
DrawPositionSlider("gAS", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2-50);
DrawScaleSlider("gAS",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Title cards (Maps) position")) {
if (ImGui::BeginTable("tabletcmaps", 1, FlagsTable)) {
ImGui::TableSetupColumn("Titlecard maps settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Title cards (overworld)", "gTCM");
DrawPositionsRadioBoxes("gTCM");
DrawPositionSlider("gTCM", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2+10);
DrawScaleSlider("gTCM",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Title cards (Bosses) position")) {
if (ImGui::BeginTable("tabletcbosses", 1, FlagsTable)) {
ImGui::TableSetupColumn("Title cards (Bosses) settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("Title cards (Bosses)", "gTCB");
DrawPositionsRadioBoxes("gTCB");
DrawPositionSlider("gTCB", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2+10);
DrawScaleSlider("gTCB",1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("In-game Gameplay Timer position")) {
if (ImGui::BeginTable("tablegameplaytimer", 1, FlagsTable)) {
ImGui::TableSetupColumn("In-game Gameplay Timer settings", FlagsCell, TablesCellsWidth);
Table_InitHeader(false);
DrawUseMarginsSlider("In-game Gameplay Timer", "gIGT");
DrawPositionsRadioBoxes("gIGT");
DrawPositionSlider("gIGT", 0, ImGui::GetWindowViewport()->Size.y / 2, -50,
ImGui::GetWindowViewport()->Size.x / 2 + 10);
DrawScaleSlider("gIGT", 1.0f);
ImGui::NewLine();
ImGui::EndTable();
}
}
Draw_Table_Dropdown("Minimaps position", "tableminimapspos", "minimaps settings", "Minimap", "gMinimap", (ImGui::GetWindowViewport()->Size.y/3)*-1, ImGui::GetWindowViewport()->Size.y/3, ImGui::GetWindowViewport()->Size.x*-1, ImGui::GetWindowViewport()->Size.x/2, 1.0f);
Draw_Table_Dropdown("Small Keys counter position", "tablesmolekeys", "Small Keys counter settings", "Small Keys counter", "gSKC", 0, ImGui::GetWindowViewport()->Size.y/3, -1, ImGui::GetWindowViewport()->Size.x/2, 1.0f);
Draw_Table_Dropdown("Rupee counter position", "tablerupeecount", "Rupee counter settings", "Rupee counter", "gRC", -2, ImGui::GetWindowViewport()->Size.y/3, -3, ImGui::GetWindowViewport()->Size.x/2, 1.0f);
Draw_Table_Dropdown("Carrots position", "tableCarrots", "Carrots settings", "Carrots", "gCarrots", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2+25, 1.0f);
Draw_Table_Dropdown("Timers position", "tabletimers", "Timers settings", "Timers", "gTimers", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2-50, 1.0f);
Draw_Table_Dropdown("Archery Scores position", "tablearchery", "Archery Scores settings", "Archery scores", "gAS", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2-50, 1.0f);
Draw_Table_Dropdown("Title cards (Maps) position", "tabletcmaps", "Titlecard maps settings", "Title cards (overworld)", "gTCM", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2+10, 1.0f);
Draw_Table_Dropdown("Title cards (Bosses) position", "tabletcbosses", "Title cards (Bosses) settings", "Title cards (Bosses)", "gTCB", 0, ImGui::GetWindowViewport()->Size.y/2, -50, ImGui::GetWindowViewport()->Size.x/2+10, 1.0f);
Draw_Table_Dropdown("In-game Gameplay Timer position", "tablegameplaytimer", "In-game Gameplay Timer settings", "In-game Gameplay Timer", "gIGT", 0, ImGui::GetWindowViewport()->Size.y / 2, -50, ImGui::GetWindowViewport()->Size.x / 2 + 10, 1.0f);
if (ImGui::CollapsingHeader("Enemy Health Bar position")) {
if (ImGui::BeginTable("enemyhealthbar", 1, FlagsTable)) {
ImGui::TableSetupColumn("Enemy Health Bar settings", FlagsCell, TablesCellsWidth);
@ -1522,7 +1350,21 @@ void Draw_Placements(){
}
}
}
void Reset_Option_Single(const char* Button_Title, const char* name) {
ImGui::SameLine();
if (ImGui::Button(Button_Title)) {
CVarClear(name);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
}
void Reset_Option_Double(const char* Button_Title, const char* name) {
ImGui::SameLine();
if (ImGui::Button(Button_Title)) {
CVarClear((std::string(name) + ".Value").c_str());
CVarClear((std::string(name) + ".Changed").c_str());
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
}
void DrawSillyTab() {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (CVarGetInteger("gLetItSnow", 0)) {
@ -1548,67 +1390,28 @@ void DrawSillyTab() {
if (UIWidgets::EnhancementSliderFloat("Link Head Scale: %.2fx", "##Link_HeadScale", "gCosmetics.Link_HeadScale.Value", 0.4f, 4.0f, "", 1.0f, false)) {
CVarSetInteger("gCosmetics.Link_HeadScale.Changed", 1);
}
ImGui::SameLine();
if (ImGui::Button("Reset##Link_HeadScale")) {
CVarClear("gCosmetics.Link_HeadScale.Value");
CVarClear("gCosmetics.Link_HeadScale.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
if (UIWidgets::EnhancementSliderFloat("Link Sword Scale: %.3fx", "##Link_SwordScale", "gCosmetics.Link_SwordScale.Value", 1.0f, 2.5f, "", 1.0f, false)) {
Reset_Option_Double("Reset##Link_HeadScale", "gCosmetics.Link_HeadScale");
if (UIWidgets::EnhancementSliderFloat("Link Sword Scale: %f", "##Link_SwordScale", "gCosmetics.Link_SwordScale.Value", 1.0f, 2.5f, "", 1.0f, false)) {
CVarSetInteger("gCosmetics.Link_SwordScale.Changed", 1);
}
ImGui::SameLine();
if (ImGui::Button("Reset##Link_SwordScale")) {
CVarClear("gCosmetics.Link_SwordScale.Value");
CVarClear("gCosmetics.Link_SwordScale.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("Bunny Hood Length: %.0f", "##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength", -300.0f, 1000.0f, "", 0.0f, false);
ImGui::SameLine();
if (ImGui::Button("Reset##BunnyHood_EarLength")) {
CVarClear("gCosmetics.BunnyHood_EarLength");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("Bunny Hood Spread: %.0f", "##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread", -300.0f, 500.0f, "", 0.0f, false);
ImGui::SameLine();
if (ImGui::Button("Reset##BunnyHood_EarSpread")) {
CVarClear("gCosmetics.BunnyHood_EarSpread");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("Goron Neck Length: %.0f", "##Goron_NeckLength", "gCosmetics.Goron_NeckLength", 0.0f, 5000.0f, "", 0.0f, false);
ImGui::SameLine();
if (ImGui::Button("Reset##Goron_NeckLength")) {
CVarClear("gCosmetics.Goron_NeckLength");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
Reset_Option_Double("Reset##Link_SwordScale", "gCosmetics.Link_SwordScale");
UIWidgets::EnhancementSliderFloat("Bunny Hood Length: %f", "##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength", -300.0f, 1000.0f, "", 0.0f, false);
Reset_Option_Single("Reset##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength");
UIWidgets::EnhancementSliderFloat("Bunny Hood Spread: %f", "##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread", -300.0f, 500.0f, "", 0.0f, false);
Reset_Option_Single("Reset##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread");
UIWidgets::EnhancementSliderFloat("Goron Neck Length: %f", "##Goron_NeckLength", "gCosmetics.Goron_NeckLength", 0.0f, 1000.0f, "", 0.0f, false);
Reset_Option_Single("Reset##Goron_NeckLength", "gCosmetics.Goron_NeckLength");
UIWidgets::EnhancementCheckbox("Unfix Goron Spin", "gUnfixGoronSpin");
UIWidgets::EnhancementSliderFloat("Fairies Size: %.2fx", "##Fairies_Size", "gCosmetics.Fairies_Size", 0.25f, 5.0f, "", 1.0f, false);
ImGui::SameLine();
if (ImGui::Button("Reset##Fairies_Size")) {
CVarClear("gCosmetics.Fairies_Size");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("N64 Logo Spin Speed: %.2fx", "##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed", 0.25f, 5.0f, "", 1.0f, false);
ImGui::SameLine();
if (ImGui::Button("Reset##N64Logo_SpinSpeed")) {
CVarClear("gCosmetics.N64Logo_SpinSpeed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("Moon Size: %.1f %%", "##Moon_Size", "gCosmetics.Moon_Size", 0.5f, 2.0f, "", 1.0f, true);
ImGui::SameLine();
if (ImGui::Button("Reset##Moon_Size")) {
CVarClear("gCosmetics.Moon_Size");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
if (UIWidgets::EnhancementSliderFloat("Kak Windmill Speed: %.0f", "##Kak_Windmill_Speed", "gCosmetics.Kak_Windmill_Speed.Value", 100.0f, 6000.0f, "", 100.0f, false)) {
UIWidgets::EnhancementSliderFloat("Fairies Size: %f", "##Fairies_Size", "gCosmetics.Fairies_Size", 0.25f, 5.0f, "", 1.0f, false);
Reset_Option_Single("Reset##Fairies_Size", "gCosmetics.Fairies_Size");
UIWidgets::EnhancementSliderFloat("N64 Logo Spin Speed: %f", "##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed", 0.25f, 5.0f, "", 1.0f, false);
Reset_Option_Single("Reset##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed");
UIWidgets::EnhancementSliderFloat("Moon Size: %f", "##Moon_Size", "gCosmetics.Moon_Size", 0.5f, 2.0f, "", 1.0f, false);
Reset_Option_Single("Reset##Moon_Size", "gCosmetics.Moon_Size");
if (UIWidgets::EnhancementSliderFloat("Kak Windmill Speed: %f", "##Kak_Windmill_Speed", "gCosmetics.Kak_Windmill_Speed.Value", 100.0f, 6000.0f, "", 100.0f, false)) {
CVarSetInteger("gCosmetics.Kak_Windmill_Speed.Changed", 1);
}
ImGui::SameLine();
if (ImGui::Button("Reset##Kak_Windmill_Speed")) {
CVarClear("gCosmetics.Kak_Windmill_Speed.Value");
CVarClear("gCosmetics.Kak_Windmill_Speed.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
Reset_Option_Double("Reset##Kak_Windmill_Speed", "gCosmetics.Kak_Windmill_Speed");
ImGui::EndDisabled();
}
@ -1631,25 +1434,20 @@ void CopyMultipliedColor(CosmeticOption& cosmeticOptionSrc, CosmeticOption& cosm
CVarSetInteger((cosmeticOptionTarget.changedCvar), 1);
}
void RandomizeColor(CosmeticOption& cosmeticOption) {
Color_RGBA8 newColor;
newColor.r = Random(0, 255);
newColor.g = Random(0, 255);
newColor.b = Random(0, 255);
newColor.a = 255;
// For alpha supported options, retain the last set alpha instead of overwriting
if (cosmeticOption.supportsAlpha) {
newColor.a = cosmeticOption.currentColor.w * 255;
void ToggleRainbow(CosmeticOption& cosmeticOption, bool state) {
if (state) {
CVarSetInteger(cosmeticOption.rainbowCvar, 1);
CVarSetInteger(cosmeticOption.changedCvar, 1);
} else {
CVarClear(cosmeticOption.rainbowCvar);
CVarClear(cosmeticOption.changedCvar);
}
}
cosmeticOption.currentColor.x = newColor.r / 255.0;
cosmeticOption.currentColor.y = newColor.g / 255.0;
cosmeticOption.currentColor.z = newColor.b / 255.0;
cosmeticOption.currentColor.w = newColor.a / 255.0;
CVarSetColor(cosmeticOption.cvar, newColor);
CVarSetInteger((cosmeticOption.rainbowCvar), 0);
CVarSetInteger((cosmeticOption.changedCvar), 1);
void ApplySideEffects(CosmeticOption& cosmeticOption) {
if (CVarGetInteger("gCosmetics.AdvancedMode", 0)) {
return;
}
// This bit is kind of experimental, not sure how I feel about it yet, but it allows for
// advanced cosmetic options to be changed based on a non-advanced option.
@ -1671,9 +1469,91 @@ void RandomizeColor(CosmeticOption& cosmeticOption) {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("SpinAttack_Level1Primary"), 2.0f);
} else if (cosmeticOption.label == "Level 2 Secondary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("SpinAttack_Level2Primary"), 2.0f);
} else if (cosmeticOption.label == "Item Select Color") {
if (CVarGetInteger(cosmeticOption.rainbowCvar, 0)) {
ToggleRainbow(cosmeticOptions.at("Kal_ItemSelB"), true);
ToggleRainbow(cosmeticOptions.at("Kal_ItemSelC"), true);
ToggleRainbow(cosmeticOptions.at("Kal_ItemSelD"), true);
} else {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_ItemSelB"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_ItemSelC"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_ItemSelD"), 1.0f);
}
} else if (cosmeticOption.label == "Equip Select Color") {
if (CVarGetInteger(cosmeticOption.rainbowCvar, 0)) {
ToggleRainbow(cosmeticOptions.at("Kal_EquipSelB"), true);
ToggleRainbow(cosmeticOptions.at("Kal_EquipSelC"), true);
ToggleRainbow(cosmeticOptions.at("Kal_EquipSelD"), true);
} else {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_EquipSelB"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_EquipSelC"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_EquipSelD"), 1.0f);
}
} else if (cosmeticOption.label == "Map Dungeon Color") {
if (CVarGetInteger(cosmeticOption.rainbowCvar, 0)) {
ToggleRainbow(cosmeticOptions.at("Kal_MapSelDunB"), true);
ToggleRainbow(cosmeticOptions.at("Kal_MapSelDunC"), true);
ToggleRainbow(cosmeticOptions.at("Kal_MapSelDunD"), true);
} else {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_MapSelDunB"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_MapSelDunC"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_MapSelDunD"), 1.0f);
}
} else if (cosmeticOption.label == "Quest Status Color") {
if (CVarGetInteger(cosmeticOption.rainbowCvar, 0)) {
ToggleRainbow(cosmeticOptions.at("Kal_QuestStatusB"), true);
ToggleRainbow(cosmeticOptions.at("Kal_QuestStatusC"), true);
ToggleRainbow(cosmeticOptions.at("Kal_QuestStatusD"), true);
} else {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_QuestStatusB"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_QuestStatusC"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_QuestStatusD"), 1.0f);
}
} else if (cosmeticOption.label == "Map Color") {
if (CVarGetInteger(cosmeticOption.rainbowCvar, 0)) {
ToggleRainbow(cosmeticOptions.at("Kal_MapSelectB"), true);
ToggleRainbow(cosmeticOptions.at("Kal_MapSelectC"), true);
ToggleRainbow(cosmeticOptions.at("Kal_MapSelectD"), true);
} else {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_MapSelectB"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_MapSelectC"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_MapSelectD"), 1.0f);
}
} else if (cosmeticOption.label == "Save Color") {
if (CVarGetInteger(cosmeticOption.rainbowCvar, 0)) {
ToggleRainbow(cosmeticOptions.at("Kal_SaveB"), true);
ToggleRainbow(cosmeticOptions.at("Kal_SaveC"), true);
ToggleRainbow(cosmeticOptions.at("Kal_SaveD"), true);
} else {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_SaveB"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_SaveC"), 2.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Kal_SaveD"), 1.0f);
}
}
}
void RandomizeColor(CosmeticOption& cosmeticOption) {
Color_RGBA8 newColor;
newColor.r = Random(0, 255);
newColor.g = Random(0, 255);
newColor.b = Random(0, 255);
newColor.a = 255;
// For alpha supported options, retain the last set alpha instead of overwriting
if (cosmeticOption.supportsAlpha) {
newColor.a = cosmeticOption.currentColor.w * 255;
}
cosmeticOption.currentColor.x = newColor.r / 255.0;
cosmeticOption.currentColor.y = newColor.g / 255.0;
cosmeticOption.currentColor.z = newColor.b / 255.0;
cosmeticOption.currentColor.w = newColor.a / 255.0;
CVarSetColor(cosmeticOption.cvar, newColor);
CVarSetInteger((cosmeticOption.rainbowCvar), 0);
CVarSetInteger((cosmeticOption.changedCvar), 1);
ApplySideEffects(cosmeticOption);
}
void ResetColor(CosmeticOption& cosmeticOption) {
Color_RGBA8 defaultColor = {cosmeticOption.defaultColor.x, cosmeticOption.defaultColor.y, cosmeticOption.defaultColor.z, cosmeticOption.defaultColor.w};
cosmeticOption.currentColor.x = defaultColor.r / 255.0;
@ -1691,7 +1571,7 @@ void ResetColor(CosmeticOption& cosmeticOption) {
CVarClear((std::string(cosmeticOption.cvar) + ".A").c_str());
CVarClear((std::string(cosmeticOption.cvar) + ".Type").c_str());
// This portion should match 1:1 the multiplied colors in `RandomizeColor()`
// This portion should match 1:1 the multiplied colors in `ApplySideEffect()`
if (cosmeticOption.label == "Bow Body") {
ResetColor(cosmeticOptions.at("Equipment_BowTips"));
ResetColor(cosmeticOptions.at("Equipment_BowHandle"));
@ -1707,6 +1587,30 @@ void ResetColor(CosmeticOption& cosmeticOption) {
ResetColor(cosmeticOptions.at("SpinAttack_Level1Primary"));
} else if (cosmeticOption.label == "Level 2 Secondary") {
ResetColor(cosmeticOptions.at("SpinAttack_Level2Primary"));
} else if (cosmeticOption.label == "Item Select Color") {
ResetColor(cosmeticOptions.at("Kal_ItemSelB"));
ResetColor(cosmeticOptions.at("Kal_ItemSelC"));
ResetColor(cosmeticOptions.at("Kal_ItemSelD"));
} else if (cosmeticOption.label == "Equip Select Color") {
ResetColor(cosmeticOptions.at("Kal_EquipSelB"));
ResetColor(cosmeticOptions.at("Kal_EquipSelC"));
ResetColor(cosmeticOptions.at("Kal_EquipSelD"));
} else if (cosmeticOption.label == "Map Dungeon Color") {
ResetColor(cosmeticOptions.at("Kal_MapSelDunB"));
ResetColor(cosmeticOptions.at("Kal_MapSelDunC"));
ResetColor(cosmeticOptions.at("Kal_MapSelDunD"));
} else if (cosmeticOption.label == "Quest Status Color") {
ResetColor(cosmeticOptions.at("Kal_QuestStatusB"));
ResetColor(cosmeticOptions.at("Kal_QuestStatusC"));
ResetColor(cosmeticOptions.at("Kal_QuestStatusD"));
} else if (cosmeticOption.label == "Map Color") {
ResetColor(cosmeticOptions.at("Kal_MapSelectB"));
ResetColor(cosmeticOptions.at("Kal_MapSelectC"));
ResetColor(cosmeticOptions.at("Kal_MapSelectD"));
} else if (cosmeticOption.label == "Save Color") {
ResetColor(cosmeticOptions.at("Kal_SaveB"));
ResetColor(cosmeticOptions.at("Kal_SaveC"));
ResetColor(cosmeticOptions.at("Kal_SaveD"));
}
}
@ -1727,6 +1631,7 @@ void DrawCosmeticRow(CosmeticOption& cosmeticOption) {
CVarSetColor(cosmeticOption.cvar, color);
CVarSetInteger((cosmeticOption.rainbowCvar), 0);
CVarSetInteger((cosmeticOption.changedCvar), 1);
ApplySideEffects(cosmeticOption);
ApplyOrResetCustomGfxPatches();
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
@ -1744,6 +1649,7 @@ void DrawCosmeticRow(CosmeticOption& cosmeticOption) {
if (ImGui::Checkbox(("Rainbow##" + cosmeticOption.label).c_str(), &isRainbow)) {
CVarSetInteger((cosmeticOption.rainbowCvar), isRainbow);
CVarSetInteger((cosmeticOption.changedCvar), 1);
ApplySideEffects(cosmeticOption);
ApplyOrResetCustomGfxPatches();
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
@ -1800,7 +1706,7 @@ static const char* colorSchemes[2] = {
};
void CosmeticsEditorWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(480, 520), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(550, 520), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Cosmetics Editor", &mIsVisible)) {
ImGui::End();
return;

View File

@ -55,6 +55,22 @@ typedef enum {
TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW = 0x9210,
TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range
TEXT_SARIAS_SONG_CHANNELING_POWER = 0x016D,
TEXT_BEAN_SALESMAN_BUY_FOR_10 = 0x405E,
TEXT_BEAN_SALESMAN_BUY_FOR_20 = 0x405F,
TEXT_BEAN_SALESMAN_BUY_FOR_30 = 0x4060,
TEXT_BEAN_SALESMAN_BUY_FOR_40 = 0x4061,
TEXT_BEAN_SALESMAN_BUY_FOR_50 = 0x4062,
TEXT_BEAN_SALESMAN_BUY_FOR_60 = 0x4063,
TEXT_BEAN_SALESMAN_BUY_FOR_70 = 0x4064,
TEXT_BEAN_SALESMAN_BUY_FOR_80 = 0x4065,
TEXT_BEAN_SALESMAN_BUY_FOR_90 = 0x4066,
TEXT_BEAN_SALESMAN_BUY_FOR_100 = 0x4067,
TEXT_BEAN_SALESMAN_OH_WELL = 0x4068,
TEXT_BEAN_SALESMAN_NOT_ENOUGH_MONEY = 0x4069,
TEXT_BEAN_SALESMAN_SET_A_BEAN_TO_C = 0x406A,
TEXT_BEAN_SALESMAN_SOLD_OUT = 0x406B,
TEXT_BEAN_SALESMAN_WANT_TO_PLANT = 0x406C,
} TextIDs;
#ifdef __cplusplus

View File

@ -0,0 +1,271 @@
#include "MessageViewer.h"
#include <soh/UIWidgets.hpp>
#include <textures/message_static/message_static.h>
#include "../custom-message/CustomMessageManager.h"
#include "functions.h"
#include "macros.h"
#include "message_data_static.h"
#include "variables.h"
#include "soh/util.h"
extern "C" u8 sMessageHasSetSfx;
void MessageViewer::InitElement() {
CustomMessageManager::Instance->AddCustomMessageTable(TABLE_ID);
mTableIdBuf = static_cast<char*>(calloc(MAX_STRING_SIZE, sizeof(char)));
mTextIdBuf = static_cast<char*>(calloc(MAX_STRING_SIZE, sizeof(char)));
mCustomMessageBuf = static_cast<char*>(calloc(MAX_STRING_SIZE, sizeof(char)));
}
void MessageViewer::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Custom Message Debugger", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::End();
return;
}
ImGui::Text("Table ID");
ImGui::SameLine();
ImGui::InputText("##TableID", mTableIdBuf, MAX_STRING_SIZE, ImGuiInputTextFlags_CallbackCharFilter, UIWidgets::TextFilters::FilterAlphaNum);
UIWidgets::InsertHelpHoverText("Leave blank for vanilla table");
ImGui::Text("Text ID");
ImGui::SameLine();
switch (mTextIdBase) {
case DECIMAL:
ImGui::InputText("##TextID", mTextIdBuf, MAX_STRING_SIZE, ImGuiInputTextFlags_CharsDecimal);
UIWidgets::InsertHelpHoverText("Decimal Text ID of the message to load. Decimal digits only (0-9).");
break;
case HEXADECIMAL:
default:
ImGui::InputText("##TextID", mTextIdBuf, MAX_STRING_SIZE, ImGuiInputTextFlags_CharsHexadecimal);
UIWidgets::InsertHelpHoverText("Hexadecimal Text ID of the message to load. Hexadecimal digits only (0-9/A-F).");
break;
}
if (ImGui::RadioButton("Hexadecimal", &mTextIdBase, HEXADECIMAL)) {
memset(mTextIdBuf, 0, sizeof(char) * MAX_STRING_SIZE);
}
ImGui::SameLine();
if (ImGui::RadioButton("Decimal", &mTextIdBase, DECIMAL)) {
memset(mTextIdBuf, 0, sizeof(char) * MAX_STRING_SIZE);
}
ImGui::Text("Language");
ImGui::SameLine();
if (ImGui::BeginCombo("##Language", mLanguages[mLanguage])) {
// ReSharper disable CppDFAUnreachableCode
for (size_t i = 0; i < mLanguages.size(); i++) {
if (strlen(mLanguages[i]) > 0) {
if (ImGui::Selectable(mLanguages[i], i == mLanguage)) {
mLanguage = i;
}
}
}
ImGui::EndCombo();
}
UIWidgets::InsertHelpHoverText("Which language to load from the selected text ID");
if (ImGui::Button("Display Message##ExistingMessage")) {
mDisplayExistingMessageClicked = true;
}
ImGui::Text("Custom Message");
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.");
ImGui::InputTextMultiline("##CustomMessage", mCustomMessageBuf, MAX_STRING_SIZE);
if (ImGui::Button("Display Message##CustomMessage")) {
mDisplayCustomMessageClicked = true;
}
ImGui::End();
// ReSharper restore CppDFAUnreachableCode
}
void MessageViewer::UpdateElement() {
if (mDisplayExistingMessageClicked) {
mTableId = std::string(mTableIdBuf);
switch (mTextIdBase) {
case DECIMAL:
mTextId = std::stoi(std::string(mTextIdBuf), nullptr, 10);
break;
case HEXADECIMAL:
default:
mTextId = std::stoi(std::string(mTextIdBuf), nullptr, 16);
break;
}
DisplayExistingMessage();
mDisplayExistingMessageClicked = false;
}
if (mDisplayCustomMessageClicked) {
mCustomMessageString = std::string(mCustomMessageBuf);
std::erase(mCustomMessageString, '\n');
DisplayCustomMessage();
mDisplayCustomMessageClicked = false;
}
}
void MessageViewer::DisplayExistingMessage() const {
MessageDebug_StartTextBox(mTableId.c_str(), mTextId, mLanguage);
}
void MessageViewer::DisplayCustomMessage() const {
MessageDebug_DisplayCustomMessage(mCustomMessageString.c_str());
}
extern "C" MessageTableEntry* sNesMessageEntryTablePtr;
extern "C" MessageTableEntry* sGerMessageEntryTablePtr;
extern "C" MessageTableEntry* sFraMessageEntryTablePtr;
extern "C" MessageTableEntry* sStaffMessageEntryTablePtr;
void FindMessage(PlayState* play, const uint16_t textId, const uint8_t language) {
const char* foundSeg;
const char* nextSeg;
MessageTableEntry* messageTableEntry = sNesMessageEntryTablePtr;
Font* font;
u16 bufferId = textId;
// Use the better owl message if better owl is enabled
if (CVarGetInteger("gBetterOwl", 0) != 0 && (bufferId == 0x2066 || bufferId == 0x607B ||
bufferId == 0x10C2 || bufferId == 0x10C6 || bufferId == 0x206A))
{
bufferId = 0x71B3;
}
if (language == LANGUAGE_GER)
messageTableEntry = sGerMessageEntryTablePtr;
else if (language == LANGUAGE_FRA)
messageTableEntry = sFraMessageEntryTablePtr;
// If PAL languages are not present in the OTR file, default to English
if (messageTableEntry == nullptr)
messageTableEntry = sNesMessageEntryTablePtr;
const char* seg = messageTableEntry->segment;
while (messageTableEntry->textId != 0xFFFF) {
font = &play->msgCtx.font;
if (messageTableEntry->textId == bufferId) {
foundSeg = messageTableEntry->segment;
font->charTexBuf[0] = messageTableEntry->typePos;
nextSeg = messageTableEntry->segment;
font->msgOffset = reinterpret_cast<uintptr_t>(messageTableEntry->segment);
font->msgLength = messageTableEntry->msgSize;
return;
}
messageTableEntry++;
}
font = &play->msgCtx.font;
messageTableEntry = sNesMessageEntryTablePtr;
foundSeg = messageTableEntry->segment;
font->charTexBuf[0] = messageTableEntry->typePos;
messageTableEntry++;
nextSeg = messageTableEntry->segment;
font->msgOffset = foundSeg - seg;
font->msgLength = nextSeg - foundSeg;
}
static const char* msgStaticTbl[] =
{
gDefaultMessageBackgroundTex,
gSignMessageBackgroundTex,
gNoteStaffMessageBackgroundTex,
gFadingMessageBackgroundTex,
gMessageContinueTriangleTex,
gMessageEndSquareTex,
gMessageArrowTex
};
void MessageDebug_StartTextBox(const char* tableId, uint16_t textId, uint8_t language) {
PlayState* play = gPlayState;
static int16_t messageStaticIndices[] = { 0, 1, 3, 2 };
const auto player = GET_PLAYER(gPlayState);
player->actor.flags |= ACTOR_FLAG_PLAYER_TALKED_TO;
MessageContext* msgCtx = &play->msgCtx;
msgCtx->ocarinaAction = 0xFFFF;
Font* font = &msgCtx->font;
sMessageHasSetSfx = 0;
for (u32 i = 0; i < FONT_CHAR_TEX_SIZE * 120; i += FONT_CHAR_TEX_SIZE) {
if (&font->charTexBuf[i] != nullptr) {
gSPInvalidateTexCache(play->state.gfxCtx->polyOpa.p++, reinterpret_cast<uintptr_t>(&font->charTexBuf[i]));
}
}
R_TEXT_CHAR_SCALE = 75;
R_TEXT_LINE_SPACING = 12;
R_TEXT_INIT_XPOS = 65;
char* buffer = font->msgBuf;
msgCtx->textId = textId;
if (strlen(tableId) == 0) {
FindMessage(play, textId, language);
msgCtx->msgLength = static_cast<int32_t>(font->msgLength);
const uintptr_t src = font->msgOffset;
memcpy(font->msgBuf, reinterpret_cast<void const *>(src), font->msgLength);
} else {
constexpr int maxBufferSize = sizeof(font->msgBuf);
const CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(tableId, textId);
font->charTexBuf[0] = (messageEntry.GetTextBoxType() << 4) | messageEntry.GetTextBoxPosition();
switch (language) {
case LANGUAGE_FRA:
font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetFrench(), maxBufferSize);
break;
case LANGUAGE_GER:
font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetGerman(), maxBufferSize);
break;
case LANGUAGE_ENG:
default:
font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetEnglish(), maxBufferSize);
break;
}
msgCtx->msgLength = static_cast<int32_t>(font->msgLength);
}
msgCtx->textBoxProperties = font->charTexBuf[0];
msgCtx->textBoxType = msgCtx->textBoxProperties >> 4;
msgCtx->textBoxPos = msgCtx->textBoxProperties & 0xF;
const int16_t textBoxType = msgCtx->textBoxType;
// "Text Box Type"
osSyncPrintf("吹き出し種類=%d\n", msgCtx->textBoxType);
if (textBoxType < TEXTBOX_TYPE_NONE_BOTTOM) {
const char* textureName = msgStaticTbl[messageStaticIndices[textBoxType]];
memcpy(msgCtx->textboxSegment, textureName, strlen(textureName) + 1);
if (textBoxType == TEXTBOX_TYPE_BLACK) {
msgCtx->textboxColorRed = 0;
msgCtx->textboxColorGreen = 0;
msgCtx->textboxColorBlue = 0;
} else if (textBoxType == TEXTBOX_TYPE_WOODEN) {
msgCtx->textboxColorRed = 70;
msgCtx->textboxColorGreen = 50;
msgCtx->textboxColorBlue = 30;
} else if (textBoxType == TEXTBOX_TYPE_BLUE) {
msgCtx->textboxColorRed = 0;
msgCtx->textboxColorGreen = 10;
msgCtx->textboxColorBlue = 50;
} else {
msgCtx->textboxColorRed = 255;
msgCtx->textboxColorGreen = 0;
msgCtx->textboxColorBlue = 0;
}
if (textBoxType == TEXTBOX_TYPE_WOODEN) {
msgCtx->textboxColorAlphaTarget = 230;
} else if (textBoxType == TEXTBOX_TYPE_OCARINA) {
msgCtx->textboxColorAlphaTarget = 180;
} else {
msgCtx->textboxColorAlphaTarget = 170;
}
msgCtx->textboxColorAlphaCurrent = 0;
}
msgCtx->choiceNum = msgCtx->textUnskippable = msgCtx->textboxEndType = 0;
msgCtx->msgBufPos = msgCtx->unk_E3D0 = msgCtx->textDrawPos = 0;
msgCtx->talkActor = &player->actor;
msgCtx->msgMode = MSGMODE_TEXT_START;
msgCtx->stateTimer = 0;
msgCtx->textDelayTimer = 0;
msgCtx->ocarinaMode = OCARINA_MODE_00;
}
void MessageDebug_DisplayCustomMessage(const char* customMessage) {
CustomMessageManager::Instance->ClearMessageTable(MessageViewer::TABLE_ID);
CustomMessageManager::Instance->CreateMessage(MessageViewer::TABLE_ID, 0,
CustomMessage(customMessage, customMessage, customMessage));
MessageDebug_StartTextBox(MessageViewer::TABLE_ID, 0, 0);
}

View File

@ -0,0 +1,62 @@
#ifndef CUSTOMMESSAGEDEBUGGER_H
#define CUSTOMMESSAGEDEBUGGER_H
#include "z64.h"
#ifdef __cplusplus
#include "GuiWindow.h"
#include <array>
extern "C" {
#endif
/**
* \brief Pulls a message from the specified message table and kicks off the process of displaying that message
* in a text box on screen.
* \param tableId the tableId string for the table we want to pull from. Empty string for authentic/vanilla messages
* \param textId The textId corresponding to the message to display. Putting in a textId that doesn't exist will
* probably result in a crash.
* \param language The Language to display on the screen.
*/
void MessageDebug_StartTextBox(const char* tableId, uint16_t textId, uint8_t language);
/**
* \brief
* \param customMessage A string using Custom Message Syntax.
*/
void MessageDebug_DisplayCustomMessage(const char* customMessage);
#ifdef __cplusplus
}
class MessageViewer : public LUS::GuiWindow {
public:
static inline const char* TABLE_ID = "MessageViewer";
using GuiWindow::GuiWindow;
void InitElement() override;
void DrawElement() override;
void UpdateElement() override;
virtual ~MessageViewer() = default;
private:
void DisplayExistingMessage() const;
void DisplayCustomMessage() const;
static constexpr uint16_t MAX_STRING_SIZE = 1024;
static constexpr std::array<const char*, LANGUAGE_MAX> mLanguages = {"English", "German", "French"};
static constexpr int HEXADECIMAL = 0;
static constexpr int DECIMAL = 1;
char* mTableIdBuf;
std::string mTableId;
char* mTextIdBuf;
uint16_t mTextId;
int mTextIdBase = HEXADECIMAL;
size_t mLanguage = LANGUAGE_ENG;
char* mCustomMessageBuf;
std::string mCustomMessageString;
bool mDisplayExistingMessageClicked = false;
bool mDisplayCustomMessageClicked = false;
};
#endif //__cplusplus
#endif //CUSTOMMESSAGEDEBUGGER_H

View File

@ -9,7 +9,6 @@
#include <bit>
#include <map>
#include <string>
#include <regex>
#include <libultraship/libultraship.h>
#include "dlViewer.h"
@ -66,16 +65,14 @@ std::map<int, std::string> cmdMap = {
};
void PerformDisplayListSearch() {
auto result = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->ListFiles("*" + std::string(searchString) + "*DL*");
std::regex dlSearch(".*((DL)|(DL_.*))$");
auto result = LUS::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->ListFiles("*" + std::string(searchString) + "*DL*");
displayListSearchResults.clear();
// Filter the file results even further as StormLib can only use wildcard searching
for (size_t i = 0; i < result->size(); i++) {
std::string val = result->at(i);
if (std::regex_search(val.c_str(), dlSearch)) {
if (val.ends_with("DL") || val.find("DL_") != std::string::npos) {
displayListSearchResults.push_back(val);
}
}
@ -99,8 +96,6 @@ void DLViewerWindow::DrawElement() {
return;
}
ImGui::Text("%d", searchDebounceFrames);
// Debounce the search field as listing otr files is expensive
if (ImGui::InputText("Search Display Lists", searchString, ARRAY_COUNT(searchString))) {
doSearch = true;
@ -134,7 +129,7 @@ void DLViewerWindow::DrawElement() {
try {
auto res = std::static_pointer_cast<LUS::DisplayList>(LUS::Context::GetInstance()->GetResourceManager()->LoadResource(activeDisplayList));
if (res->GetInitData()->Type != LUS::ResourceType::DisplayList) {
if (res->GetInitData()->Type != static_cast<uint32_t>(LUS::ResourceType::DisplayList)) {
ImGui::Text("Resource type is not a Display List. Please choose another.");
ImGui::End();
return;

View File

@ -78,4 +78,10 @@ typedef enum {
DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE,
} DekuStickType;
typedef enum {
SWORD_TOGGLE_NONE,
SWORD_TOGGLE_CHILD,
SWORD_TOGGLE_BOTH_AGES,
} SwordToggleMode;
#endif

View File

@ -37,12 +37,19 @@ GameInteractionEffectQueryResult GameInteractor::RemoveEffect(RemovableGameInter
// MARK: - Helpers
bool GameInteractor::IsSaveLoaded() {
bool GameInteractor::IsSaveLoaded(bool allowDbgSave) {
Player* player;
if (gPlayState != NULL) {
player = GET_PLAYER(gPlayState);
}
return (gPlayState == NULL || player == NULL || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) ? false : true;
// Checking for normal game mode prevents debug saves from reporting true on title screen
if (gPlayState == NULL || player == NULL || gSaveContext.gameMode != GAMEMODE_NORMAL) {
return false;
}
// Valid save file or debug save
return (gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2) || (allowDbgSave && gSaveContext.fileNum == 0xFF);
}
bool GameInteractor::IsGameplayPaused() {

View File

@ -163,11 +163,30 @@ public:
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
// Game Hooks
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; };
template <typename H> void RegisterGameHook(typename H::fn h) { RegisteredGameHooks<H>::functions.push_back(h); }
uint32_t nextHookId = 1;
template <typename H> struct RegisteredGameHooks { inline static std::unordered_map<uint32_t, typename H::fn> functions; };
template <typename H> struct HooksToUnregister { inline static std::vector<uint32_t> hooks; };
template <typename H> uint32_t RegisterGameHook(typename H::fn h) {
// 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;
while (RegisteredGameHooks<H>::functions.find(this->nextHookId) != RegisteredGameHooks<H>::functions.end()) {
this->nextHookId++;
}
RegisteredGameHooks<H>::functions[this->nextHookId] = h;
return this->nextHookId++;
}
template <typename H> void UnregisterGameHook(uint32_t id) {
HooksToUnregister<H>::hooks.push_back(id);
}
template <typename H, typename... Args> void ExecuteHooks(Args&&... args) {
for (auto& fn : RegisteredGameHooks<H>::functions) {
fn(std::forward<Args>(args)...);
for (auto& hookId : HooksToUnregister<H>::hooks) {
RegisteredGameHooks<H>::functions.erase(hookId);
}
HooksToUnregister<H>::hooks.clear();
for (auto& hook : RegisteredGameHooks<H>::functions) {
hook.second(std::forward<Args>(args)...);
}
}
@ -221,9 +240,10 @@ public:
DEFINE_HOOK(OnFileDropped, void(std::string filePath));
DEFINE_HOOK(OnAssetAltChange, void());
DEFINE_HOOK(OnKaleidoUpdate, void());
// Helpers
static bool IsSaveLoaded();
static bool IsSaveLoaded(bool allowDbgSave = false);
static bool IsGameplayPaused();
static bool CanSpawnActor();
static bool CanAddOrTakeAmmo(int16_t amount, int16_t item);

View File

@ -187,3 +187,9 @@ void GameInteractor_ExecuteOnSetGameLanguage() {
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnAssetAltChange>(fn);
}
//MARK: Pause Menu
void GameInteractor_ExecuteOnKaleidoUpdate() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnKaleidoUpdate>();
}

View File

@ -60,6 +60,9 @@ void GameInteractor_ExecuteOnSetGameLanguage();
// MARK: - System
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void));
//Mark: - Pause Menu
void GameInteractor_ExecuteOnKaleidoUpdate();
#ifdef __cplusplus
}
#endif

View File

@ -111,9 +111,9 @@ void GameInteractor::RawAction::FreezePlayer() {
void GameInteractor::RawAction::BurnPlayer() {
Player* player = GET_PLAYER(gPlayState);
for (int i = 0; i < 18; i++) {
player->flameTimers[i] = Rand_S16Offset(0, 200);
player->bodyFlameTimers[i] = Rand_S16Offset(0, 200);
}
player->isBurning = true;
player->bodyIsBurning = true;
func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0);
}

View File

@ -23,6 +23,9 @@
#include "src/overlays/actors/ovl_En_Tp/z_en_tp.h"
#include "src/overlays/actors/ovl_En_Firefly/z_en_firefly.h"
#include "src/overlays/actors/ovl_En_Xc/z_en_xc.h"
#include "src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h"
#include "objects/object_link_boy/object_link_boy.h"
#include "objects/object_link_child/object_link_child.h"
extern "C" {
#include <z64.h>
@ -31,6 +34,7 @@ extern "C" {
#include "functions.h"
#include "variables.h"
#include "functions.h"
#include "src/overlays/actors/ovl_En_Door/z_en_door.h"
void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction);
void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
@ -61,7 +65,7 @@ void ReloadSceneTogglingLinkAge() {
void RegisterInfiniteMoney() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
@ -72,7 +76,7 @@ void RegisterInfiniteMoney() {
void RegisterInfiniteHealth() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
if (gSaveContext.health < gSaveContext.healthCapacity) {
gSaveContext.health = gSaveContext.healthCapacity;
@ -83,7 +87,7 @@ void RegisterInfiniteHealth() {
void RegisterInfiniteAmmo() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
// Deku Sticks
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
@ -120,7 +124,7 @@ void RegisterInfiniteAmmo() {
void RegisterInfiniteMagic() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
@ -131,7 +135,7 @@ void RegisterInfiniteMagic() {
void RegisterInfiniteNayrusLove() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
gSaveContext.nayrusLoveTimer = 0x44B;
}
@ -140,7 +144,7 @@ void RegisterInfiniteNayrusLove() {
void RegisterMoonJumpOnL() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@ -155,7 +159,7 @@ void RegisterMoonJumpOnL() {
void RegisterInfiniteISG() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gEzISG", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@ -167,7 +171,7 @@ void RegisterInfiniteISG() {
//Permanent quick put away (QPA) glitched damage value
void RegisterEzQPA() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gEzQPA", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@ -179,7 +183,7 @@ void RegisterEzQPA() {
void RegisterUnrestrictedItems() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
@ -207,11 +211,14 @@ void RegisterFreezeTime() {
/// Switches Link's age and respawns him at the last entrance he entered.
void RegisterSwitchAge() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) {
static bool warped = false;
if (!GameInteractor::IsSaveLoaded(true)) {
CVarClear("gSwitchAge");
warped = false;
return;
}
static bool warped = false;
static Vec3f playerPos;
static int16_t playerYaw;
static RoomContext* roomCtx;
@ -245,7 +252,7 @@ void RegisterSwitchAge() {
void RegisterOcarinaTimeTravel() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
if (!GameInteractor::IsSaveLoaded()) {
if (!GameInteractor::IsSaveLoaded(true)) {
CVarClear("gTimeTravel");
return;
}
@ -516,70 +523,93 @@ void RegisterDaytimeGoldSkultullas() {
});
}
void RegisterHyperBosses() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
// Run the update function a second time to make bosses move and act twice as fast.
bool IsHyperBossesActive() {
return CVarGetInteger("gHyperBosses", 0) ||
(IS_BOSS_RUSH && gSaveContext.bossRushOptions[BR_OPTIONS_HYPERBOSSES] == BR_CHOICE_HYPERBOSSES_YES);
}
Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor);
void UpdateHyperBossesState() {
static uint32_t actorUpdateHookId = 0;
if (actorUpdateHookId != 0) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(actorUpdateHookId);
actorUpdateHookId = 0;
}
uint8_t isBossActor =
actor->id == ACTOR_BOSS_GOMA || // Gohma
actor->id == ACTOR_BOSS_DODONGO || // King Dodongo
actor->id == ACTOR_EN_BDFIRE || // King Dodongo Fire Breath
actor->id == ACTOR_BOSS_VA || // Barinade
actor->id == ACTOR_BOSS_GANONDROF || // Phantom Ganon
actor->id == ACTOR_EN_FHG_FIRE || // Phantom Ganon/Ganondorf Energy Ball/Thunder
actor->id == ACTOR_EN_FHG || // Phantom Ganon's Horse
actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || // Volvagia (grounded/flying)
actor->id == ACTOR_EN_VB_BALL || // Volvagia Rocks
actor->id == ACTOR_BOSS_MO || // Morpha
actor->id == ACTOR_BOSS_SST || // Bongo Bongo
actor->id == ACTOR_BOSS_TW || // Twinrova
actor->id == ACTOR_BOSS_GANON || // Ganondorf
actor->id == ACTOR_BOSS_GANON2; // Ganon
if (IsHyperBossesActive()) {
actorUpdateHookId = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
// Run the update function a second time to make bosses move and act twice as fast.
uint8_t hyperBossesActive =
CVarGetInteger("gHyperBosses", 0) ||
(IS_BOSS_RUSH &&
gSaveContext.bossRushOptions[BR_OPTIONS_HYPERBOSSES] == BR_CHOICE_HYPERBOSSES_YES);
Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor);
// Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses.
if (hyperBossesActive && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) {
// Barinade needs to be updated in sequence to avoid unintended behaviour.
if (actor->id == ACTOR_BOSS_VA) {
// params -1 is BOSSVA_BODY
if (actor->params == -1) {
Actor* actorList = gPlayState->actorCtx.actorLists[ACTORCAT_BOSS].head;
while (actorList != NULL) {
GameInteractor::RawAction::UpdateActor(actorList);
actorList = actorList->next;
uint8_t isBossActor =
actor->id == ACTOR_BOSS_GOMA || // Gohma
actor->id == ACTOR_BOSS_DODONGO || // King Dodongo
actor->id == ACTOR_EN_BDFIRE || // King Dodongo Fire Breath
actor->id == ACTOR_BOSS_VA || // Barinade
actor->id == ACTOR_BOSS_GANONDROF || // Phantom Ganon
actor->id == ACTOR_EN_FHG_FIRE || // Phantom Ganon/Ganondorf Energy Ball/Thunder
actor->id == ACTOR_EN_FHG || // Phantom Ganon's Horse
actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || // Volvagia (grounded/flying)
actor->id == ACTOR_EN_VB_BALL || // Volvagia Rocks
actor->id == ACTOR_BOSS_MO || // Morpha
actor->id == ACTOR_BOSS_SST || // Bongo Bongo
actor->id == ACTOR_BOSS_TW || // Twinrova
actor->id == ACTOR_BOSS_GANON || // Ganondorf
actor->id == ACTOR_BOSS_GANON2; // Ganon
// Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses.
if (IsHyperBossesActive() && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) {
// Barinade needs to be updated in sequence to avoid unintended behaviour.
if (actor->id == ACTOR_BOSS_VA) {
// params -1 is BOSSVA_BODY
if (actor->params == -1) {
Actor* actorList = gPlayState->actorCtx.actorLists[ACTORCAT_BOSS].head;
while (actorList != NULL) {
GameInteractor::RawAction::UpdateActor(actorList);
actorList = actorList->next;
}
}
} else {
GameInteractor::RawAction::UpdateActor(actor);
}
} else {
GameInteractor::RawAction::UpdateActor(actor);
}
}
});
}
}
void RegisterHyperBosses() {
UpdateHyperBossesState();
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int16_t fileNum) {
UpdateHyperBossesState();
});
}
void RegisterHyperEnemies() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
// Run the update function a second time to make enemies and minibosses move and act twice as fast.
void UpdateHyperEnemiesState() {
static uint32_t actorUpdateHookId = 0;
if (actorUpdateHookId != 0) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(actorUpdateHookId);
actorUpdateHookId = 0;
}
Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor);
if (CVarGetInteger("gHyperEnemies", 0)) {
actorUpdateHookId = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
// Run the update function a second time to make enemies and minibosses move and act twice as fast.
// Some enemies are not in the ACTORCAT_ENEMY category, and some are that aren't really enemies.
bool isEnemy = actor->category == ACTORCAT_ENEMY || actor->id == ACTOR_EN_TORCH2;
bool isExcludedEnemy = actor->id == ACTOR_EN_FIRE_ROCK || actor->id == ACTOR_EN_ENCOUNT2;
Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor);
// Don't apply during cutscenes because it causes weird behaviour and/or crashes on some cutscenes.
if (CVarGetInteger("gHyperEnemies", 0) && isEnemy && !isExcludedEnemy &&
!Player_InBlockingCsMode(gPlayState, player)) {
GameInteractor::RawAction::UpdateActor(actor);
}
});
// Some enemies are not in the ACTORCAT_ENEMY category, and some are that aren't really enemies.
bool isEnemy = actor->category == ACTORCAT_ENEMY || actor->id == ACTOR_EN_TORCH2;
bool isExcludedEnemy = actor->id == ACTOR_EN_FIRE_ROCK || actor->id == ACTOR_EN_ENCOUNT2;
// Don't apply during cutscenes because it causes weird behaviour and/or crashes on some cutscenes.
if (CVarGetInteger("gHyperEnemies", 0) && isEnemy && !isExcludedEnemy &&
!Player_InBlockingCsMode(gPlayState, player)) {
GameInteractor::RawAction::UpdateActor(actor);
}
});
}
}
void RegisterBonkDamage() {
@ -687,6 +717,54 @@ void RegisterMirrorModeHandler() {
});
}
void UpdatePatchHand() {
if ((CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && LINK_IS_CHILD) {
ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1", 92, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL));
ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2", 93, gsSPEndDisplayList());
ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1", 84, gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL));
ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2", 85, gsSPEndDisplayList());
ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1", 51, gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL));
ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2", 52, gsSPEndDisplayList());
ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1", 104, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL));
ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2", 105, gsSPEndDisplayList());
ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1", 79, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL));
ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2", 80, gsSPEndDisplayList());
ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1", 76, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL));
ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2", 77, gsSPEndDisplayList());
} else {
ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1");
ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2");
ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1");
ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2");
ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1");
ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2");
ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1");
ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2");
ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1");
ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2");
ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1");
ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2");
}
if ((CVarGetInteger("gEnhancements.EquimentAlwaysVisible", 0)) && LINK_IS_ADULT) {
ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword", 13, gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL));
ResourceMgr_PatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot", 13, gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL));
ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang", 50, gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL));
ResourceMgr_PatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield", 49, gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL));
} else {
ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword");
ResourceMgr_UnpatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot");
ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang");
ResourceMgr_UnpatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield");
}
}
void RegisterPatchHandHandler() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int32_t sceneNum) {
UpdatePatchHand();
});
}
void RegisterResetNaviTimer() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int32_t sceneNum) {
if (CVarGetInteger("gEnhancements.ResetNaviTimer", 0)) {
@ -1172,6 +1250,34 @@ void RegisterRandomizedEnemySizes() {
});
}
void RegisterOpenAllHours() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
if (CVarGetInteger("gEnhancements.OpenAllHours", 0) && (actor->id == ACTOR_EN_DOOR)) {
switch (actor->params) {
case 4753: // Night Market Bazaar
case 1678: // Night Potion Shop
case 2689: // Day Bombchu Shop
case 2703: // Night Slingshot Game
case 653: // Day Chest Game
case 6801: // Night Kak Bazaar
case 7822: // Night Kak Potion Shop
case 4751: // Night Kak Archery Game
case 3728: // Night Mask Shop
{
actor->params = (actor->params & 0xFC00) | (DOOR_SCENEEXIT << 7) | 0x3F;
EnDoor* enDoor = static_cast<EnDoor*>(refActor);
EnDoor_SetupType(enDoor, gPlayState);
break;
}
default:
break;
}
}
});
}
void PatchToTMedallions() {
// TODO: Refactor the DemoEffect_UpdateJewelAdult and DemoEffect_UpdateJewelChild from z_demo_effect
// effects to take effect in there
@ -1254,6 +1360,40 @@ void RegisterToTMedallions() {
});
}
void RegisterFloorSwitchesHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
if (actor->id != ACTOR_OBJ_SWITCH || !CVarGetInteger("gEnhancements.FixFloorSwitches", 0)) {
return;
}
ObjSwitch* switchActor = reinterpret_cast<ObjSwitch*>(actor);
s32 type = (switchActor->dyna.actor.params & 7);
if (switchActor->dyna.actor.params == 0x1200 || switchActor->dyna.actor.params == 0x3A00) {
switchActor->dyna.actor.world.pos.y -= 1;
}
});
}
void RegisterPauseMenuHooks() {
static bool pauseWarpHooksRegistered = false;
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([&]() {
if (!GameInteractor::IsSaveLoaded() || !CVarGetInteger("gPauseWarp", 0)) {
pauseWarpHooksRegistered = false;
return;
}
if (!pauseWarpHooksRegistered) {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnKaleidoUpdate>([]() {PauseWarp_HandleSelection();});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
PauseWarp_Execute();
});
pauseWarpHooksRegistered = true;
}
});
}
void InitMods() {
RegisterTTS();
RegisterInfiniteMoney();
@ -1275,7 +1415,7 @@ void InitMods() {
RegisterPermanentHeartLoss();
RegisterDeleteFileOnDeath();
RegisterHyperBosses();
RegisterHyperEnemies();
UpdateHyperEnemiesState();
RegisterBonkDamage();
RegisterMenuPathFix();
RegisterMirrorModeHandler();
@ -1286,7 +1426,11 @@ void InitMods() {
RegisterAltTrapTypes();
RegisterRandomizerSheikSpawn();
RegisterRandomizedEnemySizes();
RegisterOpenAllHours();
RegisterToTMedallions();
NameTag_RegisterHooks();
RegisterFloorSwitchesHook();
RegisterPatchHandHandler();
RegisterHurtContainerModeHandler();
RegisterPauseMenuHooks();
}

View File

@ -12,7 +12,10 @@ void UpdateMirrorModeState(int32_t sceneNum);
void UpdateHurtContainerModeState(bool newState);
void PatchToTMedallions();
void UpdatePermanentHeartLossState();
void UpdateHyperEnemiesState();
void UpdateHyperBossesState();
void InitMods();
void UpdatePatchHand();
#ifdef __cplusplus
}

View File

@ -0,0 +1,92 @@
#include "custom-message/CustomMessageTypes.h"
#include "global.h"
#include "z64.h"
#include "game-interactor/GameInteractor.h"
static const int songMessageMap[] = {
TEXT_WARP_MINUET_OF_FOREST,
TEXT_WARP_BOLERO_OF_FIRE,
TEXT_WARP_SERENADE_OF_WATER,
TEXT_WARP_REQUIEM_OF_SPIRIT,
TEXT_WARP_NOCTURNE_OF_SHADOW,
TEXT_WARP_PRELUDE_OF_LIGHT
};
static const int ocarinaSongMap[] = {
OCARINA_SONG_MINUET,
OCARINA_SONG_BOLERO,
OCARINA_SONG_SERENADE,
OCARINA_SONG_REQUIEM,
OCARINA_SONG_NOCTURNE,
OCARINA_SONG_PRELUDE
};
static const int entranceIndexMap[] = {
ENTR_SACRED_FOREST_MEADOW_2, // Minuet
ENTR_DEATH_MOUNTAIN_CRATER_4, // Bolero
ENTR_LAKE_HYLIA_8, // Serenade
ENTR_DESERT_COLOSSUS_5, // Requiem
ENTR_GRAVEYARD_7, // Nocturne
ENTR_TEMPLE_OF_TIME_7 // Prelude
};
static const int songAudioMap[] = {
NA_BGM_OCA_MINUET,
NA_BGM_OCA_BOLERO,
NA_BGM_OCA_SERENADE,
NA_BGM_OCA_REQUIEM,
NA_BGM_OCA_NOCTURNE,
NA_BGM_OCA_LIGHT
};
static bool isWarpActive = false;
void PauseWarp_Execute() {
if (!isWarpActive || gPlayState->msgCtx.msgMode != MSGMODE_NONE) {
return;
}
isWarpActive = false;
GET_PLAYER(gPlayState)->stateFlags1 &= ~PLAYER_STATE1_IN_CUTSCENE;
if (gPlayState->msgCtx.choiceIndex != 0) {
return;
}
if (IS_RANDO) {
Entrance_SetWarpSongEntrance();
return;
}
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_FADE_WHITE_FAST;
for (int i = 0; i < ARRAY_COUNT(ocarinaSongMap); i++) {
if (gPlayState->msgCtx.lastPlayedSong == ocarinaSongMap[i]) {
gPlayState->nextEntranceIndex = entranceIndexMap[i];
return;
}
}
gPlayState->transitionTrigger = TRANS_TRIGGER_OFF;
}
void ActivateWarp(PauseContext* pauseCtx, int song) {
Audio_OcaSetInstrument(0);
Interface_SetDoAction(gPlayState, DO_ACTION_NONE);
pauseCtx->state = 0x12;
WREG(2) = -6240;
func_800F64E0(0);
pauseCtx->unk_1E4 = 0;
int idx = song - QUEST_SONG_MINUET;
gPlayState->msgCtx.lastPlayedSong = ocarinaSongMap[idx];
Audio_SetSoundBanksMute(0x20);
Audio_PlayFanfare(songAudioMap[idx]);
Message_StartTextbox(gPlayState, songMessageMap[idx], NULL);
GET_PLAYER(gPlayState)->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
isWarpActive = true;
}
void PauseWarp_HandleSelection() {
if (gSaveContext.inventory.items[SLOT_OCARINA] != ITEM_NONE) {
int aButtonPressed = CHECK_BTN_ALL(gPlayState->state.input->press.button, BTN_A);
int song = gPlayState->pauseCtx.cursorPoint[PAUSE_QUEST];
if (aButtonPressed && CHECK_QUEST_ITEM(song) && song >= QUEST_SONG_MINUET && song <= QUEST_SONG_PRELUDE) {
ActivateWarp(&gPlayState->pauseCtx, song);
}
}
}

View File

@ -12,6 +12,14 @@ void clearCvars(std::vector<const char*> cvarsToClear) {
}
}
std::string FormatLocations(std::vector<RandomizerCheck> locs) {
std::string locString = "";
for (auto loc: locs) {
locString += std::to_string(loc) + ",";
}
return locString;
}
void applyPreset(std::vector<PresetEntry> entries) {
for(auto& [cvar, type, value] : entries) {
switch (type) {
@ -24,6 +32,9 @@ void applyPreset(std::vector<PresetEntry> entries) {
case PRESET_ENTRY_TYPE_STRING:
CVarSetString(cvar, std::get<const char*>(value));
break;
case PRESET_ENTRY_TYPE_CPP_STRING:
CVarSetString(cvar, std::get<std::string>(value).c_str());
break;
}
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include <variant>
#include <cstdint>
@ -11,6 +12,7 @@ enum PresetEntryType {
PRESET_ENTRY_TYPE_S32,
PRESET_ENTRY_TYPE_FLOAT,
PRESET_ENTRY_TYPE_STRING,
PRESET_ENTRY_TYPE_CPP_STRING,
};
enum PresetType {
@ -36,15 +38,19 @@ enum RandomizerPreset {
typedef struct PresetEntry {
const char* cvar;
PresetEntryType type;
std::variant<int32_t, float, const char*> value;
std::variant<int32_t, float, const char*, std::string> value;
} PresetEntry;
std::string FormatLocations(std::vector<RandomizerCheck> locs);
#define PRESET_ENTRY_S32(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_S32, value }
#define PRESET_ENTRY_FLOAT(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_FLOAT, value }
#define PRESET_ENTRY_STRING(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_STRING, value }
#define PRESET_ENTRY_CPP_STRING(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_CPP_STRING, value }
void DrawPresetSelector(PresetType presetType);
void clearCvars(std::vector<const char*> cvarsToClear);
@ -258,6 +264,24 @@ const std::vector<const char*> enhancementsCvars = {
"gAddTraps.Tele",
"gAddTraps.Void",
"gToTMedallionsColors",
"gCuccoStayDurationMultiplier",
"gDeleteFileOnDeath",
"gEnemySizeScalesHealth",
"gEnhancements.BetterAmmoRendering",
"gEnhancements.EquimentAlwaysVisible",
"gEnhancements.FixDaruniaDanceSpeed",
"gEnhancements.OpenAllHours",
"gEnhancements.ResetNaviTimer",
"gEnhancements.ScaleAdultEquimentAsChild",
"gEnhancements.LeeverSpawnRate",
"gEnhancements.SwordToggle",
"gEnhancements.FixFloorSwitches",
"gFixZoraHintDialogue",
"gHurtContainer",
"gPauseWarp",
"gPermanentHeartLoss",
"gRemoveExplosiveLimit",
"gToggleStrength",
};
const std::vector<const char*> cheatCvars = {
@ -310,6 +334,7 @@ const std::vector<const char*> cheatCvars = {
"gSwitchAge",
"gSwitchTimeline",
"gNoRedeadFreeze",
"gNoKeeseGuayTarget",
"gBombTimerMultiplier",
"gNoFishDespawn",
"gNoBugsDespawn",
@ -329,6 +354,7 @@ const std::vector<const char*> cheatCvars = {
"gCosmetics.Link_HeadScale.Value",
"gCosmetics.Link_SwordScale.Changed",
"gCosmetics.Link_SwordScale.Value",
"gEnhancements.RememberMapToggleState",
};
const std::vector<const char*> randomizerCvars = {
@ -523,6 +549,8 @@ const std::vector<PresetEntry> vanillaPlusPresetEntries = {
PRESET_ENTRY_S32("gNaviTextFix", 1),
// Extend Silver Rupee Jingle
PRESET_ENTRY_S32("gSilverRupeeJingleExtend", 1),
// Fix some Floor Switches
PRESET_ENTRY_S32("gEnhancements.FixFloorSwitches", 1),
// Red Ganon blood
PRESET_ENTRY_S32("gRedGanonBlood", 1),
@ -594,6 +622,8 @@ const std::vector<PresetEntry> enhancedPresetEntries = {
PRESET_ENTRY_S32("gSilverRupeeJingleExtend", 1),
// Fix enemies not spawning on ground over water
PRESET_ENTRY_S32("gEnemySpawnsOverWaterboxes", 1),
// Fix some Floor Switches
PRESET_ENTRY_S32("gEnhancements.FixFloorSwitches", 1),
// Red Ganon blood
PRESET_ENTRY_S32("gRedGanonBlood", 1),
@ -716,6 +746,8 @@ const std::vector<PresetEntry> randomizerPresetEntries = {
PRESET_ENTRY_S32("gNaviTextFix", 1),
// Extend Silver Rupee Jingle
PRESET_ENTRY_S32("gSilverRupeeJingleExtend", 1),
// Fix some Floor Switches
PRESET_ENTRY_S32("gEnhancements.FixFloorSwitches", 1),
// Red Ganon blood
PRESET_ENTRY_S32("gRedGanonBlood", 1),
@ -894,7 +926,8 @@ const std::vector<PresetEntry> spockRacePresetEntries = {
PRESET_ENTRY_S32("gRandomizeDampeHint", 1),
PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN),
PRESET_ENTRY_S32("gRandomizeEnableBombchuDrops", 1),
PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "78,143,144,229,"),
PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations(
{ RC_MARKET_10_BIG_POES, RC_KAK_40_GOLD_SKULLTULA_REWARD, RC_KAK_50_GOLD_SKULLTULA_REWARD, RC_ZR_FROGS_OCARINA_GAME })),
PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_OPEN),
PRESET_ENTRY_S32("gRandomizeFullWallets", 1),
PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP),
@ -986,7 +1019,8 @@ const std::vector<PresetEntry> spockRaceNoLogicPresetEntries = {
PRESET_ENTRY_S32("gRandomizeDampeHint", 1),
PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN),
PRESET_ENTRY_S32("gRandomizeEnableBombchuDrops", 1),
PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "78,143,144,229,"),
PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations(
{ RC_MARKET_10_BIG_POES, RC_KAK_40_GOLD_SKULLTULA_REWARD, RC_KAK_50_GOLD_SKULLTULA_REWARD, RC_ZR_FROGS_OCARINA_GAME })),
PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_OPEN),
PRESET_ENTRY_S32("gRandomizeFullWallets", 1),
PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP),
@ -1039,7 +1073,7 @@ const std::vector<PresetEntry> s6PresetEntries = {
PRESET_ENTRY_S32("gRandomizeBigPoeTargetCount", 1),
PRESET_ENTRY_S32("gRandomizeCuccosToReturn", 4),
PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN),
PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "48,"),
PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations({ RC_DEKU_THEATER_MASK_OF_TRUTH })),
PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_CLOSED_DEKU),
PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP),
PRESET_ENTRY_S32("gRandomizeGerudoFortress", RO_GF_FAST),

View File

@ -26,7 +26,6 @@ typedef struct {
AreaKey parentRegion;
AreaKey connectedRegion;
int16_t index;
int16_t blueWarp;
} EntranceLinkInfo;
EntranceLinkInfo NO_RETURN_ENTRANCE = {EntranceType::None, NONE, NONE, -1};
@ -40,6 +39,11 @@ using EntranceInfoPair = std::pair<EntranceLinkInfo, EntranceLinkInfo>;
using EntrancePair = std::pair<Entrance*, Entrance*>;
using EntrancePools = std::map<EntranceType, std::vector<Entrance*>>;
// Construct entrance name from parent and connected region keys
std::string EntranceNameByRegions(uint32_t parentRegion, uint32_t connectedRegion) {
return AreaTable(parentRegion)->regionName + " -> " + AreaTable(connectedRegion)->regionName;
}
//The entrance randomization algorithm used here is a direct copy of
//the algorithm used in the original N64 randomizer (except now in C++ instead
//of python). It may be easier to understand the algorithm by looking at the
@ -77,26 +81,22 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
//set data
Entrance* forwardEntrance = AreaTable(forwardEntry.parentRegion)->GetExit(forwardEntry.connectedRegion);
forwardEntrance->SetIndex(forwardEntry.index);
forwardEntrance->SetBlueWarp(forwardEntry.blueWarp);
forwardEntrance->SetType(forwardEntry.type);
forwardEntrance->SetAsPrimary();
// When decouple entrances is on, mark it for entrances except boss rooms
if (Settings::DecoupleEntrances && forwardEntry.type != EntranceType::ChildBoss &&
forwardEntry.type != EntranceType::AdultBoss) {
// When decouple entrances is on, mark the forward entrance
if (Settings::DecoupleEntrances) {
forwardEntrance->SetDecoupled();
}
if (returnEntry.parentRegion != NONE) {
Entrance* returnEntrance = AreaTable(returnEntry.parentRegion)->GetExit(returnEntry.connectedRegion);
returnEntrance->SetIndex(returnEntry.index);
returnEntrance->SetBlueWarp(returnEntry.blueWarp);
returnEntrance->SetType(returnEntry.type);
forwardEntrance->BindTwoWay(returnEntrance);
// Mark reverse entrance as decoupled
if (Settings::DecoupleEntrances && returnEntry.type != EntranceType::ChildBoss &&
returnEntry.type != EntranceType::AdultBoss) {
if (Settings::DecoupleEntrances) {
returnEntrance->SetDecoupled();
}
}
@ -687,31 +687,31 @@ int ShuffleAllEntrances() {
curNumRandomizedEntrances = 0;
std::vector<EntranceInfoPair> entranceShuffleTable = {
//Parent Region Connected Region index blue warp
//Parent Region Connected Region index
{{EntranceType::Dungeon, KF_OUTSIDE_DEKU_TREE, DEKU_TREE_ENTRYWAY, 0x0000},
{EntranceType::Dungeon, DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE, 0x0209, 0x0457}},
{EntranceType::Dungeon, DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE, 0x0209}},
{{EntranceType::Dungeon, DEATH_MOUNTAIN_TRAIL, DODONGOS_CAVERN_ENTRYWAY, 0x0004},
{EntranceType::Dungeon, DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL, 0x0242, 0x047A}},
{EntranceType::Dungeon, DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL, 0x0242}},
{{EntranceType::Dungeon, ZORAS_FOUNTAIN, JABU_JABUS_BELLY_ENTRYWAY, 0x0028},
{EntranceType::Dungeon, JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN, 0x0221, 0x010E}},
{EntranceType::Dungeon, JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN, 0x0221}},
{{EntranceType::Dungeon, SACRED_FOREST_MEADOW, FOREST_TEMPLE_ENTRYWAY, 0x0169},
{EntranceType::Dungeon, FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW, 0x0215, 0x0608}},
{EntranceType::Dungeon, FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW, 0x0215}},
{{EntranceType::Dungeon, DMC_CENTRAL_LOCAL, FIRE_TEMPLE_ENTRYWAY, 0x0165},
{EntranceType::Dungeon, FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL, 0x024A, 0x0564}},
{EntranceType::Dungeon, FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL, 0x024A}},
{{EntranceType::Dungeon, LAKE_HYLIA, WATER_TEMPLE_ENTRYWAY, 0x0010},
{EntranceType::Dungeon, WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA, 0x021D, 0x060C}},
{EntranceType::Dungeon, WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA, 0x021D}},
{{EntranceType::Dungeon, DESERT_COLOSSUS, SPIRIT_TEMPLE_ENTRYWAY, 0x0082},
{EntranceType::Dungeon, SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1, 0x0610}},
{EntranceType::Dungeon, SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1}},
{{EntranceType::Dungeon, GRAVEYARD_WARP_PAD_REGION, SHADOW_TEMPLE_ENTRYWAY, 0x0037},
{EntranceType::Dungeon, SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION, 0x0205, 0x0580}},
{EntranceType::Dungeon, SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION, 0x0205}},
{{EntranceType::Dungeon, KAKARIKO_VILLAGE, BOTTOM_OF_THE_WELL_ENTRYWAY, 0x0098},
{EntranceType::Dungeon, BOTTOM_OF_THE_WELL_ENTRYWAY, KAKARIKO_VILLAGE, 0x02A6}},
{{EntranceType::Dungeon, ZORAS_FOUNTAIN, ICE_CAVERN_ENTRYWAY, 0x0088},
{EntranceType::Dungeon, ICE_CAVERN_ENTRYWAY, ZORAS_FOUNTAIN, 0x03D4}},
{{EntranceType::Dungeon, GERUDO_FORTRESS, GERUDO_TRAINING_GROUNDS_ENTRYWAY, 0x0008},
{EntranceType::Dungeon, GERUDO_TRAINING_GROUNDS_ENTRYWAY, GERUDO_FORTRESS, 0x03A8}},
{{EntranceType::GanonDungeon, GANONS_CASTLE_LEDGE, GANONS_CASTLE_ENTRYWAY, 0x0467},
{EntranceType::GanonDungeon, GANONS_CASTLE_ENTRYWAY, CASTLE_GROUNDS_FROM_GANONS_CASTLE, 0x023D}},
{{EntranceType::GanonDungeon, GANONS_CASTLE_LEDGE, GANONS_CASTLE_ENTRYWAY, 0x0467},
{EntranceType::GanonDungeon, GANONS_CASTLE_ENTRYWAY, CASTLE_GROUNDS_FROM_GANONS_CASTLE, 0x023D}},
{{EntranceType::Interior, KOKIRI_FOREST, KF_MIDOS_HOUSE, 0x0433},
{EntranceType::Interior, KF_MIDOS_HOUSE, KOKIRI_FOREST, 0x0443}},
@ -947,21 +947,30 @@ int ShuffleAllEntrances() {
{{EntranceType::WarpSong, PRELUDE_OF_LIGHT_WARP, TEMPLE_OF_TIME, 0x05F4}, NO_RETURN_ENTRANCE},
{{EntranceType::ChildBoss, DEKU_TREE_BOSS_ENTRYWAY, DEKU_TREE_BOSS_ROOM, 0x040F},
{EntranceType::ChildBoss, DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY, 0x0252, 0x0457}},
{EntranceType::ChildBoss, DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY, 0x0252}},
{{EntranceType::ChildBoss, DODONGOS_CAVERN_BOSS_ENTRYWAY, DODONGOS_CAVERN_BOSS_ROOM, 0x040B},
{EntranceType::ChildBoss, DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5, 0x047A}},
{EntranceType::ChildBoss, DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5}},
{{EntranceType::ChildBoss, JABU_JABUS_BELLY_BOSS_ENTRYWAY, JABU_JABUS_BELLY_BOSS_ROOM, 0x0301},
{EntranceType::ChildBoss, JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407, 0x010E}},
{EntranceType::ChildBoss, JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407}},
{{EntranceType::AdultBoss, FOREST_TEMPLE_BOSS_ENTRYWAY, FOREST_TEMPLE_BOSS_ROOM, 0x000C},
{EntranceType::AdultBoss, FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E, 0x0608}},
{EntranceType::AdultBoss, FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E}},
{{EntranceType::AdultBoss, FIRE_TEMPLE_BOSS_ENTRYWAY, FIRE_TEMPLE_BOSS_ROOM, 0x0305},
{EntranceType::AdultBoss, FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175, 0x0564}},
{EntranceType::AdultBoss, FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175}},
{{EntranceType::AdultBoss, WATER_TEMPLE_BOSS_ENTRYWAY, WATER_TEMPLE_BOSS_ROOM, 0x0417},
{EntranceType::AdultBoss, WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423, 0x060C}},
{EntranceType::AdultBoss, WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423}},
{{EntranceType::AdultBoss, SPIRIT_TEMPLE_BOSS_ENTRYWAY, SPIRIT_TEMPLE_BOSS_ROOM, 0x008D},
{EntranceType::AdultBoss, SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5, 0x0610}},
{EntranceType::AdultBoss, SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5}},
{{EntranceType::AdultBoss, SHADOW_TEMPLE_BOSS_ENTRYWAY, SHADOW_TEMPLE_BOSS_ROOM, 0x0413},
{EntranceType::AdultBoss, SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2, 0x0580}},
{EntranceType::AdultBoss, SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2}},
{{EntranceType::BlueWarp, DEKU_TREE_BOSS_ROOM, KF_OUTSIDE_DEKU_TREE, 0x0457}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, DODONGOS_CAVERN_BOSS_ROOM, DEATH_MOUNTAIN_TRAIL, 0x047A}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, JABU_JABUS_BELLY_BOSS_ROOM, ZORAS_FOUNTAIN, 0x010E}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, FOREST_TEMPLE_BOSS_ROOM, SACRED_FOREST_MEADOW, 0x0608}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, FIRE_TEMPLE_BOSS_ROOM, DMC_CENTRAL_LOCAL, 0x0564}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, WATER_TEMPLE_BOSS_ROOM, LAKE_HYLIA, 0x060C}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, SPIRIT_TEMPLE_BOSS_ROOM, DESERT_COLOSSUS, 0x0610}, NO_RETURN_ENTRANCE},
{{EntranceType::BlueWarp, SHADOW_TEMPLE_BOSS_ROOM, GRAVEYARD_WARP_PAD_REGION, 0x0580}, NO_RETURN_ENTRANCE},
};
std::map<std::string, PriorityEntrance> priorityEntranceTable = {
@ -1011,6 +1020,11 @@ int ShuffleAllEntrances() {
FilterAndEraseFromPool(entrancePools[EntranceType::Boss], [](const Entrance* entrance){return entrance->GetParentRegionKey() == DEKU_TREE_BOSS_ENTRYWAY &&
entrance->GetConnectedRegionKey() == DEKU_TREE_BOSS_ROOM;});
}
if (Settings::DecoupleEntrances) {
for (Entrance* entrance : entrancePools[EntranceType::Boss]) {
entrancePools[EntranceType::BossReverse].push_back(entrance->GetReverse());
}
}
} else {
entrancePools[EntranceType::ChildBoss] = GetShuffleableEntrances(EntranceType::ChildBoss);
entrancePools[EntranceType::AdultBoss] = GetShuffleableEntrances(EntranceType::AdultBoss);
@ -1019,6 +1033,14 @@ int ShuffleAllEntrances() {
FilterAndEraseFromPool(entrancePools[EntranceType::ChildBoss], [](const Entrance* entrance){return entrance->GetParentRegionKey() == DEKU_TREE_BOSS_ENTRYWAY &&
entrance->GetConnectedRegionKey() == DEKU_TREE_BOSS_ROOM;});
}
if (Settings::DecoupleEntrances) {
for (Entrance* entrance : entrancePools[EntranceType::ChildBoss]) {
entrancePools[EntranceType::ChildBossReverse].push_back(entrance->GetReverse());
}
for (Entrance* entrance : entrancePools[EntranceType::AdultBoss]) {
entrancePools[EntranceType::AdultBossReverse].push_back(entrance->GetReverse());
}
}
}
}
@ -1082,10 +1104,13 @@ int ShuffleAllEntrances() {
SetShuffledEntrances(oneWayEntrancePools);
//combine entrance pools if mixing pools. Only continue if more than one pool is selected.
int totalMixedPools = (Settings::MixDungeons ? 1 : 0) + (Settings::MixOverworld ? 1 : 0) + (Settings::MixInteriors ? 1 : 0) + (Settings::MixGrottos ? 1 : 0);
int totalMixedPools = (Settings::MixDungeons ? 1 : 0) + (Settings::MixBosses ? 1 : 0) +
(Settings::MixOverworld ? 1 : 0) + (Settings::MixInteriors ? 1 : 0) +
(Settings::MixGrottos ? 1 : 0);
if (totalMixedPools < 2) {
Settings::MixedEntrancePools.SetSelectedIndex(OFF);
Settings::MixDungeons.SetSelectedIndex(OFF);
Settings::MixBosses.SetSelectedIndex(OFF);
Settings::MixOverworld.SetSelectedIndex(OFF);
Settings::MixInteriors.SetSelectedIndex(OFF);
Settings::MixGrottos.SetSelectedIndex(OFF);
@ -1099,6 +1124,12 @@ int ShuffleAllEntrances() {
poolsToMix.insert(EntranceType::DungeonReverse);
}
}
if (Settings::MixBosses) {
poolsToMix.insert(EntranceType::Boss);
if (Settings::DecoupleEntrances) {
poolsToMix.insert(EntranceType::BossReverse);
}
}
if (Settings::MixOverworld) {
poolsToMix.insert(EntranceType::Overworld);
}
@ -1232,6 +1263,87 @@ int ShuffleAllEntrances() {
}
}
// Determine blue warp targets
if (true /* Settings.BlueWarps.Is(BLUEWARPS_DUNGEON) */) { // RANDOTODO: add bluewarp shuffle
// If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance
std::map<std::string, Entrance*> bossExits = {
{ EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE)) },
{ EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL)) },
{ EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN)) },
{ EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW)) },
{ EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL)) },
{ EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA)) },
{ EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY)) },
{ EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY),
GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION)) },
};
// If a boss room is inside a dungeon entrance (or inside a dungeon which is inside a dungeon entrance), make the blue warp go to that dungeon's blue warp target
std::map<std::string, Entrance*> dungeonExits = {
{ EntranceNameByRegions(DEKU_TREE_ENTRYWAY, KF_OUTSIDE_DEKU_TREE),
GetEntrance(EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, KF_OUTSIDE_DEKU_TREE)) },
{ EntranceNameByRegions(DODONGOS_CAVERN_ENTRYWAY, DEATH_MOUNTAIN_TRAIL),
GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DEATH_MOUNTAIN_TRAIL)) },
{ EntranceNameByRegions(JABU_JABUS_BELLY_ENTRYWAY, ZORAS_FOUNTAIN),
GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, ZORAS_FOUNTAIN)) },
{ EntranceNameByRegions(FOREST_TEMPLE_ENTRYWAY, SACRED_FOREST_MEADOW),
GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, SACRED_FOREST_MEADOW)) },
{ EntranceNameByRegions(FIRE_TEMPLE_ENTRYWAY, DMC_CENTRAL_LOCAL),
GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, DMC_CENTRAL_LOCAL)) },
{ EntranceNameByRegions(WATER_TEMPLE_ENTRYWAY, LAKE_HYLIA),
GetEntrance(EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, LAKE_HYLIA)) },
{ EntranceNameByRegions(SPIRIT_TEMPLE_ENTRYWAY, DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY),
GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, DESERT_COLOSSUS)) },
{ EntranceNameByRegions(SHADOW_TEMPLE_ENTRYWAY, GRAVEYARD_WARP_PAD_REGION),
GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, GRAVEYARD_WARP_PAD_REGION)) },
};
// Pair <BlueWarp exit, BossRoom reverse exit>
std::vector<EntrancePair> bossRoomExitPairs = {
{ GetEntrance(EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, KF_OUTSIDE_DEKU_TREE)),
GetEntrance(EntranceNameByRegions(DEKU_TREE_BOSS_ROOM, DEKU_TREE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DEATH_MOUNTAIN_TRAIL)),
GetEntrance(EntranceNameByRegions(DODONGOS_CAVERN_BOSS_ROOM, DODONGOS_CAVERN_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, ZORAS_FOUNTAIN)),
GetEntrance(EntranceNameByRegions(JABU_JABUS_BELLY_BOSS_ROOM, JABU_JABUS_BELLY_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, SACRED_FOREST_MEADOW)),
GetEntrance(EntranceNameByRegions(FOREST_TEMPLE_BOSS_ROOM, FOREST_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, DMC_CENTRAL_LOCAL)),
GetEntrance(EntranceNameByRegions(FIRE_TEMPLE_BOSS_ROOM, FIRE_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, LAKE_HYLIA)),
GetEntrance(EntranceNameByRegions(WATER_TEMPLE_BOSS_ROOM, WATER_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, DESERT_COLOSSUS)),
GetEntrance(EntranceNameByRegions(SPIRIT_TEMPLE_BOSS_ROOM, SPIRIT_TEMPLE_BOSS_ENTRYWAY)) },
{ GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, GRAVEYARD_WARP_PAD_REGION)),
GetEntrance(EntranceNameByRegions(SHADOW_TEMPLE_BOSS_ROOM, SHADOW_TEMPLE_BOSS_ENTRYWAY)) },
};
for (EntrancePair pair : bossRoomExitPairs) {
Entrance* target = pair.second->GetReplacement() != nullptr ? pair.second->GetReplacement() : pair.second;
if (!Settings::DecoupleEntrances) {
while (bossExits.find(target->GetName()) != bossExits.end()) {
Entrance* next = bossExits.at(target->GetName());
target = next->GetReplacement() != nullptr ? next->GetReplacement() : next;
}
if (dungeonExits.find(target->GetName()) != dungeonExits.end()) {
target = dungeonExits.at(target->GetName());
}
}
pair.first->Connect(target->GetOriginalConnectedRegionKey());
pair.first->SetReplacement(target);
}
}
// Validate the world one last time to ensure all special conditions are still valid
if (!ValidateWorld(nullptr)) {
return ENTRANCE_SHUFFLE_FAILURE;
@ -1259,8 +1371,8 @@ void CreateEntranceOverrides() {
auto message = "Setting " + entrance->to_string() + "\n";
SPDLOG_DEBUG(message);
uint8_t type = (uint8_t)entrance->GetType();
int16_t originalIndex = entrance->GetIndex();
int16_t originalBlueWarp = entrance->GetBlueWarp();
int16_t replacementIndex = entrance->GetReplacement()->GetIndex();
int16_t destinationIndex = -1;
@ -1274,9 +1386,9 @@ void CreateEntranceOverrides() {
}
entranceOverrides.push_back({
.type = type,
.index = originalIndex,
.destination = destinationIndex,
.blueWarp = originalBlueWarp,
.override = replacementIndex,
.overrideDestination = replacementDestinationIndex,
});

View File

@ -18,12 +18,16 @@ enum class EntranceType {
OwlDrop,
Spawn,
WarpSong,
BlueWarp,
Dungeon,
GanonDungeon,
DungeonReverse,
Boss,
BossReverse,
ChildBoss,
ChildBossReverse,
AdultBoss,
AdultBossReverse,
Interior,
InteriorReverse,
SpecialInterior,
@ -40,6 +44,7 @@ public:
Entrance(uint32_t connectedRegion_, std::vector<ConditionFn> conditions_met_)
: connectedRegion(connectedRegion_) {
originalConnectedRegion = connectedRegion_;
conditions_met.resize(2);
for (size_t i = 0; i < conditions_met_.size(); i++) {
conditions_met[i] = conditions_met_[i];
@ -134,6 +139,10 @@ public:
return connectedRegion;
}
uint32_t GetOriginalConnectedRegionKey() const {
return originalConnectedRegion;
}
Area* GetConnectedRegion() const {
return AreaTable(connectedRegion);
}
@ -198,14 +207,6 @@ public:
index = newIndex;
}
int16_t GetBlueWarp() const {
return blueWarp;
}
void SetBlueWarp(int16_t newBlueWarp) {
blueWarp = newBlueWarp;
}
Entrance* GetAssumed() const {
return assumed;
}
@ -251,7 +252,7 @@ public:
AreaTable(ROOT)->AddExit(ROOT, connectedRegion, []{return true;});
Entrance* targetEntrance = AreaTable(ROOT)->GetExit(connectedRegion);
targetEntrance->SetReplacement(this);
targetEntrance->SetName(GetParentRegion()->regionName + " -> " + GetConnectedRegion()->regionName);
targetEntrance->SetName(AreaTable(ROOT)->regionName + " -> " + GetConnectedRegion()->regionName);
return targetEntrance;
}
@ -266,6 +267,7 @@ public:
private:
uint32_t parentRegion;
uint32_t connectedRegion;
uint32_t originalConnectedRegion;
std::vector<ConditionFn> conditions_met;
//Entrance Randomizer stuff
@ -275,7 +277,6 @@ private:
Entrance* assumed = nullptr;
Entrance* replacement = nullptr;
int16_t index = 0xFFFF;
int16_t blueWarp = 0;
bool shuffled = false;
bool primary = false;
bool addedToPool = false;
@ -285,6 +286,7 @@ private:
int ShuffleAllEntrances();
void CreateEntranceOverrides();
std::string EntranceNameByRegions(uint32_t parentRegion, uint32_t connectedRegion);
extern std::vector<std::list<Entrance*>> playthroughEntrances;
extern bool noRandomEntrances;

View File

@ -294,7 +294,12 @@ std::vector<uint32_t> GetAccessibleLocations(const std::vector<uint32_t>& allowe
}
// Add shuffled entrances to the entrance playthrough
if (mode == SearchMode::GeneratePlaythrough && exit.IsShuffled() && !exit.IsAddedToPool() && !noRandomEntrances) {
// Include bluewarps when unshuffled but dungeon or boss shuffle is on
if (mode == SearchMode::GeneratePlaythrough &&
(exit.IsShuffled() || (exit.GetType() == EntranceType::BlueWarp &&
(Settings::ShuffleDungeonEntrances.IsNot(SHUFFLEDUNGEONS_OFF) ||
Settings::ShuffleBossEntrances.IsNot(SHUFFLEBOSSES_OFF)))) &&
!exit.IsAddedToPool() && !noRandomEntrances) {
entranceSphere.push_back(&exit);
exit.AddToPool();
// Don't list a two-way coupled entrance from both directions

View File

@ -503,3 +503,16 @@ std::vector<Entrance*> GetShuffleableEntrances(EntranceType type, bool onlyPrima
}
return entrancesToShuffle;
}
// Get the specific entrance by name
Entrance* GetEntrance(const std::string name) {
for (uint32_t area : Areas::GetAllAreas()) {
for (auto& exit : AreaTable(area)->exits) {
if (exit.GetName() == name) {
return &exit;
}
}
}
return nullptr;
}

View File

@ -242,6 +242,7 @@ namespace Areas {
void AreaTable_Init();
Area* AreaTable(const uint32_t areaKey);
std::vector<Entrance*> GetShuffleableEntrances(EntranceType type, bool onlyPrimary = true);
Entrance* GetEntrance(const std::string name);
// Overworld
void AreaTable_Init_LostWoods();

View File

@ -267,5 +267,6 @@ void AreaTable_Init_DekuTree() {
{
// Exits
Entrance(DEKU_TREE_BOSS_ENTRYWAY, { [] { return true; } }),
Entrance(KF_OUTSIDE_DEKU_TREE, { [] { return DekuTreeClear; } }),
});
}

View File

@ -309,5 +309,6 @@ void AreaTable_Init_DodongosCavern() {
{
// Exits
Entrance(DODONGOS_CAVERN_BOSS_ENTRYWAY, { [] { return true; } }),
Entrance(DEATH_MOUNTAIN_TRAIL, { [] { return DodongosCavernClear; } }),
});
}

View File

@ -420,5 +420,6 @@ void AreaTable_Init_FireTemple() {
{
// Exits
Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(DMC_CENTRAL_LOCAL, { [] { return FireTempleClear; } }),
});
}

View File

@ -172,7 +172,7 @@ void AreaTable_Init_ForestTemple() {
Entrance(FOREST_TEMPLE_WEST_CORRIDOR, {[]{return true;}}),
Entrance(FOREST_TEMPLE_NW_OUTDOORS_UPPER, {[]{return CanUse(HOVER_BOOTS) || (LogicForestOutsideBackdoor && CanJumpslash && GoronBracelet);}}),
Entrance(FOREST_TEMPLE_NW_CORRIDOR_TWISTED, {[]{return IsAdult && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return IsAdult && (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
});
areaTable[FOREST_TEMPLE_NW_CORRIDOR_TWISTED] = Area("Forest Temple NW Corridor Twisted", "Forest Temple", FOREST_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, {
@ -433,5 +433,6 @@ void AreaTable_Init_ForestTemple() {
{
// Exits
Entrance(FOREST_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(SACRED_FOREST_MEADOW, { [] { return ForestTempleClear; } }),
});
}

View File

@ -179,17 +179,17 @@ void AreaTable_Init_JabuJabusBelly() {
//Locations
LocationAccess(JABU_JABUS_BELLY_MQ_SECOND_ROOM_LOWER_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_SECOND_ROOM_UPPER_CHEST, {[]{return (IsAdult && (CanUse(HOVER_BOOTS) || CanUse(HOOKSHOT))) || ChildCanAccess(JABU_JABUS_BELLY_MQ_BOSS_AREA);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_COMPASS_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_VINES_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_SWITCHES_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_COMPASS_CHEST, {[]{return (IsChild || CanDive || CanUse(IRON_BOOTS) || LogicJabuAlcoveJumpDive) && (CanUse(SLINGSHOT) || CanUse(BOW) || CanUse(HOOKSHOT) || HasBombchus || (LogicJabuMQRangJump && CanUse(BOOMERANG)));}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_VINES_CHEST, {[]{return CanUse(SLINGSHOT);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_SWITCHES_CHEST, {[]{return CanUse(SLINGSHOT);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BOOMERANG_ROOM_SMALL_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BOOMERANG_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_BOOMERANG_CHEST, {[]{return CanUse(KOKIRI_SWORD) || CanUse(MASTER_SWORD) || CanUse(BIGGORON_SWORD) || CanUse(MEGATON_HAMMER) || CanUse(SLINGSHOT) || CanUse(BOW) || CanUse(STICKS) || Bombs;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_GS_BOOMERANG_CHEST_ROOM, {[]{return CanPlay(SongOfTime) || (LogicJabuMQSoTGS && IsChild && CanUse(BOOMERANG));}}),
//Trick: CanPlay(SongOfTime) || (LogicJabuMQSoTGS && IsChild && CanUse(BOOMERANG))
}, {
//Exits
Entrance(JABU_JABUS_BELLY_MQ_BEGINNING, {[]{return true;}}),
Entrance(JABU_JABUS_BELLY_MQ_DEPTHS, {[]{return HasExplosives && IsChild && CanUse(BOOMERANG);}}),
Entrance(JABU_JABUS_BELLY_MQ_DEPTHS, {[]{return HasExplosives && CanUse(SLINGSHOT) && CanUse(BOOMERANG);}}),
});
areaTable[JABU_JABUS_BELLY_MQ_DEPTHS] = Area("Jabu Jabus Belly MQ Depths", "Jabu Jabus Belly", JABU_JABUS_BELLY, NO_DAY_NIGHT_CYCLE, {}, {
@ -209,8 +209,8 @@ void AreaTable_Init_JabuJabusBelly() {
}, {
//Locations
LocationAccess(JABU_JABUS_BELLY_MQ_COW, {[]{return CanPlay(EponasSong);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, {[]{return true;}}),
LocationAccess(JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, {[]{return CanUse(SLINGSHOT);}}),
LocationAccess(JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, {[]{return CanUse(BOOMERANG) || (LogicJabuNearBossRanged && CanUse(HOOKSHOT));}}),
}, {
//Exits
Entrance(JABU_JABUS_BELLY_MQ_MAIN, {[]{return true;}}),
@ -245,5 +245,6 @@ void AreaTable_Init_JabuJabusBelly() {
{
// Exits
Entrance(JABU_JABUS_BELLY_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(ZORAS_FOUNTAIN, { [] { return JabuJabusBellyClear; } }),
});
}

View File

@ -209,5 +209,6 @@ void AreaTable_Init_ShadowTemple() {
{
// Exits
Entrance(SHADOW_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(GRAVEYARD_WARP_PAD_REGION, { [] { return ShadowTempleClear; } }),
});
}

View File

@ -271,5 +271,6 @@ void AreaTable_Init_SpiritTemple() {
{
// Exits
Entrance(SPIRIT_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(DESERT_COLOSSUS, { [] { return SpiritTempleClear; } }),
});
}

View File

@ -331,5 +331,6 @@ void AreaTable_Init_WaterTemple() {
{
// Exits
Entrance(WATER_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }),
Entrance(LAKE_HYLIA, { [] { return WaterTempleClear; } }),
});
}

View File

@ -536,7 +536,7 @@ namespace Logic {
Fish = HasBottle && FishAccess;
Fairy = HasBottle && FairyAccess;
FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20);
FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20) && (BombBag || BombchusInLogic);
CanPlayBowling = (BombchusInLogic && FoundBombchus) || (!BombchusInLogic && BombBag);
HasBombchus = (BuyBombchus10 || BuyBombchus20 || (AmmoDrops.Is(AMMODROPS_BOMBCHU) && FoundBombchus));

View File

@ -94,6 +94,7 @@ namespace Settings {
Option ShuffleOverworldSpawns = Option::Bool("Overworld Spawns", {"Off", "On"});
Option MixedEntrancePools = Option::Bool("Mixed Entrance Pools", {"Off", "On"});
Option MixDungeons = Option::Bool("Mix Dungeons", {"Off", "On"});
Option MixBosses = Option::Bool("Mix Bosses", {"Off", "On"});
Option MixOverworld = Option::Bool("Mix Overworld", {"Off", "On"});
Option MixInteriors = Option::Bool("Mix Interiors", {"Off", "On"});
Option MixGrottos = Option::Bool("Mix Grottos", {"Off", "On"});
@ -135,6 +136,7 @@ namespace Settings {
&ShuffleOverworldSpawns,
&MixedEntrancePools,
&MixDungeons,
&MixBosses,
&MixOverworld,
&MixInteriors,
&MixGrottos,
@ -1298,6 +1300,7 @@ namespace Settings {
ctx.shuffleOverworldSpawns = (ShuffleOverworldSpawns) ? 1 : 0;
ctx.mixedEntrancePools = (MixedEntrancePools) ? 1 : 0;
ctx.mixDungeons = (MixDungeons) ? 1 : 0;
ctx.mixBosses = (MixBosses) ? 1 : 0;
ctx.mixOverworld = (MixOverworld) ? 1 : 0;
ctx.mixInteriors = (MixInteriors) ? 1 : 0;
ctx.mixGrottos = (MixGrottos) ? 1 : 0;
@ -1891,6 +1894,13 @@ namespace Settings {
MixDungeons.SetSelectedIndex(OFF);
}
if (ShuffleBossEntrances.Is(SHUFFLEBOSSES_FULL)) {
MixBosses.Unhide();
} else {
MixBosses.Hide();
MixBosses.SetSelectedIndex(OFF);
}
if (ShuffleOverworldEntrances) {
MixOverworld.Unhide();
} else {
@ -1914,6 +1924,8 @@ namespace Settings {
} else {
MixDungeons.Hide();
MixDungeons.SetSelectedIndex(OFF);
MixBosses.Hide();
MixBosses.SetSelectedIndex(OFF);
MixOverworld.Hide();
MixOverworld.SetSelectedIndex(OFF);
MixInteriors.Hide();
@ -2190,6 +2202,7 @@ namespace Settings {
if (!MixedEntrancePools) {
MixDungeons.SetSelectedIndex(OFF);
MixBosses.SetSelectedIndex(OFF);
MixOverworld.SetSelectedIndex(OFF);
MixInteriors.SetSelectedIndex(OFF);
MixGrottos.SetSelectedIndex(OFF);
@ -2344,6 +2357,7 @@ namespace Settings {
ShuffleOverworldSpawns.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_OVERWORLD_SPAWNS]);
MixedEntrancePools.SetSelectedIndex(cvarSettings[RSK_MIXED_ENTRANCE_POOLS]);
MixDungeons.SetSelectedIndex(cvarSettings[RSK_MIX_DUNGEON_ENTRANCES]);
MixBosses.SetSelectedIndex(cvarSettings[RSK_MIX_BOSS_ENTRANCES]);
MixOverworld.SetSelectedIndex(cvarSettings[RSK_MIX_OVERWORLD_ENTRANCES]);
MixInteriors.SetSelectedIndex(cvarSettings[RSK_MIX_INTERIOR_ENTRANCES]);
MixGrottos.SetSelectedIndex(cvarSettings[RSK_MIX_GROTTO_ENTRANCES]);

View File

@ -397,6 +397,7 @@ typedef struct {
uint8_t shuffleOverworldSpawns;
uint8_t mixedEntrancePools;
uint8_t mixDungeons;
uint8_t mixBosses;
uint8_t mixOverworld;
uint8_t mixInteriors;
uint8_t mixGrottos;
@ -784,6 +785,7 @@ void UpdateSettings(std::unordered_map<RandomizerSettingKey, uint8_t> cvarSettin
extern Option ShuffleOverworldSpawns;
extern Option MixedEntrancePools;
extern Option MixDungeons;
extern Option MixBosses;
extern Option MixOverworld;
extern Option MixInteriors;
extern Option MixGrottos;

View File

@ -307,35 +307,36 @@ static void WriteLocation(
static void WriteShuffledEntrance(std::string sphereString, Entrance* entrance) {
int16_t originalIndex = entrance->GetIndex();
int16_t destinationIndex = -1;
int16_t originalBlueWarp = entrance->GetBlueWarp();
int16_t replacementBlueWarp = -1;
int16_t replacementIndex = entrance->GetReplacement()->GetIndex();
int16_t replacementDestinationIndex = -1;
std::string name = entrance->GetName();
std::string text = entrance->GetConnectedRegion()->regionName + " from " + entrance->GetReplacement()->GetParentRegion()->regionName;
if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) {
// Track the reverse destination, useful for savewarp handling
if (entrance->GetReverse() != nullptr) {
destinationIndex = entrance->GetReverse()->GetIndex();
replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex();
replacementBlueWarp = entrance->GetReplacement()->GetReverse()->GetBlueWarp();
// When decouple is off we track the replacement's reverse destination, useful for recording visited entrances
if (!entrance->IsDecoupled()) {
replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex();
}
}
json entranceJson = json::object({
{"type", entrance->GetType()},
{"index", originalIndex},
{"destination", destinationIndex},
{"blueWarp", originalBlueWarp},
{"override", replacementIndex},
{"overrideDestination", replacementDestinationIndex},
});
jsonData["entrances"].push_back(entranceJson);
// When decoupled entrances is off, handle saving reverse entrances with blue warps
// When decoupled entrances is off, handle saving reverse entrances
if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) {
json reverseEntranceJson = json::object({
{"type", entrance->GetReverse()->GetType()},
{"index", replacementDestinationIndex},
{"destination", replacementIndex},
{"blueWarp", replacementBlueWarp},
{"override", destinationIndex},
{"overrideDestination", originalIndex},
});

View File

@ -315,6 +315,7 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "World Settings:Overworld Spawns", RSK_SHUFFLE_OVERWORLD_SPAWNS },
{ "World Settings:Mixed Entrance Pools", RSK_MIXED_ENTRANCE_POOLS },
{ "World Settings:Mix Dungeons", RSK_MIX_DUNGEON_ENTRANCES },
{ "World Settings:Mix Bosses", RSK_MIX_BOSS_ENTRANCES },
{ "World Settings:Mix Overworld", RSK_MIX_OVERWORLD_ENTRANCES },
{ "World Settings:Mix Interiors", RSK_MIX_INTERIOR_ENTRANCES },
{ "World Settings:Mix Grottos", RSK_MIX_GROTTO_ENTRANCES },
@ -548,13 +549,18 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) {
"\x12\x38\x82" "Aufgeben! Ich verkaufe dir einen&%g{{item}}%w&für %r{{price}} Rubine%w!\x07\x10\xA3",
"\x12\x38\x82" "J'abandonne! Tu veux bien m'acheter&un %g{{item}}%w?&Ça fera %r{{price}} Rubis%w!\x07\x10\xA3"));
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN,
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_10,
CustomMessage("I tried to be a %rmagic bean%w salesman,&but it turns out my marketing skills&weren't worth "
"beans!^Anyway, want to buy my&%gmysterious item%w for 60 Rupees?\x1B&%gYes&No%w",
"Möchten Sie einen geheimnisvollen&Gegenstand für 60 Rubine?\x1B&%gJa&Nein%w",
"J'ai essayé d'être un vendeur de&%rharicots magiques%w, mais j'étais&mauvais au niveau du marketing et&ça "
"me courait sur le haricot...^Enfin bref, ça te dirait de m'acheter un&"
"%gobjet mystérieux%w pour 60 Rubis?\x1B&%gOui&Non%w"));
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_100,
CustomMessage("I never thought I'd say this, but I'm &selling the last %rMagic Bean%w. %r99%w Rupees...\x1B&%gYes&No%w",
"\x1B&%gJa&Nein%w",
"Je te vends mon dernier %rHaricot&magique%w pour %r99 Rubis%w.\x1B&%gAcheter&Ne pas acheter%w"));
//Setup for merchant text boxes
@ -566,7 +572,7 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) {
"Wie wäre es mit %g&{{item}}%w für %y200 Rubine?%w\x1B&%gJa!&Nein!%w",
"Veux-tu acheter %g&{{item}}%w pour %y200 rubis?%w\x1B&%gOui&Non&w"));
//Granny Shopy
//Granny Shop
//RANDOTODO: Implement obscure/ambiguous hints
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_GRANNYS_SHOP,
@ -899,6 +905,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
case RSK_SHUFFLE_OVERWORLD_SPAWNS:
case RSK_MIXED_ENTRANCE_POOLS:
case RSK_MIX_DUNGEON_ENTRANCES:
case RSK_MIX_BOSS_ENTRANCES:
case RSK_MIX_OVERWORLD_ENTRANCES:
case RSK_MIX_INTERIOR_ENTRANCES:
case RSK_MIX_GROTTO_ENTRANCES:
@ -1562,9 +1569,9 @@ void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent)
// set all the entrances to be 0 to indicate an unshuffled entrance
for (auto &entranceOveride : gSaveContext.entranceOverrides) {
entranceOveride.type = 0;
entranceOveride.index = 0;
entranceOveride.destination = 0;
entranceOveride.blueWarp = 0;
entranceOveride.override = 0;
entranceOveride.overrideDestination = 0;
}
@ -1579,12 +1586,12 @@ void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent)
json entranceJson = *it;
for (auto entranceIt = entranceJson.begin(); entranceIt != entranceJson.end(); ++entranceIt) {
if (entranceIt.key() == "index") {
if (entranceIt.key() == "type") {
gSaveContext.entranceOverrides[i].type = entranceIt.value();
} else if (entranceIt.key() == "index") {
gSaveContext.entranceOverrides[i].index = entranceIt.value();
} else if (entranceIt.key() == "destination") {
gSaveContext.entranceOverrides[i].destination = entranceIt.value();
} else if (entranceIt.key() == "blueWarp") {
gSaveContext.entranceOverrides[i].blueWarp = entranceIt.value();
} else if (entranceIt.key() == "override") {
gSaveContext.entranceOverrides[i].override = entranceIt.value();
} else if (entranceIt.key() == "overrideDestination") {
@ -3008,6 +3015,7 @@ void GenerateRandomizerImgui(std::string seed = "") {
cvarSettings[RSK_SHUFFLE_OVERWORLD_SPAWNS] = CVarGetInteger("gRandomizeShuffleOverworldSpawns", RO_GENERIC_OFF);
cvarSettings[RSK_MIXED_ENTRANCE_POOLS] = CVarGetInteger("gRandomizeMixedEntrances", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_DUNGEON_ENTRANCES] = CVarGetInteger("gRandomizeMixDungeons", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_BOSS_ENTRANCES] = CVarGetInteger("gRandomizeMixBosses", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_OVERWORLD_ENTRANCES] = CVarGetInteger("gRandomizeMixOverworld", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_INTERIOR_ENTRANCES] = CVarGetInteger("gRandomizeMixInteriors", RO_GENERIC_OFF);
cvarSettings[RSK_MIX_GROTTO_ENTRANCES] = CVarGetInteger("gRandomizeMixGrottos", RO_GENERIC_OFF);
@ -3706,7 +3714,8 @@ void RandomizerSettingsWindow::DrawElement() {
// Mixed Entrance Pools
UIWidgets::EnhancementCheckbox("Mixed Entrance Pools", "gRandomizeMixedEntrances");
UIWidgets::InsertHelpHoverText(
"Shuffle entrances into a mixed pool instead of separate ones.\n"
"Shuffle entrances into a mixed pool instead of separate ones. Has no affect on pools whose "
"entrances aren't shuffled, and \"Shuffle Boss Entrances\" must be set to \"Full\" to include them.\n"
"\n"
"For example, enabling the settings to shuffle grotto, dungeon, and overworld entrances and "
"selecting grotto and dungeon entrances here will allow a dungeon to be inside a grotto or "
@ -3720,6 +3729,13 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::EnhancementCheckbox("Mix Dungeons", "gRandomizeMixDungeons");
UIWidgets::InsertHelpHoverText("Dungeon entrances will be part of the mixed pool");
}
if (CVarGetInteger("gRandomizeShuffleBossEntrances", RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) ==
RO_BOSS_ROOM_ENTRANCE_SHUFFLE_FULL) {
UIWidgets::Spacer(0);
ImGui::SetCursorPosX(20);
UIWidgets::EnhancementCheckbox("Mix Bosses", "gRandomizeMixBosses");
UIWidgets::InsertHelpHoverText("Boss entrances will be part of the mixed pool");
}
if (CVarGetInteger("gRandomizeShuffleOverworldEntrances", RO_GENERIC_OFF)) {
UIWidgets::Spacer(0);
ImGui::SetCursorPosX(20);
@ -4221,7 +4237,7 @@ void RandomizerSettingsWindow::DrawElement() {
break;
case RO_LACS_GREG_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount",
"gRandomizeLacsStoneCount", 1, 4, "", 4, true, true, false);
"gRandomizeLacsStoneCount", 1, 4, "", 3, true, true, false);
break;
case RO_LACS_WILDCARD_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount",
@ -4250,7 +4266,7 @@ void RandomizerSettingsWindow::DrawElement() {
break;
case RO_LACS_GREG_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount",
"gRandomizeLacsMedallionCount", 1, 7, "", 7, true, true, false);
"gRandomizeLacsMedallionCount", 1, 7, "", 6, true, true, false);
break;
case RO_LACS_WILDCARD_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount",
@ -4279,7 +4295,7 @@ void RandomizerSettingsWindow::DrawElement() {
break;
case RO_LACS_GREG_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount",
"gRandomizeLacsRewardCount", 1, 10, "", 10, true, true, false);
"gRandomizeLacsRewardCount", 1, 10, "", 9, true, true, false);
break;
case RO_LACS_WILDCARD_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount",
@ -4308,7 +4324,7 @@ void RandomizerSettingsWindow::DrawElement() {
break;
case RO_LACS_GREG_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount",
"gRandomizeLacsDungeonCount", 1, 9, "", 9, true, true, false);
"gRandomizeLacsDungeonCount", 1, 9, "", 8, true, true, false);
break;
case RO_LACS_WILDCARD_REWARD:
UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount",
@ -4707,7 +4723,11 @@ void RandomizerSettingsWindow::DrawElement() {
excludedLocationString += std::to_string(excludedLocationIt);
excludedLocationString += ",";
}
CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
if (excludedLocationString == "") {
CVarClear("gRandomizeExcludedLocations");
} else {
CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
ImGui::SameLine();
@ -4893,7 +4913,7 @@ void RandomizerSettingsWindow::DrawElement() {
enabledTrickString += std::to_string(enabledTrickIt);
enabledTrickString += ",";
}
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
CVarClear("gRandomizeEnabledTricks");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
ImGui::SameLine();
@ -5097,7 +5117,7 @@ void RandomizerSettingsWindow::DrawElement() {
enabledTrickString += std::to_string(enabledTrickIt);
enabledTrickString += ",";
}
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
CVarClear("gRandomizeEnabledTricks");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
@ -5135,7 +5155,11 @@ void RandomizerSettingsWindow::DrawElement() {
enabledTrickString += std::to_string(enabledTrickIt);
enabledTrickString += ",";
}
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
if (enabledTrickString == "") {
CVarClear("gRandomizeEnabledTricks");
} else {
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
DrawTagChips(*rtObject.rtTags);
@ -5374,7 +5398,7 @@ CustomMessage Randomizer::GetSheikMessage(s16 scene, u16 originalTextId) {
}
CustomMessage Randomizer::GetSariaMessage(u16 originalTextId) {
if (originalTextId == TEXT_SARIA_SFM || (originalTextId >= TEXT_SARIAS_SONG_TEXT_START && originalTextId <= TEXT_SARIAS_SONG_TEXT_END)) {
if (originalTextId == TEXT_SARIA_SFM || (originalTextId >= TEXT_SARIAS_SONG_FACE_TO_FACE && originalTextId <= TEXT_SARIAS_SONG_CHANNELING_POWER)) {
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_SARIAS_SONG_FACE_TO_FACE);
CustomMessage messageEntry2 = messageEntry;
std::string code = originalTextId == TEXT_SARIA_SFM ? "" : "\x0B";

View File

@ -1467,6 +1467,7 @@ typedef enum {
RSK_SHUFFLE_OVERWORLD_SPAWNS,
RSK_MIXED_ENTRANCE_POOLS,
RSK_MIX_DUNGEON_ENTRANCES,
RSK_MIX_BOSS_ENTRANCES,
RSK_MIX_OVERWORLD_ENTRANCES,
RSK_MIX_INTERIOR_ENTRANCES,
RSK_MIX_GROTTO_ENTRANCES,

View File

@ -762,7 +762,6 @@ void InitTrackerData(bool isDebug) {
}
}
UpdateAllOrdering();
UpdateInventoryChecks();
}
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
@ -1337,7 +1336,7 @@ void DrawLocation(RandomizerCheckObject rcObj) {
CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default);
} else if (status == RCSHOW_SCUMMED) {
if (!showHidden && CVarGetInteger("gCheckTrackerKnownHide", 0)) {
if (!showHidden && CVarGetInteger("gCheckTrackerScummedHide", 0)) {
return;
}
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) :

View File

@ -39,17 +39,12 @@ s16 dynamicExitList[] = {
// Owl Flights : 0x492064 and 0x492080
static s16 entranceOverrideTable[ENTRANCE_TABLE_SIZE] = {0};
// Boss scenes (normalize boss scene range to 0 on lookup) to the replaced dungeon scene it is connected to
static s16 dungeonBossSceneOverrides[SHUFFLEABLE_BOSS_COUNT] = {0};
// Boss scenes (normalize boss scene range to 0 on lookup) mapped to save/death warp entrance
static s16 bossSceneSaveDeathWarps[SHUFFLEABLE_BOSS_COUNT] = {0};
static ActorEntry modifiedLinkActorEntry = {0};
EntranceInfo originalEntranceTable[ENTRANCE_TABLE_SIZE] = {0};
typedef struct {
s16 blueWarp;
s16 destination;
} BlueWarpReplacement;
typedef struct {
s16 entryway;
s16 exit;
@ -61,40 +56,25 @@ typedef struct {
} DungeonEntranceInfo;
static DungeonEntranceInfo dungeons[] = {
//entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene
{ DEKU_TREE_ENTRANCE, ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, ENTR_KOKIRI_FOREST_11, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS },
{ DODONGOS_CAVERN_ENTRANCE, ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, ENTR_DEATH_MOUNTAIN_TRAIL_5, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS },
{ JABU_JABUS_BELLY_ENTRANCE, ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, ENTR_ZORAS_FOUNTAIN_0, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS },
{ FOREST_TEMPLE_ENTRANCE, ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, ENTR_SACRED_FOREST_MEADOW_3, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS },
{ FIRE_TEMPLE_ENTRANCE, ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, ENTR_DEATH_MOUNTAIN_CRATER_5, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS },
{ WATER_TEMPLE_ENTRANCE, ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, ENTR_LAKE_HYLIA_9, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS },
{ SPIRIT_TEMPLE_ENTRANCE, ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, ENTR_DESERT_COLOSSUS_8, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS },
{ SHADOW_TEMPLE_ENTRANCE, ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, ENTR_GRAVEYARD_8, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS },
//entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene
{ ENTR_DEKU_TREE_0, ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, ENTR_KOKIRI_FOREST_11, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS },
{ ENTR_DODONGOS_CAVERN_0, ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, ENTR_DEATH_MOUNTAIN_TRAIL_5, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS },
{ ENTR_JABU_JABU_0, ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, ENTR_ZORAS_FOUNTAIN_0, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS },
{ ENTR_FOREST_TEMPLE_0, ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, ENTR_SACRED_FOREST_MEADOW_3, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS },
{ ENTR_FIRE_TEMPLE_0, ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, ENTR_DEATH_MOUNTAIN_CRATER_5, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS },
{ ENTR_WATER_TEMPLE_0, ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, ENTR_LAKE_HYLIA_9, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS },
{ ENTR_SPIRIT_TEMPLE_0, ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, ENTR_DESERT_COLOSSUS_8, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS },
{ ENTR_SHADOW_TEMPLE_0, ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, ENTR_GRAVEYARD_8, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS },
};
//These variables store the new entrance indices for dungeons so that
//savewarping and game overs respawn players at the proper entrance.
//By default, these will be their vanilla values.
static s16 newDekuTreeEntrance = DEKU_TREE_ENTRANCE;
static s16 newDodongosCavernEntrance = DODONGOS_CAVERN_ENTRANCE;
static s16 newJabuJabusBellyEntrance = JABU_JABUS_BELLY_ENTRANCE;
static s16 newForestTempleEntrance = FOREST_TEMPLE_ENTRANCE;
static s16 newFireTempleEntrance = FIRE_TEMPLE_ENTRANCE;
static s16 newWaterTempleEntrance = WATER_TEMPLE_ENTRANCE;
static s16 newSpiritTempleEntrance = SPIRIT_TEMPLE_ENTRANCE;
static s16 newShadowTempleEntrance = SHADOW_TEMPLE_ENTRANCE;
static s16 newBottomOfTheWellEntrance = BOTTOM_OF_THE_WELL_ENTRANCE;
static s16 newGerudoTrainingGroundsEntrance = GERUDO_TRAINING_GROUNDS_ENTRANCE;
static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
static s8 hasCopiedEntranceTable = 0;
static s8 hasModifiedEntranceTable = 0;
void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance);
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
&& entranceOverride->override == 0 && entranceOverride->overrideDestination == 0;
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->override == 0 &&
entranceOverride->overrideDestination == 0;
}
static void Entrance_SeparateOGCFairyFountainExit(void) {
@ -113,6 +93,28 @@ static void Entrance_SeparateAdultSpawnAndPrelude() {
}
}
// Fix Adult dungeon blue warps as Child by assigning the child values for the warp pads
static void Entrance_ReplaceChildTempleWarps() {
if (Randomizer_GetSettingValue(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF ||
Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) {
// Forest Temple
gEntranceTable[ENTR_SACRED_FOREST_MEADOW_3] = gEntranceTable[ENTR_SACRED_FOREST_MEADOW_2];
gEntranceTable[ENTR_SACRED_FOREST_MEADOW_3_1] = gEntranceTable[ENTR_SACRED_FOREST_MEADOW_2_1];
// Fire Temple
gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_5] = gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_4];
gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_5_1] = gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_4_1];
// Water Temple
gEntranceTable[ENTR_LAKE_HYLIA_9] = gEntranceTable[ENTR_LAKE_HYLIA_8];
gEntranceTable[ENTR_LAKE_HYLIA_9_1] = gEntranceTable[ENTR_LAKE_HYLIA_8_1];
// Shadow Temple
gEntranceTable[ENTR_GRAVEYARD_8] = gEntranceTable[ENTR_GRAVEYARD_7];
gEntranceTable[ENTR_GRAVEYARD_8_1] = gEntranceTable[ENTR_GRAVEYARD_7_1];
// Spirit Temple
gEntranceTable[ENTR_DESERT_COLOSSUS_8] = gEntranceTable[ENTR_DESERT_COLOSSUS_5];
gEntranceTable[ENTR_DESERT_COLOSSUS_8_1] = gEntranceTable[ENTR_DESERT_COLOSSUS_5_1];
}
}
void Entrance_CopyOriginalEntranceTable(void) {
if (!hasCopiedEntranceTable) {
memcpy(originalEntranceTable, gEntranceTable, sizeof(EntranceInfo) * ENTRANCE_TABLE_SIZE);
@ -130,9 +132,6 @@ void Entrance_ResetEntranceTable(void) {
void Entrance_Init(void) {
s32 index;
size_t blueWarpRemapIdx = 0;
BlueWarpReplacement bluewarps[SHUFFLEABLE_BOSS_COUNT] = {0};
Entrance_CopyOriginalEntranceTable();
// Skip Child Stealth if given by settings
@ -149,6 +148,7 @@ void Entrance_Init(void) {
Entrance_SeparateOGCFairyFountainExit();
Entrance_SeparateAdultSpawnAndPrelude();
Entrance_ReplaceChildTempleWarps();
// Initialize the entrance override table with each index leading to itself. An
// index referring to itself means that the entrance is not currently shuffled.
@ -156,9 +156,9 @@ void Entrance_Init(void) {
entranceOverrideTable[i] = i;
}
// Initialize all boss rooms connected to their vanilla dungeon
// Initialize all boss room save/death warps with their vanilla dungeon entryway
for (s16 i = 1; i < SHUFFLEABLE_BOSS_COUNT; i++) {
dungeonBossSceneOverrides[i] = i;
bossSceneSaveDeathWarps[i] = dungeons[i].entryway;
}
// Initialize the grotto exit and load lists
@ -172,9 +172,30 @@ void Entrance_Init(void) {
}
s16 originalIndex = gSaveContext.entranceOverrides[i].index;
s16 blueWarpIndex = gSaveContext.entranceOverrides[i].blueWarp;
s16 originalDestination = gSaveContext.entranceOverrides[i].destination;
s16 overrideIndex = gSaveContext.entranceOverrides[i].override;
int16_t bossScene = -1;
int16_t saveWarpEntrance = originalDestination; // Default save warp to the original return entrance
// Search for boss room overrides and look for the matching save/death warp value to use
// If the boss room is in a dungeon, use the dungeons entryway as the save warp
// Otherwise use the "exit" value for the entrance that lead to the boss room
for (int j = 0; j <= SHUFFLEABLE_BOSS_COUNT; j++) {
if (overrideIndex == dungeons[j].bossDoor) {
bossScene = dungeons[j].bossScene;
}
if (index == dungeons[j].bossDoor) {
saveWarpEntrance = dungeons[j].entryway;
}
}
// Found a boss scene and a valid save/death warp value
if (bossScene != -1 && saveWarpEntrance != -1) {
bossSceneSaveDeathWarps[bossScene - SCENE_DEKU_TREE_BOSS] = saveWarpEntrance;
}
//Overwrite grotto related indices
if (originalIndex >= ENTRANCE_RANDO_GROTTO_EXIT_START) {
Grotto_SetExitOverride(originalIndex, overrideIndex);
@ -189,36 +210,6 @@ void Entrance_Init(void) {
// Overwrite the indices which we want to shuffle, leaving the rest as they are
entranceOverrideTable[originalIndex] = overrideIndex;
if (blueWarpIndex != 0) {
// When boss shuffle is enabled, we need to know what dungeon the boss room is connected to for
// death/save warping, and for the blue warp
if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) {
s16 bossScene = -1;
s16 replacedDungeonScene = -1;
s16 replacedDungeonExit = -1;
// Search for the boss scene and replaced blue warp exits
for (s16 j = 0; j <= SHUFFLEABLE_BOSS_COUNT; j++) {
if (blueWarpIndex == dungeons[j].blueWarp) {
bossScene = dungeons[j].bossScene;
}
if (overrideIndex == dungeons[j].bossDoorReverse) {
replacedDungeonScene = dungeons[j].scene;
replacedDungeonExit = dungeons[j].exit;
}
}
// assign the boss scene override
if (bossScene != -1 && replacedDungeonScene != -1 && replacedDungeonExit != -1) {
dungeonBossSceneOverrides[bossScene - SCENE_DEKU_TREE_BOSS] = replacedDungeonScene;
bluewarps[blueWarpRemapIdx].blueWarp = blueWarpIndex;
bluewarps[blueWarpRemapIdx].destination = replacedDungeonExit;
blueWarpRemapIdx++;
}
} else {
entranceOverrideTable[blueWarpIndex] = overrideIndex;
}
}
//Override both land and water entrances for Hyrule Field -> ZR Front and vice versa
if (originalIndex == ENTR_ZORAS_RIVER_0) { //Hyrule Field -> ZR Front land entrance
entranceOverrideTable[ENTR_ZORAS_RIVER_3] = overrideIndex;
@ -227,14 +218,6 @@ void Entrance_Init(void) {
}
}
// If we have remapped blue warps from boss shuffle, handle setting those and grabbing the override for
// the replaced dungeons exit in the event that dungeon shuffle is also turned on
for (size_t i = 0; i < ARRAY_COUNT(bluewarps); i++) {
if (bluewarps[i].blueWarp != 0 && bluewarps[i].destination != 0) {
entranceOverrideTable[bluewarps[i].blueWarp] = Entrance_GetOverride(bluewarps[i].destination);
}
}
// Stop playing background music during shuffled entrance transitions
// so that we don't get duplicated or overlapping music tracks
if (Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) {
@ -328,40 +311,39 @@ void Entrance_SetGameOverEntrance(void) {
s16 scene = gPlayState->sceneNum;
// When in a boss room and boss shuffle is on, get the connected dungeon's original boss room entrance
// then run the normal game over overrides on it
// When in a boss room and boss shuffle is on, use the boss scene to find the death warp entrance
if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF &&
scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_SHADOW_TEMPLE_BOSS) {
// Normalize boss scene range to 0 on lookup
scene = dungeonBossSceneOverrides[scene - SCENE_DEKU_TREE_BOSS];
gSaveContext.entranceIndex = dungeons[scene].bossDoor;
// Normalize boss scene range to 0 on lookup and handle for grotto entrances
gSaveContext.entranceIndex = Grotto_OverrideSpecialEntrance(bossSceneSaveDeathWarps[scene - SCENE_DEKU_TREE_BOSS]);
return;
}
//Set the current entrance depending on which entrance the player last came through
switch (gSaveContext.entranceIndex) {
case ENTR_DEKU_TREE_BOSS_0 : //Deku Tree Boss Room
gSaveContext.entranceIndex = newDekuTreeEntrance;
gSaveContext.entranceIndex = ENTR_DEKU_TREE_0;
return;
case ENTR_DODONGOS_CAVERN_BOSS_0 : //Dodongos Cavern Boss Room
gSaveContext.entranceIndex = newDodongosCavernEntrance;
gSaveContext.entranceIndex = ENTR_DODONGOS_CAVERN_0;
return;
case ENTR_JABU_JABU_BOSS_0 : //Jabu Jabus Belly Boss Room
gSaveContext.entranceIndex = newJabuJabusBellyEntrance;
gSaveContext.entranceIndex = ENTR_JABU_JABU_0;
return;
case ENTR_FOREST_TEMPLE_BOSS_0 : //Forest Temple Boss Room
gSaveContext.entranceIndex = newForestTempleEntrance;
gSaveContext.entranceIndex = ENTR_FOREST_TEMPLE_0;
return;
case ENTR_FIRE_TEMPLE_BOSS_0 : //Fire Temple Boss Room
gSaveContext.entranceIndex = newFireTempleEntrance;
gSaveContext.entranceIndex = ENTR_FIRE_TEMPLE_0;
return;
case ENTR_WATER_TEMPLE_BOSS_0 : //Water Temple Boss Room
gSaveContext.entranceIndex = newWaterTempleEntrance;
gSaveContext.entranceIndex = ENTR_WATER_TEMPLE_0;
return;
case ENTR_SPIRIT_TEMPLE_BOSS_0 : //Spirit Temple Boss Room
gSaveContext.entranceIndex = newSpiritTempleEntrance;
gSaveContext.entranceIndex = ENTR_SPIRIT_TEMPLE_0;
return;
case ENTR_SHADOW_TEMPLE_BOSS_0 : //Shadow Temple Boss Room
gSaveContext.entranceIndex = newShadowTempleEntrance;
gSaveContext.entranceIndex = ENTR_SHADOW_TEMPLE_0;
return;
case ENTR_GANONDORF_BOSS_0 : //Ganondorf Boss Room
gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb
@ -374,46 +356,51 @@ void Entrance_SetSavewarpEntrance(void) {
s16 scene = gSaveContext.savedSceneNum;
// When in a boss room and boss shuffle is on, use the boss scene override to remap to its
// connected dungeon and use that for the final entrance
// When in a boss room and boss shuffle is on, use the boss scene to find the savewarp entrance
if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF &&
scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_SHADOW_TEMPLE_BOSS) {
// Normalize boss scene range to 0 on lookup
scene = dungeonBossSceneOverrides[scene - SCENE_DEKU_TREE_BOSS];
// Normalize boss scene range to 0 on lookup and handle for grotto entrances
gSaveContext.entranceIndex = Grotto_OverrideSpecialEntrance(bossSceneSaveDeathWarps[scene - SCENE_DEKU_TREE_BOSS]);
return;
}
if (scene == SCENE_DEKU_TREE || scene == SCENE_DEKU_TREE_BOSS) {
gSaveContext.entranceIndex = newDekuTreeEntrance;
gSaveContext.entranceIndex = ENTR_DEKU_TREE_0;
} else if (scene == SCENE_DODONGOS_CAVERN || scene == SCENE_DODONGOS_CAVERN_BOSS) {
gSaveContext.entranceIndex = newDodongosCavernEntrance;
gSaveContext.entranceIndex = ENTR_DODONGOS_CAVERN_0;
} else if (scene == SCENE_JABU_JABU || scene == SCENE_JABU_JABU_BOSS) {
gSaveContext.entranceIndex = newJabuJabusBellyEntrance;
gSaveContext.entranceIndex = ENTR_JABU_JABU_0;
} else if (scene == SCENE_FOREST_TEMPLE || scene == SCENE_FOREST_TEMPLE_BOSS) { //Forest Temple Boss Room
gSaveContext.entranceIndex = newForestTempleEntrance;
gSaveContext.entranceIndex = ENTR_FOREST_TEMPLE_0;
} else if (scene == SCENE_FIRE_TEMPLE || scene == SCENE_FIRE_TEMPLE_BOSS) { //Fire Temple Boss Room
gSaveContext.entranceIndex = newFireTempleEntrance;
gSaveContext.entranceIndex = ENTR_FIRE_TEMPLE_0;
} else if (scene == SCENE_WATER_TEMPLE || scene == SCENE_WATER_TEMPLE_BOSS) { //Water Temple Boss Room
gSaveContext.entranceIndex = newWaterTempleEntrance;
gSaveContext.entranceIndex = ENTR_WATER_TEMPLE_0;
} else if (scene == SCENE_SPIRIT_TEMPLE || scene == SCENE_SPIRIT_TEMPLE_BOSS) { //Spirit Temple Boss Room
gSaveContext.entranceIndex = newSpiritTempleEntrance;
gSaveContext.entranceIndex = ENTR_SPIRIT_TEMPLE_0;
} else if (scene == SCENE_SHADOW_TEMPLE || scene == SCENE_SHADOW_TEMPLE_BOSS) { //Shadow Temple Boss Room
gSaveContext.entranceIndex = newShadowTempleEntrance;
gSaveContext.entranceIndex = ENTR_SHADOW_TEMPLE_0;
} else if (scene == SCENE_BOTTOM_OF_THE_WELL) { // BOTW
gSaveContext.entranceIndex = newBottomOfTheWellEntrance;
gSaveContext.entranceIndex = ENTR_BOTTOM_OF_THE_WELL_0;
} else if (scene == SCENE_GERUDO_TRAINING_GROUND) { // GTG
gSaveContext.entranceIndex = newGerudoTrainingGroundsEntrance;
gSaveContext.entranceIndex = ENTR_GERUDO_TRAINING_GROUND_0;
} else if (scene == SCENE_ICE_CAVERN) { // Ice cavern
gSaveContext.entranceIndex = newIceCavernEntrance;
gSaveContext.entranceIndex = ENTR_ICE_CAVERN_0;
} else if (scene == SCENE_INSIDE_GANONS_CASTLE) {
gSaveContext.entranceIndex = GANONS_CASTLE_ENTRANCE;
gSaveContext.entranceIndex = ENTR_INSIDE_GANONS_CASTLE_0;
} else if (scene == SCENE_GANONS_TOWER || scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || scene == SCENE_GANON_BOSS || scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) {
gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb
} else if (scene == SCENE_THIEVES_HIDEOUT) { // Theives hideout
gSaveContext.entranceIndex = ENTR_THIEVES_HIDEOUT_0; // Gerudo Fortress -> Thieve's Hideout spawn 0
} else if (scene == SCENE_LINKS_HOUSE) {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE);
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_LINKS_HOUSE_0);
} else if (CVarGetInteger("gRememberSaveLocation", 0) && scene != SCENE_FAIRYS_FOUNTAIN && scene != SCENE_GROTTOS &&
gSaveContext.entranceIndex != ENTR_LOAD_OPENING) {
// Use the saved entrance value with remember save location, except when in grottos/fairy fountains or if
// the entrance index is -1 (new save)
return;
} else if (LINK_IS_CHILD) {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); // Child Overworld Spawn
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_LINKS_HOUSE_0); // Child Overworld Spawn
} else {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_HYRULE_FIELD_10); // Adult Overworld Spawn (Normally 0x5F4 (ENTR_TEMPLE_OF_TIME_7), but 0x282 (ENTR_HYRULE_FIELD_10) has been repurposed to differentiate from Prelude which also uses 0x5F4)
}
@ -494,7 +481,7 @@ void Entrance_OverrideBlueWarp(void) {
void Entrance_OverrideCutsceneEntrance(u16 cutsceneCmd) {
switch (cutsceneCmd) {
case 24: // Dropping a fish for Jabu Jabu
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(newJabuJabusBellyEntrance);
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_JABU_JABU_0);
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
// In case Jabu's mouth leads to a grotto return
@ -513,8 +500,8 @@ void Entrance_EnableFW(void) {
gPlayState->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR || gPlayState->sceneNum == SCENE_GROTTOS ||
gPlayState->sceneNum == SCENE_FISHING_POND || gPlayState->sceneNum == SCENE_GANON_BOSS ||
gSaveContext.eventInf[0] & 0x1 || // Ingo's Minigame state
player->stateFlags1 & 0x08A02000 || // Swimming, riding horse, Down A, hanging from a ledge
player->stateFlags2 & 0x00040000 // Blank A
player->stateFlags1 & (PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LADDER | PLAYER_STATE1_ON_HORSE | PLAYER_STATE1_IN_WATER) || // Swimming, riding horse, Down A, hanging from a ledge
player->stateFlags2 & PLAYER_STATE2_CRAWLING // Blank A
// Shielding, spinning and getting skull tokens still disable buttons automatically
) {
return;

View File

@ -8,20 +8,6 @@
#define ENTRANCE_TABLE_SIZE ENTR_MAX
#define DEKU_TREE_ENTRANCE ENTR_DEKU_TREE_0
#define DODONGOS_CAVERN_ENTRANCE ENTR_DODONGOS_CAVERN_0
#define JABU_JABUS_BELLY_ENTRANCE ENTR_JABU_JABU_0
#define FOREST_TEMPLE_ENTRANCE ENTR_FOREST_TEMPLE_0
#define FIRE_TEMPLE_ENTRANCE ENTR_FIRE_TEMPLE_0
#define WATER_TEMPLE_ENTRANCE ENTR_WATER_TEMPLE_0
#define SPIRIT_TEMPLE_ENTRANCE ENTR_SPIRIT_TEMPLE_0
#define SHADOW_TEMPLE_ENTRANCE ENTR_SHADOW_TEMPLE_0
#define BOTTOM_OF_THE_WELL_ENTRANCE ENTR_BOTTOM_OF_THE_WELL_0
#define GERUDO_TRAINING_GROUNDS_ENTRANCE ENTR_GERUDO_TRAINING_GROUND_0
#define ICE_CAVERN_ENTRANCE ENTR_ICE_CAVERN_0
#define GANONS_CASTLE_ENTRANCE ENTR_INSIDE_GANONS_CASTLE_0
#define LINK_HOUSE_SAVEWARP_ENTRANCE ENTR_LINKS_HOUSE_0
#define ENTRANCE_RANDO_GROTTO_LOAD_START 0x0700
#define ENTRANCE_RANDO_GROTTO_EXIT_START 0x0800
#define MAX_ENTRANCE_RANDO_USED_INDEX 0x0820
@ -66,7 +52,7 @@ typedef enum {
#define ENTRANCE_RANDO_GROTTO_LOAD(index) ENTRANCE_RANDO_GROTTO_LOAD_START + index
#define ENTRANCE_RANDO_GROTTO_EXIT(index) ENTRANCE_RANDO_GROTTO_EXIT_START + index
#define ENTRANCE_OVERRIDES_MAX_COUNT 259 // 11 one-way entrances + 124 two-way entrances (x2)
#define ENTRANCE_OVERRIDES_MAX_COUNT 267 // 19 one-way entrances + 124 two-way entrances (x2)
#define SHUFFLEABLE_BOSS_COUNT 8
#define SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT 66 // Max entrance rando index is 0x0820, (2080 / 32 == 65) + 1
@ -79,9 +65,9 @@ typedef enum {
(((startTransType) << ENTRANCE_INFO_START_TRANS_TYPE_SHIFT) & ENTRANCE_INFO_START_TRANS_TYPE_MASK))
typedef struct {
uint16_t type;
int16_t index;
int16_t destination;
int16_t blueWarp;
int16_t override;
int16_t overrideDestination;
} EntranceOverride;

View File

@ -104,6 +104,7 @@ const EntranceData entranceData[] = {
{ ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, ""},
{ ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree Boss Door", "Gohma", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_DEKU_TREE_1, ENTR_DEKU_TREE_BOSS_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE_BOSS), "Gohma", "Deku Tree Boss Door", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_KOKIRI_FOREST_11, -1, SINGLE_SCENE_INFO(SCENE_DEKU_TREE_BOSS), "Gohma", "Deku Tree Blue Warp", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Lost Woods
{ ENTR_KOKIRI_FOREST_2, ENTR_LOST_WOODS_9, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods Bridge", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"},
@ -131,6 +132,7 @@ const EntranceData entranceData[] = {
{ ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON},
{ ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple Boss Door", "Phantom Ganon", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_FOREST_TEMPLE_1, ENTR_FOREST_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE_BOSS), "Phantom Ganon", "Forest Temple Boss Door", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_SACRED_FOREST_MEADOW_3, -1, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE_BOSS), "Phantom Ganon", "Forest Temple Blue Warp", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Kakariko Village
{ ENTR_HYRULE_FIELD_1, ENTR_KAKARIKO_VILLAGE_0, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Hyrule Field", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
@ -179,6 +181,7 @@ const EntranceData entranceData[] = {
{ ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON},
{ ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple Boss Door", "Bongo-Bongo", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_SHADOW_TEMPLE_1, ENTR_SHADOW_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE_BOSS), "Bongo-Bongo", "Shadow Temple Boss Door", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_GRAVEYARD_8, -1, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE_BOSS), "Bongo-Bongo", "Shadow Temple Blue Warp", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Death Mountain Trail
{ ENTR_GORON_CITY_0, ENTR_DEATH_MOUNTAIN_TRAIL_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
@ -194,6 +197,7 @@ const EntranceData entranceData[] = {
{ ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc"},
{ ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern Boss Door", "King Dodongo", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ ENTR_DODONGOS_CAVERN_1, ENTR_DODONGOS_CAVERN_BOSS_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN_BOSS), "King Dodongo", "Dodongo's Cavern Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ ENTR_DEATH_MOUNTAIN_TRAIL_5, -1, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN_BOSS), "King Dodongo", "Dodongo's Cavern Blue Warp", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_ONE_WAY, "dc,bw", 1},
// Death Mountain Crater
{ ENTR_GORON_CITY_1, ENTR_DEATH_MOUNTAIN_CRATER_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
@ -208,6 +212,7 @@ const EntranceData entranceData[] = {
{ ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON},
{ ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple Boss Door", "Volvagia", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_FIRE_TEMPLE_1, ENTR_FIRE_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE_BOSS), "Volvagia", "Fire Temple Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_DEATH_MOUNTAIN_CRATER_5, -1, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE_BOSS), "Volvagia", "Fire Temple Blue Warp", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Goron City
{ ENTR_DEATH_MOUNTAIN_TRAIL_1, ENTR_GORON_CITY_0, SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "DMT", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD, "gc"},
@ -247,6 +252,7 @@ const EntranceData entranceData[] = {
{ ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
{ ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly Boss Door", "Barinade", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_JABU_JABU_1, ENTR_JABU_JABU_BOSS_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU_BOSS), "Barinade", "Jabu Jabu's Belly Boss Door", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_ZORAS_FOUNTAIN_0, -1, SINGLE_SCENE_INFO(SCENE_JABU_JABU_BOSS), "Barinade", "Jabu Jabu's Belly Blue Warp", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
{ ENTR_ZORAS_FOUNTAIN_3, ENTR_ICE_CAVERN_0, SINGLE_SCENE_INFO(SCENE_ICE_CAVERN), "Ice Cavern", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
// Hyrule Field
@ -298,6 +304,7 @@ const EntranceData entranceData[] = {
{ ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh"},
{ ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple Boss Door", "Morpha", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
{ ENTR_WATER_TEMPLE_1, ENTR_WATER_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE_BOSS), "Morpha", "Water Temple Boss Door", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
{ ENTR_LAKE_HYLIA_9, -1, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE_BOSS), "Morpha", "Water Temple Blue Warp", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_ONE_WAY, "lh,bw", 1},
// Gerudo Area
{ ENTR_HYRULE_FIELD_5, ENTR_GERUDO_VALLEY_0, SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "Hyrule Field", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
@ -328,6 +335,7 @@ const EntranceData entranceData[] = {
{ ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc"},
{ ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple Boss Door", "Twinrova", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_SPIRIT_TEMPLE_1, ENTR_SPIRIT_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE_BOSS), "Twinrova", "Spirit Temple Boss Door", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1},
{ ENTR_DESERT_COLOSSUS_8, -1, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE_BOSS), "Twinrova", "Spirit Temple Blue Warp", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_ONE_WAY, "bw", 1},
// Market
{ ENTR_HYRULE_FIELD_7, ENTR_MARKET_ENTRANCE_DAY_1, {SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_DAY), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_RUINS)}, "Market Entrance", "Hyrule Field", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
@ -797,6 +805,11 @@ void EntranceTrackerWindow::DrawElement() {
continue;
}
// RANDOTODO: Only show blue warps if bluewarp shuffle is on
if (original->metaTag.ends_with("bw") || override->metaTag.ends_with("bw")) {
continue;
}
bool isDiscovered = IsEntranceDiscovered(entrance.index);
bool showOriginal = (!destToggle ? CVarGetInteger("gEntranceTrackerShowTo", 0) : CVarGetInteger("gEntranceTrackerShowFrom", 0)) || isDiscovered;

View File

@ -30,6 +30,8 @@ void DrawBottle(ItemTrackerItem item);
void DrawQuest(ItemTrackerItem item);
void DrawSong(ItemTrackerItem item);
int itemTrackerSectionId;
bool shouldUpdateVectors = true;
std::vector<ItemTrackerItem> mainWindowItems = {};
@ -282,11 +284,6 @@ void ItemTrackerOnFrame() {
}
}
void SaveNotes(uint32_t fileNum) {
CVarSetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), std::string(std::begin(itemTrackerNotes), std::end(itemTrackerNotes)).c_str());
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
bool IsValidSaveFile() {
bool validSave = gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2;
return validSave;
@ -412,6 +409,7 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
void DrawItemCount(ItemTrackerItem item) {
int iconSize = CVarGetInteger("gItemTrackerIconSize", 36);
int textSize = CVarGetInteger("gTrackers.ItemTracker.ItemTrackerTextSize", 13);
ItemTrackerNumbers currentAndMax = GetItemCurrentAndMax(item);
ImVec2 p = ImGui::GetCursorScreenPos();
int32_t trackerNumberDisplayMode = CVarGetInteger("gItemTrackerCapacityTrack", ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY);
@ -428,13 +426,15 @@ void DrawItemCount(ItemTrackerItem item) {
textScalingFactor / 2) + 8 * textScalingFactor, p.y - 22 * textScalingFactor);
ImGui::SetCursorScreenPos(textPos);
ImGui::SetWindowFontScale(textScalingFactor);
ImGui::Text(item.id == ITEM_HOOKSHOT ? "H" : "L");
ImGui::SetWindowFontScale(1.0f); // Reset font scale to the original state
}
}
ImGui::SetWindowFontScale(textSize / 13.0f);
if (item.id == ITEM_KEY_SMALL && IsValidSaveFile()) {
std::string currentString = "";
std::string maxString = std::to_string(currentAndMax.maxCapacity);
@ -689,7 +689,7 @@ void DrawDungeonItem(ItemTrackerItem item) {
ImVec2 p = ImGui::GetCursorScreenPos();
std::string dungeonName = itemTrackerDungeonShortNames[item.data];
ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + 16)));
ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + CVarGetInteger("gTrackers.ItemTracker.ItemTrackerTextSize", 13) + 3)));
ImGui::PushStyleColor(ImGuiCol_Text, dungeonColor);
ImGui::Text("%s", dungeonName.c_str());
ImGui::PopStyleColor();
@ -752,7 +752,7 @@ void DrawNotes(bool resizeable = false) {
}
if ((ImGui::IsItemDeactivatedAfterEdit() || (notesNeedSave && notesIdleFrames > notesMaxIdleFrames)) && IsValidSaveFile()) {
notesNeedSave = false;
SaveNotes(gSaveContext.fileNum);
SaveManager::Instance->SaveSection(gSaveContext.fileNum, itemTrackerSectionId, true);
}
ImGui::EndGroup();
}
@ -977,6 +977,26 @@ void UpdateVectors() {
shouldUpdateVectors = false;
}
void ItemTrackerInitFile(bool isDebug) {
itemTrackerNotes.clear();
itemTrackerNotes.push_back(0);
}
void ItemTrackerSaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveManager::Instance->SaveData("personalNotes", std::string(std::begin(itemTrackerNotes), std::end(itemTrackerNotes)).c_str());
}
void ItemTrackerLoadFile() {
std::string initialTrackerNotes = "";
SaveManager::Instance->LoadData("personalNotes", initialTrackerNotes);
itemTrackerNotes.resize(initialTrackerNotes.length() + 1);
if (initialTrackerNotes != "") {
SohUtils::CopyStringToCharArray(itemTrackerNotes.Data, initialTrackerNotes.c_str(), itemTrackerNotes.size());
} else {
itemTrackerNotes.push_back(0);
}
}
void ItemTrackerWindow::DrawElement() {
UpdateVectors();
@ -1145,6 +1165,7 @@ void ItemTrackerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator();
UIWidgets::EnhancementSliderInt("Icon size : %dpx", "##ITEMTRACKERICONSIZE", "gItemTrackerIconSize", 25, 128, "", 36);
UIWidgets::EnhancementSliderInt("Icon margins : %dpx", "##ITEMTRACKERSPACING", "gItemTrackerIconSpacing", -5, 50, "", 12);
UIWidgets::EnhancementSliderInt("Text size : %dpx", "##ITEMTRACKERTEXTSIZE", "gTrackers.ItemTracker.ItemTrackerTextSize", 1, 30, "", 13);
UIWidgets::Spacer(0);
@ -1245,14 +1266,9 @@ void ItemTrackerWindow::InitElement() {
itemTrackerNotes.push_back(0);
}
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadFile>([](uint32_t fileNum) {
const char* initialTrackerNotes = CVarGetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), "");
itemTrackerNotes.resize(strlen(initialTrackerNotes) + 1);
strcpy(itemTrackerNotes.Data, initialTrackerNotes);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnDeleteFile>([](uint32_t fileNum) {
CVarSetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), "");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
});
SaveManager::Instance->AddInitFunction(ItemTrackerInitFile);
itemTrackerSectionId = SaveManager::Instance->AddSaveFunction("itemTrackerData", 1, ItemTrackerSaveFile, true, -1);
SaveManager::Instance->AddLoadFunction("itemTrackerData", 1, ItemTrackerLoadFile);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(ItemTrackerOnFrame);
}

View File

@ -115,7 +115,7 @@ void AdvancedResolutionSettingsWindow::DrawElement() {
const bool disabled_resolutionSlider = (CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0) &&
CVarGetInteger("gAdvancedResolution.Enabled", 0)) ||
CVarGetInteger("gLowResMode", 0);
if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f,
if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %.1f%%", "##IMul", "gInternalResolution", 0.5f,
2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) {
LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(
CVarGetFloat("gInternalResolution", 1));

View File

@ -0,0 +1,16 @@
#include "SpeechLogger.h"
#include <libultraship/libultraship.h>
SpeechLogger::SpeechLogger() {
}
void SpeechLogger::Speak(const char* text, const char* language) {
lusprintf(__FILE__, __LINE__, 2, "Spoken Text (%s): %s", language, text);
}
bool SpeechLogger::DoInit() {
return true;
}
void SpeechLogger::DoUninitialize() {
}

View File

@ -0,0 +1,17 @@
#ifndef SOHSpeechLogger_h
#define SOHSpeechLogger_h
#include "SpeechSynthesizer.h"
class SpeechLogger : public SpeechSynthesizer {
public:
SpeechLogger();
void Speak(const char* text, const char* language);
protected:
bool DoInit(void);
void DoUninitialize(void);
};
#endif

View File

@ -36,3 +36,5 @@ class SpeechSynthesizer {
#elif defined(__APPLE__)
#include "DarwinSpeechSynthesizer.h"
#endif
#include "SpeechLogger.h"

View File

@ -11,6 +11,8 @@
#include "message_data_static.h"
#include "overlays/gamestates/ovl_file_choose/file_choose.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/resource/type/SohResourceType.h"
#include "soh/resource/type/RawJson.h"
extern "C" {
extern MapData* gMapData;
@ -168,7 +170,7 @@ void RegisterOnInterfaceUpdateHook() {
prevTimer = timer;
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
static int16_t lostHealth = 0;
static int16_t prevHealth = 0;
@ -1037,25 +1039,22 @@ void InitTTSBank() {
break;
}
auto sceneFile = LUS::Context::GetInstance()->GetResourceManager()->LoadFile("accessibility/texts/scenes" + languageSuffix);
if (sceneFile != nullptr) {
sceneMap = nlohmann::json::parse(sceneFile->Buffer, nullptr, true, true);
}
auto initData = std::make_shared<LUS::ResourceInitData>();
initData->Format = RESOURCE_FORMAT_BINARY;
initData->Type = static_cast<uint32_t>(SOH::ResourceType::SOH_RawJson);
initData->ResourceVersion = 0;
auto miscFile = LUS::Context::GetInstance()->GetResourceManager()->LoadFile("accessibility/texts/misc" + languageSuffix);
if (miscFile != nullptr) {
miscMap = nlohmann::json::parse(miscFile->Buffer, nullptr, true, true);
}
auto kaleidoFile = LUS::Context::GetInstance()->GetResourceManager()->LoadFile("accessibility/texts/kaleidoscope" + languageSuffix);
if (kaleidoFile != nullptr) {
kaleidoMap = nlohmann::json::parse(kaleidoFile->Buffer, nullptr, true, true);
}
auto fileChooseFile = LUS::Context::GetInstance()->GetResourceManager()->LoadFile("accessibility/texts/filechoose" + languageSuffix);
if (fileChooseFile != nullptr) {
fileChooseMap = nlohmann::json::parse(fileChooseFile->Buffer, nullptr, true, true);
}
sceneMap = std::static_pointer_cast<SOH::RawJson>(
LUS::Context::GetInstance()->GetResourceManager()->LoadResource("accessibility/texts/scenes" + languageSuffix, true, initData))->Data;
miscMap = std::static_pointer_cast<SOH::RawJson>(
LUS::Context::GetInstance()->GetResourceManager()->LoadResource("accessibility/texts/misc" + languageSuffix, true, initData))->Data;
kaleidoMap = std::static_pointer_cast<SOH::RawJson>(
LUS::Context::GetInstance()->GetResourceManager()->LoadResource("accessibility/texts/kaleidoscope" + languageSuffix, true, initData))->Data;
fileChooseMap = std::static_pointer_cast<SOH::RawJson>(
LUS::Context::GetInstance()->GetResourceManager()->LoadResource("accessibility/texts/filechoose" + languageSuffix, true, initData))->Data;
}
void RegisterOnSetGameLanguageHook() {

View File

@ -107,7 +107,8 @@ void Extractor::ShowSizeErrorBox() const {
}
void Extractor::ShowCrcErrorBox() const {
ShowErrorBox("Rom CRC invalid", "Rom CRC did not match the list of known good roms. Please find another.");
ShowErrorBox("Rom CRC invalid", "Rom CRC did not match the list of known compatible roms. Please find another.\n\n"
"Visit https://ship.equipment/ to validate your ROM and see a list of compatible versions");
}
void Extractor::ShowCompressedErrorBox() const {
@ -479,7 +480,8 @@ bool Extractor::Run(std::string searchPath, RomSearchMode searchMode) {
ShowCrcErrorBox();
} else {
ShowErrorBox("Rom CRC invalid",
"Rom CRC did not match the list of known good roms. Trying the next one...");
"Rom CRC did not match the list of known compatible roms. Trying the next one...\n\n"
"Visit https://ship.equipment/ to validate your ROM and see a list of compatible versions");
}
continue;
}

Some files were not shown because too many files have changed in this diff Show More