Merge remote-tracking branch 'origin/develop' into merge-macready-805

This commit is contained in:
Adam Bird 2024-02-28 23:49:06 -05:00
commit 7bd2a7c3a6
474 changed files with 19391 additions and 10366 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 ninja-build

View file

@ -13,33 +13,37 @@ jobs:
with: with:
submodules: true submodules: true
- name: ccache - name: ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2.11
with: 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 - name: Install dependencies
if: ${{ !vars.LINUX_RUNNER }} if: ${{ !vars.LINUX_RUNNER }}
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y $(cat .github/workflows/apt-deps.txt) 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 - name: Install latest SDL
if: ${{ !vars.LINUX_RUNNER }} if: ${{ !vars.LINUX_RUNNER }}
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz if [ ! -d "SDL2-2.28.5" ]; then
tar -xzf SDL2-2.26.1.tar.gz wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz
cd SDL2-2.26.1 tar -xzf SDL2-2.28.5.tar.gz
./configure fi
make -j 10 cd SDL2-2.28.5
sudo make install ./configure --enable-hidapi-libusb
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Install latest SDL_net
if: ${{ !vars.LINUX_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
cd SDL2_net-2.2.0
./configure
make -j 10 make -j 10
sudo make install sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
@ -48,7 +52,7 @@ jobs:
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" 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 --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release
cmake --build build-cmake --config Release --target GenerateSohOtr cmake --build build-cmake --config Release --target GenerateSohOtr
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: soh.otr name: soh.otr
path: soh.otr path: soh.otr
@ -61,9 +65,12 @@ jobs:
with: with:
submodules: true submodules: true
- name: ccache - name: ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2.11
with: 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 - name: Install gtar wrapper
if: ${{ !vars.MAC_RUNNER }} if: ${{ !vars.MAC_RUNNER }}
run: | run: |
@ -96,13 +103,13 @@ jobs:
sudo port install $(cat .github/workflows/macports-deps.txt) sudo port install $(cat .github/workflows/macports-deps.txt)
brew install ninja brew install ninja
- name: Download soh.otr - name: Download soh.otr
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: soh.otr name: soh.otr
- name: Build SoH - name: Build SoH
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" 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 -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DBUILD_REMOTE_CONTROL=1
cmake --build build-cmake --config Release --parallel 10 cmake --build build-cmake --config Release --parallel 10
mv soh.otr build-cmake/soh mv soh.otr build-cmake/soh
(cd build-cmake && cpack) (cd build-cmake && cpack)
@ -110,7 +117,7 @@ jobs:
mv _packages/*.dmg SoH.dmg mv _packages/*.dmg SoH.dmg
mv README.md readme.txt mv README.md readme.txt
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: soh-mac name: soh-mac
path: | path: |
@ -139,17 +146,32 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y $(cat .github/workflows/apt-deps.txt) sudo apt-get install -y $(cat .github/workflows/apt-deps.txt)
- name: ccache - name: ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2.11
with: 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 - name: Install latest SDL
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }} if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }}
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz if [ ! -d "SDL2-2.28.5" ]; then
tar -xzf SDL2-2.26.1.tar.gz wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz
cd SDL2-2.26.1 tar -xzf SDL2-2.28.5.tar.gz
./configure fi
cd SDL2-2.28.5
./configure --enable-hidapi-libusb
make -j 10 make -j 10
sudo make install sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
@ -157,21 +179,23 @@ jobs:
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }} if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }}
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" 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 if [ ! -d "SDL2_net-2.2.0" ]; then
tar -xzf SDL2_net-2.2.0.tar.gz 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 cd SDL2_net-2.2.0
./configure ./configure
make -j 10 make -j 10
sudo make install sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Download soh.otr - name: Download soh.otr
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: soh.otr name: soh.otr
- name: Build SoH - name: Build SoH
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" 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 --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_REMOTE_CONTROL=1
cmake --build build-cmake --config Release -j3 cmake --build build-cmake --config Release -j3
(cd build-cmake && cpack -G External) (cd build-cmake && cpack -G External)
@ -181,7 +205,7 @@ jobs:
CC: gcc-${{ matrix.gcc }} CC: gcc-${{ matrix.gcc }}
CXX: g++-${{ matrix.gcc }} CXX: g++-${{ matrix.gcc }}
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: soh-linux-${{ matrix.archive-suffix }} name: soh-linux-${{ matrix.archive-suffix }}
path: | path: |
@ -197,6 +221,9 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y ninja-build 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
- name: Fix dubious ownership error - name: Fix dubious ownership error
if: ${{ vars.LINUX_RUNNER }} if: ${{ vars.LINUX_RUNNER }}
run: git config --global --add safe.directory '*' run: git config --global --add safe.directory '*'
@ -204,9 +231,12 @@ jobs:
with: with:
submodules: true submodules: true
- name: ccache - name: ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2.11
with: 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 - name: Build SoH
run: | 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 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
@ -215,11 +245,11 @@ jobs:
mv build-switch/soh/*.nro soh.nro mv build-switch/soh/*.nro soh.nro
mv README.md readme.txt mv README.md readme.txt
- name: Download soh.otr - name: Download soh.otr
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: soh.otr name: soh.otr
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: soh-switch name: soh-switch
path: | path: |
@ -237,13 +267,19 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y ninja-build 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
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: ccache - name: ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2.11
with: 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 - name: Build SoH
run: | 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 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
@ -256,11 +292,11 @@ jobs:
DEVKITPRO: /opt/devkitpro DEVKITPRO: /opt/devkitpro
DEVKITPPC: /opt/devkitpro/devkitPPC DEVKITPPC: /opt/devkitpro/devkitPPC
- name: Download soh.otr - name: Download soh.otr
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: soh.otr name: soh.otr
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: soh-wiiu name: soh-wiiu
path: | path: |
@ -281,16 +317,33 @@ jobs:
with: with:
submodules: true submodules: true
- name: ccache - name: ccache
uses: dcvz/ccache-action@27b9f33213c0079872f064f6b6ba0233dfa16ba2 uses: hendrikmuhs/ccache-action@v1.2.11
with: with:
key: ${{ runner.os }}-ccache variant: sccache
- uses: ilammy/msvc-dev-cmd@v1 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:
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 - name: Build SoH
env: env:
VCPKG_ROOT: D:/a/vcpkg VCPKG_ROOT: ${{github.workspace}}/vcpkg
run: | run: |
set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH" 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 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 cmake --build build-windows --config Release --parallel 10
mkdir soh-windows mkdir soh-windows
@ -303,12 +356,12 @@ jobs:
mv ./build-windows/gamecontrollerdb.txt ./soh-windows/gamecontrollerdb.txt mv ./build-windows/gamecontrollerdb.txt ./soh-windows/gamecontrollerdb.txt
mv ./x64/Release/assets ./soh-windows mv ./x64/Release/assets ./soh-windows
- name: Download soh.otr - name: Download soh.otr
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: soh.otr name: soh.otr
path: soh-windows path: soh-windows
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: soh-windows name: soh-windows
path: soh-windows path: soh-windows

View file

@ -1 +1 @@
libsdl2 +universal libpng +universal glew +universal libsdl2 +universal libsdl2_net +universal libpng +universal glew +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 sdl2_net boost
pacman -Syu --noconfirm
pacman -S --noconfirm ${{ matrix.cc }} git cmake ninja lsb-release sdl2 libpng 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 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 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 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 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
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
- 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_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_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() endif()

View file

@ -13,20 +13,18 @@ set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>) add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/utf-8>) add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/utf-8>)
if (CMAKE_SYSTEM_NAME MATCHES "Windows|Linux")
if(NOT DEFINED BUILD_CROWD_CONTROL)
set(BUILD_CROWD_CONTROL ON)
endif()
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
include(CMake/automate-vcpkg.cmake) include(CMake/automate-vcpkg.cmake)
set(VCPKG_TRIPLET x64-windows-static) set(VCPKG_TRIPLET x64-windows-static)
set(VCPKG_TARGET_TRIPLET x64-windows-static) set(VCPKG_TARGET_TRIPLET x64-windows-static)
vcpkg_bootstrap() vcpkg_bootstrap()
vcpkg_install_packages(zlib bzip2 libpng sdl2 sdl2-net glew glfw3) vcpkg_install_packages(zlib bzip2 libpng sdl2 sdl2-net glew glfw3)
if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache")
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)
endif()
endif() endif()
################################################################################ ################################################################################

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

View file

@ -92,7 +92,7 @@ If you want to playtest a continuous integration build, you can find them at the
* [Windows](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-windows.zip) * [Windows](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-windows.zip)
* [macOS](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-mac.zip) * [macOS](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-mac.zip)
* [Linux (performance)](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux-performance.zip) _(requires `glibc 2.35` or newer, but will be more performant than the compatibility build.)_ * [Linux (performance)](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux-performance.zip) _(requires `glibc 2.35` or newer, but will be more performant than the compatibility build.)_
* [Linux (compatibility)](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux-compatiblity.zip) _(compatible with most Linux distributions, but may not be as performant as the performance build.)_ * [Linux (compatibility)](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux-compatibility.zip) _(compatible with most Linux distributions, but may not be as performant as the performance build.)_
* [Switch](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-switch.zip) * [Switch](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-switch.zip)
* [Wii U](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-wiiu.zip) * [Wii U](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-wiiu.zip)

View file

@ -84,17 +84,49 @@ cd "build/x64"
``` ```
## Linux ## 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 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 libboost-dev libopengl-dev
```
#### Arch
```sh
# using gcc
pacman -S gcc git cmake ninja lsb-release sdl2 libpng 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 sdl2_net boost
```
#### Fedora
```sh
# using gcc
dnf install gcc gcc-c++ git cmake ninja-build lsb_release SDL2-devel libpng-devel boost-devel
# or using clang
dnf install clang git cmake ninja-build lsb_release SDL2-devel libpng-devel boost-devel
```
#### openSUSE
```sh
# using gcc
zypper in gcc gcc-c++ git cmake ninja SDL2-devel libpng16-devel boost
# or using clang
zypper in clang libstdc++-devel git cmake ninja SDL2-devel libpng16-devel boost
```
### 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 ```bash
# Clone the repo # Clone the repo
git clone https://github.com/HarbourMasters/Shipwright.git git clone https://github.com/HarbourMasters/Shipwright.git
cd Shipwright cd Shipwright
# Clone the submodule libultraship # Clone the submodules
git submodule update --init git submodule update --init
# Copy the baserom to the OTRExporter folder # Copy the baserom to the OTRExporter folder
cp <path to your ROM> OTRExporter cp <path to your ROM> OTRExporter

View file

@ -188,4 +188,4 @@ Assuming all went well, you can now push your changes to your fork with the foll
```bash ```bash
git push origin <YOUR BRANCH NAME> git push origin <YOUR BRANCH NAME>
``` ```

@ -1 +1 @@
Subproject commit 96c8a8929c18c1bffd7d92a35a589f74cf16fc59 Subproject commit a516b66ce0c89fe4e33c55b1fbfbde845d0bf129

View file

@ -21,12 +21,17 @@ fi
while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do
for romfile in "$SHIP_HOME"/*.*64 for romfile in "$SHIP_HOME"/*.*64
do do
if [[ -e $romfile ]]; then if [[ -e "$romfile" ]] || [[ -L "$romfile" ]]; then
export ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)" export ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
ln -s "$HERE"/usr/bin/{assets,soh.elf,ZAPD} "$ASSETDIR" ln -s "$SHIP_BIN_DIR"/{assets,soh.elf,ZAPD} "$ASSETDIR"
export OLDPWD="$PWD" export OLDPWD="$PWD"
mkdir -p "$ASSETDIR"/tmp mkdir -p "$ASSETDIR"/tmp
ln -s "$romfile" "$ASSETDIR"/tmp/rom.z64 if [[ -e "$romfile" ]]; then
ln -s "$romfile" "$ASSETDIR"/tmp/rom.z64
else
ORIG_ROM_PATH=$(readlink "$romfile")
ln -s "$ORIG_ROM_PATH" "$ASSETDIR"/tmp/rom.z64
fi
cd "$ASSETDIR" cd "$ASSETDIR"
ROMHASH=$(sha1sum -b "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }') ROMHASH=$(sha1sum -b "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')

View file

@ -8,5 +8,9 @@ if(MSVC)
set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL") 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_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() endif()

View file

@ -154,7 +154,7 @@ list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/gfx.*")
# handle crowd control removals # handle crowd control removals
list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.cs") list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.cs")
list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak") list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak")
if (!BUILD_CROWD_CONTROL) if (!BUILD_REMOTE_CONTROL)
list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*") list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*")
endif() endif()
@ -354,9 +354,15 @@ endif()
find_package(SDL2) find_package(SDL2)
set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS}) set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS})
if (BUILD_CROWD_CONTROL) if (BUILD_REMOTE_CONTROL)
find_package(SDL2_net) 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() endif()
target_include_directories(${PROJECT_NAME} PRIVATE assets target_include_directories(${PROJECT_NAME} PRIVATE assets
@ -408,9 +414,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"$<$<CONFIG:Release>:" "$<$<CONFIG:Release>:"
"NDEBUG" "NDEBUG"
">" ">"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"INCLUDE_GAME_PRINTF;" "INCLUDE_GAME_PRINTF;"
"ENABLE_CROWD_CONTROL;"
"UNICODE;" "UNICODE;"
"_UNICODE" "_UNICODE"
STORMLIB_NO_AUTO_LINK STORMLIB_NO_AUTO_LINK
@ -455,7 +460,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
"$<$<CONFIG:Release>:" "$<$<CONFIG:Release>:"
"NDEBUG" "NDEBUG"
">" ">"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"SPDLOG_ACTIVE_LEVEL=0;" "SPDLOG_ACTIVE_LEVEL=0;"
"_CONSOLE;" "_CONSOLE;"
"_CRT_SECURE_NO_WARNINGS;" "_CRT_SECURE_NO_WARNINGS;"
@ -624,6 +629,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
-Wno-parentheses -Wno-parentheses
-Wno-narrowing -Wno-narrowing
-Wno-missing-braces -Wno-missing-braces
-Wno-int-conversion
-Wno-implicit-int
$<$<COMPILE_LANGUAGE:C>: $<$<COMPILE_LANGUAGE:C>:
-Werror-implicit-function-declaration -Werror-implicit-function-declaration
-Wno-incompatible-pointer-types -Wno-incompatible-pointer-types
@ -689,7 +696,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"glu32;" "glu32;"
"SDL2::SDL2;" "SDL2::SDL2;"
"SDL2::SDL2main;" "SDL2::SDL2main;"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net-static>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net-static>"
"glfw;" "glfw;"
"winmm;" "winmm;"
"imm32;" "imm32;"
@ -742,7 +749,7 @@ else()
"ZAPDUtils;" "ZAPDUtils;"
"ZAPDLib;" "ZAPDLib;"
SDL2::SDL2 SDL2::SDL2
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net>"
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
Threads::Threads Threads::Threads
) )

View file

@ -28,4 +28,10 @@
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application> </application>
</compatibility> </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> </assembly>

View file

@ -4,4 +4,4 @@
*.cfg *.cfg
*.vtx.inc *.vtx.inc
*.dlist.inc *.dlist.inc
*.txt !*.png

View file

@ -1,5 +1,5 @@
{ {
"0": "Abre Mojo", "0": "Arbre Mojo",
"1": "Caverne Dodongo", "1": "Caverne Dodongo",
"2": "Ventre de Jabu-Jabu", "2": "Ventre de Jabu-Jabu",
"3": "Temple de la Forêt", "3": "Temple de la Forêt",
@ -58,9 +58,9 @@
"56": "Laboratoire du Lac", "56": "Laboratoire du Lac",
"57": "", // Tente du Marathonien (No title card) "57": "", // Tente du Marathonien (No title card)
"58": "Cabane du fossoyeur", "58": "Cabane du fossoyeur",
"59": "Fountaine Royale des Fées", "59": "Fontaine Royale des Fées",
"60": "Fountaine des Fées", "60": "Fontaine des Fées",
"61": "Fountaine Royale des Fées", "61": "Fontaine Royale des Fées",
"62": "", // Grottes (No title card) "62": "", // Grottes (No title card)
"63": "", // Tombe 1 (No title card) "63": "", // Tombe 1 (No title card)
"64": "", // Tombe 2 (No title card) "64": "", // Tombe 2 (No title card)
@ -109,4 +109,4 @@
"107": "", "107": "",
"108": "", // Debug: SRD Room (No title card) "108": "", // Debug: SRD Room (No title card)
"109": "" // Debug: Treasure Chest Warp (No title card) "109": "" // Debug: Treasure Chest Warp (No title card)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

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

Before After
Before After

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

Before After
Before After

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

Before After
Before After

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

Before After
Before After

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

Before After
Before After

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 KiB

Before After
Before After

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

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 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

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1,005 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,15 @@
Complete triforce:
DL name: gTriforcePieceCompletedDL
Export Path: objects/object_triforce_completed
Shard 0:
DL name: gTriforcePiece0DL
Export Path: objects/object_triforce_piece_0
Shard 1:
DL name: gTriforcePiece1DL
Export Path: objects/object_triforce_piece_1
Shard 2:
DL name: gTriforcePiece2DL
Export Path: objects/object_triforce_piece_2

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

View file

@ -1250,7 +1250,7 @@ s32 Object_IsLoaded(ObjectContext* objectCtx, s32 bankIndex);
void func_800981B8(ObjectContext* objectCtx); void func_800981B8(ObjectContext* objectCtx);
s32 Scene_ExecuteCommands(PlayState* play, SceneCmd* sceneCmd); s32 Scene_ExecuteCommands(PlayState* play, SceneCmd* sceneCmd);
void TransitionActor_InitContext(GameState* state, TransitionActorContext* transiActorCtx); void TransitionActor_InitContext(GameState* state, TransitionActorContext* transiActorCtx);
void func_800994A0(PlayState* play); void Scene_SetTransitionForNextEntrance(PlayState* play);
void Scene_Draw(PlayState* play); void Scene_Draw(PlayState* play);
void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable, void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 dListIndex); OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 dListIndex);
@ -1537,7 +1537,7 @@ void KaleidoScopeCall_Draw(PlayState* play);
void func_800BC490(PlayState* play, s16 point); void func_800BC490(PlayState* play, s16 point);
s32 func_800BC56C(PlayState* play, s16 arg1); s32 func_800BC56C(PlayState* play, s16 arg1);
void func_800BC590(PlayState* play); void func_800BC590(PlayState* play);
void func_800BC5E0(PlayState* play, s32 arg1); void Gameplay_SetupTransition(PlayState* play, s32 arg1);
Gfx* Play_SetFog(PlayState* play, Gfx* gfx); Gfx* Play_SetFog(PlayState* play, Gfx* gfx);
void Play_Destroy(GameState* thisx); void Play_Destroy(GameState* thisx);
void Play_Init(GameState* thisx); void Play_Init(GameState* thisx);
@ -2171,7 +2171,7 @@ void func_800FA18C(u8, u8);
void Audio_SetVolScale(u8 playerIdx, u8 scaleIdx, u8 targetVol, u8 volFadeTimer); void Audio_SetVolScale(u8 playerIdx, u8 scaleIdx, u8 targetVol, u8 volFadeTimer);
void func_800FA3DC(void); void func_800FA3DC(void);
u8 func_800FAD34(void); u8 func_800FAD34(void);
void func_800FADF8(void); void Audio_ResetActiveSequences(void);
void func_800FAEB4(void); void func_800FAEB4(void);
void GfxPrint_SetColor(GfxPrint* this, u32 r, u32 g, u32 b, u32 a); void GfxPrint_SetColor(GfxPrint* this, u32 r, u32 g, u32 b, u32 a);
void GfxPrint_SetPosPx(GfxPrint* this, s32 x, s32 y); void GfxPrint_SetPosPx(GfxPrint* this, s32 x, s32 y);
@ -2210,6 +2210,14 @@ s8 PadUtils_GetRelYImpl(Input* input);
s8 PadUtils_GetRelX(Input* input); s8 PadUtils_GetRelX(Input* input);
s8 PadUtils_GetRelY(Input* input); s8 PadUtils_GetRelY(Input* input);
void PadUtils_UpdateRelXY(Input* input); void PadUtils_UpdateRelXY(Input* input);
s8 PadUtils_GetCurRX(Input* input);
s8 PadUtils_GetCurRY(Input* input);
void PadUtils_SetRelRXY(Input* input, s32 x, s32 y);
s8 PadUtils_GetRelRXImpl(Input* input);
s8 PadUtils_GetRelRYImpl(Input* input);
s8 PadUtils_GetRelRX(Input* input);
s8 PadUtils_GetRelRY(Input* input);
void PadUtils_UpdateRelRXY(Input* input);
s32 PadSetup_Init(OSMesgQueue* mq, u8* outMask, OSContStatus* status); s32 PadSetup_Init(OSMesgQueue* mq, u8* outMask, OSContStatus* status);
f32 Math_FTanF(f32 x); f32 Math_FTanF(f32 x);
f32 Math_FFloorF(f32 x); f32 Math_FFloorF(f32 x);
@ -2459,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_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH);
void Interface_RandoRestoreSwordless(void); void Interface_RandoRestoreSwordless(void);
//Pause Warp
void PauseWarp_HandleSelection();
void PauseWarp_Execute();
// #endregion // #endregion
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -86,6 +86,8 @@
#define R_ITEM_ICON_X(i) ZREG(82 + i) #define R_ITEM_ICON_X(i) ZREG(82 + i)
#define R_ITEM_ICON_Y(i) ZREG(86 + i) #define R_ITEM_ICON_Y(i) ZREG(86 + i)
#define R_ITEM_ICON_DD(i) ZREG(90 + i) #define R_ITEM_ICON_DD(i) ZREG(90 + i)
#define R_TRANS_DBG_ENABLED CREG(11)
#define R_TRANS_DBG_TYPE CREG(12)
#define R_ENV_WIND_DIR(i) CREG(16 + i) #define R_ENV_WIND_DIR(i) CREG(16 + i)
#define R_ENV_WIND_SPEED CREG(19) #define R_ENV_WIND_SPEED CREG(19)
#define R_A_BTN_Y XREG(16) #define R_A_BTN_Y XREG(16)

File diff suppressed because it is too large Load diff

View file

@ -107,7 +107,7 @@ extern "C"
extern s16 gLinkObjectIds[2]; extern s16 gLinkObjectIds[2];
extern u32 gObjectTableSize; extern u32 gObjectTableSize;
extern RomFile gObjectTable[OBJECT_ID_MAX]; extern RomFile gObjectTable[OBJECT_ID_MAX];
extern EntranceInfo gEntranceTable[1556]; extern EntranceInfo gEntranceTable[ENTR_MAX];
extern SceneTableEntry gSceneTable[SCENE_ID_MAX]; extern SceneTableEntry gSceneTable[SCENE_ID_MAX];
extern u16 gSramSlotOffsets[]; extern u16 gSramSlotOffsets[];
// 4 16-colors palettes // 4 16-colors palettes
@ -224,7 +224,7 @@ extern "C"
extern u16 gAudioSfxSwapSource[10]; extern u16 gAudioSfxSwapSource[10];
extern u16 gAudioSfxSwapTarget[10]; extern u16 gAudioSfxSwapTarget[10];
extern u8 gAudioSfxSwapMode[10]; extern u8 gAudioSfxSwapMode[10];
extern unk_D_8016E750 D_8016E750[4]; extern ActiveSequence gActiveSeqs[4];
extern AudioContext gAudioContext; extern AudioContext gAudioContext;
extern void(*D_801755D0)(void); extern void(*D_801755D0)(void);

View file

@ -1122,6 +1122,82 @@ typedef struct {
/* 0x4C */ u32 unk_4C; /* 0x4C */ u32 unk_4C;
} PreRender; // size = 0x50 } PreRender; // size = 0x50
#define TRANS_TRIGGER_OFF 0 // transition is not active
#define TRANS_TRIGGER_START 20 // start transition (exiting an area)
#define TRANS_TRIGGER_END -20 // transition is ending (arriving in a new area)
typedef enum {
/* 0 */ TRANS_MODE_OFF,
/* 1 */ TRANS_MODE_SETUP,
/* 2 */ TRANS_MODE_INSTANCE_INIT,
/* 3 */ TRANS_MODE_INSTANCE_RUNNING,
/* 4 */ TRANS_MODE_FILL_WHITE_INIT,
/* 5 */ TRANS_MODE_FILL_IN,
/* 6 */ TRANS_MODE_FILL_OUT,
/* 7 */ TRANS_MODE_FILL_BROWN_INIT,
/* 8 */ TRANS_MODE_08, // unused
/* 9 */ TRANS_MODE_09, // unused
/* 10 */ TRANS_MODE_INSTANT,
/* 11 */ TRANS_MODE_INSTANCE_WAIT,
/* 12 */ TRANS_MODE_SANDSTORM_INIT,
/* 13 */ TRANS_MODE_SANDSTORM,
/* 14 */ TRANS_MODE_SANDSTORM_END_INIT,
/* 15 */ TRANS_MODE_SANDSTORM_END,
/* 16 */ TRANS_MODE_CS_BLACK_FILL_INIT,
/* 17 */ TRANS_MODE_CS_BLACK_FILL
} TransitionMode;
typedef enum {
/* 0 */ TRANS_TYPE_WIPE,
/* 1 */ TRANS_TYPE_TRIFORCE,
/* 2 */ TRANS_TYPE_FADE_BLACK,
/* 3 */ TRANS_TYPE_FADE_WHITE,
/* 4 */ TRANS_TYPE_FADE_BLACK_FAST,
/* 5 */ TRANS_TYPE_FADE_WHITE_FAST,
/* 6 */ TRANS_TYPE_FADE_BLACK_SLOW,
/* 7 */ TRANS_TYPE_FADE_WHITE_SLOW,
/* 8 */ TRANS_TYPE_WIPE_FAST,
/* 9 */ TRANS_TYPE_FILL_WHITE2,
/* 10 */ TRANS_TYPE_FILL_WHITE,
/* 11 */ TRANS_TYPE_INSTANT,
/* 12 */ TRANS_TYPE_FILL_BROWN,
/* 13 */ TRANS_TYPE_FADE_WHITE_CS_DELAYED,
/* 14 */ TRANS_TYPE_SANDSTORM_PERSIST,
/* 15 */ TRANS_TYPE_SANDSTORM_END,
/* 16 */ TRANS_TYPE_CS_BLACK_FILL,
/* 17 */ TRANS_TYPE_FADE_WHITE_INSTANT,
/* 18 */ TRANS_TYPE_FADE_GREEN,
/* 19 */ TRANS_TYPE_FADE_BLUE,
// transition types 20 - 31 are unused
// transition types 32 - 55 are constructed using the TRANS_TYPE_CIRCLE macro
/* 56 */ TRANS_TYPE_MAX = 56
} TransitionType;
#define TRANS_NEXT_TYPE_DEFAULT 0xFF // when `nextTransitionType` is set to default, the type will be taken from the entrance table for the ending transition
typedef enum {
/* 0 */ TCA_NORMAL,
/* 1 */ TCA_WAVE,
/* 2 */ TCA_RIPPLE,
/* 3 */ TCA_STARBURST
} TransitionCircleAppearance;
typedef enum {
/* 0 */ TCC_BLACK,
/* 1 */ TCC_WHITE,
/* 2 */ TCC_GRAY,
/* 3 */ TCC_SPECIAL // color varies depending on appearance. unused and appears broken
} TransitionCircleColor;
typedef enum {
/* 0 */ TCS_FAST,
/* 1 */ TCS_SLOW
} TransitionCircleSpeed;
#define TC_SET_PARAMS (1 << 7)
#define TRANS_TYPE_CIRCLE(appearance, color, speed) ((1 << 5) | ((color & 3) << 3) | ((appearance & 3) << 1) | (speed & 1))
typedef struct { typedef struct {
union { union {
TransitionFade fade; TransitionFade fade;
@ -1384,14 +1460,14 @@ typedef struct PlayState {
/* 0x11E0C */ ElfMessage* cUpElfMsgs; /* 0x11E0C */ ElfMessage* cUpElfMsgs;
/* 0x11E10 */ void* specialEffects; /* 0x11E10 */ void* specialEffects;
/* 0x11E14 */ u8 skyboxId; /* 0x11E14 */ u8 skyboxId;
/* 0x11E15 */ s8 sceneLoadFlag; // "fade_direction" /* 0x11E15 */ s8 transitionTrigger; // "fade_direction"
/* 0x11E16 */ s16 unk_11E16; /* 0x11E16 */ s16 unk_11E16;
/* 0x11E18 */ s16 unk_11E18; /* 0x11E18 */ s16 unk_11E18;
/* 0x11E1A */ s16 nextEntranceIndex; /* 0x11E1A */ s16 nextEntranceIndex;
/* 0x11E1C */ char unk_11E1C[0x40]; /* 0x11E1C */ char unk_11E1C[0x40];
/* 0x11E5C */ s8 shootingGalleryStatus; /* 0x11E5C */ s8 shootingGalleryStatus;
/* 0x11E5D */ s8 bombchuBowlingStatus; // "bombchu_game_flag" /* 0x11E5D */ s8 bombchuBowlingStatus; // "bombchu_game_flag"
/* 0x11E5E */ u8 fadeTransition; /* 0x11E5E */ u8 transitionType;
/* 0x11E60 */ CollisionCheckContext colChkCtx; /* 0x11E60 */ CollisionCheckContext colChkCtx;
/* 0x120FC */ u16 envFlags[20]; /* 0x120FC */ u16 envFlags[20];
/* 0x12124 */ PreRender pauseBgPreRender; /* 0x12124 */ PreRender pauseBgPreRender;
@ -1513,6 +1589,20 @@ typedef struct {
uint16_t bossRushArrowOffset; uint16_t bossRushArrowOffset;
} FileChooseContext; // size = 0x1CAE0 } FileChooseContext; // size = 0x1CAE0
// Macros for `EntranceInfo.field`
#define ENTRANCE_INFO_CONTINUE_BGM_FLAG (1 << 15)
#define ENTRANCE_INFO_DISPLAY_TITLE_CARD_FLAG (1 << 14)
#define ENTRANCE_INFO_END_TRANS_TYPE_MASK 0x3F80
#define ENTRANCE_INFO_END_TRANS_TYPE_SHIFT 7
#define ENTRANCE_INFO_END_TRANS_TYPE(field) \
(((field) >> ENTRANCE_INFO_END_TRANS_TYPE_SHIFT) \
& (ENTRANCE_INFO_END_TRANS_TYPE_MASK >> ENTRANCE_INFO_END_TRANS_TYPE_SHIFT))
#define ENTRANCE_INFO_START_TRANS_TYPE_MASK 0x7F
#define ENTRANCE_INFO_START_TRANS_TYPE_SHIFT 0
#define ENTRANCE_INFO_START_TRANS_TYPE(field) \
(((field) >> ENTRANCE_INFO_START_TRANS_TYPE_SHIFT) \
& (ENTRANCE_INFO_START_TRANS_TYPE_MASK >> ENTRANCE_INFO_START_TRANS_TYPE_SHIFT))
typedef enum { typedef enum {
DPM_UNK = 0, DPM_UNK = 0,
DPM_PLAYER = 1, DPM_PLAYER = 1,

View file

@ -970,43 +970,43 @@ typedef struct {
} AudioContextInitSizes; // size = 0xC } AudioContextInitSizes; // size = 0xC
typedef struct { typedef struct {
/* 0x00 */ f32 unk_00; /* 0x00 */ f32 volCur;
/* 0x04 */ f32 unk_04; /* 0x04 */ f32 volTarget;
/* 0x08 */ f32 unk_08; /* 0x08 */ f32 volStep;
/* 0x0C */ u16 unk_0C; /* 0x0C */ u16 volTimer;
/* 0x10 */ f32 unk_10; /* 0x10 */ f32 freqScaleCur;
/* 0x14 */ f32 unk_14; /* 0x14 */ f32 freqScaleTarget;
/* 0x18 */ f32 unk_18; /* 0x18 */ f32 freqScaleStep;
/* 0x1C */ u16 unk_1C; /* 0x1C */ u16 freqScaleTimer;
} unk_50_s; // size = 0x20 } ActiveSequenceChannelData; // size = 0x20
typedef struct { typedef struct {
/* 0x000 */ f32 volCur; /* 0x000 */ f32 volCur;
/* 0x004 */ f32 volTarget; /* 0x004 */ f32 volTarget;
/* 0x008 */ f32 unk_08; /* 0x008 */ f32 volStep;
/* 0x00C */ u16 unk_0C; /* 0x00C */ u16 volTimer;
/* 0x00E */ u8 volScales[0x4]; /* 0x00E */ u8 volScales[4];
/* 0x012 */ u8 volFadeTimer; /* 0x012 */ u8 volFadeTimer;
/* 0x013 */ u8 fadeVolUpdate; /* 0x013 */ u8 fadeVolUpdate;
/* 0x014 */ u32 unk_14; /* 0x014 */ u32 tempoCmd;
/* 0x018 */ u16 unk_18; /* 0x018 */ u16 tempoOriginal; // stores the original tempo before modifying it (to reset back to)
/* 0x01C */ f32 unk_1C; /* 0x01C */ f32 tempoCur;
/* 0x020 */ f32 unk_20; /* 0x020 */ f32 tempoTarget;
/* 0x024 */ f32 unk_24; /* 0x024 */ f32 tempoStep;
/* 0x028 */ u16 unk_28; /* 0x028 */ u16 tempoTimer;
/* 0x02C */ u32 unk_2C[8]; /* 0x02C */ u32 setupCmd[8]; // a queue of cmds to execute once the player is disabled
/* 0x04C */ u8 unk_4C; /* 0x04C */ u8 setupCmdTimer; // only execute setup commands when the timer is at 0.
/* 0x04D */ u8 unk_4D; /* 0x04D */ u8 setupCmdNum; // number of setup commands requested once the player is disabled
/* 0x04E */ u8 unk_4E; /* 0x04E */ u8 setupFadeTimer;
/* 0x050 */ unk_50_s unk_50[0x10]; /* 0x050 */ ActiveSequenceChannelData channelData[16];
/* 0x250 */ u16 unk_250; /* 0x250 */ u16 freqScaleChannelFlags;
/* 0x252 */ u16 unk_252; /* 0x252 */ u16 volChannelFlags;
/* 0x254 */ u16 unk_254; /* 0x254 */ u16 seqId; // active seqId currently playing. Resets when sequence stops
/* 0x256 */ u16 unk_256; /* 0x256 */ u16 prevSeqId; // last seqId played on a player. Does not reset when sequence stops
/* 0x258 */ u16 unk_258; /* 0x258 */ u16 channelPortMask;
/* 0x25C */ u32 unk_25C; /* 0x25C */ u32 startSeqCmd; // This name comes from MM
/* 0x260 */ u8 unk_260; /* 0x260 */ u8 isWaitingForFonts; // This name comes from MM
} unk_D_8016E750; // size = 0x264 } ActiveSequence; // size = 0x264
typedef enum { typedef enum {
/* 0 */ BANK_PLAYER, /* 0 */ BANK_PLAYER,

View file

@ -30,6 +30,14 @@ typedef enum {
/* 13 */ SKYBOX_DMA_PAL2_START /* 13 */ SKYBOX_DMA_PAL2_START
} SkyboxDmaState; } SkyboxDmaState;
typedef enum {
/* 0 */ SANDSTORM_OFF,
/* 1 */ SANDSTORM_FILL,
/* 2 */ SANDSTORM_UNFILL,
/* 3 */ SANDSTORM_ACTIVE,
/* 4 */ SANDSTORM_DISSIPATE
} SandstormState;
typedef struct { typedef struct {
/* 0x00 */ u8 state; /* 0x00 */ u8 state;
/* 0x01 */ u8 flashRed; /* 0x01 */ u8 flashRed;

View file

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

View file

@ -314,8 +314,34 @@ enum SceneID {
/* 0x6E */ SCENE_ID_MAX /* 0x6E */ SCENE_ID_MAX
}; };
// this define exists to preserve shiftability for an unused scene that is
// listed in the entrance table
#define SCENE_UNUSED_6E SCENE_ID_MAX
#undef DEFINE_SCENE #undef DEFINE_SCENE
// Entrance Index Enum
#define DEFINE_ENTRANCE(enum, _1, _2, _3, _4, _5, _6) enum,
typedef enum {
#include "tables/entrance_table.h"
/* 0x614 */ ENTR_MAX
} EntranceIndex;
#define ENTR_LOAD_OPENING -1
typedef enum {
/* 0x7FF9 */ ENTR_RETURN_YOUSEI_IZUMI_YOKO = 0x7FF9, // Great Fairy Fountain (spells)
/* 0x7FFA */ ENTR_RETURN_SYATEKIJYOU, // Shooting gallery
/* 0x7FFB */ ENTR_RETURN_2, // unused
/* 0x7FFC */ ENTR_RETURN_SHOP1, // Bazaar
/* 0x7FFD */ ENTR_RETURN_4, // unused
/* 0x7FFE */ ENTR_RETURN_DAIYOUSEI_IZUMI, // Great Fairy Fountain (magic, double magic, double defense)
/* 0x7FFF */ ENTR_RETURN_GROTTO // Grottos and normal Fairy Fountain
} ReturnEntranceIndex;
#undef DEFINE_ENTRANCE
typedef enum { typedef enum {
/* 0 */ SDC_DEFAULT, /* 0 */ SDC_DEFAULT,
/* 1 */ SDC_HYRULE_FIELD, /* 1 */ SDC_HYRULE_FIELD,

View file

@ -50,11 +50,16 @@ typedef struct {
/* 0x004 */ Color_RGBA8_u32 envColor; /* 0x004 */ Color_RGBA8_u32 envColor;
/* 0x008 */ s32 texX; /* 0x008 */ s32 texX;
/* 0x00C */ s32 texY; /* 0x00C */ s32 texY;
/* 0x010 */ s32 step; // /* 0x010 */ s32 step;
/* 0x014 */ u8 unk_14; // /* 0x014 */ u8 unk_14;
/* 0x015 */ u8 typeColor; // /* 0x015 */ u8 typeColor;
/* 0x016 */ u8 speed; // /* 0x016 */ u8 speed;
/* 0x017 */ u8 effect; // /* 0x017 */ u8 effect;
/* 0x010 */ s32 speed;
/* 0x014 */ u8 direction;
/* 0x015 */ u8 colorType;
/* 0x016 */ u8 speedType;
/* 0x017 */ u8 appearanceType;
/* 0x018 */ u8 isDone; /* 0x018 */ u8 isDone;
/* 0x019 */ u8 frame; /* 0x019 */ u8 frame;
/* 0x01A */ u16 normal; /* 0x01A */ u16 normal;

View file

@ -33,5 +33,10 @@
<string>public.app-category.games</string> <string>public.app-category.games</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>10.15</string> <string>10.15</string>
<key>LSArchitecturePriority</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
</dict> </dict>
</plist> </plist>

View file

@ -14,11 +14,6 @@ if [ ! -e "$SHIP_HOME"/mods ]; then
touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt
fi fi
arch_name="$(uname -m)" "$RESPATH"/soh-macos
launch_arch="arm64"
if [ "${arch_name}" = "x86_64" ] && [ "$(sysctl -in sysctl.proc_translated)" != "1" ]; then
launch_arch="x86_64"
fi
arch -${launch_arch} "$RESPATH"/soh-macos
exit exit

View file

@ -74,7 +74,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
{ ACTOR_EN_BUBBLE, "Shabom" }, { ACTOR_EN_BUBBLE, "Shabom" },
{ ACTOR_DOOR_SHUTTER, "Shutter Door" }, { ACTOR_DOOR_SHUTTER, "Shutter Door" },
{ ACTOR_EN_DODOJR, "Baby Dodongo" }, { ACTOR_EN_DODOJR, "Baby Dodongo" },
{ ACTOR_EN_BDFIRE, "Empty" }, { ACTOR_EN_BDFIRE, "King Dodongo's Fire Breath" },
{ ACTOR_EN_BOOM, "Boomerang" }, { ACTOR_EN_BOOM, "Boomerang" },
{ ACTOR_EN_TORCH2, "Dark Link" }, { ACTOR_EN_TORCH2, "Dark Link" },
{ ACTOR_EN_BILI, "Biri" }, { ACTOR_EN_BILI, "Biri" },
@ -132,7 +132,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
{ ACTOR_BG_TOKI_HIKARI, "Windows (Temple of Time)" }, { ACTOR_BG_TOKI_HIKARI, "Windows (Temple of Time)" },
{ ACTOR_EN_YUKABYUN, "Flying Floor Tile" }, { ACTOR_EN_YUKABYUN, "Flying Floor Tile" },
{ ACTOR_BG_TOKI_SWD, "Master Sword" }, { ACTOR_BG_TOKI_SWD, "Master Sword" },
{ ACTOR_EN_FHG_FIRE, "Empty" }, { ACTOR_EN_FHG_FIRE, "Phantom Ganon's Lighting Attack" },
{ ACTOR_BG_MJIN, "Warp Song Pad" }, { ACTOR_BG_MJIN, "Warp Song Pad" },
{ ACTOR_BG_HIDAN_KOUSI, "Sliding Metal Gate" }, { ACTOR_BG_HIDAN_KOUSI, "Sliding Metal Gate" },
{ ACTOR_DOOR_TOKI, "Door of Time Collision" }, { ACTOR_DOOR_TOKI, "Door of Time Collision" },
@ -548,6 +548,10 @@ int ActorDB::RetrieveId(const std::string& name) {
return entry->second; return entry->second;
} }
int ActorDB::GetEntryCount() {
return db.size();
}
ActorDB::Entry::Entry() { ActorDB::Entry::Entry() {
entry.name = nullptr; entry.name = nullptr;
entry.desc = nullptr; entry.desc = nullptr;

View file

@ -64,6 +64,7 @@ public:
static void AddBuiltInCustomActors(); static void AddBuiltInCustomActors();
int GetEntryCount();
private: private:
Entry& AddEntry(const std::string& name, const std::string& desc, size_t index); Entry& AddEntry(const std::string& name, const std::string& desc, size_t index);
Entry& AddEntry(const std::string& name, const std::string& desc, const ActorInit& init); Entry& AddEntry(const std::string& name, const std::string& desc, const ActorInit& init);

View file

@ -12,6 +12,7 @@
#include <Utils/StringHelper.h> #include <Utils/StringHelper.h>
#include "../../UIWidgets.hpp" #include "../../UIWidgets.hpp"
#include "AudioCollection.h" #include "AudioCollection.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
Vec3f pos = { 0.0f, 0.0f, 0.0f }; Vec3f pos = { 0.0f, 0.0f, 0.0f };
f32 freqScale = 1.0f; f32 freqScale = 1.0f;
@ -78,7 +79,12 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) {
void RandomizeGroup(SeqType type) { void RandomizeGroup(SeqType type) {
std::vector<u16> values; std::vector<u16> values;
// An empty IncludedSequences set means that the AudioEditor window has never been drawn
if (AudioCollection::Instance->GetIncludedSequences().empty()) {
AudioCollection::Instance->InitializeShufflePool();
}
// use a while loop to add duplicates if we don't have enough included sequences // use a while loop to add duplicates if we don't have enough included sequences
while (values.size() < AuthenticCountBySequenceType(type)) { while (values.size() < AuthenticCountBySequenceType(type)) {
for (const auto& seqData : AudioCollection::Instance->GetIncludedSequences()) { for (const auto& seqData : AudioCollection::Instance->GetIncludedSequences()) {
@ -123,6 +129,34 @@ void ResetGroup(const std::map<u16, SequenceInfo>& map, SeqType type) {
} }
} }
void LockGroup(const std::map<u16, SequenceInfo>& map, SeqType type) {
for (const auto& [defaultValue, seqData] : map) {
if (seqData.category == type) {
// Only save authentic sequence CVars
if (seqData.category == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
CVarSetInteger(cvarLockKey.c_str(), 1);
}
}
}
void UnlockGroup(const std::map<u16, SequenceInfo>& map, SeqType type) {
for (const auto& [defaultValue, seqData] : map) {
if (seqData.category == type) {
// Only save authentic sequence CVars
if (seqData.category == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
CVarSetInteger(cvarLockKey.c_str(), 0);
}
}
}
void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequenceType) { void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequenceType) {
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(sfxKey); const std::string cvarKey = AudioCollection::Instance->GetCvarKey(sfxKey);
const std::string hiddenKey = "##" + cvarKey; const std::string hiddenKey = "##" + cvarKey;
@ -163,6 +197,8 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
const std::string hiddenTabId = "##" + tabId; const std::string hiddenTabId = "##" + tabId;
const std::string resetAllButton = "Reset All" + hiddenTabId; const std::string resetAllButton = "Reset All" + hiddenTabId;
const std::string randomizeAllButton = "Randomize All" + hiddenTabId; const std::string randomizeAllButton = "Randomize All" + hiddenTabId;
const std::string lockAllButton = "Lock All" + hiddenTabId;
const std::string unlockAllButton = "Unlock All" + hiddenTabId;
if (ImGui::Button(resetAllButton.c_str())) { if (ImGui::Button(resetAllButton.c_str())) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM); auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
@ -184,6 +220,28 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
ReplayCurrentBGM(); ReplayCurrentBGM();
} }
} }
ImGui::SameLine();
if (ImGui::Button(lockAllButton.c_str())) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
LockGroup(map, type);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
}
}
ImGui::SameLine();
if (ImGui::Button(unlockAllButton.c_str())) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
UnlockGroup(map, type);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
}
}
ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit); ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
@ -350,6 +408,19 @@ void DrawTypeChip(SeqType type) {
ImGui::EndDisabled(); ImGui::EndDisabled();
} }
void AudioEditorRegisterOnSceneInitHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) {
if (CVarGetInteger("gAudioEditor.RandomizeAllOnNewScene", 0)) {
AudioEditor_RandomizeAll();
}
});
}
void AudioEditor::InitElement() {
AudioEditorRegisterOnSceneInitHook();
}
void AudioEditor::DrawElement() { void AudioEditor::DrawElement() {
AudioCollection::Instance->InitializeShufflePool(); AudioCollection::Instance->InitializeShufflePool();
@ -359,6 +430,28 @@ void AudioEditor::DrawElement() {
return; return;
} }
float buttonSegments = ImGui::GetContentRegionAvail().x / 4;
if (ImGui::Button("Randomize All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_RandomizeAll();
}
UIWidgets::Tooltip("Randomizes all unlocked music and sound effects across tab groups");
ImGui::SameLine();
if (ImGui::Button("Reset All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_ResetAll();
}
UIWidgets::Tooltip("Resets all unlocked music and sound effects across tab groups");
ImGui::SameLine();
if (ImGui::Button("Lock All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_LockAll();
}
UIWidgets::Tooltip("Locks all music and sound effects across tab groups");
ImGui::SameLine();
if (ImGui::Button("Unlock All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_UnlockAll();
}
UIWidgets::Tooltip("Unlocks all music and sound effects across tab groups");
if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
if (ImGui::BeginTabItem("Background Music")) { if (ImGui::BeginTabItem("Background Music")) {
Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD); Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD);
@ -422,8 +515,8 @@ void AudioEditor::DrawElement() {
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::NewLine(); ImGui::NewLine();
ImGui::PopItemWidth(); ImGui::PopItemWidth();
UIWidgets::EnhancementSliderFloat("Link's voice pitch multiplier: %f", "##linkVoiceFreqMultiplier", UIWidgets::EnhancementSliderFloat("Link's voice pitch multiplier: %.1f %%", "##linkVoiceFreqMultiplier",
"gLinkVoiceFreqMultiplier", 0.4, 2.5, "", 1.0, false, false); "gLinkVoiceFreqMultiplier", 0.4, 2.5, "", 1.0, true, true);
ImGui::SameLine(); ImGui::SameLine();
const std::string resetButton = "Reset##linkVoiceFreqMultiplier"; const std::string resetButton = "Reset##linkVoiceFreqMultiplier";
if (ImGui::Button(resetButton.c_str())) { if (ImGui::Button(resetButton.c_str())) {
@ -431,6 +524,10 @@ void AudioEditor::DrawElement() {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
ImGui::NewLine();
UIWidgets::EnhancementCheckbox("Randomize All Music and Sound Effects on New Scene", "gAudioEditor.RandomizeAllOnNewScene");
UIWidgets::Tooltip("Enables randomizing all unlocked music and sound effects when you enter a new scene.");
ImGui::NewLine(); ImGui::NewLine();
ImGui::PushItemWidth(-FLT_MIN); ImGui::PushItemWidth(-FLT_MIN);
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
@ -625,3 +722,19 @@ void AudioEditor_ResetAll() {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
ReplayCurrentBGM(); ReplayCurrentBGM();
} }
void AudioEditor_LockAll() {
for (auto type : allTypes) {
LockGroup(AudioCollection::Instance->GetAllSequences(), type);
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
void AudioEditor_UnlockAll() {
for (auto type : allTypes) {
UnlockGroup(AudioCollection::Instance->GetAllSequences(), type);
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}

View file

@ -4,6 +4,9 @@
#ifdef __cplusplus #ifdef __cplusplus
#include <libultraship/libultraship.h> #include <libultraship/libultraship.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h> #include <ImGui/imgui.h>
class AudioEditor : public LUS::GuiWindow { class AudioEditor : public LUS::GuiWindow {
@ -11,13 +14,15 @@ class AudioEditor : public LUS::GuiWindow {
using LUS::GuiWindow::GuiWindow; using LUS::GuiWindow::GuiWindow;
void DrawElement() override; void DrawElement() override;
void InitElement() override {}; void InitElement() override;
void UpdateElement() override {}; void UpdateElement() override {};
~AudioEditor() {}; ~AudioEditor() {};
}; };
void AudioEditor_RandomizeAll(); void AudioEditor_RandomizeAll();
void AudioEditor_ResetAll(); void AudioEditor_ResetAll();
void AudioEditor_LockAll();
void AudioEditor_UnlockAll();
extern "C" { extern "C" {
#endif #endif

View file

@ -175,33 +175,33 @@ void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) {
// Gohma & Phantom Ganon // Gohma & Phantom Ganon
if (warpPosX == -100 && warpPosZ == -170) { if (warpPosX == -100 && warpPosZ == -170) {
if (gSaveContext.linkAge == LINK_AGE_CHILD) { if (gSaveContext.linkAge == LINK_AGE_CHILD) {
play->nextEntranceIndex = 0x040F; play->nextEntranceIndex = ENTR_DEKU_TREE_BOSS_0;
} else { } else {
play->nextEntranceIndex = 0x000C; play->nextEntranceIndex = ENTR_FOREST_TEMPLE_BOSS_0;
} }
// King Dodongo & Volvagia // King Dodongo & Volvagia
} else if (warpPosX == 100 && warpPosZ == -170) { } else if (warpPosX == 100 && warpPosZ == -170) {
if (gSaveContext.linkAge == LINK_AGE_CHILD) { if (gSaveContext.linkAge == LINK_AGE_CHILD) {
play->nextEntranceIndex = 0x040B; play->nextEntranceIndex = ENTR_DODONGOS_CAVERN_BOSS_0;
} else { } else {
play->nextEntranceIndex = 0x0305; play->nextEntranceIndex = ENTR_FIRE_TEMPLE_BOSS_0;
} }
// Barinade & Morb // Barinade & Morb
} else if (warpPosX == 199 && warpPosZ == 0) { } else if (warpPosX == 199 && warpPosZ == 0) {
if (gSaveContext.linkAge == LINK_AGE_CHILD) { if (gSaveContext.linkAge == LINK_AGE_CHILD) {
play->nextEntranceIndex = 0x0301; play->nextEntranceIndex = ENTR_JABU_JABU_BOSS_0;
} else { } else {
play->nextEntranceIndex = 0x0417; play->nextEntranceIndex = ENTR_WATER_TEMPLE_BOSS_0;
} }
// Twinrova // Twinrova
} else if (warpPosX == 100 && warpPosZ == 170) { } else if (warpPosX == 100 && warpPosZ == 170) {
play->nextEntranceIndex = 0x05EC; play->nextEntranceIndex = ENTR_SPIRIT_TEMPLE_BOSS_2;
// Bongo Bongo // Bongo Bongo
} else if (warpPosX == -100 && warpPosZ == 170) { } else if (warpPosX == -100 && warpPosZ == 170) {
play->nextEntranceIndex = 0x0413; play->nextEntranceIndex = ENTR_SHADOW_TEMPLE_BOSS_0;
// Ganondork // Ganondork
} else if (warpPosX == -199 && warpPosZ == 0) { } else if (warpPosX == -199 && warpPosZ == 0) {
play->nextEntranceIndex = 0x041F; play->nextEntranceIndex = ENTR_GANONDORF_BOSS_0;
} }
// If coming from a boss room, teleport back to Chamber of Sages and set flag. // If coming from a boss room, teleport back to Chamber of Sages and set flag.
} else { } else {
@ -216,10 +216,10 @@ void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) {
BossRush_SetEquipment(LINK_AGE_ADULT); BossRush_SetEquipment(LINK_AGE_ADULT);
// Warp to credits. // Warp to credits.
} else if (gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_CHILD) { } else if (gSaveContext.bossRushOptions[BR_OPTIONS_BOSSES] == BR_CHOICE_BOSSES_CHILD) {
play->nextEntranceIndex = 0x6B; play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0xFFF2; gSaveContext.nextCutsceneIndex = 0xFFF2;
play->sceneLoadFlag = 0x14; play->transitionTrigger = TRANS_TRIGGER_START;
play->fadeTransition = 3; play->transitionType = TRANS_TYPE_FADE_WHITE;
} }
} }
} }
@ -293,7 +293,7 @@ void BossRush_InitSave() {
gSaveContext.questId = QUEST_BOSSRUSH; gSaveContext.questId = QUEST_BOSSRUSH;
gSaveContext.isBossRushPaused = 1; gSaveContext.isBossRushPaused = 1;
gSaveContext.entranceIndex = 107; gSaveContext.entranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.cutsceneIndex = 0x8000; gSaveContext.cutsceneIndex = 0x8000;
gSaveContext.isMagicAcquired = 1; gSaveContext.isMagicAcquired = 1;

View file

@ -1,413 +0,0 @@
#include "GameControlEditor.h"
#include <string>
#include <list>
#include <unordered_map>
#include <utility>
#include <iterator>
#include <variables.h>
#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 "../../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();
}
// CurrentPort is indexed started at 1 here due to the Generic tab, instead of 0 like in InputEditorWindow
// Therefore CurrentPort - 1 must always be used inside this function instead of CurrentPort
void DrawCustomButtons() {
auto inputEditorWindow = std::reinterpret_pointer_cast<LUS::InputEditorWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Input Editor"));
inputEditorWindow->DrawControllerSelect(CurrentPort - 1);
inputEditorWindow->DrawButton("Modifier 1", BTN_MODIFIER1, CurrentPort - 1, &BtnReading);
inputEditorWindow->DrawButton("Modifier 2", BTN_MODIFIER2, CurrentPort - 1, &BtnReading);
}
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");
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("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("gFirstPersonCameraSensitivity");
}
}
if (CVarGetInteger("gEnableFirstPersonSensitivity", 0)) {
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %d %%", "##FirstPersonSensitivity Horizontal",
"gFirstPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true);
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %d %%", "##FirstPersonSensitivity Vertical",
"gFirstPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true);
}
UIWidgets::Spacer(0);
window->EndGroupPanelPublic(0);
UIWidgets::Spacer(0);
window->BeginGroupPanelPublic("Free Look/Third-person Camera", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Enable Free Look", "gFreeCamera");
DrawHelpIcon("Enables free look camera control\nNote: You must remap C buttons off of the right stick in the "
"controller config menu, and map the camera stick to the right stick.");
UIWidgets::PaddedEnhancementCheckbox("Invert X Axis", "gInvertXAxis");
DrawHelpIcon("Inverts the Camera X Axis in:\n-Free Look");
UIWidgets::PaddedEnhancementCheckbox("Invert Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free Look");
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementSliderFloat("Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal",
"gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
DrawHelpIcon("Changes the sensitivity of the X axis control for Free Look");
UIWidgets::PaddedEnhancementSliderFloat("Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical",
"gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
DrawHelpIcon("Changes the sensitivity of the Y axis control for Free Look");
UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist",
"gFreeCameraDistMax", 100, 900, "", 185, true, false, true);
DrawHelpIcon("How far the camera sits from Link while in Free Look mode");
UIWidgets::PaddedEnhancementSliderInt("Transition Speed: %d", "##CamTranSpeed",
"gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true);
DrawHelpIcon("How quickly the camera changes to the distance specified above");
window->EndGroupPanelPublic(0);
}
void DrawDpadControlPanel(GameControlEditorWindow* window) {
if (!ImGui::CollapsingHeader("D-Pad Controls")) {
return;
}
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
window->BeginGroupPanelPublic("D-Pad Options", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("D-pad Support on Pause Screen", "gDpadPause");
DrawHelpIcon("Navigate Pause with the D-pad\nIf used with D-pad as Equip Items, you must hold C-Up to equip instead of navigate\n"
"To make the cursor only move a single space no matter how long a direction is held, manually set gDpadHoldChange to 0");
UIWidgets::PaddedEnhancementCheckbox("D-pad Support in Text Boxes", "gDpadText");
DrawHelpIcon("Navigate choices in text boxes, shop item selection, and the file select / name entry screens with the D-pad\n"
"To make the cursor only move a single space during name entry no matter how long a direction is held, manually set gDpadHoldChange to 0");
UIWidgets::PaddedEnhancementCheckbox("D-pad as Equip Items", "gDpadEquips");
DrawHelpIcon("Equip items and equipment on the D-pad\nIf used with D-pad on Pause Screen, you must hold C-Up to equip instead of navigate");
window->EndGroupPanelPublic(0);
}
void DrawMiscControlPanel(GameControlEditorWindow* window) {
if (!ImGui::CollapsingHeader("Miscellaneous Controls")) {
return;
}
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5));
window->BeginGroupPanelPublic("Misc Controls", ImGui::GetContentRegionAvail());
UIWidgets::PaddedText("Allow the cursor to be on any slot");
static const char* cursorOnAnySlot[3] = { "Only in Rando", "Always", "Never" };
UIWidgets::EnhancementCombobox("gPauseAnyCursor", cursorOnAnySlot, PAUSE_ANY_CURSOR_RANDO_ONLY);
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
"certain items.");
UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false);
DrawHelpIcon("Hold the assigned button to change the maximum walking speed\nTo change the assigned button, go into the Ports tabs above");
if (CVarGetInteger("gEnableWalkModify", 0)) {
UIWidgets::Spacer(5);
window->BeginGroupPanelPublic("Walk Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", "gWalkSpeedToggle", true, false);
UIWidgets::PaddedEnhancementCheckbox("Don't affect jump distance/velocity", "gWalkModifierDoesntChangeJump", true, false);
UIWidgets::PaddedEnhancementSliderFloat("Modifier 1: %d %%", "##WalkMod1", "gWalkModifierOne", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %d %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
window->EndGroupPanelPublic(0);
}
ImGui::EndDisabled();
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
window->EndGroupPanelPublic(0);
}
void DrawLEDControlPanel(GameControlEditorWindow* window) {
window->BeginGroupPanelPublic("LED Colors", ImGui::GetContentRegionAvail());
static const char* ledSources[] = { "Original Tunic Colors", "Cosmetics Tunic Colors", "Health Colors",
"Original Navi Targeting Colors", "Cosmetics Navi Targeting Colors", "Custom" };
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");
if (CVarGetInteger("gLedColorSource", 1) == LED_SOURCE_CUSTOM) {
UIWidgets::Spacer(3);
auto port1Color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
ImVec4 colorVec = { port1Color.r / 255.0f, port1Color.g / 255.0f, port1Color.b / 255.0f, 1.0f };
if (ImGui::ColorEdit3("", (float*)&colorVec, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) {
Color_RGB8 color;
color.r = colorVec.x * 255.0;
color.g = colorVec.y * 255.0;
color.b = colorVec.z * 255.0;
CVarSetColor24("gLedPort1Color", color);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
ImGui::SameLine();
ImGui::Text("Custom Color");
}
UIWidgets::PaddedEnhancementSliderFloat("Brightness: %d%%", "##LED_Brightness", "gLedBrightness",
0.0f, 1.0f, "", 1.0f, true, true);
DrawHelpIcon("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.");
window->EndGroupPanelPublic(0);
}
void GameControlEditorWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(465, 430), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Game Controls Configuration", &mIsVisible)) {
ImGui::BeginTabBar("##CustomControllers");
if (ImGui::BeginTabItem("Generic")) {
CurrentPort = 0;
ImGui::EndTabItem();
}
for (int i = 1; i <= 4; i++) {
if (ImGui::BeginTabItem(StringHelper::Sprintf("Port %d", i).c_str())) {
CurrentPort = i;
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
if (CurrentPort == 0) {
DrawOcarinaControlPanel(this);
DrawCameraControlPanel(this);
DrawDpadControlPanel(this);
DrawMiscControlPanel(this);
} else {
DrawCustomButtons();
if (CurrentPort == 1 && LUS::Context::GetInstance()->GetControlDeck()->GetDeviceFromPortIndex(0)->CanSetLed()) {
DrawLEDControlPanel(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();
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,107 @@
#pragma once
#include "stdint.h"
#include <libultraship/libultraship.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h>
#include <unordered_map>
#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:
using GuiWindow::GuiWindow;
~SohInputEditorWindow();
void DrawButton(const char* label, int32_t n64Btn, int32_t currentPort, int32_t* btnReading);
void DrawInputChip(const char* buttonName, ImVec4 color);
void DrawAnalogPreview(const char* label, ImVec2 stick, float deadzone = 0, bool gyro = false);
void DrawControllerSchema();
bool TestingRumble();
protected:
void InitElement() override;
void DrawElement() override;
void UpdateElement() override;
private:
void DrawStickDirectionLine(const char* axisDirectionName, uint8_t port, uint8_t stick, LUS::Direction direction,
ImVec4 color);
void DrawButtonLine(const char* buttonName, uint8_t port, uint16_t bitmask, ImVec4 color);
void DrawButtonLineEditMappingButton(uint8_t port, uint16_t bitmask, std::string id);
void DrawButtonLineAddMappingButton(uint8_t port, uint16_t bitmask);
void DrawStickDirectionLineEditMappingButton(uint8_t port, uint8_t stick, LUS::Direction direction, std::string id);
void DrawStickDirectionLineAddMappingButton(uint8_t port, uint8_t stick, LUS::Direction direction);
void DrawStickSection(uint8_t port, uint8_t stick, int32_t id, ImVec4 color);
void DrawRumbleSection(uint8_t port);
void DrawRemoveRumbleMappingButton(uint8_t port, std::string id);
void DrawAddRumbleMappingButton(uint8_t port);
void DrawLEDSection(uint8_t port);
void DrawRemoveLEDMappingButton(uint8_t port, std::string id);
void DrawAddLEDMappingButton(uint8_t port);
void DrawGyroSection(uint8_t port);
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;
std::shared_ptr<LUS::ControllerRumbleMapping> mRumbleMappingToTest;
// mBitmaskToMappingIds[port][bitmask] = { id0, id1, ... }
std::unordered_map<uint8_t, std::unordered_map<uint16_t, std::vector<std::string>>> mBitmaskToMappingIds;
// mStickDirectionToMappingIds[port][stick][direction] = { id0, id1, ... }
std::unordered_map<uint8_t,
std::unordered_map<uint8_t, std::unordered_map<LUS::Direction, std::vector<std::string>>>>
mStickDirectionToMappingIds;
void UpdateBitmaskToMappingIds(uint8_t port);
void UpdateStickDirectionToMappingIds(uint8_t port);
void GetButtonColorsForLUSDeviceIndex(LUS::LUSDeviceIndex lusIndex, ImVec4& buttonColor,
ImVec4& buttonHoveredColor);
void DrawLinkTab();
void DrawIvanTab();
void DrawDebugPortTab(uint8_t portIndex, std::string customName = "");
void DrawDevicesTab();
std::set<uint16_t> mButtonsBitmasks;
std::set<uint16_t> mDpadBitmasks;
std::set<uint16_t> mModifierButtonsBitmasks;
void DrawButtonDeviceIcons(uint8_t portIndex, std::set<uint16_t> bitmasks);
void DrawAnalogStickDeviceIcons(uint8_t portIndex, LUS::Stick stick);
void DrawRumbleDeviceIcons(uint8_t portIndex);
void DrawGyroDeviceIcons(uint8_t portIndex);
void DrawLEDDeviceIcons(uint8_t portIndex);
bool mInputEditorPopupOpen;
void DrawSetDefaultsButton(uint8_t portIndex);
void DrawClearAllButton(uint8_t portIndex);
};

View file

@ -67,6 +67,7 @@ typedef enum {
GROUP_EQUIPMENT, GROUP_EQUIPMENT,
GROUP_CONSUMABLE, GROUP_CONSUMABLE,
GROUP_HUD, GROUP_HUD,
GROUP_KALEIDO,
GROUP_TITLE, GROUP_TITLE,
GROUP_NPC, GROUP_NPC,
GROUP_WORLD, GROUP_WORLD,
@ -75,6 +76,7 @@ typedef enum {
GROUP_SPIN_ATTACK, GROUP_SPIN_ATTACK,
GROUP_TRAILS, GROUP_TRAILS,
GROUP_NAVI, GROUP_NAVI,
GROUP_IVAN,
} CosmeticGroup; } CosmeticGroup;
std::map<CosmeticGroup, const char*> groupLabels = { std::map<CosmeticGroup, const char*> groupLabels = {
@ -85,6 +87,7 @@ std::map<CosmeticGroup, const char*> groupLabels = {
{ GROUP_EQUIPMENT, "Equipment" }, { GROUP_EQUIPMENT, "Equipment" },
{ GROUP_CONSUMABLE, "Consumables" }, { GROUP_CONSUMABLE, "Consumables" },
{ GROUP_HUD, "HUD" }, { GROUP_HUD, "HUD" },
{ GROUP_KALEIDO, "Pause Menu" },
{ GROUP_TITLE, "Title Screen" }, { GROUP_TITLE, "Title Screen" },
{ GROUP_NPC, "NPCs" }, { GROUP_NPC, "NPCs" },
{ GROUP_WORLD, "World" }, { GROUP_WORLD, "World" },
@ -93,6 +96,7 @@ std::map<CosmeticGroup, const char*> groupLabels = {
{ GROUP_SPIN_ATTACK, "Spin Attack" }, { GROUP_SPIN_ATTACK, "Spin Attack" },
{ GROUP_TRAILS, "Trails" }, { GROUP_TRAILS, "Trails" },
{ GROUP_NAVI, "Navi" }, { GROUP_NAVI, "Navi" },
{ GROUP_IVAN, "Ivan" }
}; };
typedef struct { typedef struct {
@ -265,6 +269,38 @@ 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_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("Hud_NameTagActorBackground", "Nametag Background", GROUP_HUD, ImVec4(0, 0, 0, 80), true, false, true),
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", 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", 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 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", 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 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),
COSMETIC_OPTION("Title_FileChoose", "File Choose", GROUP_TITLE, ImVec4(100, 150, 255, 255), false, true, false), COSMETIC_OPTION("Title_FileChoose", "File Choose", GROUP_TITLE, ImVec4(100, 150, 255, 255), false, true, false),
COSMETIC_OPTION("Title_NintendoLogo", "Nintendo Logo", GROUP_TITLE, ImVec4( 0, 0, 255, 255), false, true, true), COSMETIC_OPTION("Title_NintendoLogo", "Nintendo Logo", GROUP_TITLE, ImVec4( 0, 0, 255, 255), false, true, true),
COSMETIC_OPTION("Title_N64LogoRed", "N64 Red", GROUP_TITLE, ImVec4(150, 0, 0, 255), false, true, true), COSMETIC_OPTION("Title_N64LogoRed", "N64 Red", GROUP_TITLE, ImVec4(150, 0, 0, 255), false, true, true),
@ -316,6 +352,9 @@ static std::map<std::string, CosmeticOption> cosmeticOptions = {
COSMETIC_OPTION("Navi_EnemySecondary", "Enemy Secondary", GROUP_NAVI, ImVec4(200, 155, 0, 0), false, true, true), COSMETIC_OPTION("Navi_EnemySecondary", "Enemy Secondary", GROUP_NAVI, ImVec4(200, 155, 0, 0), false, true, true),
COSMETIC_OPTION("Navi_PropsPrimary", "Props Primary", GROUP_NAVI, ImVec4( 0, 255, 0, 255), false, true, false), COSMETIC_OPTION("Navi_PropsPrimary", "Props Primary", GROUP_NAVI, ImVec4( 0, 255, 0, 255), false, true, false),
COSMETIC_OPTION("Navi_PropsSecondary", "Props Secondary", GROUP_NAVI, ImVec4( 0, 255, 0, 0), false, true, true), COSMETIC_OPTION("Navi_PropsSecondary", "Props Secondary", GROUP_NAVI, ImVec4( 0, 255, 0, 0), false, true, true),
COSMETIC_OPTION("Ivan_IdlePrimary", "Ivan Idle Primary", GROUP_IVAN, ImVec4(255, 255, 255, 255), false, true, false),
COSMETIC_OPTION("Ivan_IdleSecondary", "Ivan Idle Secondary", GROUP_IVAN, ImVec4( 0, 255, 0, 255), false, true, true),
COSMETIC_OPTION("NPC_FireKeesePrimary", "Fire Keese Primary", GROUP_NPC, ImVec4(255, 255, 255, 255), false, true, false), COSMETIC_OPTION("NPC_FireKeesePrimary", "Fire Keese Primary", GROUP_NPC, ImVec4(255, 255, 255, 255), false, true, false),
COSMETIC_OPTION("NPC_FireKeeseSecondary", "Fire Keese Secondary", GROUP_NPC, ImVec4(255, 255, 255, 255), false, true, true), COSMETIC_OPTION("NPC_FireKeeseSecondary", "Fire Keese Secondary", GROUP_NPC, ImVec4(255, 255, 255, 255), false, true, true),
@ -1026,12 +1065,16 @@ void ApplyOrResetCustomGfxPatches(bool manualChange) {
if (manualChange || CVarGetInteger(npcGoldenSkulltula.rainbowCvar, 0)) { if (manualChange || CVarGetInteger(npcGoldenSkulltula.rainbowCvar, 0)) {
static Color_RGBA8 defaultColor = {npcGoldenSkulltula.defaultColor.x, npcGoldenSkulltula.defaultColor.y, npcGoldenSkulltula.defaultColor.z, npcGoldenSkulltula.defaultColor.w}; static Color_RGBA8 defaultColor = {npcGoldenSkulltula.defaultColor.x, npcGoldenSkulltula.defaultColor.y, npcGoldenSkulltula.defaultColor.z, npcGoldenSkulltula.defaultColor.w};
Color_RGBA8 color = CVarGetColor(npcGoldenSkulltula.cvar, defaultColor); 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(gSkulltulaTokenDL, "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(gSkulltulaTokenDL, "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(gSkulltulaTokenFlameDL, "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(gSkulltulaTokenFlameDL, "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(gGiSkulltulaTokenDL, "NPC_GoldenSkulltula5", npcGoldenSkulltula.changedCvar, 5, 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(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"); static CosmeticOption& npcGerudo = cosmeticOptions.at("NPC_Gerudo");
@ -1142,6 +1185,43 @@ void DrawScaleSlider(const std::string CvarName,float DefaultValue){
//Disabled for now. feature not done and several fixes needed to be merged. //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); //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(){ void Draw_Placements(){
if (ImGui::BeginTable("tableMargins", 1, FlagsTable)) { if (ImGui::BeginTable("tableMargins", 1, FlagsTable)) {
ImGui::TableSetupColumn("General margins settings", FlagsCell, TablesCellsWidth); ImGui::TableSetupColumn("General margins settings", FlagsCell, TablesCellsWidth);
@ -1208,126 +1288,13 @@ void Draw_Placements(){
ImGui::EndTable(); ImGui::EndTable();
} }
} }
if (ImGui::CollapsingHeader("B Button position")) { 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);
if (ImGui::BeginTable("tablebbtn", 1, FlagsTable)) { 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);
ImGui::TableSetupColumn("B Button settings", FlagsCell, TablesCellsWidth); 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);
Table_InitHeader(false); C_Button_Dropdown("C Button Up position", "tablecubtn", "C Button Up settings", "C Button Up", "gCBtnU", "gCBtnUPosType", 0.5f);
DrawUseMarginsSlider("B Button", "gBBtn"); C_Button_Dropdown("C Button Down position", "tablecdbtn", "C Button Down settings", "C Button Down", "gCBtnD", "gCBtnDPosType", 0.87f);
DrawPositionsRadioBoxes("gBBtn"); C_Button_Dropdown("C Button Left position", "tableclbtn", "C Button Left settings", "C Button Left", "gCBtnL", "gCBtnLPosType", 0.87f);
DrawPositionSlider("gBBtn", 0, ImGui::GetWindowViewport()->Size.y/4+50, -1, ImGui::GetWindowViewport()->Size.x-50); C_Button_Dropdown("C Button Right position", "tablecrbtn", "C Button Right settings", "C Button Right", "gCBtnR", "gCBtnRPosType", 0.87f);
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();
}
}
if (CVarGetInteger("gDpadEquips",0) && ImGui::CollapsingHeader("DPad items position")) { if (CVarGetInteger("gDpadEquips",0) && ImGui::CollapsingHeader("DPad items position")) {
if (ImGui::BeginTable("tabledpaditems", 1, FlagsTable)) { if (ImGui::BeginTable("tabledpaditems", 1, FlagsTable)) {
ImGui::TableSetupColumn("DPad items settings", FlagsCell, TablesCellsWidth); ImGui::TableSetupColumn("DPad items settings", FlagsCell, TablesCellsWidth);
@ -1347,115 +1314,15 @@ void Draw_Placements(){
ImGui::EndTable(); ImGui::EndTable();
} }
} }
if (ImGui::CollapsingHeader("Minimaps position")) { 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);
if (ImGui::BeginTable("tableminimapspos", 1, FlagsTable)) { 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);
ImGui::TableSetupColumn("minimaps settings", FlagsCell, TablesCellsWidth); 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);
Table_InitHeader(false); 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);
DrawUseMarginsSlider("Minimap", "gMinimap"); 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);
DrawPositionsRadioBoxes("gMinimap", false); 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);
DrawPositionSlider("gMinimap", (ImGui::GetWindowViewport()->Size.y/3)*-1, ImGui::GetWindowViewport()->Size.y/3, ImGui::GetWindowViewport()->Size.x*-1, ImGui::GetWindowViewport()->Size.x/2); 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);
DrawScaleSlider("gMinimap",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);
ImGui::NewLine(); 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);
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();
}
}
if (ImGui::CollapsingHeader("Enemy Health Bar position")) { if (ImGui::CollapsingHeader("Enemy Health Bar position")) {
if (ImGui::BeginTable("enemyhealthbar", 1, FlagsTable)) { if (ImGui::BeginTable("enemyhealthbar", 1, FlagsTable)) {
ImGui::TableSetupColumn("Enemy Health Bar settings", FlagsCell, TablesCellsWidth); ImGui::TableSetupColumn("Enemy Health Bar settings", FlagsCell, TablesCellsWidth);
@ -1483,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() { void DrawSillyTab() {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0)); ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (CVarGetInteger("gLetItSnow", 0)) { if (CVarGetInteger("gLetItSnow", 0)) {
@ -1491,7 +1372,7 @@ void DrawSillyTab() {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
} }
if (UIWidgets::EnhancementSliderFloat("Link Body Scale: %f", "##Link_BodyScale", "gCosmetics.Link_BodyScale.Value", 0.001f, 0.025f, "", 0.01f, true)) { if (UIWidgets::EnhancementSliderFloat("Link Body Scale: %.3fx", "##Link_BodyScale", "gCosmetics.Link_BodyScale.Value", 0.001f, 0.025f, "", 0.01f, true)) {
CVarSetInteger("gCosmetics.Link_BodyScale.Changed", 1); CVarSetInteger("gCosmetics.Link_BodyScale.Changed", 1);
} }
ImGui::SameLine(); ImGui::SameLine();
@ -1506,70 +1387,31 @@ void DrawSillyTab() {
player->actor.scale.z = 0.01f; player->actor.scale.z = 0.01f;
} }
} }
if (UIWidgets::EnhancementSliderFloat("Link Head Scale: %f", "##Link_HeadScale", "gCosmetics.Link_HeadScale.Value", 0.4f, 4.0f, "", 1.0f, false)) { 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); CVarSetInteger("gCosmetics.Link_HeadScale.Changed", 1);
} }
ImGui::SameLine(); Reset_Option_Double("Reset##Link_HeadScale", "gCosmetics.Link_HeadScale");
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: %f", "##Link_SwordScale", "gCosmetics.Link_SwordScale.Value", 1.0f, 2.5f, "", 1.0f, false)) { 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); CVarSetInteger("gCosmetics.Link_SwordScale.Changed", 1);
} }
ImGui::SameLine(); Reset_Option_Double("Reset##Link_SwordScale", "gCosmetics.Link_SwordScale");
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: %f", "##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength", -300.0f, 1000.0f, "", 0.0f, false); UIWidgets::EnhancementSliderFloat("Bunny Hood Length: %f", "##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength", -300.0f, 1000.0f, "", 0.0f, false);
ImGui::SameLine(); Reset_Option_Single("Reset##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength");
if (ImGui::Button("Reset##BunnyHood_EarLength")) {
CVarClear("gCosmetics.BunnyHood_EarLength");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("Bunny Hood Spread: %f", "##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread", -300.0f, 500.0f, "", 0.0f, false); UIWidgets::EnhancementSliderFloat("Bunny Hood Spread: %f", "##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread", -300.0f, 500.0f, "", 0.0f, false);
ImGui::SameLine(); Reset_Option_Single("Reset##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread");
if (ImGui::Button("Reset##BunnyHood_EarSpread")) { UIWidgets::EnhancementSliderFloat("Goron Neck Length: %f", "##Goron_NeckLength", "gCosmetics.Goron_NeckLength", 0.0f, 1000.0f, "", 0.0f, false);
CVarClear("gCosmetics.BunnyHood_EarSpread"); Reset_Option_Single("Reset##Goron_NeckLength", "gCosmetics.Goron_NeckLength");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("Goron Neck Length: %f", "##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();
}
UIWidgets::EnhancementCheckbox("Unfix Goron Spin", "gUnfixGoronSpin"); UIWidgets::EnhancementCheckbox("Unfix Goron Spin", "gUnfixGoronSpin");
UIWidgets::EnhancementSliderFloat("Fairies Size: %f", "##Fairies_Size", "gCosmetics.Fairies_Size", 0.25f, 5.0f, "", 1.0f, false); UIWidgets::EnhancementSliderFloat("Fairies Size: %f", "##Fairies_Size", "gCosmetics.Fairies_Size", 0.25f, 5.0f, "", 1.0f, false);
ImGui::SameLine(); Reset_Option_Single("Reset##Fairies_Size", "gCosmetics.Fairies_Size");
if (ImGui::Button("Reset##Fairies_Size")) {
CVarClear("gCosmetics.Fairies_Size");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("N64 Logo Spin Speed: %f", "##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed", 0.25f, 5.0f, "", 1.0f, false); UIWidgets::EnhancementSliderFloat("N64 Logo Spin Speed: %f", "##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed", 0.25f, 5.0f, "", 1.0f, false);
ImGui::SameLine(); Reset_Option_Single("Reset##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed");
if (ImGui::Button("Reset##N64Logo_SpinSpeed")) {
CVarClear("gCosmetics.N64Logo_SpinSpeed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::EnhancementSliderFloat("Moon Size: %f", "##Moon_Size", "gCosmetics.Moon_Size", 0.5f, 2.0f, "", 1.0f, false); UIWidgets::EnhancementSliderFloat("Moon Size: %f", "##Moon_Size", "gCosmetics.Moon_Size", 0.5f, 2.0f, "", 1.0f, false);
ImGui::SameLine(); Reset_Option_Single("Reset##Moon_Size", "gCosmetics.Moon_Size");
if (ImGui::Button("Reset##Moon_Size")) {
CVarClear("gCosmetics.Moon_Size");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
if (UIWidgets::EnhancementSliderFloat("Kak Windmill Speed: %f", "##Kak_Windmill_Speed", "gCosmetics.Kak_Windmill_Speed.Value", 100.0f, 6000.0f, "", 100.0f, false)) { 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); CVarSetInteger("gCosmetics.Kak_Windmill_Speed.Changed", 1);
} }
ImGui::SameLine(); Reset_Option_Double("Reset##Kak_Windmill_Speed", "gCosmetics.Kak_Windmill_Speed");
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();
}
ImGui::EndDisabled(); ImGui::EndDisabled();
} }
@ -1592,6 +1434,104 @@ void CopyMultipliedColor(CosmeticOption& cosmeticOptionSrc, CosmeticOption& cosm
CVarSetInteger((cosmeticOptionTarget.changedCvar), 1); CVarSetInteger((cosmeticOptionTarget.changedCvar), 1);
} }
void ToggleRainbow(CosmeticOption& cosmeticOption, bool state) {
if (state) {
CVarSetInteger(cosmeticOption.rainbowCvar, 1);
CVarSetInteger(cosmeticOption.changedCvar, 1);
} else {
CVarClear(cosmeticOption.rainbowCvar);
CVarClear(cosmeticOption.changedCvar);
}
}
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.
if (cosmeticOption.label == "Bow Body") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Equipment_BowTips"), 0.5f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Equipment_BowHandle"), 1.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOption, 4.0f);
} else if (cosmeticOption.label == "Idle Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_IdleSecondary"), 0.5f);
} else if (cosmeticOption.label == "Enemy Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_EnemySecondary"), 0.5f);
} else if (cosmeticOption.label == "NPC Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_NPCSecondary"), 1.0f);
} else if (cosmeticOption.label == "Props Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_PropsSecondary"), 1.0f);
} else if (cosmeticOption.label == "Ivan Idle Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Ivan_IdleSecondary"), 0.5f);
} else if (cosmeticOption.label == "Level 1 Secondary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("SpinAttack_Level1Primary"), 2.0f);
} else if (cosmeticOption.label == "Level 2 Secondary") {
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) { void RandomizeColor(CosmeticOption& cosmeticOption) {
Color_RGBA8 newColor; Color_RGBA8 newColor;
newColor.r = Random(0, 255); newColor.r = Random(0, 255);
@ -1611,26 +1551,7 @@ void RandomizeColor(CosmeticOption& cosmeticOption) {
CVarSetColor(cosmeticOption.cvar, newColor); CVarSetColor(cosmeticOption.cvar, newColor);
CVarSetInteger((cosmeticOption.rainbowCvar), 0); CVarSetInteger((cosmeticOption.rainbowCvar), 0);
CVarSetInteger((cosmeticOption.changedCvar), 1); CVarSetInteger((cosmeticOption.changedCvar), 1);
ApplySideEffects(cosmeticOption);
// 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.
if (cosmeticOption.label == "Bow Body") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Equipment_BowTips"), 0.5f);
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Equipment_BowHandle"), 1.0f);
CopyMultipliedColor(cosmeticOption, cosmeticOption, 4.0f);
} else if (cosmeticOption.label == "Idle Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_IdleSecondary"), 0.5f);
} else if (cosmeticOption.label == "Enemy Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_EnemySecondary"), 0.5f);
} else if (cosmeticOption.label == "NPC Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_NPCSecondary"), 1.0f);
} else if (cosmeticOption.label == "Props Primary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("Navi_PropsSecondary"), 1.0f);
} else if (cosmeticOption.label == "Level 1 Secondary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("SpinAttack_Level1Primary"), 2.0f);
} else if (cosmeticOption.label == "Level 2 Secondary") {
CopyMultipliedColor(cosmeticOption, cosmeticOptions.at("SpinAttack_Level2Primary"), 2.0f);
}
} }
void ResetColor(CosmeticOption& cosmeticOption) { void ResetColor(CosmeticOption& cosmeticOption) {
@ -1650,7 +1571,7 @@ void ResetColor(CosmeticOption& cosmeticOption) {
CVarClear((std::string(cosmeticOption.cvar) + ".A").c_str()); CVarClear((std::string(cosmeticOption.cvar) + ".A").c_str());
CVarClear((std::string(cosmeticOption.cvar) + ".Type").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") { if (cosmeticOption.label == "Bow Body") {
ResetColor(cosmeticOptions.at("Equipment_BowTips")); ResetColor(cosmeticOptions.at("Equipment_BowTips"));
ResetColor(cosmeticOptions.at("Equipment_BowHandle")); ResetColor(cosmeticOptions.at("Equipment_BowHandle"));
@ -1666,6 +1587,30 @@ void ResetColor(CosmeticOption& cosmeticOption) {
ResetColor(cosmeticOptions.at("SpinAttack_Level1Primary")); ResetColor(cosmeticOptions.at("SpinAttack_Level1Primary"));
} else if (cosmeticOption.label == "Level 2 Secondary") { } else if (cosmeticOption.label == "Level 2 Secondary") {
ResetColor(cosmeticOptions.at("SpinAttack_Level2Primary")); 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"));
} }
} }
@ -1686,6 +1631,7 @@ void DrawCosmeticRow(CosmeticOption& cosmeticOption) {
CVarSetColor(cosmeticOption.cvar, color); CVarSetColor(cosmeticOption.cvar, color);
CVarSetInteger((cosmeticOption.rainbowCvar), 0); CVarSetInteger((cosmeticOption.rainbowCvar), 0);
CVarSetInteger((cosmeticOption.changedCvar), 1); CVarSetInteger((cosmeticOption.changedCvar), 1);
ApplySideEffects(cosmeticOption);
ApplyOrResetCustomGfxPatches(); ApplyOrResetCustomGfxPatches();
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
@ -1703,6 +1649,7 @@ void DrawCosmeticRow(CosmeticOption& cosmeticOption) {
if (ImGui::Checkbox(("Rainbow##" + cosmeticOption.label).c_str(), &isRainbow)) { if (ImGui::Checkbox(("Rainbow##" + cosmeticOption.label).c_str(), &isRainbow)) {
CVarSetInteger((cosmeticOption.rainbowCvar), isRainbow); CVarSetInteger((cosmeticOption.rainbowCvar), isRainbow);
CVarSetInteger((cosmeticOption.changedCvar), 1); CVarSetInteger((cosmeticOption.changedCvar), 1);
ApplySideEffects(cosmeticOption);
ApplyOrResetCustomGfxPatches(); ApplyOrResetCustomGfxPatches();
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
@ -1759,7 +1706,7 @@ static const char* colorSchemes[2] = {
}; };
void CosmeticsEditorWindow::DrawElement() { void CosmeticsEditorWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(480, 520), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 520), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Cosmetics Editor", &mIsVisible)) { if (!ImGui::Begin("Cosmetics Editor", &mIsVisible)) {
ImGui::End(); ImGui::End();
return; return;
@ -1793,15 +1740,12 @@ void CosmeticsEditorWindow::DrawElement() {
} }
} }
UIWidgets::EnhancementCheckbox("Sync Rainbow colors", "gCosmetics.RainbowSync"); UIWidgets::EnhancementCheckbox("Sync Rainbow colors", "gCosmetics.RainbowSync");
UIWidgets::EnhancementSliderFloat("Rainbow Speed: %f", "##rainbowSpeed", "gCosmetics.RainbowSpeed", 0.03f, 1.0f, "", 0.6f, false); UIWidgets::EnhancementSliderFloat("Rainbow Speed: %.3f", "##rainbowSpeed", "gCosmetics.RainbowSpeed", 0.03f, 1.0f, "", 0.6f, false, true);
UIWidgets::EnhancementCheckbox("Randomize All on New Scene", "gCosmetics.RandomizeAllOnNewScene");
UIWidgets::Tooltip("Enables randomizing all unlocked cosmetics when you enter a new scene.");
if (ImGui::Button("Randomize All", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) { if (ImGui::Button("Randomize All", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) {
for (auto& [id, cosmeticOption] : cosmeticOptions) { CosmeticsEditor_RandomizeAll();
if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && (!cosmeticOption.advancedOption || CVarGetInteger("gCosmetics.AdvancedMode", 0))) {
RandomizeColor(cosmeticOption);
}
}
ApplyOrResetCustomGfxPatches();
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reset All", ImVec2(ImGui::GetContentRegionAvail().x, 30.0f))) { if (ImGui::Button("Reset All", ImVec2(ImGui::GetContentRegionAvail().x, 30.0f))) {
@ -1861,6 +1805,7 @@ void CosmeticsEditorWindow::DrawElement() {
if (ImGui::BeginTabItem("World & NPCs")) { if (ImGui::BeginTabItem("World & NPCs")) {
DrawCosmeticGroup(GROUP_WORLD); DrawCosmeticGroup(GROUP_WORLD);
DrawCosmeticGroup(GROUP_NAVI); DrawCosmeticGroup(GROUP_NAVI);
DrawCosmeticGroup(GROUP_IVAN);
DrawCosmeticGroup(GROUP_NPC); DrawCosmeticGroup(GROUP_NPC);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
@ -1873,10 +1818,16 @@ void CosmeticsEditorWindow::DrawElement() {
DrawCosmeticGroup(GROUP_TITLE); DrawCosmeticGroup(GROUP_TITLE);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem("HUD Placement")) { if (ImGui::BeginTabItem("HUD Placement")) {
Draw_Placements(); Draw_Placements();
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem("Pause Menu")) {
DrawCosmeticGroup(GROUP_KALEIDO);
ImGui::EndTabItem();
}
ImGui::EndTabBar(); ImGui::EndTabBar();
} }
ImGui::End(); ImGui::End();
@ -1894,6 +1845,14 @@ void RegisterOnGameFrameUpdateHook() {
}); });
} }
void Cosmetics_RegisterOnSceneInitHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) {
if (CVarGetInteger("gCosmetics.RandomizeAllOnNewScene", 0)) {
CosmeticsEditor_RandomizeAll();
}
});
}
void CosmeticsEditorWindow::InitElement() { void CosmeticsEditorWindow::InitElement() {
// Convert the `current color` into the format that the ImGui color picker expects // Convert the `current color` into the format that the ImGui color picker expects
for (auto& [id, cosmeticOption] : cosmeticOptions) { for (auto& [id, cosmeticOption] : cosmeticOptions) {
@ -1911,6 +1870,7 @@ void CosmeticsEditorWindow::InitElement() {
RegisterOnLoadGameHook(); RegisterOnLoadGameHook();
RegisterOnGameFrameUpdateHook(); RegisterOnGameFrameUpdateHook();
Cosmetics_RegisterOnSceneInitHook();
} }
void CosmeticsEditor_RandomizeAll() { void CosmeticsEditor_RandomizeAll() {

View file

@ -1,4 +1,4 @@
#ifdef ENABLE_CROWD_CONTROL #ifdef ENABLE_REMOTE_CONTROL
#include "CrowdControl.h" #include "CrowdControl.h"
#include "CrowdControlTypes.h" #include "CrowdControlTypes.h"
@ -17,25 +17,17 @@ extern "C" {
extern PlayState* gPlayState; extern PlayState* gPlayState;
} }
void CrowdControl::Init() {
SDLNet_Init();
}
void CrowdControl::Shutdown() {
SDLNet_Quit();
}
void CrowdControl::Enable() { void CrowdControl::Enable() {
if (isEnabled) { if (isEnabled) {
return; return;
} }
if (SDLNet_ResolveHost(&ip, "127.0.0.1", 43384) == -1) {
SPDLOG_ERROR("[CrowdControl] SDLNet_ResolveHost: {}", SDLNet_GetError());
}
isEnabled = true; isEnabled = true;
ccThreadReceive = std::thread(&CrowdControl::ListenToServer, this); GameInteractor::Instance->EnableRemoteInteractor();
GameInteractor::Instance->RegisterRemoteJsonHandler([&](nlohmann::json payload) {
HandleRemoteData(payload);
});
ccThreadProcess = std::thread(&CrowdControl::ProcessActiveEffects, this); ccThreadProcess = std::thread(&CrowdControl::ProcessActiveEffects, this);
} }
@ -45,87 +37,42 @@ void CrowdControl::Disable() {
} }
isEnabled = false; isEnabled = false;
ccThreadReceive.join();
ccThreadProcess.join(); ccThreadProcess.join();
GameInteractor::Instance->DisableRemoteInteractor();
} }
void CrowdControl::ListenToServer() { void CrowdControl::HandleRemoteData(nlohmann::json payload) {
while (isEnabled) { Effect* incomingEffect = ParseMessage(payload);
while (!connected && isEnabled) { if (!incomingEffect) {
SPDLOG_TRACE("[CrowdControl] Attempting to make connection to server..."); return;
tcpsock = SDLNet_TCP_Open(&ip); }
if (tcpsock) { // If effect is not a timed effect, execute and return result.
connected = true; if (!incomingEffect->timeRemaining) {
SPDLOG_TRACE("[CrowdControl] Connection to server established!"); EffectResult result = CrowdControl::ExecuteEffect(incomingEffect);
EmitMessage(incomingEffect->id, incomingEffect->timeRemaining, result);
} else {
// If another timed effect is already active that conflicts with the incoming effect.
bool isConflictingEffectActive = false;
for (Effect* effect : activeEffects) {
if (effect != incomingEffect && effect->category == incomingEffect->category && effect->id < incomingEffect->id) {
isConflictingEffectActive = true;
EmitMessage(incomingEffect->id, incomingEffect->timeRemaining, EffectResult::Retry);
break; break;
} }
} }
SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(1); if (!isConflictingEffectActive) {
if (tcpsock) { // Check if effect can be applied, if it can't, let CC know.
SDLNet_TCP_AddSocket(socketSet, tcpsock); EffectResult result = CrowdControl::CanApplyEffect(incomingEffect);
} if (result == EffectResult::Retry || result == EffectResult::Failure) {
EmitMessage(incomingEffect->id, incomingEffect->timeRemaining, result);
// Listen to socket messages return;
while (connected && tcpsock && isEnabled) {
// we check first if socket has data, to not block in the TCP_Recv
int socketsReady = SDLNet_CheckSockets(socketSet, 0);
if (socketsReady == -1) {
SPDLOG_ERROR("[CrowdControl] SDLNet_CheckSockets: {}", SDLNet_GetError());
break;
} }
if (socketsReady == 0) { activeEffectsMutex.lock();
continue; activeEffects.push_back(incomingEffect);
} activeEffectsMutex.unlock();
int len = SDLNet_TCP_Recv(tcpsock, &received, sizeof(received));
if (!len || !tcpsock || len == -1) {
SPDLOG_ERROR("[CrowdControl] SDLNet_TCP_Recv: {}", SDLNet_GetError());
break;
}
Effect* incomingEffect = ParseMessage(received);
if (!incomingEffect) {
continue;
}
// If effect is not a timed effect, execute and return result.
if (!incomingEffect->timeRemaining) {
EffectResult result = CrowdControl::ExecuteEffect(incomingEffect);
EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result);
} else {
// If another timed effect is already active that conflicts with the incoming effect.
bool isConflictingEffectActive = false;
for (Effect* effect : activeEffects) {
if (effect != incomingEffect && effect->category == incomingEffect->category && effect->id < incomingEffect->id) {
isConflictingEffectActive = true;
EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, EffectResult::Retry);
break;
}
}
if (!isConflictingEffectActive) {
// Check if effect can be applied, if it can't, let CC know.
EffectResult result = CrowdControl::CanApplyEffect(incomingEffect);
if (result == EffectResult::Retry || result == EffectResult::Failure) {
EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result);
continue;
}
activeEffectsMutex.lock();
activeEffects.push_back(incomingEffect);
activeEffectsMutex.unlock();
}
}
}
if (connected) {
SDLNet_TCP_Close(tcpsock);
connected = false;
SPDLOG_TRACE("[CrowdControl] Ending Listen thread...");
} }
} }
} }
@ -147,13 +94,13 @@ void CrowdControl::ProcessActiveEffects() {
if (effect->timeRemaining <= 0) { if (effect->timeRemaining <= 0) {
it = activeEffects.erase(std::remove(activeEffects.begin(), activeEffects.end(), effect), it = activeEffects.erase(std::remove(activeEffects.begin(), activeEffects.end(), effect),
activeEffects.end()); activeEffects.end());
GameInteractor::RemoveEffect(effect->giEffect); GameInteractor::RemoveEffect(dynamic_cast<RemovableGameInteractionEffect*>(effect->giEffect));
delete effect; delete effect;
} else { } else {
// If we have a success after previously being paused, tell CC to resume timer. // If we have a success after previously being paused, tell CC to resume timer.
if (effect->isPaused) { if (effect->isPaused) {
effect->isPaused = false; effect->isPaused = false;
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Resumed); EmitMessage(effect->id, effect->timeRemaining, EffectResult::Resumed);
// If not paused before, subtract time from the timer and send a Success event if // If not paused before, subtract time from the timer and send a Success event if
// the result is different from the last time this was ran. // the result is different from the last time this was ran.
// Timed events are put on a thread that runs once per second. // Timed events are put on a thread that runs once per second.
@ -161,7 +108,7 @@ void CrowdControl::ProcessActiveEffects() {
effect->timeRemaining -= 1000; effect->timeRemaining -= 1000;
if (result != effect->lastExecutionResult) { if (result != effect->lastExecutionResult) {
effect->lastExecutionResult = result; effect->lastExecutionResult = result;
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Success); EmitMessage(effect->id, effect->timeRemaining, EffectResult::Success);
} }
} }
it++; it++;
@ -169,7 +116,7 @@ void CrowdControl::ProcessActiveEffects() {
} else { // Timed effects only do Success or Retry } else { // Timed effects only do Success or Retry
if (!effect->isPaused && effect->timeRemaining > 0) { if (!effect->isPaused && effect->timeRemaining > 0) {
effect->isPaused = true; effect->isPaused = true;
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Paused); EmitMessage(effect->id, effect->timeRemaining, EffectResult::Paused);
} }
it++; it++;
} }
@ -182,7 +129,7 @@ void CrowdControl::ProcessActiveEffects() {
SPDLOG_TRACE("[CrowdControl] Ending Process thread..."); SPDLOG_TRACE("[CrowdControl] Ending Process thread...");
} }
void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status) { void CrowdControl::EmitMessage(uint32_t eventId, long timeRemaining, EffectResult status) {
nlohmann::json payload; nlohmann::json payload;
payload["id"] = eventId; payload["id"] = eventId;
@ -190,8 +137,9 @@ void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRema
payload["timeRemaining"] = timeRemaining; payload["timeRemaining"] = timeRemaining;
payload["status"] = status; payload["status"] = status;
std::string jsonPayload = payload.dump(); SPDLOG_INFO("[CrowdControl] Sending payload:\n{}", payload.dump());
SDLNet_TCP_Send(socket, jsonPayload.c_str(), jsonPayload.size() + 1);
GameInteractor::Instance->TransmitJsonToRemote(payload);
} }
CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) { CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) {
@ -229,13 +177,14 @@ CrowdControl::EffectResult CrowdControl::TranslateGiEnum(GameInteractionEffectQu
return result; return result;
} }
CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) { CrowdControl::Effect* CrowdControl::ParseMessage(nlohmann::json dataReceived) {
nlohmann::json dataReceived = nlohmann::json::parse(payload, nullptr, false); if (!dataReceived.contains("id") || !dataReceived.contains("type")) {
if (dataReceived.is_discarded()) { SPDLOG_ERROR("[CrowdControl] Invalid payload received:\n{}", dataReceived);
SPDLOG_ERROR("Error parsing JSON");
return nullptr; return nullptr;
} }
SPDLOG_INFO("[CrowdControl] Received payload:\n{}", dataReceived.dump());
Effect* effect = new Effect(); Effect* effect = new Effect();
effect->lastExecutionResult = EffectResult::Initiate; effect->lastExecutionResult = EffectResult::Initiate;
effect->id = dataReceived["id"]; effect->id = dataReceived["id"];
@ -333,13 +282,13 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
effect->category = kEffectCatDamageTaken; effect->category = kEffectCatDamageTaken;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier(); effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier();
effect->giEffect->parameters[0] = 2; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 2;
break; break;
case kEffectTakeDoubleDamage: case kEffectTakeDoubleDamage:
effect->category = kEffectCatDamageTaken; effect->category = kEffectCatDamageTaken;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier(); effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier();
effect->giEffect->parameters[0] = -2; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = -2;
break; break;
case kEffectOneHitKo: case kEffectOneHitKo:
effect->category = kEffectCatDamageTaken; effect->category = kEffectCatDamageTaken;
@ -356,37 +305,37 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
effect->category = kEffectCatSpeed; effect->category = kEffectCatSpeed;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier(); effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier();
effect->giEffect->parameters[0] = 2; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 2;
break; break;
case kEffectDecreaseSpeed: case kEffectDecreaseSpeed:
effect->category = kEffectCatSpeed; effect->category = kEffectCatSpeed;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier(); effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier();
effect->giEffect->parameters[0] = -2; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = -2;
break; break;
case kEffectLowGravity: case kEffectLowGravity:
effect->category = kEffectCatGravity; effect->category = kEffectCatGravity;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyGravity(); effect->giEffect = new GameInteractionEffect::ModifyGravity();
effect->giEffect->parameters[0] = GI_GRAVITY_LEVEL_LIGHT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_GRAVITY_LEVEL_LIGHT;
break; break;
case kEffectHighGravity: case kEffectHighGravity:
effect->category = kEffectCatGravity; effect->category = kEffectCatGravity;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyGravity(); effect->giEffect = new GameInteractionEffect::ModifyGravity();
effect->giEffect->parameters[0] = GI_GRAVITY_LEVEL_HEAVY; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_GRAVITY_LEVEL_HEAVY;
break; break;
case kEffectForceIronBoots: case kEffectForceIronBoots:
effect->category = kEffectCatBoots; effect->category = kEffectCatBoots;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ForceEquipBoots(); effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_IRON; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = EQUIP_VALUE_BOOTS_IRON;
break; break;
case kEffectForceHoverBoots: case kEffectForceHoverBoots:
effect->category = kEffectCatBoots; effect->category = kEffectCatBoots;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ForceEquipBoots(); effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_HOVER; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = EQUIP_VALUE_BOOTS_HOVER;
break; break;
case kEffectSlipperyFloor: case kEffectSlipperyFloor:
effect->category = kEffectCatSlipperyFloor; effect->category = kEffectCatSlipperyFloor;
@ -412,23 +361,23 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
// Hurt or Heal Link // Hurt or Heal Link
case kEffectEmptyHeart: case kEffectEmptyHeart:
effect->giEffect = new GameInteractionEffect::ModifyHealth(); effect->giEffect = new GameInteractionEffect::ModifyHealth();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
break; break;
case kEffectFillHeart: case kEffectFillHeart:
effect->giEffect = new GameInteractionEffect::ModifyHealth(); effect->giEffect = new GameInteractionEffect::ModifyHealth();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
break; break;
case kEffectKnockbackLinkWeak: case kEffectKnockbackLinkWeak:
effect->giEffect = new GameInteractionEffect::KnockbackPlayer(); effect->giEffect = new GameInteractionEffect::KnockbackPlayer();
effect->giEffect->parameters[0] = 1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 1;
break; break;
case kEffectKnockbackLinkStrong: case kEffectKnockbackLinkStrong:
effect->giEffect = new GameInteractionEffect::KnockbackPlayer(); effect->giEffect = new GameInteractionEffect::KnockbackPlayer();
effect->giEffect->parameters[0] = 3; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 3;
break; break;
case kEffectKnockbackLinkMega: case kEffectKnockbackLinkMega:
effect->giEffect = new GameInteractionEffect::KnockbackPlayer(); effect->giEffect = new GameInteractionEffect::KnockbackPlayer();
effect->giEffect->parameters[0] = 6; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 6;
break; break;
case kEffectBurnLink: case kEffectBurnLink:
effect->giEffect = new GameInteractionEffect::BurnPlayer(); effect->giEffect = new GameInteractionEffect::BurnPlayer();
@ -441,109 +390,109 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
break; break;
case kEffectKillLink: case kEffectKillLink:
effect->giEffect = new GameInteractionEffect::SetPlayerHealth(); effect->giEffect = new GameInteractionEffect::SetPlayerHealth();
effect->giEffect->parameters[0] = 0; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 0;
break; break;
// Give Items and Consumables // Give Items and Consumables
case kEffectAddHeartContainer: case kEffectAddHeartContainer:
effect->giEffect = new GameInteractionEffect::ModifyHeartContainers(); effect->giEffect = new GameInteractionEffect::ModifyHeartContainers();
effect->giEffect->parameters[0] = 1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 1;
break; break;
case kEffectFillMagic: case kEffectFillMagic:
effect->giEffect = new GameInteractionEffect::FillMagic(); effect->giEffect = new GameInteractionEffect::FillMagic();
break; break;
case kEffectAddRupees: case kEffectAddRupees:
effect->giEffect = new GameInteractionEffect::ModifyRupees(); effect->giEffect = new GameInteractionEffect::ModifyRupees();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
break; break;
case kEffectGiveDekuShield: case kEffectGiveDekuShield:
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
effect->giEffect->parameters[0] = ITEM_SHIELD_DEKU; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = ITEM_SHIELD_DEKU;
break; break;
case kEffectGiveHylianShield: case kEffectGiveHylianShield:
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
effect->giEffect->parameters[0] = ITEM_SHIELD_HYLIAN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = ITEM_SHIELD_HYLIAN;
break; break;
case kEffectRefillSticks: case kEffectRefillSticks:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
effect->giEffect->parameters[1] = ITEM_STICK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_STICK;
break; break;
case kEffectRefillNuts: case kEffectRefillNuts:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
effect->giEffect->parameters[1] = ITEM_NUT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_NUT;
break; break;
case kEffectRefillBombs: case kEffectRefillBombs:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
effect->giEffect->parameters[1] = ITEM_BOMB; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOMB;
break; break;
case kEffectRefillSeeds: case kEffectRefillSeeds:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
effect->giEffect->parameters[1] = ITEM_SLINGSHOT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_SLINGSHOT;
break; break;
case kEffectRefillArrows: case kEffectRefillArrows:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
effect->giEffect->parameters[1] = ITEM_BOW; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOW;
break; break;
case kEffectRefillBombchus: case kEffectRefillBombchus:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
effect->giEffect->parameters[1] = ITEM_BOMBCHU; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOMBCHU;
break; break;
// Take Items and Consumables // Take Items and Consumables
case kEffectRemoveHeartContainer: case kEffectRemoveHeartContainer:
effect->giEffect = new GameInteractionEffect::ModifyHeartContainers(); effect->giEffect = new GameInteractionEffect::ModifyHeartContainers();
effect->giEffect->parameters[0] = -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = -1;
break; break;
case kEffectEmptyMagic: case kEffectEmptyMagic:
effect->giEffect = new GameInteractionEffect::EmptyMagic(); effect->giEffect = new GameInteractionEffect::EmptyMagic();
break; break;
case kEffectRemoveRupees: case kEffectRemoveRupees:
effect->giEffect = new GameInteractionEffect::ModifyRupees(); effect->giEffect = new GameInteractionEffect::ModifyRupees();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
break; break;
case kEffectTakeDekuShield: case kEffectTakeDekuShield:
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
effect->giEffect->parameters[0] = -ITEM_SHIELD_DEKU; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = -ITEM_SHIELD_DEKU;
break; break;
case kEffectTakeHylianShield: case kEffectTakeHylianShield:
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
effect->giEffect->parameters[0] = -ITEM_SHIELD_HYLIAN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = -ITEM_SHIELD_HYLIAN;
break; break;
case kEffectTakeSticks: case kEffectTakeSticks:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
effect->giEffect->parameters[1] = ITEM_STICK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_STICK;
break; break;
case kEffectTakeNuts: case kEffectTakeNuts:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
effect->giEffect->parameters[1] = ITEM_NUT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_NUT;
break; break;
case kEffectTakeBombs: case kEffectTakeBombs:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
effect->giEffect->parameters[1] = ITEM_BOMB; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOMB;
break; break;
case kEffectTakeSeeds: case kEffectTakeSeeds:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
effect->giEffect->parameters[1] = ITEM_SLINGSHOT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_SLINGSHOT;
break; break;
case kEffectTakeArrows: case kEffectTakeArrows:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
effect->giEffect->parameters[1] = ITEM_BOW; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOW;
break; break;
case kEffectTakeBombchus: case kEffectTakeBombchus:
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
effect->giEffect->parameters[0] = receivedParameter * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
effect->giEffect->parameters[1] = ITEM_BOMBCHU; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOMBCHU;
break; break;
// Link Size Modifiers // Link Size Modifiers
@ -551,25 +500,25 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
effect->category = kEffectCatLinkSize; effect->category = kEffectCatLinkSize;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); effect->giEffect = new GameInteractionEffect::ModifyLinkSize();
effect->giEffect->parameters[0] = GI_LINK_SIZE_GIANT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_LINK_SIZE_GIANT;
break; break;
case kEffectMinishLink: case kEffectMinishLink:
effect->category = kEffectCatLinkSize; effect->category = kEffectCatLinkSize;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); effect->giEffect = new GameInteractionEffect::ModifyLinkSize();
effect->giEffect->parameters[0] = GI_LINK_SIZE_MINISH; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_LINK_SIZE_MINISH;
break; break;
case kEffectPaperLink: case kEffectPaperLink:
effect->category = kEffectCatLinkSize; effect->category = kEffectCatLinkSize;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); effect->giEffect = new GameInteractionEffect::ModifyLinkSize();
effect->giEffect->parameters[0] = GI_LINK_SIZE_PAPER; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_LINK_SIZE_PAPER;
break; break;
case kEffectSquishedLink: case kEffectSquishedLink:
effect->category = kEffectCatLinkSize; effect->category = kEffectCatLinkSize;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); effect->giEffect = new GameInteractionEffect::ModifyLinkSize();
effect->giEffect->parameters[0] = GI_LINK_SIZE_SQUISHED; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_LINK_SIZE_SQUISHED;
break; break;
case kEffectInvisibleLink: case kEffectInvisibleLink:
effect->category = kEffectCatLinkSize; effect->category = kEffectCatLinkSize;
@ -585,11 +534,11 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
break; break;
case kEffectSetTimeToDawn: case kEffectSetTimeToDawn:
effect->giEffect = new GameInteractionEffect::SetTimeOfDay(); effect->giEffect = new GameInteractionEffect::SetTimeOfDay();
effect->giEffect->parameters[0] = GI_TIMEOFDAY_DAWN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TIMEOFDAY_DAWN;
break; break;
case kEffectSetTimeToDusk: case kEffectSetTimeToDusk:
effect->giEffect = new GameInteractionEffect::SetTimeOfDay(); effect->giEffect = new GameInteractionEffect::SetTimeOfDay();
effect->giEffect->parameters[0] = GI_TIMEOFDAY_DUSK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TIMEOFDAY_DUSK;
break; break;
// Visual Effects // Visual Effects
@ -632,186 +581,186 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
effect->category = kEffectCatRandomButtons; effect->category = kEffectCatRandomButtons;
effect->timeRemaining = 30000; effect->timeRemaining = 30000;
effect->giEffect = new GameInteractionEffect::PressRandomButton(); effect->giEffect = new GameInteractionEffect::PressRandomButton();
effect->giEffect->parameters[0] = 30; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 30;
break; break;
case kEffectClearCbuttons: case kEffectClearCbuttons:
effect->giEffect = new GameInteractionEffect::ClearAssignedButtons(); effect->giEffect = new GameInteractionEffect::ClearAssignedButtons();
effect->giEffect->parameters[0] = GI_BUTTONS_CBUTTONS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_BUTTONS_CBUTTONS;
break; break;
case kEffectClearDpad: case kEffectClearDpad:
effect->giEffect = new GameInteractionEffect::ClearAssignedButtons(); effect->giEffect = new GameInteractionEffect::ClearAssignedButtons();
effect->giEffect->parameters[0] = GI_BUTTONS_DPAD; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_BUTTONS_DPAD;
break; break;
// Teleport Player // Teleport Player
case kEffectTpLinksHouse: case kEffectTpLinksHouse:
effect->giEffect = new GameInteractionEffect::TeleportPlayer(); effect->giEffect = new GameInteractionEffect::TeleportPlayer();
effect->giEffect->parameters[0] = GI_TP_DEST_LINKSHOUSE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_LINKSHOUSE;
break; break;
case kEffectTpMinuet: case kEffectTpMinuet:
effect->giEffect = new GameInteractionEffect::TeleportPlayer(); effect->giEffect = new GameInteractionEffect::TeleportPlayer();
effect->giEffect->parameters[0] = GI_TP_DEST_MINUET; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_MINUET;
break; break;
case kEffectTpBolero: case kEffectTpBolero:
effect->giEffect = new GameInteractionEffect::TeleportPlayer(); effect->giEffect = new GameInteractionEffect::TeleportPlayer();
effect->giEffect->parameters[0] = GI_TP_DEST_BOLERO; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_BOLERO;
break; break;
case kEffectTpSerenade: case kEffectTpSerenade:
effect->giEffect = new GameInteractionEffect::TeleportPlayer(); effect->giEffect = new GameInteractionEffect::TeleportPlayer();
effect->giEffect->parameters[0] = GI_TP_DEST_SERENADE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_SERENADE;
break; break;
case kEffectTpRequiem: case kEffectTpRequiem:
effect->giEffect = new GameInteractionEffect::TeleportPlayer(); effect->giEffect = new GameInteractionEffect::TeleportPlayer();
effect->giEffect->parameters[0] = GI_TP_DEST_REQUIEM; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_REQUIEM;
break; break;
case kEffectTpNocturne: case kEffectTpNocturne:
effect->giEffect = new GameInteractionEffect::TeleportPlayer(); effect->giEffect = new GameInteractionEffect::TeleportPlayer();
effect->giEffect->parameters[0] = GI_TP_DEST_NOCTURNE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_NOCTURNE;
break; break;
case kEffectTpPrelude: case kEffectTpPrelude:
effect->giEffect = new GameInteractionEffect::TeleportPlayer(); effect->giEffect = new GameInteractionEffect::TeleportPlayer();
effect->giEffect->parameters[0] = GI_TP_DEST_PRELUDE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_PRELUDE;
break; break;
// Tunic Color (Bidding War) // Tunic Color (Bidding War)
case kEffectTunicRed: case kEffectTunicRed:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_RED; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_RED;
break; break;
case kEffectTunicGreen: case kEffectTunicGreen:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_GREEN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_GREEN;
break; break;
case kEffectTunicBlue: case kEffectTunicBlue:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_BLUE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BLUE;
break; break;
case kEffectTunicOrange: case kEffectTunicOrange:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_ORANGE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_ORANGE;
break; break;
case kEffectTunicYellow: case kEffectTunicYellow:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_YELLOW; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_YELLOW;
break; break;
case kEffectTunicPurple: case kEffectTunicPurple:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_PURPLE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_PURPLE;
break; break;
case kEffectTunicPink: case kEffectTunicPink:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_PINK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_PINK;
break; break;
case kEffectTunicBrown: case kEffectTunicBrown:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_BROWN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BROWN;
break; break;
case kEffectTunicBlack: case kEffectTunicBlack:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
effect->giEffect->parameters[1] = GI_COLOR_BLACK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BLACK;
break; break;
// Navi Color (Bidding War) // Navi Color (Bidding War)
case kEffectNaviRed: case kEffectNaviRed:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_RED; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_RED;
break; break;
case kEffectNaviGreen: case kEffectNaviGreen:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_GREEN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_GREEN;
break; break;
case kEffectNaviBlue: case kEffectNaviBlue:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_BLUE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BLUE;
break; break;
case kEffectNaviOrange: case kEffectNaviOrange:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_ORANGE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_ORANGE;
break; break;
case kEffectNaviYellow: case kEffectNaviYellow:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_YELLOW; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_YELLOW;
break; break;
case kEffectNaviPurple: case kEffectNaviPurple:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_PURPLE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_PURPLE;
break; break;
case kEffectNaviPink: case kEffectNaviPink:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_PINK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_PINK;
break; break;
case kEffectNaviBrown: case kEffectNaviBrown:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_BROWN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BROWN;
break; break;
case kEffectNaviBlack: case kEffectNaviBlack:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
effect->giEffect->parameters[1] = GI_COLOR_BLACK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BLACK;
break; break;
// Link's Hair Color (Bidding War) // Link's Hair Color (Bidding War)
case kEffectHairRed: case kEffectHairRed:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_RED; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_RED;
break; break;
case kEffectHairGreen: case kEffectHairGreen:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_GREEN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_GREEN;
break; break;
case kEffectHairBlue: case kEffectHairBlue:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_BLUE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BLUE;
break; break;
case kEffectHairOrange: case kEffectHairOrange:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_ORANGE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_ORANGE;
break; break;
case kEffectHairYellow: case kEffectHairYellow:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_YELLOW; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_YELLOW;
break; break;
case kEffectHairPurple: case kEffectHairPurple:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_PURPLE; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_PURPLE;
break; break;
case kEffectHairPink: case kEffectHairPink:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_PINK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_PINK;
break; break;
case kEffectHairBrown: case kEffectHairBrown:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_BROWN; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BROWN;
break; break;
case kEffectHairBlack: case kEffectHairBlack:
effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); effect->giEffect = new GameInteractionEffect::SetCosmeticsColor();
effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
effect->giEffect->parameters[1] = GI_COLOR_BLACK; dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BLACK;
break; break;
default: default:

View file

@ -1,4 +1,4 @@
#ifdef ENABLE_CROWD_CONTROL #ifdef ENABLE_REMOTE_CONTROL
#ifndef _CROWDCONTROL_C #ifndef _CROWDCONTROL_C
#define _CROWDCONTROL_C #define _CROWDCONTROL_C
@ -73,33 +73,24 @@ class CrowdControl {
EffectResult lastExecutionResult; EffectResult lastExecutionResult;
} Effect; } Effect;
std::thread ccThreadReceive;
std::thread ccThreadProcess; std::thread ccThreadProcess;
TCPsocket tcpsock;
IPaddress ip;
bool isEnabled; bool isEnabled;
bool connected;
char received[512];
std::vector<Effect*> activeEffects; std::vector<Effect*> activeEffects;
std::mutex activeEffectsMutex; std::mutex activeEffectsMutex;
void ListenToServer(); void HandleRemoteData(nlohmann::json payload);
void ProcessActiveEffects(); void ProcessActiveEffects();
void EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status); void EmitMessage(uint32_t eventId, long timeRemaining, EffectResult status);
Effect* ParseMessage(char payload[512]); Effect* ParseMessage(nlohmann::json payload);
EffectResult ExecuteEffect(Effect* effect); EffectResult ExecuteEffect(Effect* effect);
EffectResult CanApplyEffect(Effect *effect); EffectResult CanApplyEffect(Effect *effect);
EffectResult TranslateGiEnum(GameInteractionEffectQueryResult giResult); EffectResult TranslateGiEnum(GameInteractionEffectQueryResult giResult);
public: public:
static CrowdControl* Instance; static CrowdControl* Instance;
void Init();
void Shutdown();
void Enable(); void Enable();
void Disable(); void Disable();
}; };

View file

@ -38,6 +38,7 @@ typedef enum {
TEXT_CARPET_SALESMAN_1 = 0x6077, TEXT_CARPET_SALESMAN_1 = 0x6077,
TEXT_CARPET_SALESMAN_2 = 0x6078, TEXT_CARPET_SALESMAN_2 = 0x6078,
TEXT_MARKET_GUARD_NIGHT = 0x7003, TEXT_MARKET_GUARD_NIGHT = 0x7003,
TEXT_FISHERMAN_LEAVE = 0x409E,
TEXT_SHEIK_NEED_HOOK = 0x700F, TEXT_SHEIK_NEED_HOOK = 0x700F,
TEXT_SHEIK_HAVE_HOOK = 0x7010, TEXT_SHEIK_HAVE_HOOK = 0x7010,
TEXT_SCRUB_RANDOM = 0x9000, TEXT_SCRUB_RANDOM = 0x9000,
@ -54,6 +55,22 @@ typedef enum {
TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW = 0x9210, 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_SIGN = 0x346, // 0x3yy for cuttable sign range
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg 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; } TextIDs;
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -17,6 +17,10 @@
#include <Window.h> #include <Window.h>
#include <Context.h> #include <Context.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h>
#include <ImGui/imgui_internal.h> #include <ImGui/imgui_internal.h>
#undef PATH_HACK #undef PATH_HACK
#undef Path #undef Path
@ -99,7 +103,7 @@ static bool ActorSpawnHandler(std::shared_ptr<LUS::Console> Console, const std::
static bool KillPlayerHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>&, std::string* output) { static bool KillPlayerHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>&, std::string* output) {
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
effect->parameters[0] = 0; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = 0;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] You've met with a terrible fate, haven't you?"); INFO_MESSAGE("[SOH] You've met with a terrible fate, haven't you?");
@ -130,7 +134,7 @@ static bool SetPlayerHealthHandler(std::shared_ptr<LUS::Console> Console, const
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
effect->parameters[0] = health; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = health;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Player health updated to %d", health); INFO_MESSAGE("[SOH] Player health updated to %d", health);
@ -247,8 +251,8 @@ static bool AddAmmoHandler(std::shared_ptr<LUS::Console> Console, const std::vec
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo(); GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo();
effect->parameters[0] = amount; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = amount;
effect->parameters[1] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[1] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -287,8 +291,8 @@ static bool TakeAmmoHandler(std::shared_ptr<LUS::Console> Console, const std::ve
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo(); GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo();
effect->parameters[0] = -amount; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -amount;
effect->parameters[1] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[1] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -397,9 +401,9 @@ static bool EntranceHandler(std::shared_ptr<LUS::Console> Console, const std::ve
} }
gPlayState->nextEntranceIndex = entrance; gPlayState->nextEntranceIndex = entrance;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 11; gPlayState->transitionType = TRANS_TYPE_INSTANT;
gSaveContext.nextTransitionType = 11; gSaveContext.nextTransitionType = TRANS_TYPE_INSTANT;
} }
static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) { static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) {
@ -407,10 +411,10 @@ static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector
gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwchFlags = gPlayState->actorCtx.flags.tempSwch; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwchFlags = gPlayState->actorCtx.flags.tempSwch;
gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = gPlayState->actorCtx.flags.tempCollect; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = gPlayState->actorCtx.flags.tempCollect;
gSaveContext.respawnFlag = 1; gSaveContext.respawnFlag = 1;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex; gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex;
gPlayState->fadeTransition = 2; gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
gSaveContext.nextTransitionType = 2; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK;
} else { } else {
ERROR_MESSAGE("gPlayState == nullptr"); ERROR_MESSAGE("gPlayState == nullptr");
return 1; return 1;
@ -421,9 +425,9 @@ static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector
static bool ReloadHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) { static bool ReloadHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) {
if (gPlayState != nullptr) { if (gPlayState != nullptr) {
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex; gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 11; gPlayState->transitionType = TRANS_TYPE_INSTANT;
gSaveContext.nextTransitionType = 11; gSaveContext.nextTransitionType = TRANS_TYPE_INSTANT;
} else { } else {
ERROR_MESSAGE("gPlayState == nullptr"); ERROR_MESSAGE("gPlayState == nullptr");
return 1; return 1;
@ -457,9 +461,9 @@ static bool FWHandler(std::shared_ptr<LUS::Console> Console, const std::vector<s
break; break;
case 1: //warp case 1: //warp
if (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0) { if (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0) {
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex; gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex;
gPlayState->fadeTransition = 5; gPlayState->transitionType = TRANS_TYPE_FADE_WHITE_FAST;
} else { } else {
ERROR_MESSAGE("Farore's wind not set!"); ERROR_MESSAGE("Farore's wind not set!");
return 1; return 1;
@ -577,7 +581,7 @@ static bool InvisibleHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::InvisibleLink(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::InvisibleLink();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -604,8 +608,8 @@ static bool GiantLinkHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameters[0] = GI_LINK_SIZE_GIANT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_GIANT;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -632,8 +636,8 @@ static bool MinishLinkHandler(std::shared_ptr<LUS::Console> Console, const std::
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameters[0] = GI_LINK_SIZE_MINISH; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_MINISH;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -666,7 +670,7 @@ static bool AddHeartContainerHandler(std::shared_ptr<LUS::Console> Console, cons
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
effect->parameters[0] = hearts; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = hearts;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Added %d heart containers", hearts); INFO_MESSAGE("[SOH] Added %d heart containers", hearts);
@ -697,7 +701,7 @@ static bool RemoveHeartContainerHandler(std::shared_ptr<LUS::Console> Console, c
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
effect->parameters[0] = -hearts; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -hearts;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Removed %d heart containers", hearts); INFO_MESSAGE("[SOH] Removed %d heart containers", hearts);
@ -717,7 +721,7 @@ static bool GravityHandler(std::shared_ptr<LUS::Console> Console, const std::vec
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity();
try { try {
effect->parameters[0] = LUS::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = LUS::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Gravity value must be a number."); ERROR_MESSAGE("[SOH] Gravity value must be a number.");
return 1; return 1;
@ -747,7 +751,7 @@ static bool NoUIHandler(std::shared_ptr<LUS::Console> Console, const std::vector
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::NoUI(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::NoUI();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -782,7 +786,7 @@ static bool DefenseModifierHandler(std::shared_ptr<LUS::Console> Console, const
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier();
try { try {
effect->parameters[0] = std::stoi(args[1], nullptr, 10); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Defense modifier value must be a number."); ERROR_MESSAGE("[SOH] Defense modifier value must be a number.");
return 1; return 1;
@ -790,7 +794,7 @@ static bool DefenseModifierHandler(std::shared_ptr<LUS::Console> Console, const
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Defense modifier set to %d", effect->parameters[0]); INFO_MESSAGE("[SOH] Defense modifier set to %d", dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0]);
return 0; return 0;
} else { } else {
INFO_MESSAGE("[SOH] Command failed: Could not set defense modifier."); INFO_MESSAGE("[SOH] Command failed: Could not set defense modifier.");
@ -812,7 +816,7 @@ static bool DamageHandler(std::shared_ptr<LUS::Console> Console, const std::vect
return 1; return 1;
} }
effect->parameters[0] = -value; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Damage value must be a number."); ERROR_MESSAGE("[SOH] Damage value must be a number.");
return 1; return 1;
@ -842,7 +846,7 @@ static bool HealHandler(std::shared_ptr<LUS::Console> Console, const std::vector
return 1; return 1;
} }
effect->parameters[0] = value; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Damage value must be a number."); ERROR_MESSAGE("[SOH] Damage value must be a number.");
return 1; return 1;
@ -898,7 +902,7 @@ static bool NoZHandler(std::shared_ptr<LUS::Console> Console, const std::vector<
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::DisableZTargeting(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::DisableZTargeting();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -926,7 +930,7 @@ static bool OneHitKOHandler(std::shared_ptr<LUS::Console> Console, const std::ve
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::OneHitKO(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::OneHitKO();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -954,7 +958,7 @@ static bool PacifistHandler(std::shared_ptr<LUS::Console> Console, const std::ve
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::PacifistMode(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::PacifistMode();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -982,8 +986,8 @@ static bool PaperLinkHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameters[0] = GI_LINK_SIZE_PAPER; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_PAPER;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -1011,7 +1015,7 @@ static bool RainstormHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::WeatherRainstorm(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::WeatherRainstorm();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -1039,7 +1043,7 @@ static bool ReverseControlsHandler(std::shared_ptr<LUS::Console> Console, const
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ReverseControls(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ReverseControls();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -1062,7 +1066,7 @@ static bool UpdateRupeesHandler(std::shared_ptr<LUS::Console> Console, const std
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees();
try { try {
effect->parameters[0] = std::stoi(args[1], nullptr, 10); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Rupee value must be a number."); ERROR_MESSAGE("[SOH] Rupee value must be a number.");
return 1; return 1;
@ -1086,7 +1090,7 @@ static bool SpeedModifierHandler(std::shared_ptr<LUS::Console> Console, const st
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier();
try { try {
effect->parameters[0] = std::stoi(args[1], nullptr, 10); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Speed modifier value must be a number."); ERROR_MESSAGE("[SOH] Speed modifier value must be a number.");
return 1; return 1;
@ -1121,7 +1125,7 @@ static bool BootsHandler(std::shared_ptr<LUS::Console> Console, const std::vecto
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots(); GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots();
effect->parameters[0] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -1152,7 +1156,7 @@ static bool GiveShieldHandler(std::shared_ptr<LUS::Console> Console, const std::
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield(); GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield();
effect->parameters[0] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -1177,7 +1181,7 @@ static bool TakeShieldHandler(std::shared_ptr<LUS::Console> Console, const std::
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield(); GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield();
effect->parameters[0] = it->second * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second * -1;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -1203,7 +1207,7 @@ static bool KnockbackHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
effect->parameters[0] = value; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Knockback value must be a number."); ERROR_MESSAGE("[SOH] Knockback value must be a number.");
return 1; return 1;

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

@ -8,6 +8,7 @@
#include <array> #include <array>
#include <bit> #include <bit>
#include <map> #include <map>
#include <unordered_map>
#include <string> #include <string>
#include <libultraship/bridge.h> #include <libultraship/bridge.h>
#include <libultraship/libultraship.h> #include <libultraship/libultraship.h>
@ -24,6 +25,7 @@ extern PlayState* gPlayState;
#include "textures/icon_item_24_static/icon_item_24_static.h" #include "textures/icon_item_24_static/icon_item_24_static.h"
} }
#define DEKUNUTS_FLOWER 10
#define DEBUG_ACTOR_NAMETAG_TAG "debug_actor_viewer" #define DEBUG_ACTOR_NAMETAG_TAG "debug_actor_viewer"
typedef struct { typedef struct {
@ -107,6 +109,783 @@ void PopulateActorDropdown(int i, std::vector<Actor*>& data) {
} }
} }
//actors that don't use params at all
static std::vector<u16> noParamsActors = {
ACTOR_ARMS_HOOK,
ACTOR_ARROW_FIRE,
ACTOR_ARROW_ICE,
ACTOR_ARROW_LIGHT,
ACTOR_BG_BOM_GUARD,
ACTOR_BG_DY_YOSEIZO,
ACTOR_BG_GATE_SHUTTER,
ACTOR_BG_GJYO_BRIDGE,
ACTOR_BG_HIDAN_FSLIFT,
ACTOR_BG_HIDAN_RSEKIZOU,
ACTOR_BG_HIDAN_SYOKU,
ACTOR_BG_JYA_GOROIWA,
ACTOR_BG_MIZU_UZU,
ACTOR_BG_MORI_RAKKATENJO,
ACTOR_BG_PUSHBOX,
ACTOR_BG_SPOT01_FUSYA,
ACTOR_BG_SPOT01_IDOHASHIRA,
ACTOR_BG_SPOT01_IDOMIZU,
ACTOR_BG_SPOT01_IDOSOKO,
ACTOR_BG_SPOT11_OASIS,
ACTOR_BG_SPOT15_SAKU,
ACTOR_BG_SPOT18_FUTA,
ACTOR_BG_TOKI_SWD,
ACTOR_BG_TREEMOUTH,
ACTOR_BG_VB_SIMA,
ACTOR_BOSS_DODONGO,
ACTOR_BOSS_FD,
ACTOR_BOSS_GOMA,
ACTOR_DEMO_EXT,
ACTOR_DEMO_SHD,
ACTOR_DEMO_TRE_LGT,
ACTOR_DOOR_TOKI,
ACTOR_EFC_ERUPC,
ACTOR_EN_ANI,
ACTOR_EN_AROW_TRAP,
ACTOR_EN_BIRD,
ACTOR_EN_BLKOBJ,
ACTOR_EN_BOM_BOWL_MAN,
ACTOR_EN_BOM_BOWL_PIT,
ACTOR_EN_BOM_CHU,
ACTOR_EN_BUBBLE,
ACTOR_EN_DIVING_GAME,
ACTOR_EN_DNT_DEMO,
ACTOR_EN_DNT_JIJI,
ACTOR_EN_DS,
ACTOR_EN_DU,
ACTOR_EN_EG,
ACTOR_EN_FU,
ACTOR_EN_GB,
ACTOR_EN_GE3,
ACTOR_EN_GUEST,
ACTOR_EN_HATA,
ACTOR_EN_HORSE_GANON,
ACTOR_EN_HORSE_LINK_CHILD,
ACTOR_EN_HORSE_ZELDA,
ACTOR_EN_HS2,
ACTOR_EN_JS,
ACTOR_EN_KAKASI,
ACTOR_EN_KAKASI3,
ACTOR_EN_MA1,
ACTOR_EN_MA2,
ACTOR_EN_MA3,
ACTOR_EN_MAG,
ACTOR_EN_MK,
ACTOR_EN_MS,
ACTOR_EN_NIW_LADY,
ACTOR_EN_NWC,
ACTOR_EN_OE2,
ACTOR_EN_OKARINA_EFFECT,
ACTOR_EN_RR,
ACTOR_EN_SA,
ACTOR_EN_SCENE_CHANGE,
ACTOR_EN_SKJNEEDLE,
ACTOR_EN_SYATEKI_ITM,
ACTOR_EN_SYATEKI_MAN,
ACTOR_EN_TAKARA_MAN,
ACTOR_EN_TORYO,
ACTOR_EN_VASE,
ACTOR_EN_ZL1,
ACTOR_MAGIC_DARK,
ACTOR_MAGIC_FIRE,
ACTOR_OBJ_DEKUJR,
ACTOR_OCEFF_SPOT,
ACTOR_UNSET_1,
ACTOR_UNSET_3,
ACTOR_UNSET_5,
ACTOR_UNSET_6,
ACTOR_UNSET_17,
ACTOR_UNSET_1A,
ACTOR_UNSET_1F,
ACTOR_UNSET_22,
ACTOR_UNSET_31,
ACTOR_UNSET_36,
ACTOR_UNSET_53,
ACTOR_UNSET_73,
ACTOR_UNSET_74,
ACTOR_UNSET_75,
ACTOR_UNSET_76,
ACTOR_UNSET_78,
ACTOR_UNSET_79,
ACTOR_UNSET_7A,
ACTOR_UNSET_7B,
ACTOR_UNSET_7E,
ACTOR_UNSET_7F,
ACTOR_UNSET_83,
ACTOR_UNSET_A0,
ACTOR_UNSET_B2,
ACTOR_UNSET_CE,
ACTOR_UNSET_D8,
ACTOR_UNSET_EA,
ACTOR_UNSET_EB,
ACTOR_UNSET_F2,
ACTOR_UNSET_F3,
ACTOR_UNSET_FB,
ACTOR_UNSET_109,
ACTOR_UNSET_10D,
ACTOR_UNSET_10E,
ACTOR_UNSET_128,
ACTOR_UNSET_129,
ACTOR_UNSET_134,
ACTOR_UNSET_154,
ACTOR_UNSET_15D,
ACTOR_UNSET_161,
ACTOR_UNSET_180,
ACTOR_UNSET_1AA
};
static std::unordered_map<u16, std::function<s16(s16)>> actorSpecificData;
void CreateActorSpecificData() {
if (!actorSpecificData.empty()) {
return;
}
actorSpecificData[ACTOR_EN_DEKUNUTS] = [](s16 params) -> s16 {
bool isFlower = params == DEKUNUTS_FLOWER;
s16 shotsPerRound = (params >> 8) & 0xFF;
if (shotsPerRound == 0xFF || shotsPerRound == 0) {
shotsPerRound = 1;
}
ImGui::Checkbox("Flower", &isFlower);
if (!isFlower) {
ImGui::InputScalar("Shots Per Round", ImGuiDataType_S16, &shotsPerRound);
}
return isFlower ? DEKUNUTS_FLOWER : (shotsPerRound << 8);
};
actorSpecificData[ACTOR_EN_TITE] = [](s16 params) -> s16 {
static const char* items[] = { "Blue", "Red" };
if (params == 0) {
params = -2;
}
//the + 2 is because the params are -2 & -1 instead of 0 & 1
int selectedItem = params + 2;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem - 2;
}
return params;
};
actorSpecificData[ACTOR_EN_AM] = [](s16 params) -> s16 {
static const char* items[] = { "Statue", "Enemy" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_BG_ICE_TURARA] = [](s16 params) -> s16 {
static const char* items[] = { "Stalagmite", "Stalactite", "Stalactite (Regrow)" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_BG_BREAKWALL] = [](s16 params) -> s16 {
static const char* items[] = { "DC Entrance", "Wall", "KD Floor", "KD Lava Cover" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_TEST] = [](s16 params) -> s16 {
static const char* items[] = { "Invisible", "1", "2", "Ceiling", "4", "5" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_TANA] = [](s16 params) -> s16 {
static const char* items[] = { "Wooden", "Stone (1)", "Stone (2)" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_XC] = [](s16 params) -> s16 {
static const char* items[] = { "0", "1", "2", "3", "4", "5", "Minuet", "Bolero", "Serenade", "9" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_SHOT_SUN] = [](s16 params) -> s16 {
static const char* items[] = { "Sun's Song", "Song of Storms", "LH Sun" };
if (params == 0) {
params = 0x40;
}
//the - 0x40 is because the params are 0x40 & 0x41 instead of 0 & 1
int selectedItem = params - 0x40;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem + 0x40;
}
return params;
};
actorSpecificData[ACTOR_EN_HONOTRAP] = [](s16 params) -> s16 {
static const char* items[] = { "Eye", "Flame Move", "Flame Drop" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_REEBA] = [](s16 params) -> s16 {
bool isBig = params != 0;
ImGui::Checkbox("Big", &isBig);
return isBig;
};
actorSpecificData[ACTOR_EN_TK] = [](s16 params) -> s16 {
bool canTurn = params >= 0;
ImGui::Checkbox("Can Turn", &canTurn);
return canTurn ? 0 : -1;
};
actorSpecificData[ACTOR_EN_ITEM00] = [](s16 params) -> s16 {
bool autoCollect = params & 0x8000;
ImGui::Checkbox("Automatically Collect", &autoCollect);
u8 collectibleFlag = (params & 0x3F00) >> 8;
ImGui::InputScalar("Collectible Flag", ImGuiDataType_U8, &collectibleFlag);
if (collectibleFlag > 0x3F) {
collectibleFlag = 0x3F;
}
static const char* items[] = {
"Green Rupee",
"Blue Rupee",
"Red Rupee",
"Recovery Heart",
"Bombs (A)",
"Arrow",
"Heart Piece",
"Heart Container",
"Arrows (5)",
"Arrows (10)",
"Arrows (30)",
"Bombs (B)",
"Deku Nuts (5)",
"Deku Stick",
"Magic (Large)",
"Magic (Small)",
"Deku Seeds (5)",
"Small Key",
"Flexible",
"Gold Rupee",
"Purple Rupee",
"Deku Shield",
"Hylian Shield",
"Zora Tunic",
"Goron Tunic",
"Bombs (Special)",
"Bombchus"
};
int selectedItem = params & 0xFF;
ImGui::Combo("Item", &selectedItem, items, IM_ARRAYSIZE(items));
return autoCollect * 0x8000 + (collectibleFlag << 8) + selectedItem;
};
actorSpecificData[ACTOR_OBJ_COMB] = [](s16 params) -> s16 {
static const char* items[] = {
"Green Rupee",
"Blue Rupee",
"Red Rupee",
"Recovery Heart",
"Bombs (A)",
"Arrow",
"Heart Piece",
"Heart Container",
"Arrows (5)",
"Arrows (10)",
"Arrows (30)",
"Bombs (B)",
"Deku Nuts (5)",
"Deku Stick",
"Magic (Large)",
"Magic (Small)",
"Deku Seeds (5)",
"Small Key",
"Flexible",
"Gold Rupee",
"Purple Rupee",
"Deku Shield",
"Hylian Shield",
"Zora Tunic",
"Goron Tunic",
"Bombs (Special)",
"Bombchus"
};
int selectedItem = params & 0xFF;
ImGui::Combo("Item Drop", &selectedItem, items, IM_ARRAYSIZE(items));
u8 collectibleFlag = (params & 0x3F00) >> 8;
if (selectedItem == 6) {
ImGui::InputScalar("PoH Collectible Flag", ImGuiDataType_U8, &collectibleFlag);
if (collectibleFlag > 0x3F) {
collectibleFlag = 0x3F;
}
}
return (collectibleFlag << 8) + selectedItem;
};
actorSpecificData[ACTOR_EN_GM] = [](s16 params) -> s16 {
u8 switchFlag = (params & 0x3F00) >> 8;
ImGui::InputScalar("Switch Flag", ImGuiDataType_U8, &switchFlag);
if (switchFlag > 0x3F) {
switchFlag = 0x3F;
}
return switchFlag << 8;
};
actorSpecificData[ACTOR_EN_GIRLA] = [](s16 params) -> s16 {
static const char* items[] = {
"Deku Nuts (5)",
"Arrows (30)",
"Arrows (50)",
"Bombs (5) (25 Rupees)",
"Deku Nuts (10)",
"Deku Stick",
"Bombs (10)",
"Fish",
"Red Potion (30 Rupees)",
"Green Potion",
"Blue Potion",
"Longsword",
"Hylian Shield",
"Deku Shield",
"Goron Tunic",
"Zora Tunic",
"Heart",
"Milk Bottle",
"Weird Egg",
"19",
"20",
"Bomchu (10) [1]",
"Bomchu (20) [1]",
"Bomchu (20) [2]",
"Bomchu (10) [2]",
"Bomchu (10) [3]",
"Bomchu (20) [3]",
"Bomchu (20) [4]",
"Bomchu (10) [4]",
"Deku Seeds (30)",
"Keaton Mask",
"Spooky Mask",
"Skull Mask",
"Bunny Hood",
"Mask Of Truth",
"Zora Mask",
"Goron Mask",
"Gerudo Mask",
"Sold Out",
"Blue Fire",
"Bugs",
"Big Poe",
"Poe",
"Fairy",
"Arrows (10)",
"Bombs (20)",
"Bombs (30)",
"Bombs (5) (35 Rupees)",
"Red Potion (40 Rupees)",
"Red Potion (50 Rupees)",
"Randomizer Item"
};
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_FIRE_ROCK] = [](s16 params) -> s16 {
static const char* items[] = {
"Spawned Falling (1)",
"Broken Piece (1)",
"Broken Piece (2)",
"Spawned Falling (2)",
//"INVALID",
"Ceiling Spot Spawner",
"On Floor"
};
int selectedItem = params > 3 ? params - 1 : params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem > 3 ? selectedItem + 1 : selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_EX_ITEM] = [](s16 params) -> s16 {
static const char* items[] = {
"Bomb Bag Bowling",
"Heart Piece Bowling",
"Bombchus Bowling",
"Bombs Bowling",
"Purple Rupee Bowling",
"Bomb Bag Counter",
"Heart Piece Counter",
"Bombchus Counter",
"Bombs Counter",
"Purple Rupee Counter",
"Green Rupee Chest",
"Blue Rupee Chest",
"Red Rupee Chest",
"13",
"14",
"Small Key Chest",
"Magic Fire",
"Magic Wind",
"Magic Dark",
"Bullet Bag"
};
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_ELF] = [](s16 params) -> s16 {
static const char* items[] = {
"Navi",
"Revive Bottle",
"Heal Timed",
"Kokiri",
"Spawner",
"Revive Death",
"Heal",
"Heal Big"
};
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_CLEAR_TAG] = [](s16 params) -> s16 {
static const char* items[] = {
"Cutscene", //0
"Normal", //1
"Laser" //100
};
int selectedItem = params == 100 ? 2 : params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem == 2 ? 100 : selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_BOMBF] = [](s16 params) -> s16 {
static const char* items[] = { "Flower", "Body", "Explosion" };
//the + 1 is because the params are -1, 0 & 1 instead of 0, 1 & 2
int selectedItem = params + 1;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem - 1;
}
return params;
};
actorSpecificData[ACTOR_EN_BOM] = [](s16 params) -> s16 {
static const char* items[] = { "Body", "Explosion" };
int selectedItem = params;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_DOOR_WARP1] = [](s16 params) -> s16 {
static const char* items[] = {
"Blue Crystal", // -2
"Dungeon Adult",
"Dungeon Child",
"Clear Flag", // Activate on temp clear flag
"Sages", // Used by sages warping into chamber of sages during their cutscene
"Purple Crystal",
"Yellow", // The colored variants don't warp, they are cutscene setpieces
"Blue Ruto",
"Destination", // Spawning in after having taken a warp
"UNK 7",
"Orange",
"Green",
"Red"
};
int selectedItem = params + 2;
if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem - 2;
}
return params;
};
actorSpecificData[ACTOR_EN_DY_EXTRA] = [](s16 params) -> s16 {
static const char* items[] = { "Orange", "Green" };
int selectedItem = params;
if (ImGui::Combo("Color", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
actorSpecificData[ACTOR_EN_SKB] = [](s16 params) -> s16 {
u8 size = params;
ImGui::InputScalar("Size", ImGuiDataType_U8, &size);
return size;
};
actorSpecificData[ACTOR_EN_WF] = [](s16 params) -> s16 {
static const char* items[] = { "Normal", "White" };
int selectedItem = params;
ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items));
u8 switchFlag = (params & 0x3F00) >> 8;
ImGui::InputScalar("Switch Flag", ImGuiDataType_U8, &switchFlag);
return (switchFlag << 8) + selectedItem;
};
actorSpecificData[ACTOR_EN_BOX] = [](s16 params) -> s16 {
/*
trasureFlag = params & 0x1F; //0b0000 0000 0001 1111
itemId = (params >> 5) & 0x7F; //0b0000 1111 1110 0000
type = (params >> 12) & 0xF; //0b1111 0000 0000 0000
*/
u8 treasureFlag = params & 0x1F;
ImGui::InputScalar("Treasure Flag", ImGuiDataType_U8, &treasureFlag);
if (treasureFlag > 0x1F) {
treasureFlag = 0x1F;
}
u8 itemId = (params >> 5) & 0x7F;
ImGui::InputScalar("Item Id", ImGuiDataType_U8, &itemId);
if (itemId > 0x7F) {
itemId = 0x7F;
}
static const char* items[] = {
"Big (Default)",
"Room Clear Big",
"Decorated Big",
"Switch Flag Fall Big",
"4",
"Small",
"6",
"Room Clear Small",
"Switch Flag Fall Small",
"9",
"10",
"Switch Flag Big"
};
int type = (params >> 12) & 0xF;
ImGui::Combo("Type", &type, items, IM_ARRAYSIZE(items));
if (type > 0xF) {
type = 0xF;
}
return (type << 12) + (itemId << 5) + treasureFlag;
};
actorSpecificData[ACTOR_EN_DOOR] = [](s16 params) -> s16 {
/**
* Actor Parameters
*
* | | | |
* | Transition Index | Type | Double Door | Switch Flag OR Text Id - 0x0200
* |------------------|-------|-------------|---------------------------------
* | 0 0 0 0 0 0 | 0 0 0 | 0 | 0 0 0 0 0 0
* | 6 | 3 | 1 | 6
* |
*
* Transition Index 1111110000000000 Set by the actor engine when the door is spawned
* Type 0000001110000000
* Double Door 0000000001000000
* Switch Flag 0000000000111111 For use with the `DOOR_LOCKED` type
* Text id - 0x0200 0000000000111111 For use with the `DOOR_CHECKABLE` type
*
*/
u8 transitionIndex = params >> 10;
ImGui::InputScalar("Transition Index", ImGuiDataType_U8, &transitionIndex);
if (transitionIndex > 0x3F) {
transitionIndex = 0x3F;
}
static const char* items[] = {
"Room Load", // loads rooms
"Locked", // small key locked door
"Room Load (2)", // loads rooms
"Scene Exit", // doesn't load rooms, used for doors paired with scene transition polygons
"Ajar", // open slightly but slams shut if Link gets too close
"Checkable", // doors that display a textbox when interacting
"Evening", // unlocked between 18:00 and 21:00, Dampé's hut
"Room Load (7)" // loads rooms
};
int type = (params >> 7) & 7;
ImGui::Combo("Type", &type, items, IM_ARRAYSIZE(items));
if (type > 7) {
type = 7;
}
bool doubleDoor = ((params >> 6) & 1) != 0;
ImGui::Checkbox("Double Door", &doubleDoor);
u8 lowerBits = params & 0x3F;
if (type == 1) {
ImGui::InputScalar("Switch Flag", ImGuiDataType_U8, &lowerBits);
if (lowerBits > 0x3F) {
lowerBits = 0x3F;
}
} else if (type == 5) {
ImGui::InputScalar("Text ID - 0x200", ImGuiDataType_U8, &lowerBits);
if (lowerBits > 0x3F) {
lowerBits = 0x3F;
}
} else {
lowerBits = 0;
}
return (transitionIndex << 10) + (type << 7) + (doubleDoor << 6) + lowerBits;
};
actorSpecificData[ACTOR_EN_PO_DESERT] = [](s16 params) -> s16 {
u8 switchFlag = params >> 8;
ImGui::InputScalar("Path", ImGuiDataType_U8, &switchFlag);
return switchFlag << 8;
};
actorSpecificData[ACTOR_EN_KANBAN] = [](s16 params) -> s16 {
bool piece = params == (s16)0xFFDD;
bool fishingSign = params == 0x300;
if (ImGui::Checkbox("Piece", &piece)) {
fishingSign = false;
}
if (ImGui::Checkbox("Fishing Sign", &fishingSign)) {
piece = false;
}
u8 textId = params;
if (!piece && !fishingSign) {
if (ImGui::InputScalar("Text ID", ImGuiDataType_U8, &textId)) {
textId |= 0x300;
}
}
return piece ? (s16)0xFFDD : (fishingSign ? 0x300 : textId);
};
actorSpecificData[ACTOR_EN_KUSA] = [](s16 params) -> s16 {
static const char* items[] = {
"0",
"1",
"2"
};
int type = params & 3;
ImGui::Combo("Type", &type, items, IM_ARRAYSIZE(items));
bool bugs = ((params >> 4) & 1) != 0;
ImGui::Checkbox("Bugs", &bugs);
u8 drop = (params >> 8) & 0xF;
if (type == 2) {
ImGui::InputScalar("Random Drop Params", ImGuiDataType_U8, &drop);
if (drop > 0xD) {
drop = 0xD;
}
} else {
drop = 0;
}
return (drop << 8) + (bugs << 4) + type;
};
actorSpecificData[ActorDB::Instance->RetrieveId("En_Partner")] = [](s16 params) -> s16 {
static const char* items[] = {
"Port 1",
"Port 2",
"Port 3",
"Port 4"
};
int selectedItem = params;
if (ImGui::Combo("Controller Port", &selectedItem, items, IM_ARRAYSIZE(items))) {
return selectedItem;
}
return params;
};
}
std::vector<u16> GetActorsWithDescriptionContainingString(std::string s) {
std::locale loc;
for (size_t i = 0; i < s.length(); i += 1) {
s[i] = std::tolower(s[i], loc);
}
std::vector<u16> actors;
for (int i = 0; i < ActorDB::Instance->GetEntryCount(); i += 1) {
ActorDB::Entry actorEntry = ActorDB::Instance->RetrieveEntry(i);
std::string desc = actorEntry.desc;
for (size_t j = 0; j < desc.length(); j += 1) {
desc[j] = std::tolower(desc[j], loc);
}
if (desc.find(s) != std::string::npos) {
actors.push_back((u16)i);
}
}
return actors;
}
void ActorViewer_AddTagForActor(Actor* actor) { void ActorViewer_AddTagForActor(Actor* actor) {
int val = CVarGetInteger("gDebugActorViewerNameTags", ACTORVIEWER_NAMETAGS_NONE); int val = CVarGetInteger("gDebugActorViewerNameTags", ACTORVIEWER_NAMETAGS_NONE);
auto entry = ActorDB::Instance->RetrieveEntry(actor->id); auto entry = ActorDB::Instance->RetrieveEntry(actor->id);
@ -163,6 +942,9 @@ void ActorViewerWindow::DrawElement() {
static std::string filler = "Please select"; static std::string filler = "Please select";
static std::vector<Actor*> list; static std::vector<Actor*> list;
static u16 lastSceneId = 0; static u16 lastSceneId = 0;
static char searchString[64] = "";
static s16 currentSelectedInDropdown;
static std::vector<u16> actors;
if (gPlayState != nullptr) { if (gPlayState != nullptr) {
needs_reset = lastSceneId != gPlayState->sceneNum; needs_reset = lastSceneId != gPlayState->sceneNum;
@ -173,6 +955,11 @@ void ActorViewerWindow::DrawElement() {
filler = "Please Select"; filler = "Please Select";
list.clear(); list.clear();
needs_reset = false; needs_reset = false;
for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) {
searchString[i] = 0;
}
currentSelectedInDropdown = -1;
actors.clear();
} }
lastSceneId = gPlayState->sceneNum; lastSceneId = gPlayState->sceneNum;
if (ImGui::BeginCombo("Actor Type", acMapping[category])) { if (ImGui::BeginCombo("Actor Type", acMapping[category])) {
@ -316,9 +1103,48 @@ void ActorViewerWindow::DrawElement() {
if (ImGui::TreeNode("New...")) { if (ImGui::TreeNode("New...")) {
ImGui::PushItemWidth(ImGui::GetFontSize() * 10); ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
if (ImGui::InputText("Search Actor", searchString, ARRAY_COUNT(searchString))) {
actors = GetActorsWithDescriptionContainingString(std::string(searchString));
currentSelectedInDropdown = -1;
}
if (searchString[0] != 0 && !actors.empty()) {
std::string preview = currentSelectedInDropdown == -1 ? "Please Select" : ActorDB::Instance->RetrieveEntry(actors[currentSelectedInDropdown]).desc;
if (ImGui::BeginCombo("Results", preview.c_str())) {
for (u8 i = 0; i < actors.size(); i++) {
if (ImGui::Selectable(
ActorDB::Instance->RetrieveEntry(actors[i]).desc.c_str(),
i == currentSelectedInDropdown
)) {
currentSelectedInDropdown = i;
newActor.id = actors[i];
}
}
ImGui::EndCombo();
}
}
ImGui::Text("%s", GetActorDescription(newActor.id).c_str()); ImGui::Text("%s", GetActorDescription(newActor.id).c_str());
ImGui::InputScalar("ID", ImGuiDataType_S16, &newActor.id, &one); if (ImGui::InputScalar("ID", ImGuiDataType_S16, &newActor.id, &one)) {
ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one); newActor.params = 0;
}
UIWidgets::EnhancementCheckbox("Advanced mode", "gActorViewerAdvancedParams");
UIWidgets::InsertHelpHoverText("Changes the actor specific param menus with a direct input");
if (CVarGetInteger("gActorViewerAdvancedParams", 0)) {
ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one);
} else if (std::find(noParamsActors.begin(), noParamsActors.end(), newActor.id) == noParamsActors.end()) {
CreateActorSpecificData();
if (actorSpecificData.find(newActor.id) == actorSpecificData.end()) {
ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one);
} else {
DrawGroupWithBorder([&]() {
ImGui::Text("Actor Specific Data");
newActor.params = actorSpecificData[newActor.id](newActor.params);
});
}
}
ImGui::PushItemWidth(ImGui::GetFontSize() * 6); ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
@ -401,6 +1227,11 @@ void ActorViewerWindow::DrawElement() {
filler = "Please Select"; filler = "Please Select";
list.clear(); list.clear();
needs_reset = false; needs_reset = false;
for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) {
searchString[i] = 0;
}
currentSelectedInDropdown = -1;
actors.clear();
} }
} }

View file

@ -515,7 +515,7 @@ void DrawInfoTab() {
UIWidgets::InsertHelpHoverText("Z-Targeting behavior"); UIWidgets::InsertHelpHoverText("Z-Targeting behavior");
if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT)) { if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT)) {
ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U16, &gSaveContext.triforcePiecesCollected); ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U8, &gSaveContext.triforcePiecesCollected);
UIWidgets::InsertHelpHoverText("Currently obtained Triforce Pieces. For Triforce Hunt."); UIWidgets::InsertHelpHoverText("Currently obtained Triforce Pieces. For Triforce Hunt.");
} }
@ -1573,7 +1573,7 @@ void DrawPlayerTab() {
ImGui::InputScalar("Y Velocity", ImGuiDataType_Float, &player->actor.velocity.y); ImGui::InputScalar("Y Velocity", ImGuiDataType_Float, &player->actor.velocity.y);
UIWidgets::InsertHelpHoverText("Link's speed along the Y plane. Caps at -20"); UIWidgets::InsertHelpHoverText("Link's speed along the Y plane. Caps at -20");
ImGui::InputScalar("Wall Height", ImGuiDataType_Float, &player->wallHeight); ImGui::InputScalar("Wall Height", ImGuiDataType_Float, &player->yDistToLedge);
UIWidgets::InsertHelpHoverText("Height used to determine whether Link can climb or grab a ledge at the top"); UIWidgets::InsertHelpHoverText("Height used to determine whether Link can climb or grab a ledge at the top");
ImGui::InputScalar("Invincibility Timer", ImGuiDataType_S8, &player->invincibilityTimer); ImGui::InputScalar("Invincibility Timer", ImGuiDataType_S8, &player->invincibilityTimer);

View file

@ -505,6 +505,7 @@ const std::vector<FlagTable> flagTables = {
{ RAND_INF_CHILD_FISHING, "RAND_INF_CHILD_FISHING" }, { RAND_INF_CHILD_FISHING, "RAND_INF_CHILD_FISHING" },
{ RAND_INF_ADULT_FISHING, "RAND_INF_ADULT_FISHING" }, { RAND_INF_ADULT_FISHING, "RAND_INF_ADULT_FISHING" },
{ RAND_INF_10_BIG_POES, "RAND_INF_10_BIG_POES" }, { RAND_INF_10_BIG_POES, "RAND_INF_10_BIG_POES" },
{ RAND_INF_GRANT_GANONS_BOSSKEY, "RAND_INF_GRANT_GANONS_BOSSKEY" },
} }, } },
}; };

View file

@ -18,15 +18,13 @@ extern "C" {
#include "variables.h" #include "variables.h"
#include "functions.h" #include "functions.h"
#include "macros.h" #include "macros.h"
extern PlayState* gPlayState;
char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
} }
char searchString[64] = ""; char searchString[64] = "";
int displayListsSearchResultsCount; std::string activeDisplayList = "";
char** displayListsSearchResults; std::vector<std::string> displayListSearchResults;
char* activeDisplayList = nullptr; int16_t searchDebounceFrames = -1;
bool doSearch = false;
std::map<int, std::string> cmdMap = { std::map<int, std::string> cmdMap = {
{ G_SETPRIMCOLOR, "gsDPSetPrimColor" }, { G_SETPRIMCOLOR, "gsDPSetPrimColor" },
@ -36,8 +34,61 @@ std::map<int, std::string> cmdMap = {
{ G_SETINTENSITY, "gsDPSetGrayscaleColor" }, { G_SETINTENSITY, "gsDPSetGrayscaleColor" },
{ G_LOADTLUT, "gsDPLoadTLUT" }, { G_LOADTLUT, "gsDPLoadTLUT" },
{ G_ENDDL, "gsSPEndDisplayList" }, { G_ENDDL, "gsSPEndDisplayList" },
{ G_TEXTURE, "gsSPTexture" },
{ G_SETTIMG, "gsDPSetTextureImage" },
{ G_SETTIMG_OTR_HASH, "gsDPSetTextureImage" },
{ G_SETTIMG_OTR_FILEPATH, "gsDPSetTextureImage" },
{ G_RDPTILESYNC, "gsDPTileSync" },
{ G_SETTILE, "gsDPSetTile" },
{ G_RDPLOADSYNC, "gsDPLoadSync" },
{ G_LOADBLOCK, "gsDPLoadBlock" },
{ G_SETTILESIZE, "gsDPSetTileSize" },
{ G_DL, "gsSPDisplayList" },
{ G_DL_OTR_FILEPATH, "gsSPDisplayList" },
{ G_DL_OTR_HASH, "gsSPDisplayList" },
{ G_MTX, "gsSPMatrix" },
{ G_MTX_OTR, "gsSPMatrix" },
{ G_VTX, "gsSPVertex" },
{ G_VTX_OTR_FILEPATH, "gsSPVertex" },
{ G_VTX_OTR_HASH, "gsSPVertex" },
{ G_GEOMETRYMODE, "gsSPSetGeometryMode" },
{ G_SETOTHERMODE_H, "gsSPSetOtherMode_H" },
{ G_SETOTHERMODE_L, "gsSPSetOtherMode_L" },
{ G_TRI1, "gsSP1Triangle" },
{ G_TRI1_OTR, "gsSP1Triangle" },
{ G_TRI2, "gsSP2Triangles" },
{ G_SETCOMBINE, "gsDPSetCombineLERP" },
{ G_CULLDL, "gsSPCullDisplayList" },
{ G_NOOP, "gsDPNoOp" },
{ G_SPNOOP, "gsSPNoOp" },
{ G_MARKER, "LUS Custom Marker" },
}; };
void PerformDisplayListSearch() {
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 (val.ends_with("DL") || val.find("DL_") != std::string::npos) {
displayListSearchResults.push_back(val);
}
}
// Sort the final list
std::sort(displayListSearchResults.begin(), displayListSearchResults.end(), [](const std::string& a, const std::string& b) {
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
[](char c1, char c2) {
return std::tolower(c1) < std::tolower(c2);
}
);
});
}
void DLViewerWindow::DrawElement() { void DLViewerWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Display List Viewer", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) { if (!ImGui::Begin("Display List Viewer", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
@ -45,22 +96,48 @@ void DLViewerWindow::DrawElement() {
return; return;
} }
// Debounce the search field as listing otr files is expensive
if (ImGui::InputText("Search Display Lists", searchString, ARRAY_COUNT(searchString))) { if (ImGui::InputText("Search Display Lists", searchString, ARRAY_COUNT(searchString))) {
displayListsSearchResults = ResourceMgr_ListFiles(("*" + std::string(searchString) + "*DL").c_str(), &displayListsSearchResultsCount); doSearch = true;
searchDebounceFrames = 30;
} }
if (ImGui::BeginCombo("Active Display List", activeDisplayList)) { if (doSearch) {
for (int i = 0; i < displayListsSearchResultsCount; i++) { if (searchDebounceFrames == 0) {
if (ImGui::Selectable(displayListsSearchResults[i])) { doSearch = false;
activeDisplayList = displayListsSearchResults[i]; PerformDisplayListSearch();
}
searchDebounceFrames--;
}
if (ImGui::BeginCombo("Active Display List", activeDisplayList.c_str())) {
for (size_t i = 0; i < displayListSearchResults.size(); i++) {
if (ImGui::Selectable(displayListSearchResults[i].c_str())) {
activeDisplayList = displayListSearchResults[i];
break; break;
} }
} }
ImGui::EndCombo(); ImGui::EndCombo();
} }
if (activeDisplayList != nullptr) {
if (activeDisplayList == "") {
ImGui::End();
return;
}
try {
auto res = std::static_pointer_cast<LUS::DisplayList>(LUS::Context::GetInstance()->GetResourceManager()->LoadResource(activeDisplayList)); auto res = std::static_pointer_cast<LUS::DisplayList>(LUS::Context::GetInstance()->GetResourceManager()->LoadResource(activeDisplayList));
for (int i = 0; i < res->Instructions.size(); i++) {
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;
}
ImGui::Text("Total Instruction Size: %lu", res->Instructions.size());
for (size_t i = 0; i < res->Instructions.size(); i++) {
std::string id = "##CMD" + std::to_string(i); std::string id = "##CMD" + std::to_string(i);
Gfx* gfx = (Gfx*)&res->Instructions[i]; Gfx* gfx = (Gfx*)&res->Instructions[i];
int cmd = gfx->words.w0 >> 24; int cmd = gfx->words.w0 >> 24;
@ -70,10 +147,11 @@ void DLViewerWindow::DrawElement() {
ImGui::BeginGroup(); ImGui::BeginGroup();
ImGui::PushItemWidth(25.0f); ImGui::PushItemWidth(25.0f);
ImGui::Text("%d", i); ImGui::Text("%lu", i);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::SameLine(); ImGui::SameLine();
ImGui::PushItemWidth(150.0f); ImGui::PushItemWidth(175.0f);
if (ImGui::BeginCombo(("CMD" + id).c_str(), cmdLabel.c_str())) { if (ImGui::BeginCombo(("CMD" + id).c_str(), cmdLabel.c_str())) {
if (ImGui::Selectable("gsDPSetPrimColor") && cmd != G_SETPRIMCOLOR) { if (ImGui::Selectable("gsDPSetPrimColor") && cmd != G_SETPRIMCOLOR) {
*gfx = gsDPSetPrimColor(0, 0, 0, 0, 0, 255); *gfx = gsDPSetPrimColor(0, 0, 0, 0, 0, 255);
@ -92,8 +170,10 @@ void DLViewerWindow::DrawElement() {
} }
ImGui::EndCombo(); ImGui::EndCombo();
} }
ImGui::PopItemWidth(); ImGui::PopItemWidth();
if (gfx->words.w0 >> 24 == G_SETPRIMCOLOR || gfx->words.w0 >> 24 == G_SETINTENSITY || gfx->words.w0 >> 24 == G_SETENVCOLOR) {
if (cmd == G_SETPRIMCOLOR || cmd == G_SETINTENSITY || cmd == G_SETENVCOLOR) {
uint8_t r = _SHIFTR(gfx->words.w1, 24, 8); uint8_t r = _SHIFTR(gfx->words.w1, 24, 8);
uint8_t g = _SHIFTR(gfx->words.w1, 16, 8); uint8_t g = _SHIFTR(gfx->words.w1, 16, 8);
uint8_t b = _SHIFTR(gfx->words.w1, 8, 8); uint8_t b = _SHIFTR(gfx->words.w1, 8, 8);
@ -117,21 +197,141 @@ void DLViewerWindow::DrawElement() {
} }
ImGui::PopItemWidth(); ImGui::PopItemWidth();
} }
if (gfx->words.w0 >> 24 == G_RDPPIPESYNC) { if (cmd == G_RDPPIPESYNC) {
} }
if (gfx->words.w0 >> 24 == G_SETGRAYSCALE) { if (cmd == G_SETGRAYSCALE) {
bool* state = (bool*)&gfx->words.w1; bool* state = (bool*)&gfx->words.w1;
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Checkbox(("state" + id).c_str(), state)) { if (ImGui::Checkbox(("state" + id).c_str(), state)) {
// //
} }
} }
if (cmd == G_SETTILE) {
ImGui::SameLine();
ImGui::Text("FMT: %u", _SHIFTR(gfx->words.w0, 21, 3));
ImGui::SameLine();
ImGui::Text("SIZ: %u", _SHIFTR(gfx->words.w0, 19, 2));
ImGui::SameLine();
ImGui::Text("LINE: %u", _SHIFTR(gfx->words.w0, 9, 9));
ImGui::SameLine();
ImGui::Text("TMEM: %u", _SHIFTR(gfx->words.w0, 0, 9));
ImGui::SameLine();
ImGui::Text("TILE: %u", _SHIFTR(gfx->words.w1, 24, 3));
ImGui::SameLine();
ImGui::Text("PAL: %u", _SHIFTR(gfx->words.w1, 20, 4));
ImGui::SameLine();
ImGui::Text("CMT: %u", _SHIFTR(gfx->words.w1, 18, 2));
ImGui::SameLine();
ImGui::Text("MASKT: %u", _SHIFTR(gfx->words.w1, 14, 4));
ImGui::SameLine();
ImGui::Text("SHIFT: %u", _SHIFTR(gfx->words.w1, 10, 4));
ImGui::SameLine();
ImGui::Text("CMS: %u", _SHIFTR(gfx->words.w1, 8, 2));
ImGui::SameLine();
ImGui::Text("MASKS: %u", _SHIFTR(gfx->words.w1, 4, 4));
ImGui::SameLine();
ImGui::Text("SHIFTS: %u", _SHIFTR(gfx->words.w1, 0, 4));
}
if (cmd == G_SETTIMG) {
ImGui::SameLine();
ImGui::Text("FMT: %u", _SHIFTR(gfx->words.w0, 21, 3));
ImGui::SameLine();
ImGui::Text("SIZ: %u", _SHIFTR(gfx->words.w0, 19, 2));
ImGui::SameLine();
ImGui::Text("WIDTH: %u", _SHIFTR(gfx->words.w0, 0, 10));
ImGui::SameLine();
}
if (cmd == G_SETTIMG_OTR_HASH) {
gfx++;
uint64_t hash = ((uint64_t)gfx->words.w0 << 32) + (uint64_t)gfx->words.w1;
const char* fileName = ResourceGetNameByCrc(hash);
gfx--;
ImGui::SameLine();
ImGui::Text("FMT: %u", _SHIFTR(gfx->words.w0, 21, 3));
ImGui::SameLine();
ImGui::Text("SIZ: %u", _SHIFTR(gfx->words.w0, 19, 2));
ImGui::SameLine();
ImGui::Text("WIDTH: %u", _SHIFTR(gfx->words.w0, 0, 10));
ImGui::SameLine();
ImGui::Text("Texture Name: %s", fileName);
}
if (cmd == G_SETTIMG_OTR_FILEPATH) {
char* fileName = (char*)gfx->words.w1;
gfx++;
ImGui::SameLine();
ImGui::Text("FMT: %u", _SHIFTR(gfx->words.w0, 21, 3));
ImGui::SameLine();
ImGui::Text("SIZ: %u", _SHIFTR(gfx->words.w0, 19, 2));
ImGui::SameLine();
ImGui::Text("WIDTH: %u", _SHIFTR(gfx->words.w0, 0, 10));
ImGui::SameLine();
ImGui::Text("Texture Name: %s", fileName);
}
if (cmd == G_VTX) {
ImGui::SameLine();
ImGui::Text("Num VTX: %u", _SHIFTR(gfx->words.w0, 12, 8));
ImGui::SameLine();
ImGui::Text("Offset: %u", _SHIFTR(gfx->words.w0, 1, 7) - _SHIFTR(gfx->words.w0, 12, 8));
}
if (cmd == G_VTX_OTR_HASH) {
gfx++;
uint64_t hash = ((uint64_t)gfx->words.w0 << 32) + (uint64_t)gfx->words.w1;
const char* fileName = ResourceGetNameByCrc(hash);
gfx--;
ImGui::SameLine();
ImGui::Text("Num VTX: %u", _SHIFTR(gfx->words.w0, 12, 8));
ImGui::SameLine();
ImGui::Text("Offset: %u", _SHIFTR(gfx->words.w0, 1, 7) - _SHIFTR(gfx->words.w0, 12, 8));
ImGui::SameLine();
ImGui::Text("Vertex Name: %s", fileName);
}
if (cmd == G_VTX_OTR_FILEPATH) {
char* fileName = (char*)gfx->words.w1;
gfx++;
ImGui::SameLine();
ImGui::Text("Num VTX: %u", _SHIFTR(gfx->words.w0, 12, 8));
ImGui::SameLine();
ImGui::Text("Offset: %u", _SHIFTR(gfx->words.w0, 1, 7) - _SHIFTR(gfx->words.w0, 12, 8));
ImGui::SameLine();
ImGui::Text("Vertex Name: %s", fileName);
}
if (cmd == G_DL) {
}
if (cmd == G_DL_OTR_HASH) {
gfx++;
uint64_t hash = ((uint64_t)gfx->words.w0 << 32) + (uint64_t)gfx->words.w1;
const char* fileName = ResourceGetNameByCrc(hash);
ImGui::SameLine();
ImGui::Text("DL Name: %s", fileName);
}
if (cmd == G_DL_OTR_FILEPATH) {
char* fileName = (char*)gfx->words.w1;
ImGui::SameLine();
ImGui::Text("DL Name: %s", fileName);
}
// Skip second half of instructions that are over 128-bit wide
if (cmd == G_SETTIMG_OTR_HASH || cmd == G_DL_OTR_HASH || cmd == G_VTX_OTR_HASH ||
cmd == G_BRANCH_Z_OTR || cmd == G_MARKER || cmd == G_MTX_OTR) {
i++;
ImGui::Text("%lu - Reserved - Second half of %s", i, cmdLabel.c_str());
}
ImGui::EndGroup(); ImGui::EndGroup();
} }
} catch (const std::exception& e) {
ImGui::Text("Error displaying DL instructions.");
ImGui::End();
return;
} }
ImGui::End(); ImGui::End();
} }
void DLViewerWindow::InitElement() { void DLViewerWindow::InitElement() {
displayListsSearchResults = ResourceMgr_ListFiles("*DL", &displayListsSearchResultsCount); PerformDisplayListSearch();
} }

View file

@ -0,0 +1,219 @@
#include "valueViewer.h"
#include "../../UIWidgets.hpp"
extern "C" {
#include <z64.h>
#include "variables.h"
#include "functions.h"
#include "macros.h"
extern PlayState* gPlayState;
void GfxPrint_SetColor(GfxPrint* printer, u32 r, u32 g, u32 b, u32 a);
void GfxPrint_SetPos(GfxPrint* printer, s32 x, s32 y);
s32 GfxPrint_Printf(GfxPrint* printer, const char* fmt, ...);
}
ImVec4 WHITE = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
std::vector<ValueTableElement> valueTable = {
{ "Time", "gSaveContext.dayTime", "TIME:", TYPE_U16, false, []() -> void* { return &gSaveContext.dayTime; }, WHITE },
{ "Age", "gSaveContext.linkAge", "AGE:", TYPE_S32, false, []() -> void* { return &gSaveContext.linkAge; }, WHITE },
{ "Health", "gSaveContext.health", "HP:", TYPE_S16, false, []() -> void* { return &gSaveContext.health; }, WHITE },
{ "Navi Timer", "gSaveContext.naviTimer", "NAVI:", TYPE_U16, false, []() -> void* { return &gSaveContext.naviTimer; }, WHITE },
{ "Scene ID", "play->sceneNum", "SCENE:", TYPE_S16, true, []() -> void* { return &gPlayState->sceneNum; }, WHITE },
{ "Room ID", "play->roomCtx.curRoom.num", "ROOM:", TYPE_S8, true, []() -> void* { return &gPlayState->roomCtx.curRoom.num; }, WHITE },
{ "Entrance ID", "gSaveContext.entranceIndex", "ENTR:", TYPE_S32, false, []() -> void* { return &gSaveContext.entranceIndex; }, WHITE },
{ "Cutscene ID", "gSaveContext.cutsceneIndex", "CUTS:", TYPE_S32, false, []() -> void* { return &gSaveContext.cutsceneIndex; }, WHITE },
{ "Link X", "Player->actor.world.pos.x", "X:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.x; }, WHITE },
{ "Link Y", "Player->actor.world.pos.y", "Y:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.y; }, WHITE },
{ "Link Z", "Player->actor.world.pos.z", "Z:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.z; }, WHITE },
{ "Link Yaw", "Player->actor.world.rot.y", "ROT:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.rot.y; }, WHITE },
{ "Link Velocity", "Player->linearVelocity", "V:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->linearVelocity; }, WHITE },
{ "Link X Velocity", "Player->actor.velocity.x", "XV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.x; }, WHITE },
{ "Link Y Velocity", "Player->actor.velocity.y", "YV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.y; }, WHITE },
{ "Link Z Velocity", "Player->actor.velocity.z", "ZV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.z; }, WHITE },
{ "Text ID", "play->msgCtx.textId", "TEXTID:", TYPE_U16, true, []() -> void* { return &gPlayState->msgCtx.textId; }, WHITE },
{ "Analog Stick X", "play->state.input->cur.stick_x", "AX:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_x; }, WHITE },
{ "Analog Stick Y", "play->state.input->cur.stick_y", "AY:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_y; }, WHITE },
/* TODO: Find these (from GZ)
"XZ Units Traveled (Camera based speed variable)" f32 0x801C9018
"Movement Angle" x16 0x801DBB1C
"Camera Angle" u16 0x801C907C
"Time of Day" x16 0x8011AC8C
"Global Frame Counter" s32 0x801C8DFC
"Lit Deku Stick Timer" u16 0x801DBB40
"Cutscene Pointer" u32 0x801CAAC8
"Get Item Value" s8 0x801DB714
"Last RNG Value" x32 0x80105A80
"Last Item Button Pressed" u8 0x801DB430
"Last Damage Value" x32 0x801DB7DC
"Temp B Value" u8 0x8011C062
"Framerate Divisor" u8 0x801C7861
"Heads Up Display (HUD)" u16 0x8011C068
"Analog Stick Angle" s16 0x803AA698
"Deku Tree Warp Timer (Reload Room)" u16 0x801F0352
"Dodongo's Cavern Warp Timer" u16 0x801E30B2
"Jabu-Jabu Warp Timer" u16 0x802008B2
"Forest Temple Warp Timer" u16 0x801EC5B2
"Fire Temple Warp Timer" u16 0x801F3E42
"Water Temple Warp Timer" u16 0x801F8762
"Shadow Temple Warp Timer" u16 0x801F48A2
"Spirit Temple Warp Timer" u16 0x801FD562
"Deku Tree Warp Timer" u16 0x801F83A2
*/
};
extern "C" void ValueViewer_Draw(GfxPrint* printer) {
for (int i = 0; i < valueTable.size(); i++) {
ValueTableElement& element = valueTable[i];
if (!element.isActive || !element.isPrinted || (gPlayState == NULL && element.requiresPlayState)) continue;
GfxPrint_SetColor(printer, element.color.x * 255, element.color.y * 255, element.color.z * 255, element.color.w * 255);
GfxPrint_SetPos(printer, element.x, element.y);
switch (element.type) {
case TYPE_S8:
GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%d"), element.prefix.c_str(), *(s8*)element.valueFn());
break;
case TYPE_U8:
GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%u"), element.prefix.c_str(), *(u8*)element.valueFn());
break;
case TYPE_S16:
GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%d"), element.prefix.c_str(), *(s16*)element.valueFn());
break;
case TYPE_U16:
GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%u"), element.prefix.c_str(), *(u16*)element.valueFn());
break;
case TYPE_S32:
GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%d"), element.prefix.c_str(), *(s32*)element.valueFn());
break;
case TYPE_U32:
GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%u"), element.prefix.c_str(), *(u32*)element.valueFn());
break;
case TYPE_CHAR:
GfxPrint_Printf(printer, "%s%c", element.prefix.c_str(), *(char*)element.valueFn());
break;
case TYPE_STRING:
GfxPrint_Printf(printer, "%s%s", element.prefix.c_str(), (char*)element.valueFn());
break;
case TYPE_FLOAT:
GfxPrint_Printf(printer, (element.typeFormat ? "%s%4.1f" : "%s%f"), element.prefix.c_str(), *(float*)element.valueFn());
break;
}
}
}
void ValueViewerWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Value Viewer", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::End();
return;
}
UIWidgets::PaddedEnhancementCheckbox("Enable Printing", "gValueViewer.EnablePrinting");
ImGui::BeginGroup();
static int selectedElement = -1;
std::string selectedElementText = (selectedElement == -1) ? "Select a value" : (
std::string(valueTable[selectedElement].name) + " (" + std::string(valueTable[selectedElement].path) + ")"
);
if (ImGui::BeginCombo("##valueViewerElement", selectedElementText.c_str())) {
for (int i = 0; i < valueTable.size(); i++) {
if (valueTable[i].isActive) continue;
bool isSelected = (selectedElement == i);
std::string elementText = (
std::string(valueTable[i].name) + " (" + std::string(valueTable[i].path) + ")"
);
if (ImGui::Selectable(elementText.c_str(), isSelected)) {
selectedElement = i;
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (selectedElement != -1 && ImGui::Button("+")) {
valueTable[selectedElement].isActive = true;
selectedElement = -1;
}
ImGui::EndGroup();
for (int i = 0; i < valueTable.size(); i++) {
ValueTableElement& element = valueTable[i];
if (!element.isActive || (gPlayState == NULL && element.requiresPlayState)) continue;
if (ImGui::Button(("x##" + std::string(element.name)).c_str())) {
element.isActive = false;
element.isPrinted = false;
}
ImGui::SameLine();
ImGui::Text("%s:", element.name);
ImGui::SameLine();
switch (element.type) {
case TYPE_S8:
ImGui::Text(element.typeFormat ? "0x%x" : "%d", *(s8*)element.valueFn());
break;
case TYPE_U8:
ImGui::Text(element.typeFormat ? "0x%x" : "%u", *(u8*)element.valueFn());
break;
case TYPE_S16:
ImGui::Text(element.typeFormat ? "0x%x" : "%d", *(s16*)element.valueFn());
break;
case TYPE_U16:
ImGui::Text(element.typeFormat ? "0x%x" : "%u", *(u16*)element.valueFn());
break;
case TYPE_S32:
ImGui::Text(element.typeFormat ? "0x%x" : "%d", *(s32*)element.valueFn());
break;
case TYPE_U32:
ImGui::Text(element.typeFormat ? "0x%x" : "%u", *(u32*)element.valueFn());
break;
case TYPE_CHAR:
ImGui::Text("%c", *(char*)element.valueFn());
break;
case TYPE_STRING:
ImGui::Text("%s", (char*)element.valueFn());
break;
case TYPE_FLOAT:
ImGui::Text(element.typeFormat ? "%4.1f" : "%f", *(float*)element.valueFn());
break;
}
ImGui::SameLine();
if (element.type <= TYPE_U32) {
ImGui::Checkbox(("Hex##" + std::string(element.name)).c_str(), &element.typeFormat);
ImGui::SameLine();
} else if (element.type == TYPE_FLOAT) {
ImGui::Checkbox(("Trim##" + std::string(element.name)).c_str(), &element.typeFormat);
ImGui::SameLine();
}
ImGui::BeginGroup();
if (CVarGetInteger("gValueViewer.EnablePrinting", 0)) {
ImGui::Checkbox(("Print##" + std::string(element.name)).c_str(), &element.isPrinted);
if (element.isPrinted) {
char* prefix = (char*)element.prefix.c_str();
ImGui::SameLine();
ImGui::SetNextItemWidth(80.0f);
if (ImGui::InputText(("Prefix##" + std::string(element.name)).c_str(), prefix, 10)) {
element.prefix = prefix;
}
ImGui::SameLine();
ImGui::ColorEdit3(("##color" + std::string(element.name)).c_str(), (float*)&element.color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel);
ImGui::SameLine();
if (ImGui::Button(("Position##" + std::string(element.name)).c_str())) {
ImGui::OpenPopup(("Position Picker##" + std::string(element.name)).c_str());
}
if (ImGui::BeginPopup(("Position Picker##" + std::string(element.name)).c_str())) {
ImGui::DragInt("X", (int*)&element.x, 1.0f, 0, 44);
ImGui::DragInt("Y", (int*)&element.y, 1.0f, 0, 29);
ImGui::EndPopup();
}
}
}
ImGui::EndGroup();
}
ImGui::End();
}
void ValueViewerWindow::InitElement() {
}

View file

@ -0,0 +1,53 @@
#pragma once
#ifdef __cplusplus
#include <libultraship/libultraship.h>
typedef enum {
TYPE_S8,
TYPE_U8,
TYPE_S16,
TYPE_U16,
TYPE_S32,
TYPE_U32,
TYPE_CHAR,
TYPE_STRING,
TYPE_FLOAT,
} ValueType;
typedef void* (*ValueFn)();
typedef struct {
const char* name;
const char* path;
std::string prefix;
ValueType type;
bool requiresPlayState;
ValueFn valueFn;
ImVec4 color;
bool isActive;
bool isPrinted;
bool typeFormat;
uint32_t x;
uint32_t y;
} ValueTableElement;
class ValueViewerWindow : public LUS::GuiWindow {
public:
using GuiWindow::GuiWindow;
void InitElement() override;
void DrawElement() override;
void UpdateElement() override {};
};
extern "C" {
#include <z64.h>
#endif
void ValueViewer_Draw(GfxPrint* printer);
#ifdef __cplusplus
}
#endif

View file

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

View file

@ -31,11 +31,11 @@ GameInteractionEffectQueryResult GameInteractionEffectBase::Apply() {
} }
/// For most effects, CanBeRemoved is the same as CanBeApplied. When its not: please override `CanBeRemoved`. /// For most effects, CanBeRemoved is the same as CanBeApplied. When its not: please override `CanBeRemoved`.
GameInteractionEffectQueryResult GameInteractionEffectBase::CanBeRemoved() { GameInteractionEffectQueryResult RemovableGameInteractionEffect::CanBeRemoved() {
return CanBeApplied(); return CanBeApplied();
} }
GameInteractionEffectQueryResult GameInteractionEffectBase::Remove() { GameInteractionEffectQueryResult RemovableGameInteractionEffect::Remove() {
GameInteractionEffectQueryResult result = CanBeRemoved(); GameInteractionEffectQueryResult result = CanBeRemoved();
if (result != GameInteractionEffectQueryResult::Possible) { if (result != GameInteractionEffectQueryResult::Possible) {
return result; return result;

View file

@ -15,38 +15,46 @@ enum GameInteractionEffectQueryResult {
class GameInteractionEffectBase { class GameInteractionEffectBase {
public: public:
virtual GameInteractionEffectQueryResult CanBeApplied() = 0; virtual GameInteractionEffectQueryResult CanBeApplied() = 0;
virtual GameInteractionEffectQueryResult CanBeRemoved();
GameInteractionEffectQueryResult Apply(); GameInteractionEffectQueryResult Apply();
GameInteractionEffectQueryResult Remove(); protected:
int32_t parameters[3];
protected:
virtual void _Apply() = 0; virtual void _Apply() = 0;
};
class RemovableGameInteractionEffect: public GameInteractionEffectBase {
public:
virtual GameInteractionEffectQueryResult CanBeRemoved();
GameInteractionEffectQueryResult Remove();
protected:
virtual void _Remove() {}; virtual void _Remove() {};
}; };
class ParameterizedGameInteractionEffect {
public:
int32_t parameters[3];
};
namespace GameInteractionEffect { namespace GameInteractionEffect {
class SetSceneFlag: public GameInteractionEffectBase { class SetSceneFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class UnsetSceneFlag: public GameInteractionEffectBase { class UnsetSceneFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class SetFlag: public GameInteractionEffectBase { class SetFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class UnsetFlag: public GameInteractionEffectBase { class UnsetFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class ModifyHeartContainers: public GameInteractionEffectBase { class ModifyHeartContainers: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
@ -61,29 +69,29 @@ namespace GameInteractionEffect {
void _Apply() override; void _Apply() override;
}; };
class ModifyRupees: public GameInteractionEffectBase { class ModifyRupees: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class NoUI: public GameInteractionEffectBase { class NoUI: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class ModifyGravity: public GameInteractionEffectBase { class ModifyGravity: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class ModifyHealth: public GameInteractionEffectBase { class ModifyHealth: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class SetPlayerHealth: public GameInteractionEffectBase { class SetPlayerHealth: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
@ -103,98 +111,98 @@ namespace GameInteractionEffect {
void _Apply() override; void _Apply() override;
}; };
class KnockbackPlayer: public GameInteractionEffectBase { class KnockbackPlayer: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class ModifyLinkSize: public GameInteractionEffectBase { class ModifyLinkSize: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class InvisibleLink : public GameInteractionEffectBase { class InvisibleLink : public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class PacifistMode : public GameInteractionEffectBase { class PacifistMode : public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class DisableZTargeting: public GameInteractionEffectBase { class DisableZTargeting: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class WeatherRainstorm: public GameInteractionEffectBase { class WeatherRainstorm: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class ReverseControls: public GameInteractionEffectBase { class ReverseControls: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class ForceEquipBoots: public GameInteractionEffectBase { class ForceEquipBoots: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class ModifyRunSpeedModifier: public GameInteractionEffectBase { class ModifyRunSpeedModifier: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class OneHitKO : public GameInteractionEffectBase { class OneHitKO : public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class ModifyDefenseModifier: public GameInteractionEffectBase { class ModifyDefenseModifier: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class GiveOrTakeShield: public GameInteractionEffectBase { class GiveOrTakeShield: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class TeleportPlayer: public GameInteractionEffectBase { class TeleportPlayer: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class ClearAssignedButtons: public GameInteractionEffectBase { class ClearAssignedButtons: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class SetTimeOfDay: public GameInteractionEffectBase { class SetTimeOfDay: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class SetCollisionViewer: public GameInteractionEffectBase { class SetCollisionViewer: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class SetCosmeticsColor: public GameInteractionEffectBase { class SetCosmeticsColor: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
@ -204,52 +212,52 @@ namespace GameInteractionEffect {
void _Apply() override; void _Apply() override;
}; };
class PressButton: public GameInteractionEffectBase { class PressButton: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class PressRandomButton: public GameInteractionEffectBase { class PressRandomButton: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class AddOrTakeAmmo: public GameInteractionEffectBase { class AddOrTakeAmmo: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class RandomBombFuseTimer: public GameInteractionEffectBase { class RandomBombFuseTimer: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class DisableLedgeGrabs: public GameInteractionEffectBase { class DisableLedgeGrabs: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class RandomWind: public GameInteractionEffectBase { class RandomWind: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class RandomBonks: public GameInteractionEffectBase { class RandomBonks: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class PlayerInvincibility: public GameInteractionEffectBase { class PlayerInvincibility: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;
}; };
class SlipperyFloor: public GameInteractionEffectBase { class SlipperyFloor: public RemovableGameInteractionEffect {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
void _Remove() override; void _Remove() override;

View file

@ -31,7 +31,7 @@ GameInteractionEffectQueryResult GameInteractor::ApplyEffect(GameInteractionEffe
return effect->Apply(); return effect->Apply();
} }
GameInteractionEffectQueryResult GameInteractor::RemoveEffect(GameInteractionEffectBase* effect) { GameInteractionEffectQueryResult GameInteractor::RemoveEffect(RemovableGameInteractionEffect* effect) {
return effect->Remove(); return effect->Remove();
} }

View file

@ -5,6 +5,12 @@
#include "GameInteractionEffect.h" #include "GameInteractionEffect.h"
#include "soh/Enhancements/item-tables/ItemTableTypes.h" #include "soh/Enhancements/item-tables/ItemTableTypes.h"
#include <z64.h>
typedef enum {
GI_SCHEME_SAIL,
GI_SCHEME_CROWD_CONTROL,
} GIScheme;
typedef enum { typedef enum {
/* 0x00 */ GI_LINK_SIZE_NORMAL, /* 0x00 */ GI_LINK_SIZE_NORMAL,
@ -52,13 +58,13 @@ typedef enum {
} GIColors; } GIColors;
typedef enum { typedef enum {
/* */ GI_TP_DEST_LINKSHOUSE = 187, /* */ GI_TP_DEST_LINKSHOUSE = ENTR_LINKS_HOUSE_0,
/* */ GI_TP_DEST_MINUET = 1536, /* */ GI_TP_DEST_MINUET = ENTR_SACRED_FOREST_MEADOW_2,
/* */ GI_TP_DEST_BOLERO = 1270, /* */ GI_TP_DEST_BOLERO = ENTR_DEATH_MOUNTAIN_CRATER_4,
/* */ GI_TP_DEST_SERENADE = 1540, /* */ GI_TP_DEST_SERENADE = ENTR_LAKE_HYLIA_8,
/* */ GI_TP_DEST_REQUIEM = 497, /* */ GI_TP_DEST_REQUIEM = ENTR_DESERT_COLOSSUS_5,
/* */ GI_TP_DEST_NOCTURNE = 1384, /* */ GI_TP_DEST_NOCTURNE = ENTR_GRAVEYARD_7,
/* */ GI_TP_DEST_PRELUDE = 1524, /* */ GI_TP_DEST_PRELUDE = ENTR_TEMPLE_OF_TIME_7,
} GITeleportDestinations; } GITeleportDestinations;
#ifdef __cplusplus #ifdef __cplusplus
@ -91,11 +97,16 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
#ifdef __cplusplus #ifdef __cplusplus
#include <thread>
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <string> #include <string>
#ifdef ENABLE_REMOTE_CONTROL
#include <SDL2/SDL_net.h>
#include <nlohmann/json.hpp>
#endif
#define DEFINE_HOOK(name, type) \ #define DEFINE_HOOK(name, type) \
struct name { \ struct name { \
typedef std::function<type> fn; \ typedef std::function<type> fn; \
@ -132,17 +143,50 @@ public:
static void SetPacifistMode(bool active); static void SetPacifistMode(bool active);
}; };
#ifdef ENABLE_REMOTE_CONTROL
bool isRemoteInteractorEnabled;
bool isRemoteInteractorConnected;
void EnableRemoteInteractor();
void DisableRemoteInteractor();
void RegisterRemoteDataHandler(std::function<void(char payload[512])> method);
void RegisterRemoteJsonHandler(std::function<void(nlohmann::json)> method);
void RegisterRemoteConnectedHandler(std::function<void()> method);
void RegisterRemoteDisconnectedHandler(std::function<void()> method);
void TransmitDataToRemote(const char* payload);
void TransmitJsonToRemote(nlohmann::json packet);
#endif
// Effects // Effects
static GameInteractionEffectQueryResult CanApplyEffect(GameInteractionEffectBase* effect); static GameInteractionEffectQueryResult CanApplyEffect(GameInteractionEffectBase* effect);
static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect); static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect);
static GameInteractionEffectQueryResult RemoveEffect(GameInteractionEffectBase* effect); static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
// Game Hooks // Game Hooks
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; }; uint32_t nextHookId = 1;
template <typename H> void RegisterGameHook(typename H::fn h) { RegisteredGameHooks<H>::functions.push_back(h); } 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) { template <typename H, typename... Args> void ExecuteHooks(Args&&... args) {
for (auto& fn : RegisteredGameHooks<H>::functions) { for (auto& hookId : HooksToUnregister<H>::hooks) {
fn(std::forward<Args>(args)...); RegisteredGameHooks<H>::functions.erase(hookId);
}
HooksToUnregister<H>::hooks.clear();
for (auto& hook : RegisteredGameHooks<H>::functions) {
hook.second(std::forward<Args>(args)...);
} }
} }
@ -196,6 +240,7 @@ public:
DEFINE_HOOK(OnFileDropped, void(std::string filePath)); DEFINE_HOOK(OnFileDropped, void(std::string filePath));
DEFINE_HOOK(OnAssetAltChange, void()); DEFINE_HOOK(OnAssetAltChange, void());
DEFINE_HOOK(OnKaleidoUpdate, void());
// Helpers // Helpers
static bool IsSaveLoaded(bool allowDbgSave = false); static bool IsSaveLoaded(bool allowDbgSave = false);
@ -239,6 +284,21 @@ public:
static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams); static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams);
static GameInteractionEffectQueryResult SpawnActor(uint32_t actorId, int32_t actorParams); static GameInteractionEffectQueryResult SpawnActor(uint32_t actorId, int32_t actorParams);
}; };
private:
#ifdef ENABLE_REMOTE_CONTROL
IPaddress remoteIP;
TCPsocket remoteSocket;
std::thread remoteThreadReceive;
std::function<void(char payload[512])> remoteDataHandler;
std::function<void(nlohmann::json)> remoteJsonHandler;
std::function<void()> remoteConnectedHandler;
std::function<void()> remoteDisconnectedHandler;
void ReceiveFromServer();
void HandleRemoteData(char payload[512]);
void HandleRemoteJson(std::string payload);
#endif
}; };
#endif /* __cplusplus */ #endif /* __cplusplus */

View file

@ -187,3 +187,9 @@ void GameInteractor_ExecuteOnSetGameLanguage() {
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) { void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnAssetAltChange>(fn); 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 // MARK: - System
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)); void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void));
//Mark: - Pause Menu
void GameInteractor_ExecuteOnKaleidoUpdate();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -111,9 +111,9 @@ void GameInteractor::RawAction::FreezePlayer() {
void GameInteractor::RawAction::BurnPlayer() { void GameInteractor::RawAction::BurnPlayer() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
for (int i = 0; i < 18; i++) { 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); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0);
} }
@ -326,9 +326,9 @@ void GameInteractor::RawAction::UpdateActor(void* refActor) {
void GameInteractor::RawAction::TeleportPlayer(int32_t nextEntrance) { void GameInteractor::RawAction::TeleportPlayer(int32_t nextEntrance) {
Audio_PlaySoundGeneral(NA_SE_EN_GANON_LAUGH, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); Audio_PlaySoundGeneral(NA_SE_EN_GANON_LAUGH, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
gPlayState->nextEntranceIndex = nextEntrance; gPlayState->nextEntranceIndex = nextEntrance;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 2; gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
gSaveContext.nextTransitionType = 2; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK;
} }
void GameInteractor::RawAction::ClearAssignedButtons(uint8_t buttonSet) { void GameInteractor::RawAction::ClearAssignedButtons(uint8_t buttonSet) {
@ -520,7 +520,7 @@ void GameInteractor::RawAction::SetRandomWind(bool active) {
if (active) { if (active) {
GameInteractor::State::RandomWindActive = 1; GameInteractor::State::RandomWindActive = 1;
if (GameInteractor::State::RandomWindSecondsSinceLastDirectionChange == 0) { if (GameInteractor::State::RandomWindSecondsSinceLastDirectionChange == 0) {
player->windDirection = (rand() % 49152) - 32767; player->pushedYaw = (rand() % 49152) - 32767;
GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 5; GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 5;
} else { } else {
GameInteractor::State::RandomWindSecondsSinceLastDirectionChange--; GameInteractor::State::RandomWindSecondsSinceLastDirectionChange--;
@ -528,8 +528,8 @@ void GameInteractor::RawAction::SetRandomWind(bool active) {
} else { } else {
GameInteractor::State::RandomWindActive = 0; GameInteractor::State::RandomWindActive = 0;
GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 0; GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 0;
player->windSpeed = 0.0f; player->pushedSpeed = 0.0f;
player->windDirection = 0.0f; player->pushedYaw = 0.0f;
} }
} }

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