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

This commit is contained in:
Adam Bird 2024-05-06 12:22:48 -04:00
commit 439574d0d9
603 changed files with 25974 additions and 20371 deletions

View File

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

View File

@ -13,42 +13,60 @@ jobs:
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-soh-otr-ccache
key: ${{ runner.os }}-otr-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-otr-ccache-${{ github.ref }}
${{ runner.os }}-otr-ccache-
- name: Install dependencies
if: ${{ !vars.LINUX_RUNNER }}
run: |
sudo apt-get update
sudo apt-get install -y $(cat .github/workflows/apt-deps.txt)
- name: Cache build folders
uses: actions/cache@v4
with:
key: ${{ runner.os }}-otr-build-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-otr-build-${{ github.ref }}
${{ runner.os }}-otr-build-
path: |
build-cmake
SDL2-2.28.5
- name: Install latest SDL
if: ${{ !vars.LINUX_RUNNER }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz
tar -xzf SDL2-2.26.1.tar.gz
cd SDL2-2.26.1
./configure
if [ ! -d "SDL2-2.28.5" ]; then
wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz
tar -xzf SDL2-2.28.5.tar.gz
fi
cd SDL2-2.28.5
./configure --enable-hidapi-libusb
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Install latest SDL_net
if: ${{ !vars.LINUX_RUNNER }}
- name: Install latest tinyxml2
run: |
sudo apt-get remove libtinyxml2-dev
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
if [ ! -d "tinyxml2-10.0.0" ]; then
wget https://github.com/leethomason/tinyxml2/archive/refs/tags/10.0.0.tar.gz
tar -xzf 10.0.0.tar.gz
fi
cd tinyxml2-10.0.0
mkdir build
cd build
cmake ..
make
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Generate soh.otr
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release
cmake --build build-cmake --config Release --target GenerateSohOtr
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: soh.otr
path: soh.otr
@ -61,9 +79,12 @@ jobs:
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-ccache
key: ${{ runner.os }}-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-ccache-${{ github.ref }}
${{ runner.os }}-ccache-
- name: Install gtar wrapper
if: ${{ !vars.MAC_RUNNER }}
run: |
@ -85,24 +106,24 @@ jobs:
if [ -d /opt/local/ ]; then
echo "MacPorts already installed"
else
wget https://github.com/macports/macports-base/releases/download/v2.7.2/MacPorts-2.7.2-12-Monterey.pkg
sudo installer -pkg ./MacPorts-2.7.2-12-Monterey.pkg -target /
wget https://github.com/macports/macports-base/releases/download/v2.9.3/MacPorts-2.9.3-12-Monterey.pkg
sudo installer -pkg ./MacPorts-2.9.3-12-Monterey.pkg -target /
fi
echo "/opt/local/bin:/opt/local/sbin" >> $GITHUB_PATH
- name: Install dependencies
if: ${{ !vars.MAC_RUNNER }}
run: |
brew uninstall --ignore-dependencies libpng
brew uninstall --ignore-dependencies libpng libzip
sudo port install $(cat .github/workflows/macports-deps.txt)
brew install ninja
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
- name: Build SoH
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 -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
mv soh.otr build-cmake/soh
(cd build-cmake && cpack)
@ -110,7 +131,7 @@ jobs:
mv _packages/*.dmg SoH.dmg
mv README.md readme.txt
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-mac
path: |
@ -139,39 +160,101 @@ jobs:
sudo apt-get update
sudo apt-get install -y $(cat .github/workflows/apt-deps.txt)
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ matrix.os }}-ccache
key: ${{ matrix.os }}-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ matrix.os }}-ccache-${{ github.ref }}
${{ matrix.os }}-ccache-
- name: Cache build folders
uses: actions/cache@v4
with:
key: ${{ matrix.os }}-build-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ matrix.os }}-build-${{ github.ref }}
${{ matrix.os }}-build-
path: |
SDL2-2.28.5
SDL2_net-2.2.0
- name: Install latest SDL
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/release/SDL2-2.26.1.tar.gz
tar -xzf SDL2-2.26.1.tar.gz
cd SDL2-2.26.1
./configure
if [ ! -d "SDL2-2.28.5" ]; then
wget https://www.libsdl.org/release/SDL2-2.28.5.tar.gz
tar -xzf SDL2-2.28.5.tar.gz
fi
cd SDL2-2.28.5
./configure --enable-hidapi-libusb
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Install latest libzip
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) }}
run: |
sudo apt-get remove libzip-dev zipcmp zipmerge ziptool
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
if [ ! -d "libzip-1.10.1" ]; then
wget https://libzip.org/download/libzip-1.10.1.tar.gz
tar -xzvf libzip-1.10.1.tar.gz
fi
cd libzip-1.10.1
mkdir build
cd build
cmake ..
make
sudo make install
- name: Install latest nlohmann
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) }}
run: |
sudo apt-get remove nlohmann-json3-dev
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
if [ ! -d "json-3.11.3" ]; then
wget https://github.com/nlohmann/json/archive/refs/tags/v3.11.3.tar.gz
tar -xzvf v3.11.3.tar.gz
fi
cd json-3.11.3
mkdir build
cd build
cmake ..
make
sudo make install
- name: Install latest tinyxml2
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }}
run: |
sudo apt-get remove libtinyxml2-dev
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
if [ ! -d "tinyxml2-10.0.0" ]; then
wget https://github.com/leethomason/tinyxml2/archive/refs/tags/10.0.0.tar.gz
tar -xzf 10.0.0.tar.gz
fi
cd tinyxml2-10.0.0
mkdir build
cd build
cmake ..
make
sudo make install
- name: Install latest SDL_net
if: ${{ (matrix.os == 'ubuntu-20.04' && !vars.LINUX_COMPATIBILITY_RUNNER) || (matrix.os == 'ubuntu-22.04' && !vars.LINUX_PERFORMANCE_RUNNER) }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz
tar -xzf SDL2_net-2.2.0.tar.gz
if [ ! -d "SDL2_net-2.2.0" ]; then
wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz
tar -xzf SDL2_net-2.2.0.tar.gz
fi
cd SDL2_net-2.2.0
./configure
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
- name: Build SoH
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_REMOTE_CONTROL=1
cmake --build build-cmake --config Release -j3
(cd build-cmake && cpack -G External)
@ -181,92 +264,12 @@ jobs:
CC: gcc-${{ matrix.gcc }}
CXX: g++-${{ matrix.gcc }}
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-linux-${{ matrix.archive-suffix }}
path: |
soh.appimage
readme.txt
build-switch:
needs: generate-soh-otr
runs-on: ${{ (vars.LINUX_RUNNER && fromJSON(vars.LINUX_RUNNER)) || 'ubuntu-latest' }}
container:
image: devkitpro/devkita64:20240120
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ninja-build
- name: Fix dubious ownership error
if: ${{ vars.LINUX_RUNNER }}
run: git config --global --add safe.directory '*'
- uses: actions/checkout@v3
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ runner.os }}-switch-ccache
- name: Build SoH
run: |
cmake -H. -Bbuild-switch -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
cmake --build build-switch --target soh_nro -j3
mv build-switch/soh/*.nro soh.nro
mv README.md readme.txt
- name: Download soh.otr
uses: actions/download-artifact@v3
with:
name: soh.otr
- name: Upload build
uses: actions/upload-artifact@v3
with:
name: soh-switch
path: |
soh.nro
soh.otr
readme.txt
build-wiiu:
needs: generate-soh-otr
runs-on: ${{ (vars.LINUX_RUNNER && fromJSON(vars.LINUX_RUNNER)) || 'ubuntu-latest' }}
container:
image: devkitpro/devkitppc:20230110
steps:
- name: Install dependencies
if: ${{ !vars.LINUX_RUNNER }}
run: |
sudo apt-get install -y ninja-build
- uses: actions/checkout@v3
with:
submodules: true
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ runner.os }}-wiiu-ccache
- name: Build SoH
run: |
cmake -H. -Bbuild-wiiu -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
cmake --build build-wiiu --target soh_wuhb --config Release -j3
mv build-wiiu/soh/*.rpx soh.rpx
mv build-wiiu/soh/*.wuhb soh.wuhb
mv README.md readme.txt
env:
DEVKITPRO: /opt/devkitpro
DEVKITPPC: /opt/devkitpro/devkitPPC
- name: Download soh.otr
uses: actions/download-artifact@v3
with:
name: soh.otr
- name: Upload build
uses: actions/upload-artifact@v3
with:
name: soh-wiiu
path: |
soh.rpx
soh.wuhb
soh.otr
readme.txt
build-windows:
needs: generate-soh-otr
runs-on: ${{ (vars.WINDOWS_RUNNER && fromJSON(vars.WINDOWS_RUNNER)) || 'windows-latest' }}
@ -280,16 +283,33 @@ jobs:
with:
submodules: true
- name: ccache
uses: dcvz/ccache-action@27b9f33213c0079872f064f6b6ba0233dfa16ba2
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-ccache
- uses: ilammy/msvc-dev-cmd@v1
variant: sccache
max-size: "1G"
key: ${{ runner.os }}-ccache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-ccache-${{ github.ref }}
${{ runner.os }}-ccache-
- name: Cache build folder
uses: actions/cache@v4
with:
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
env:
VCPKG_ROOT: D:/a/vcpkg
VCPKG_ROOT: ${{github.workspace}}/vcpkg
run: |
set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH"
cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DBUILD_REMOTE_CONTROL=1
cmake --build build-windows --config Release --parallel 10
mkdir soh-windows
@ -302,12 +322,12 @@ jobs:
mv ./build-windows/gamecontrollerdb.txt ./soh-windows/gamecontrollerdb.txt
mv ./x64/Release/assets ./soh-windows
- name: Download soh.otr
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: soh.otr
path: soh-windows
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: soh-windows
path: soh-windows

View File

@ -1 +1 @@
libsdl2 +universal libpng +universal glew +universal
libsdl2 +universal libsdl2_net +universal libpng +universal glew +universal libzip +universal nlohmann-json +universal tinyxml2 +universal

View File

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

2
.gitignore vendored
View File

@ -448,5 +448,5 @@ _packages
*/extract_assets_cmake*
/build*
soh/build.c
soh/src/boot/build.c
soh/properties.h

View File

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

24
CMake/lus-cvars.cmake Normal file
View File

@ -0,0 +1,24 @@
set(CVAR_VSYNC_ENABLED "${CVAR_PREFIX_SETTING}.VsyncEnabled" CACHE STRING "")
set(CVAR_Z_FIGHTING_MODE "${CVAR_PREFIX_SETTING}.ZFightingMode" CACHE STRING "")
set(CVAR_NEW_FILE_DROPPED "${CVAR_PREFIX_GENERAL}.NewFileDropped" CACHE STRING "")
set(CVAR_DROPPED_FILE "${CVAR_PREFIX_GENERAL}.DroppedFile" CACHE STRING "")
set(CVAR_INTERNAL_RESOLUTION "${CVAR_PREFIX_SETTING}.InternalResolution" CACHE STRING "")
set(CVAR_MSAA_VALUE "${CVAR_PREFIX_SETTING}.MSAAValue" CACHE STRING "")
set(CVAR_SDL_WINDOWED_FULLSCREEN "${CVAR_PREFIX_SETTING}.SdlWindowedFullscreen" CACHE STRING "")
set(CVAR_TEXTURE_FILTER "${CVAR_PREFIX_SETTING}.TextureFilter" CACHE STRING "")
set(CVAR_IMGUI_CONTROLLER_NAV "${CVAR_PREFIX_SETTING}.ControlNav" CACHE STRING "")
set(CVAR_CONSOLE_WINDOW_OPEN "${CVAR_PREFIX_WINDOW}.Console" CACHE STRING "")
set(CVAR_CONTROLLER_CONFIGURATION_WINDOW_OPEN "${CVAR_PREFIX_WINDOW}.ControllerConfiguration" CACHE STRING "")
set(CVAR_CONTROLLER_DISCONNECTED_WINDOW_OPEN "${CVAR_PREFIX_WINDOW}.ControllerDisconnected" CACHE STRING "")
set(CVAR_CONTROLLER_REORDERING_WINDOW_OPEN "${CVAR_PREFIX_WINDOW}.ControllerReordering" CACHE STRING "")
set(CVAR_GFX_DEBUGGER_WINDOW_OPEN "${CVAR_PREFIX_WINDOW}.GfxDebugger" CACHE STRING "")
set(CVAR_STATS_WINDOW_OPEN "${CVAR_PREFIX_WINDOW}.Stats" CACHE STRING "")
set(CVAR_ENABLE_MULTI_VIEWPORTS "${CVAR_PREFIX_SETTING}.EnableMultiViewports" CACHE STRING "")
set(CVAR_LOW_RES_MODE "${CVAR_PREFIX_SETTING}.LowResMode" CACHE STRING "")
set(CVAR_SIMULATED_INPUT_LAG "${CVAR_PREFIX_SETTING}.SimulatedInputLag" CACHE STRING "")
set(CVAR_ALT_ASSETS "${CVAR_PREFIX_ENHANCEMENT}.AltAssets" CACHE STRING "")
set(CVAR_GAME_OVERLAY_FONT "${CVAR_PREFIX_SETTING}.OverlayFont" CACHE STRING "")
set(CVAR_MENU_BAR_OPEN "${CVAR_PREFIX_SETTING}.OpenMenuBar" CACHE STRING "")
set(CVAR_PREFIX_CONTROLLERS "${CVAR_PREFIX_SETTING}.Controllers" CACHE STRING "")
set(CVAR_PREFIX_ADVANCED_RESOLUTION "${CVAR_PREFIX_SETTING}.AdvancedResolution" CACHE STRING "")
include("libultraship/cmake/cvars.cmake")

26
CMake/soh-cvars.cmake Normal file
View File

@ -0,0 +1,26 @@
set(CVAR_PREFIX_RANDOMIZER_ENHANCEMENT "gRandoEnhancements")
set(CVAR_PREFIX_RANDOMIZER_SETTING "gRandoSettings")
set(CVAR_PREFIX_COSMETIC "gCosmetics")
set(CVAR_PREFIX_AUDIO "gAudioEditor")
set(CVAR_PREFIX_CHEAT "gCheats")
set(CVAR_PREFIX_ENHANCEMENT "gEnhancements")
set(CVAR_PREFIX_SETTING "gSettings")
set(CVAR_PREFIX_WINDOW "gOpenWindows")
set(CVAR_PREFIX_TRACKER "gTrackers")
set(CVAR_PREFIX_DEVELOPER_TOOLS "gDeveloperTools")
set(CVAR_PREFIX_GENERAL "gGeneral")
set(CVAR_PREFIX_REMOTE "gRemote")
add_compile_definitions(
CVAR_PREFIX_RANDOMIZER_ENHANCEMENT="${CVAR_PREFIX_RANDOMIZER_ENHANCEMENT}"
CVAR_PREFIX_RANDOMIZER_SETTING="${CVAR_PREFIX_RANDOMIZER_SETTING}"
CVAR_PREFIX_COSMETIC="${CVAR_PREFIX_COSMETIC}"
CVAR_PREFIX_AUDIO="${CVAR_PREFIX_AUDIO}"
CVAR_PREFIX_CHEAT="${CVAR_PREFIX_CHEAT}"
CVAR_PREFIX_ENHANCEMENT="${CVAR_PREFIX_ENHANCEMENT}"
CVAR_PREFIX_SETTING="${CVAR_PREFIX_SETTING}"
CVAR_PREFIX_WINDOW="${CVAR_PREFIX_WINDOW}"
CVAR_PREFIX_TRACKER="${CVAR_PREFIX_TRACKER}"
CVAR_PREFIX_DEVELOPER_TOOLS="${CVAR_PREFIX_DEVELOPER_TOOLS}"
CVAR_PREFIX_GENERAL="${CVAR_PREFIX_GENERAL}"
CVAR_PREFIX_REMOTE="${CVAR_PREFIX_REMOTE}"
)

View File

@ -6,6 +6,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
project(Ship VERSION 8.0.5 LANGUAGES C CXX)
include(CMake/soh-cvars.cmake)
include(CMake/lus-cvars.cmake)
set(PROJECT_BUILD_NAME "MacReady Foxtrot" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
@ -13,20 +15,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>:/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")
include(CMake/automate-vcpkg.cmake)
include(CMake/automate-vcpkg.cmake)
set(VCPKG_TRIPLET x64-windows-static)
set(VCPKG_TARGET_TRIPLET x64-windows-static)
set(VCPKG_TRIPLET x64-windows-static)
set(VCPKG_TARGET_TRIPLET x64-windows-static)
vcpkg_bootstrap()
vcpkg_install_packages(zlib bzip2 libpng sdl2 sdl2-net glew glfw3)
vcpkg_bootstrap()
vcpkg_install_packages(zlib bzip2 libzip libpng sdl2 sdl2-net glew glfw3 nlohmann-json tinyxml2 spdlog)
if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache")
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)
endif()
endif()
################################################################################

@ -1 +1 @@
Subproject commit 04b85b95fab07a394b62dcd28a502a3040f08e0c
Subproject commit 3cea9ee7c017d842aa4d9ceeb8d3ffbf29c6effb

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)
* [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 (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)
* [Wii U](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-wiiu.zip)

2
ZAPDTR

@ -1 +1 @@
Subproject commit eff29036118349e142ee8efca80fd975a2a2b6ff
Subproject commit f38a9c92eb99368c0607acf356d5651ebdc96f51

View File

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

View File

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

@ -1 +1 @@
Subproject commit 96c8a8929c18c1bffd7d92a35a589f74cf16fc59
Subproject commit 7d71a290657a2d3b09a83e8b33025e807f4fb38e

View File

@ -21,12 +21,17 @@ fi
while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do
for romfile in "$SHIP_HOME"/*.*64
do
if [[ -e $romfile ]]; then
if [[ -e "$romfile" ]] || [[ -L "$romfile" ]]; then
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"
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"
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_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc")
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi")
if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache")
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Z7")
else()
set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi")
endif()
endif()

View File

@ -92,10 +92,6 @@ if (NOT TARGET libultraship)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libultraship ${CMAKE_BINARY_DIR}/libultraship)
endif()
if (NOT TARGET ZAPDUtils)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPDUtils ${CMAKE_BINARY_DIR}/ZAPDUtils)
endif()
if (NOT TARGET ZAPDLib)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPD ${CMAKE_BINARY_DIR}/ZAPD)
endif()
@ -105,8 +101,8 @@ set(PROJECT_NAME soh)
################################################################################
# Sources
################################################################################
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/boot/build.c.in ${CMAKE_BINARY_DIR}/build.c @ONLY)
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/boot/properties.h.in ${CMAKE_CURRENT_SOURCE_DIR}/properties.h @ONLY)
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/boot/build.c.in ${CMAKE_CURRENT_SOURCE_DIR}/src/boot/build.c @ONLY)
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/properties.h.in ${CMAKE_CURRENT_SOURCE_DIR}/properties.h @ONLY)
set(Header_Files "resource.h")
source_group("headers" FILES ${Header_Files})
@ -154,7 +150,7 @@ list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/gfx.*")
# 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.ccpak")
if (!BUILD_CROWD_CONTROL)
if (!BUILD_REMOTE_CONTROL)
list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*")
endif()
@ -217,8 +213,6 @@ source_group("soh\\resource\\importer\\scenecommand" REGULAR_EXPRESSION "soh/res
# src (decomp) {{{
file(GLOB_RECURSE src__ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.c" "src/*.h")
list(APPEND src__ ${CMAKE_BINARY_DIR}/build.c)
list(APPEND src__ ${CMAKE_CURRENT_SOURCE_DIR}/properties.h)
list(APPEND src__ ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc)
list(FILTER src__ EXCLUDE REGEX "src/dmadata/*")
list(FILTER src__ EXCLUDE REGEX "src/elf_message/*")
@ -238,7 +232,6 @@ list(REMOVE_ITEM src__ "src/libultra/gu/sqrtf.c")
list(REMOVE_ITEM src__ "src/libultra/gu/us2dex.c")
source_group("src" REGULAR_EXPRESSION "src/*")
source_group("src\\build" FILES ${CMAKE_BINARY_DIR}/build.c ${CMAKE_CURRENT_SOURCE_DIR}/properties.h ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc)
source_group("src\\boot" REGULAR_EXPRESSION "src/boot/*")
source_group("src\\buffers" REGULAR_EXPRESSION "src/buffers/*")
source_group("src\\code" REGULAR_EXPRESSION "src/code/*")
@ -354,9 +347,15 @@ endif()
find_package(SDL2)
set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS})
if (BUILD_CROWD_CONTROL)
if (BUILD_REMOTE_CONTROL)
find_package(SDL2_net)
set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
if(NOT SDL2_net_FOUND)
message(STATUS "SDL2_net not found (it's possible the version installed is too old). Disabling BUILD_REMOTE_CONTROL.")
set(BUILD_REMOTE_CONTROL 0)
else()
set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
endif()
endif()
target_include_directories(${PROJECT_NAME} PRIVATE assets
@ -385,10 +384,8 @@ target_include_directories(${PROJECT_NAME} PRIVATE assets
${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/extern/tinyxml2
${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/libultraship/Lib/
${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/libultraship/Lib/libjpeg/include/
${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/libultraship/Lib/spdlog/include/
${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/graphic/Fast3D/U64/PR
${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/graphic
${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPDUtils
${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPD/resource/type
${SDL2-INCLUDE}
${SDL2-NET-INCLUDE}
@ -408,13 +405,13 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"$<$<CONFIG:Release>:"
"NDEBUG"
">"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"INCLUDE_GAME_PRINTF;"
"ENABLE_CROWD_CONTROL;"
"UNICODE;"
"_UNICODE"
STORMLIB_NO_AUTO_LINK
"_CRT_SECURE_NO_WARNINGS;"
NOMINMAX
)
elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
target_compile_definitions(${PROJECT_NAME} PRIVATE
@ -432,6 +429,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"UNICODE;"
"_UNICODE"
STORMLIB_NO_AUTO_LINK
NOMINMAX
)
endif()
elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS")
@ -455,7 +453,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
"$<$<CONFIG:Release>:"
"NDEBUG"
">"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"SPDLOG_ACTIVE_LEVEL=0;"
"_CONSOLE;"
"_CRT_SECURE_NO_WARNINGS;"
@ -624,6 +622,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
-Wno-parentheses
-Wno-narrowing
-Wno-missing-braces
-Wno-int-conversion
-Wno-implicit-int
$<$<COMPILE_LANGUAGE:C>:
-Werror-implicit-function-declaration
-Wno-incompatible-pointer-types
@ -643,15 +643,6 @@ endif()
################################################################################
# Pre build events
################################################################################
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_custom_command_if(
TARGET ${PROJECT_NAME}
PRE_BUILD
COMMANDS
COMMAND $<CONFIG:Debug> copy /b $<SHELL_PATH:${CMAKE_BINARY_DIR}/>build.c +,,
)
endif()
if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS")
add_custom_command(
TARGET ${PROJECT_NAME}
@ -670,7 +661,6 @@ endif()
# Dependencies
################################################################################
add_dependencies(${PROJECT_NAME}
ZAPDUtils
libultraship
)
if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS")
@ -684,12 +674,11 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
set(ADDITIONAL_LIBRARY_DEPENDENCIES
"libultraship;"
"ZAPDUtils;"
"ZAPDLib;"
"glu32;"
"SDL2::SDL2;"
"SDL2::SDL2main;"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net-static>"
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net-static>"
"glfw;"
"winmm;"
"imm32;"
@ -699,7 +688,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
set(ADDITIONAL_LIBRARY_DEPENDENCIES
"libultraship;"
"ZAPDUtils;"
"ZAPDLib;"
"glu32;"
"SDL2::SDL2;"
@ -717,7 +705,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch")
find_package(Threads REQUIRED)
set(ADDITIONAL_LIBRARY_DEPENDENCIES
"libultraship;"
"ZAPDUtils;"
SDL2::SDL2
-lglad
Threads::Threads
@ -739,10 +726,9 @@ else()
find_package(Threads REQUIRED)
set(ADDITIONAL_LIBRARY_DEPENDENCIES
"libultraship;"
"ZAPDUtils;"
"ZAPDLib;"
SDL2::SDL2
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net>"
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net>"
${CMAKE_DL_LIBS}
Threads::Threads
)

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

After

Width:  |  Height:  |  Size: 992 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -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

@ -1230,8 +1230,8 @@ Gfx* Gfx_EnvColor(GraphicsContext* gfxCtx, s32 r, s32 g, s32 b, s32 a);
void Gfx_SetupFrame(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b);
void func_80095974(GraphicsContext* gfxCtx);
void func_80095AA0(PlayState* play, Room* room, Input* arg2, UNK_TYPE arg3);
void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 height, u8 fmt, u8 siz, u16 mode0,
u16 tlutCount, f32 frameX, f32 frameY);
void Room_DrawBackground2D(Gfx** gfxP, void* tex, void* tlut, u16 width, u16 height, u8 fmt, u8 siz, u16 tlutMode,
u16 tlutCount, f32 offsetX, f32 offsetY);
void func_80096FD4(PlayState* play, Room* room);
u32 func_80096FE8(PlayState* play, RoomContext* roomCtx);
s32 func_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomNum);
@ -1250,7 +1250,7 @@ s32 Object_IsLoaded(ObjectContext* objectCtx, s32 bankIndex);
void func_800981B8(ObjectContext* objectCtx);
s32 Scene_ExecuteCommands(PlayState* play, SceneCmd* sceneCmd);
void TransitionActor_InitContext(GameState* state, TransitionActorContext* transiActorCtx);
void func_800994A0(PlayState* play);
void Scene_SetTransitionForNextEntrance(PlayState* play);
void Scene_Draw(PlayState* play);
void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 dListIndex);
@ -1537,7 +1537,7 @@ void KaleidoScopeCall_Draw(PlayState* play);
void func_800BC490(PlayState* play, s16 point);
s32 func_800BC56C(PlayState* play, s16 arg1);
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);
void Play_Destroy(GameState* thisx);
void Play_Init(GameState* thisx);
@ -1828,8 +1828,8 @@ MtxF* Matrix_CheckFloats(MtxF* mf, char* file, s32 line);
void Matrix_SetTranslateScaleMtx2(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY,
f32 translateZ);
uintptr_t SysUcode_GetUCodeBoot(void);
uintptr_t SysUcode_GetUCodeBootSize(void);
uintptr_t SysUcode_GetUCode(void);
size_t SysUcode_GetUCodeBootSize(void);
uint32_t SysUcode_GetUCode(void);
uintptr_t SysUcode_GetUCodeData(void);
void func_800D2E30(UnkRumbleStruct* arg0);
void func_800D3140(UnkRumbleStruct* arg0);
@ -2171,7 +2171,7 @@ void func_800FA18C(u8, u8);
void Audio_SetVolScale(u8 playerIdx, u8 scaleIdx, u8 targetVol, u8 volFadeTimer);
void func_800FA3DC(void);
u8 func_800FAD34(void);
void func_800FADF8(void);
void Audio_ResetActiveSequences(void);
void func_800FAEB4(void);
void GfxPrint_SetColor(GfxPrint* this, u32 r, u32 g, u32 b, u32 a);
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_GetRelY(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);
f32 Math_FTanF(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_RandoRestoreSwordless(void);
//Pause Warp
void PauseWarp_HandleSelection();
void PauseWarp_Execute();
// #endregion
#ifdef __cplusplus

View File

@ -86,6 +86,8 @@
#define R_ITEM_ICON_X(i) ZREG(82 + i)
#define R_ITEM_ICON_Y(i) ZREG(86 + 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_SPEED CREG(19)
#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 u32 gObjectTableSize;
extern RomFile gObjectTable[OBJECT_ID_MAX];
extern EntranceInfo gEntranceTable[1556];
extern EntranceInfo gEntranceTable[ENTR_MAX];
extern SceneTableEntry gSceneTable[SCENE_ID_MAX];
extern u16 gSramSlotOffsets[];
// 4 16-colors palettes
@ -224,7 +224,7 @@ extern "C"
extern u16 gAudioSfxSwapSource[10];
extern u16 gAudioSfxSwapTarget[10];
extern u8 gAudioSfxSwapMode[10];
extern unk_D_8016E750 D_8016E750[4];
extern ActiveSequence gActiveSeqs[4];
extern AudioContext gAudioContext;
extern void(*D_801755D0)(void);

View File

@ -1122,6 +1122,82 @@ typedef struct {
/* 0x4C */ u32 unk_4C;
} 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 {
union {
TransitionFade fade;
@ -1384,14 +1460,14 @@ typedef struct PlayState {
/* 0x11E0C */ ElfMessage* cUpElfMsgs;
/* 0x11E10 */ void* specialEffects;
/* 0x11E14 */ u8 skyboxId;
/* 0x11E15 */ s8 sceneLoadFlag; // "fade_direction"
/* 0x11E15 */ s8 transitionTrigger; // "fade_direction"
/* 0x11E16 */ s16 unk_11E16;
/* 0x11E18 */ s16 unk_11E18;
/* 0x11E1A */ s16 nextEntranceIndex;
/* 0x11E1C */ char unk_11E1C[0x40];
/* 0x11E5C */ s8 shootingGalleryStatus;
/* 0x11E5D */ s8 bombchuBowlingStatus; // "bombchu_game_flag"
/* 0x11E5E */ u8 fadeTransition;
/* 0x11E5E */ u8 transitionType;
/* 0x11E60 */ CollisionCheckContext colChkCtx;
/* 0x120FC */ u16 envFlags[20];
/* 0x12124 */ PreRender pauseBgPreRender;
@ -1513,6 +1589,20 @@ typedef struct {
uint16_t bossRushArrowOffset;
} 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 {
DPM_UNK = 0,
DPM_PLAYER = 1,

View File

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

View File

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

View File

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

View File

@ -314,8 +314,34 @@ enum SceneID {
/* 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
// 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 {
/* 0 */ SDC_DEFAULT,
/* 1 */ SDC_HYRULE_FIELD,

View File

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

View File

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

View File

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

View File

@ -74,7 +74,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
{ ACTOR_EN_BUBBLE, "Shabom" },
{ ACTOR_DOOR_SHUTTER, "Shutter Door" },
{ ACTOR_EN_DODOJR, "Baby Dodongo" },
{ ACTOR_EN_BDFIRE, "Empty" },
{ ACTOR_EN_BDFIRE, "King Dodongo's Fire Breath" },
{ ACTOR_EN_BOOM, "Boomerang" },
{ ACTOR_EN_TORCH2, "Dark Link" },
{ 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_EN_YUKABYUN, "Flying Floor Tile" },
{ 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_HIDAN_KOUSI, "Sliding Metal Gate" },
{ ACTOR_DOOR_TOKI, "Door of Time Collision" },
@ -548,6 +548,10 @@ int ActorDB::RetrieveId(const std::string& name) {
return entry->second;
}
int ActorDB::GetEntryCount() {
return db.size();
}
ActorDB::Entry::Entry() {
entry.name = nullptr;
entry.desc = nullptr;

View File

@ -64,6 +64,7 @@ public:
static void AddBuiltInCustomActors();
int GetEntryCount();
private:
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);

View File

@ -2,9 +2,10 @@
#include "sequence.h"
#include "sfx.h"
#include <vector>
#include <Utils/StringHelper.h>
#include <utils/StringHelper.h>
#include <libultraship/bridge.h>
#include <libultraship/classes.h>
#include <soh/OTRGlobals.h>
#include <locale>
#include <filesystem>
@ -330,11 +331,13 @@ AudioCollection::AudioCollection() {
}
std::string AudioCollection::GetCvarKey(std::string sfxKey) {
return "gAudioEditor.ReplacedSequences." + sfxKey + ".value";
auto prefix = CVAR_AUDIO("ReplacedSequences.");
return prefix + sfxKey + ".value";
}
std::string AudioCollection::GetCvarLockKey(std::string sfxKey) {
return "gAudioEditor.ReplacedSequences." + sfxKey + ".locked";
auto prefix = std::string(CVAR_AUDIO("ReplacedSequences."));
return prefix + sfxKey + ".locked";
}
void AudioCollection::AddToCollection(char* otrPath, uint16_t seqNum) {
@ -362,7 +365,7 @@ uint16_t AudioCollection::GetReplacementSequence(uint16_t seqId) {
// for Hyrule Field instead. Otherwise, leave it alone, so that without any sfx editor modifications we will
// play the normal track as usual.
if (seqId == NA_BGM_FIELD_MORNING) {
if (CVarGetInteger("gAudioEditor.ReplacedSequences.NA_BGM_FIELD_LOGIC.value", NA_BGM_FIELD_LOGIC) != NA_BGM_FIELD_LOGIC) {
if (CVarGetInteger(CVAR_AUDIO("ReplacedSequences.NA_BGM_FIELD_LOGIC.value"), NA_BGM_FIELD_LOGIC) != NA_BGM_FIELD_LOGIC) {
seqId = NA_BGM_FIELD_LOGIC;
}
}
@ -381,19 +384,19 @@ uint16_t AudioCollection::GetReplacementSequence(uint16_t seqId) {
}
void AudioCollection::RemoveFromShufflePool(SequenceInfo* seqInfo) {
const std::string cvarKey = "gAudioEditor.Excluded." + seqInfo->sfxKey;
const std::string cvarKey = std::string(CVAR_AUDIO("Excluded.")) + seqInfo->sfxKey;
excludedSequences.insert(seqInfo);
includedSequences.erase(seqInfo);
CVarSetInteger(cvarKey.c_str(), 1);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
void AudioCollection::AddToShufflePool(SequenceInfo* seqInfo) {
const std::string cvarKey = "gAudioEditor.Excluded." + seqInfo->sfxKey;
const std::string cvarKey = std::string(CVAR_AUDIO("Excluded.")) + seqInfo->sfxKey;
includedSequences.insert(seqInfo);
excludedSequences.erase(seqInfo);
CVarClear(cvarKey.c_str());
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
void AudioCollection::InitializeShufflePool() {
@ -401,7 +404,7 @@ void AudioCollection::InitializeShufflePool() {
for (auto& [seqId, seqInfo] : sequenceMap) {
if (!seqInfo.canBeUsedAsReplacement) continue;
const std::string cvarKey = "gAudioEditor.Excluded." + seqInfo.sfxKey;
const std::string cvarKey = std::string(CVAR_AUDIO("Excluded.")) + seqInfo.sfxKey;
if (CVarGetInteger(cvarKey.c_str(), 0)) {
excludedSequences.insert(&seqInfo);
} else {

View File

@ -1,3 +1,4 @@
#pragma once
#ifdef __cplusplus
#include <map>
#include <string>

View File

@ -9,9 +9,10 @@
#include <functions.h>
#include "../randomizer/3drando/random.hpp"
#include "../../OTRGlobals.h"
#include <Utils/StringHelper.h>
#include <utils/StringHelper.h>
#include "../../UIWidgets.hpp"
#include "AudioCollection.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
Vec3f pos = { 0.0f, 0.0f, 0.0f };
f32 freqScale = 1.0f;
@ -78,7 +79,12 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) {
void RandomizeGroup(SeqType type) {
std::vector<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
while (values.size() < AuthenticCountBySequenceType(type)) {
for (const auto& seqData : AudioCollection::Instance->GetIncludedSequences()) {
@ -123,23 +129,51 @@ 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) {
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(sfxKey);
const std::string hiddenKey = "##" + cvarKey;
const std::string stopButton = ICON_FA_STOP + hiddenKey;
const std::string previewButton = ICON_FA_PLAY + hiddenKey;
if (CVarGetInteger("gAudioEditor.Playing", 0) == sequenceId) {
if (CVarGetInteger(CVAR_AUDIO("Playing"), 0) == sequenceId) {
if (ImGui::Button(stopButton.c_str())) {
func_800F5C2C();
CVarSetInteger("gAudioEditor.Playing", 0);
CVarSetInteger(CVAR_AUDIO("Playing"), 0);
}
UIWidgets::Tooltip("Stop Preview");
} else {
if (ImGui::Button(previewButton.c_str())) {
if (CVarGetInteger("gAudioEditor.Playing", 0) != 0) {
if (CVarGetInteger(CVAR_AUDIO("Playing"), 0) != 0) {
func_800F5C2C();
CVarSetInteger("gAudioEditor.Playing", 0);
CVarSetInteger(CVAR_AUDIO("Playing"), 0);
} else {
if (sequenceType == SEQ_SFX || sequenceType == SEQ_VOICE) {
Audio_PlaySoundGeneral(sequenceId, &pos, 4, &freqScale, &freqScale, &reverbAdd);
@ -149,7 +183,7 @@ void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequence
} else {
// TODO: Cant do both here, so have to click preview button twice
PreviewSequence(sequenceId);
CVarSetInteger("gAudioEditor.Playing", sequenceId);
CVarSetInteger(CVAR_AUDIO("Playing"), sequenceId);
}
}
}
@ -163,11 +197,13 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
const std::string hiddenTabId = "##" + tabId;
const std::string resetAllButton = "Reset All" + hiddenTabId;
const std::string randomizeAllButton = "Randomize All" + hiddenTabId;
const std::string lockAllButton = "Lock All" + hiddenTabId;
const std::string unlockAllButton = "Unlock All" + hiddenTabId;
if (ImGui::Button(resetAllButton.c_str())) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
ResetGroup(map, type);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
@ -178,7 +214,29 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
RandomizeGroup(type);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
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);
Ship::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);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
@ -223,7 +281,7 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
if (ImGui::Selectable(seqData.label.c_str())) {
CVarSetInteger(cvarKey.c_str(), value);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
UpdateCurrentBGM(defaultValue, type);
}
@ -243,7 +301,7 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
if (ImGui::Button(resetButton.c_str())) {
CVarClear(cvarKey.c_str());
CVarClear(cvarLockKey.c_str());
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
UpdateCurrentBGM(defaultValue, seqData.category);
}
UIWidgets::Tooltip("Reset to default");
@ -264,7 +322,7 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
if (locked) {
CVarClear(cvarLockKey.c_str());
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
UpdateCurrentBGM(defaultValue, type);
}
}
@ -277,7 +335,7 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
} else {
CVarSetInteger(cvarLockKey.c_str(), 1);
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
UIWidgets::Tooltip(locked ? "Sound locked" : "Sound unlocked");
}
@ -350,6 +408,19 @@ void DrawTypeChip(SeqType type) {
ImGui::EndDisabled();
}
void AudioEditorRegisterOnSceneInitHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) {
if (CVarGetInteger(CVAR_AUDIO("RandomizeAllOnNewScene"), 0)) {
AudioEditor_RandomizeAll();
}
});
}
void AudioEditor::InitElement() {
AudioEditorRegisterOnSceneInitHook();
}
void AudioEditor::DrawElement() {
AudioCollection::Instance->InitializeShufflePool();
@ -359,6 +430,28 @@ void AudioEditor::DrawElement() {
return;
}
float buttonSegments = ImGui::GetContentRegionAvail().x / 4;
if (ImGui::Button("Randomize All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_RandomizeAll();
}
UIWidgets::Tooltip("Randomizes all unlocked music and sound effects across tab groups");
ImGui::SameLine();
if (ImGui::Button("Reset All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_ResetAll();
}
UIWidgets::Tooltip("Resets all unlocked music and sound effects across tab groups");
ImGui::SameLine();
if (ImGui::Button("Lock All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_LockAll();
}
UIWidgets::Tooltip("Locks all music and sound effects across tab groups");
ImGui::SameLine();
if (ImGui::Button("Unlock All Groups", ImVec2(buttonSegments, 30.0f))) {
AudioEditor_UnlockAll();
}
UIWidgets::Tooltip("Unlocks all music and sound effects across tab groups");
if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
if (ImGui::BeginTabItem("Background Music")) {
Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD);
@ -399,18 +492,18 @@ void AudioEditor::DrawElement() {
ImGui::TableNextColumn();
if (ImGui::BeginChild("SfxOptions", ImVec2(0, -8))) {
ImGui::PushItemWidth(-FLT_MIN);
UIWidgets::EnhancementCheckbox("Disable Enemy Proximity Music", "gEnemyBGMDisable");
UIWidgets::EnhancementCheckbox("Disable Enemy Proximity Music", CVAR_AUDIO("EnemyBGMDisable"));
UIWidgets::InsertHelpHoverText(
"Disables the music change when getting close to enemies. Useful for hearing "
"your custom music for each scene more often.");
UIWidgets::EnhancementCheckbox("Disable Leading Music in Lost Woods", "gLostWoodsConsistentVolume");
UIWidgets::EnhancementCheckbox("Disable Leading Music in Lost Woods", CVAR_AUDIO("LostWoodsConsistentVolume"));
UIWidgets::InsertHelpHoverText(
"Disables the volume shifting in the Lost Woods. Useful for hearing "
"your custom music in the Lost Woods if you don't need the navigation assitance "
"the volume changing provides. If toggling this while in the Lost Woods, reload "
"the area for the effect to kick in."
);
UIWidgets::EnhancementCheckbox("Display Sequence Name on Overlay", "gSeqNameOverlay");
UIWidgets::EnhancementCheckbox("Display Sequence Name on Overlay", CVAR_AUDIO("SeqNameOverlay"));
UIWidgets::InsertHelpHoverText(
"Displays the name of the current sequence in the corner of the screen whenever a new sequence "
"is loaded to the main sequence player (does not apply to fanfares or enemy BGM)."
@ -418,24 +511,28 @@ void AudioEditor::DrawElement() {
ImGui::SameLine();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
UIWidgets::EnhancementSliderInt("Overlay Duration: %d seconds", "##SeqNameOverlayDuration",
"gSeqNameOverlayDuration", 1, 10, "", 5);
CVAR_AUDIO("SeqNameOverlayDuration"), 1, 10, "", 5);
ImGui::PopItemWidth();
ImGui::NewLine();
ImGui::PopItemWidth();
UIWidgets::EnhancementSliderFloat("Link's voice pitch multiplier: %f", "##linkVoiceFreqMultiplier",
"gLinkVoiceFreqMultiplier", 0.4, 2.5, "", 1.0, false, false);
UIWidgets::EnhancementSliderFloat("Link's voice pitch multiplier: %.1f %%", "##linkVoiceFreqMultiplier",
CVAR_AUDIO("LinkVoiceFreqMultiplier"), 0.4, 2.5, "", 1.0, true, true);
ImGui::SameLine();
const std::string resetButton = "Reset##linkVoiceFreqMultiplier";
if (ImGui::Button(resetButton.c_str())) {
CVarSetFloat("gLinkVoiceFreqMultiplier", 1.0f);
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
CVarSetFloat(CVAR_AUDIO("LinkVoiceFreqMultiplier"), 1.0f);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
ImGui::NewLine();
UIWidgets::EnhancementCheckbox("Randomize All Music and Sound Effects on New Scene", CVAR_AUDIO("RandomizeAllOnNewScene"));
UIWidgets::Tooltip("Enables randomizing all unlocked music and sound effects when you enter a new scene.");
ImGui::NewLine();
ImGui::PushItemWidth(-FLT_MIN);
UIWidgets::PaddedSeparator();
UIWidgets::PaddedText("The following options are experimental and may cause music\nto sound odd or have other undesireable effects.");
UIWidgets::EnhancementCheckbox("Lower Octaves of Unplayable High Notes", "gExperimentalOctaveDrop");
UIWidgets::EnhancementCheckbox("Lower Octaves of Unplayable High Notes", CVAR_AUDIO("ExperimentalOctaveDrop"));
UIWidgets::InsertHelpHoverText("Some custom sequences may have notes that are too high for the game's audio "
"engine to play. Enabling this checkbox will cause these notes to drop a "
"couple of octaves so they can still harmonize with the other notes of the "
@ -613,7 +710,14 @@ void AudioEditor_RandomizeAll() {
RandomizeGroup(type);
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
ReplayCurrentBGM();
}
void AudioEditor_RandomizeGroup(SeqType group) {
RandomizeGroup(group);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
ReplayCurrentBGM();
}
@ -622,6 +726,29 @@ void AudioEditor_ResetAll() {
ResetGroup(AudioCollection::Instance->GetAllSequences(), type);
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
ReplayCurrentBGM();
}
void AudioEditor_ResetGroup(SeqType group) {
ResetGroup(AudioCollection::Instance->GetAllSequences(), group);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
ReplayCurrentBGM();
}
void AudioEditor_LockAll() {
for (auto type : allTypes) {
LockGroup(AudioCollection::Instance->GetAllSequences(), type);
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
void AudioEditor_UnlockAll() {
for (auto type : allTypes) {
UnlockGroup(AudioCollection::Instance->GetAllSequences(), type);
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}

View File

@ -4,20 +4,28 @@
#ifdef __cplusplus
#include <libultraship/libultraship.h>
#include <ImGui/imgui.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <imgui.h>
#include "AudioCollection.h"
class AudioEditor : public LUS::GuiWindow {
class AudioEditor : public Ship::GuiWindow {
public:
using LUS::GuiWindow::GuiWindow;
using GuiWindow::GuiWindow;
void DrawElement() override;
void InitElement() override {};
void InitElement() override;
void UpdateElement() override {};
~AudioEditor() {};
};
void AudioEditor_RandomizeAll();
void AudioEditor_RandomizeGroup(SeqType group);
void AudioEditor_ResetAll();
void AudioEditor_ResetGroup(SeqType group);
void AudioEditor_LockAll();
void AudioEditor_UnlockAll();
extern "C" {
#endif

View File

@ -9,6 +9,7 @@
#include <string.h>
#include <stdarg.h>
#include <z64.h>
#include "soh/OTRGlobals.h"
uint8_t gLoadFileSelect = 0, gSkipLogoTest = 0;
@ -21,15 +22,15 @@ static BootCommand sCommands[] = { { "--skiplogo", BootCommands_Command_SkipLogo
void BootCommands_Init()
{
// Clears vars to prevent randomizer menu from being disabled
CVarClear("gRandoGenerating"); // Clear when a crash happened during rando seed generation
CVarClear("gNewSeedGenerated");
CVarClear("gOnFileSelectNameEntry"); // Clear when soh is killed on the file name entry page
CVarClear("gBetterDebugWarpScreenMQMode");
CVarClear("gBetterDebugWarpScreenMQModeScene");
CVarClear("gCheatEasyPauseBufferLastInputs");
CVarClear("gCheatEasyPauseBufferTimer");
CVarClear(CVAR_GENERAL("RandoGenerating")); // Clear when a crash happened during rando seed generation
CVarClear(CVAR_GENERAL("NewSeedGenerated"));
CVarClear(CVAR_GENERAL("OnFileSelectNameEntry")); // Clear when soh is killed on the file name entry page
CVarClear(CVAR_GENERAL("BetterDebugWarpScreenMQMode"));
CVarClear(CVAR_GENERAL("BetterDebugWarpScreenMQModeScene"));
CVarClear(CVAR_GENERAL("CheatEasyPauseBufferLastInputs"));
CVarClear(CVAR_GENERAL("CheatEasyPauseBufferTimer"));
#if defined(__SWITCH__) || defined(__WIIU__)
CVarRegisterInteger("gControlNav", 1); // always enable controller nav on switch/wii u
CVarRegisterInteger(CVAR_IMGUI_CONTROLLER_NAV, 1); // always enable controller nav on switch/wii u
#endif
}

View File

@ -175,33 +175,33 @@ void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) {
// Gohma & Phantom Ganon
if (warpPosX == -100 && warpPosZ == -170) {
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
play->nextEntranceIndex = 0x040F;
play->nextEntranceIndex = ENTR_DEKU_TREE_BOSS_0;
} else {
play->nextEntranceIndex = 0x000C;
play->nextEntranceIndex = ENTR_FOREST_TEMPLE_BOSS_0;
}
// King Dodongo & Volvagia
} else if (warpPosX == 100 && warpPosZ == -170) {
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
play->nextEntranceIndex = 0x040B;
play->nextEntranceIndex = ENTR_DODONGOS_CAVERN_BOSS_0;
} else {
play->nextEntranceIndex = 0x0305;
play->nextEntranceIndex = ENTR_FIRE_TEMPLE_BOSS_0;
}
// Barinade & Morb
} else if (warpPosX == 199 && warpPosZ == 0) {
if (gSaveContext.linkAge == LINK_AGE_CHILD) {
play->nextEntranceIndex = 0x0301;
play->nextEntranceIndex = ENTR_JABU_JABU_BOSS_0;
} else {
play->nextEntranceIndex = 0x0417;
play->nextEntranceIndex = ENTR_WATER_TEMPLE_BOSS_0;
}
// Twinrova
} else if (warpPosX == 100 && warpPosZ == 170) {
play->nextEntranceIndex = 0x05EC;
play->nextEntranceIndex = ENTR_SPIRIT_TEMPLE_BOSS_2;
// Bongo Bongo
} else if (warpPosX == -100 && warpPosZ == 170) {
play->nextEntranceIndex = 0x0413;
play->nextEntranceIndex = ENTR_SHADOW_TEMPLE_BOSS_0;
// Ganondork
} 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.
} else {
@ -216,10 +216,10 @@ void BossRush_HandleBlueWarp(PlayState* play, f32 warpPosX, f32 warpPosZ) {
BossRush_SetEquipment(LINK_AGE_ADULT);
// Warp to credits.
} 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;
play->sceneLoadFlag = 0x14;
play->fadeTransition = 3;
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
}
}
}
@ -293,7 +293,7 @@ void BossRush_InitSave() {
gSaveContext.questId = QUEST_BOSSRUSH;
gSaveContext.isBossRushPaused = 1;
gSaveContext.entranceIndex = 107;
gSaveContext.entranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.cutsceneIndex = 0x8000;
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,520 @@
#include "InputViewer.h"
#include "public/bridge/consolevariablebridge.h"
#include "libultraship/libultra/controller.h"
#include "Context.h"
#include "soh/OTRGlobals.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <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(Ship::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(Ship::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(CVAR_WINDOW("InputViewer"), 0)) {
static bool sButtonTexturesLoaded = false;
if (!sButtonTexturesLoaded) {
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage(
"Input-Viewer-Background", "textures/buttons/InputViewerBackground.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("A-Btn", "textures/buttons/ABtn.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("B-Btn", "textures/buttons/BBtn.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("L-Btn", "textures/buttons/LBtn.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("R-Btn", "textures/buttons/RBtn.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Z-Btn", "textures/buttons/ZBtn.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Start-Btn",
"textures/buttons/StartBtn.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Left", "textures/buttons/CLeft.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Right", "textures/buttons/CRight.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Up", "textures/buttons/CUp.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Down", "textures/buttons/CDown.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Analog-Stick",
"textures/buttons/AnalogStick.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Left",
"textures/buttons/DPadLeft.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Right",
"textures/buttons/DPadRight.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Up", "textures/buttons/DPadUp.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Down",
"textures/buttons/DPadDown.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Right-Stick",
"textures/buttons/RightStick.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("A-Btn Outline",
"textures/buttons/ABtnOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("B-Btn Outline",
"textures/buttons/BBtnOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("L-Btn Outline",
"textures/buttons/LBtnOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("R-Btn Outline",
"textures/buttons/RBtnOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Z-Btn Outline",
"textures/buttons/ZBtnOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Start-Btn Outline",
"textures/buttons/StartBtnOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Left Outline",
"textures/buttons/CLeftOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Right Outline",
"textures/buttons/CRightOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Up Outline",
"textures/buttons/CUpOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Down Outline",
"textures/buttons/CDownOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Analog-Stick Outline",
"textures/buttons/AnalogStickOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Left Outline",
"textures/buttons/DPadLeftOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Right Outline",
"textures/buttons/DPadRightOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Up Outline",
"textures/buttons/DPadUpOutline.png");
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Down Outline",
"textures/buttons/DPadDownOutline.png");
Ship::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(CVAR_INPUT_VIEWER("Scale"), 1.0f) * 2.0f;
#else
const float scale = CVarGetFloat(CVAR_INPUT_VIEWER("Scale"), 1.0f);
#endif
const int showAnalogAngles = CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Enabled"), 0);
const int buttonOutlineMode = CVarGetInteger(CVAR_INPUT_VIEWER("ButtonOutlineMode"), BUTTON_OUTLINE_NOT_PRESSED);
ImVec2 bgSize = Ship::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(CVAR_INPUT_VIEWER("AnalogAngles.Scale"), 1.0f) + 20));
ImGui::SetNextWindowContentSize(
ImVec2(scaledBGSize.x, scaledBGSize.y + (showAnalogAngles ? 15 : 0) * scale *
CVarGetFloat(CVAR_INPUT_VIEWER("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 = Ship::Context::GetInstance()->GetControlDeck()->GetPads();
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoFocusOnAppearing;
if (!CVarGetInteger(CVAR_INPUT_VIEWER("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(CVAR_INPUT_VIEWER("ShowBackground"), 1)) {
ImGui::SetNextItemAllowOverlap();
// Background
ImGui::Image(
Ship::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(CVAR_INPUT_VIEWER("BBtn"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("B-Btn", "B-Btn Outline", pads[0].button & BTN_B, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger(CVAR_INPUT_VIEWER("ABtn"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("A-Btn", "A-Btn Outline", pads[0].button & BTN_A, scaledBGSize, buttonOutlineMode);
}
// C buttons
if (CVarGetInteger(CVAR_INPUT_VIEWER("CUp"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("C-Up", "C-Up Outline", pads[0].button & BTN_CUP, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger(CVAR_INPUT_VIEWER("CLeft"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("C-Left", "C-Left Outline", pads[0].button & BTN_CLEFT, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger(CVAR_INPUT_VIEWER("CRight"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("C-Right", "C-Right Outline", pads[0].button & BTN_CRIGHT, scaledBGSize,
buttonOutlineMode);
}
if (CVarGetInteger(CVAR_INPUT_VIEWER("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(CVAR_INPUT_VIEWER("LBtn"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("L-Btn", "L-Btn Outline", pads[0].button & BTN_L, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger(CVAR_INPUT_VIEWER("RBtn"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("R-Btn", "R-Btn Outline", pads[0].button & BTN_R, scaledBGSize, buttonOutlineMode);
}
if (CVarGetInteger(CVAR_INPUT_VIEWER("ZBtn"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Z-Btn", "Z-Btn Outline", pads[0].button & BTN_Z, scaledBGSize, buttonOutlineMode);
}
// Start
if (CVarGetInteger(CVAR_INPUT_VIEWER("StartBtn"), 1)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
RenderButton("Start-Btn", "Start-Btn Outline", pads[0].button & BTN_START, scaledBGSize,
buttonOutlineMode);
}
// Dpad
if (CVarGetInteger(CVAR_INPUT_VIEWER("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(CVAR_INPUT_VIEWER("AnalogStick.OutlineMode"), STICK_MODE_ALWAYS_SHOWN);
const float maxStickDistance = CVarGetInteger(CVAR_INPUT_VIEWER("AnalogStick.Movement"), 12);
if (analogOutlineMode == STICK_MODE_ALWAYS_SHOWN ||
(analogOutlineMode == STICK_MODE_HIDDEN_IN_DEADZONE && !analogStickIsInDeadzone)) {
ImGui::SetNextItemAllowOverlap();
ImGui::SetCursorPos(aPos);
ImGui::Image(
Ship::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(CVAR_INPUT_VIEWER("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(Ship::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(CVAR_INPUT_VIEWER("RightStick.Movement"), 7);
const int rightOutlineMode =
CVarGetInteger(CVAR_INPUT_VIEWER("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(
Ship::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(CVAR_INPUT_VIEWER("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(Ship::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(CVAR_INPUT_VIEWER("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(CVAR_INPUT_VIEWER("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(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Min"), 8);
const int range1Max = CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Max"), 27);
// Walking speed range
const int range2Min = CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Min"), 27);
const int range2Max = CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Max"), 62);
// Push color based on angle ranges
if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Enabled"), 0) &&
(rSquared >= (range1Min * range1Min)) && (rSquared < (range1Max * range1Max))) {
ImGui::PushStyleColor(
ImGuiCol_Text,
color2Vec(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Color"), vec2Color(range1Color))));
} else if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Enabled"), 0) &&
(rSquared >= (range2Min * range2Min)) && (rSquared < (range2Max * range2Max))) {
ImGui::PushStyleColor(
ImGuiCol_Text,
color2Vec(CVarGetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Color"), vec2Color(range2Color))));
} else {
ImGui::PushStyleColor(ImGuiCol_Text, color2Vec(CVarGetColor(CVAR_INPUT_VIEWER("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", CVAR_INPUT_VIEWER("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", CVAR_INPUT_VIEWER("EnableDragging"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
UIWidgets::PaddedSeparator(true, true);
// gInputViewer.ShowBackground
UIWidgets::EnhancementCheckbox("Show Background Layer", CVAR_INPUT_VIEWER("ShowBackground"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
UIWidgets::PaddedSeparator(true, true);
if (ImGui::CollapsingHeader("Buttons")) {
// gInputViewer.ABtn
UIWidgets::EnhancementCheckbox("Show A-Button Layers", CVAR_INPUT_VIEWER("ABtn"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.BBtn
UIWidgets::EnhancementCheckbox("Show B-Button Layers", CVAR_INPUT_VIEWER("BBtn"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CUp
UIWidgets::EnhancementCheckbox("Show C-Up Layers", CVAR_INPUT_VIEWER("CUp"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CRight
UIWidgets::EnhancementCheckbox("Show C-Right Layers", CVAR_INPUT_VIEWER("CRight"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CDown
UIWidgets::EnhancementCheckbox("Show C-Down Layers", CVAR_INPUT_VIEWER("CDown"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.CLeft
UIWidgets::EnhancementCheckbox("Show C-Left Layers", CVAR_INPUT_VIEWER("CLeft"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.LBtn
UIWidgets::EnhancementCheckbox("Show L-Button Layers", CVAR_INPUT_VIEWER("LBtn"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.RBtn
UIWidgets::EnhancementCheckbox("Show R-Button Layers", CVAR_INPUT_VIEWER("RBtn"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.ZBtn
UIWidgets::EnhancementCheckbox("Show Z-Button Layers", CVAR_INPUT_VIEWER("ZBtn"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.StartBtn
UIWidgets::EnhancementCheckbox("Show Start Button Layers", CVAR_INPUT_VIEWER("StartBtn"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, true);
// gInputViewer.Dpad
UIWidgets::EnhancementCheckbox("Show D-Pad Layers", CVAR_INPUT_VIEWER("Dpad"), false, "",
UIWidgets::CheckboxGraphics::Checkmark, false);
// gInputViewer.ButtonOutlineMode
UIWidgets::PaddedText("Button Outlines/Backgrounds", true, false);
UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("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(CVAR_INPUT_VIEWER("AnalogStick.VisibilityMode"), stickModeOptions,
STICK_MODE_ALWAYS_SHOWN);
UIWidgets::Tooltip(
"Determines the conditions under which the moving layer of the analog stick texture is visible.");
// gInputViewer.AnalogStick.OutlineMode
UIWidgets::PaddedText("Analog Stick Outline/Background Visibility", true, false);
UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("AnalogStick.OutlineMode"), stickModeOptions,
STICK_MODE_ALWAYS_SHOWN);
UIWidgets::Tooltip(
"Determines the conditions under which the analog stick outline/background texture is visible.");
// gInputViewer.AnalogStick.Movement
UIWidgets::EnhancementSliderInt("Analog Stick Movement: %dpx", "##AnalogMovement",
CVAR_INPUT_VIEWER("AnalogStick.Movement"), 0, 200, "", 12, true);
UIWidgets::Tooltip(
"Sets the distance to move the analog stick in the input viewer. Useful for custom input viewers.");
UIWidgets::PaddedSeparator(true, true);
}
if (ImGui::CollapsingHeader("Additional (\"Right\") Stick")) {
// gInputViewer.RightStick.VisibilityMode
UIWidgets::PaddedText("Right Stick Visibility", true, false);
UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("RightStick.VisibilityMode"), stickModeOptions,
STICK_MODE_HIDDEN_IN_DEADZONE);
UIWidgets::Tooltip(
"Determines the conditions under which the moving layer of the right stick texture is visible.");
// gInputViewer.RightStick.OutlineMode
UIWidgets::PaddedText("Right Stick Outline/Background Visibility", true, false);
UIWidgets::EnhancementCombobox(CVAR_INPUT_VIEWER("RightStick.OutlineMode"), stickModeOptions,
STICK_MODE_HIDDEN_IN_DEADZONE);
UIWidgets::Tooltip(
"Determines the conditions under which the right stick outline/background texture is visible.");
// gInputViewer.RightStick.Movement
UIWidgets::EnhancementSliderInt("Right Stick Movement: %dpx", "##RightMovement",
CVAR_INPUT_VIEWER("RightStick.Movement"), 0, 200, "", 7, true);
UIWidgets::Tooltip(
"Sets the distance to move the right stick in the input viewer. Useful for custom input viewers.");
UIWidgets::PaddedSeparator(true, true);
}
if (ImGui::CollapsingHeader("Analog Angle Values")) {
// gAnalogAngles
UIWidgets::EnhancementCheckbox("Show Analog Stick Angle Values", CVAR_INPUT_VIEWER("AnalogAngles.Enabled"));
UIWidgets::Tooltip("Displays analog stick angle values in the input viewer");
if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Enabled"), 0)) {
// gInputViewer.AnalogAngles.TextColor
if (ImGui::ColorEdit4("Text Color", (float*)&textColor)) {
CVarSetColor(CVAR_INPUT_VIEWER("AnalogAngles.TextColor"), vec2Color(textColor));
}
// gAnalogAngleScale
UIWidgets::EnhancementSliderFloat("Angle Text Scale: %.2f%%", "##AnalogAngleScale",
CVAR_INPUT_VIEWER("AnalogAngles.Scale"), 0.1f, 5.0f, "", 1.0f, true, true);
// gInputViewer.AnalogAngles.Offset
UIWidgets::EnhancementSliderInt("Angle Text Offset: %dpx", "##AnalogAngleOffset",
CVAR_INPUT_VIEWER("AnalogAngles.Offset"), 0, 400, "", 0, true);
UIWidgets::PaddedSeparator(true, true);
// gInputViewer.AnalogAngles.Range1.Enabled
UIWidgets::EnhancementCheckbox("Highlight ESS Position", CVAR_INPUT_VIEWER("AnalogAngles.Range1.Enabled"));
UIWidgets::Tooltip(
"Highlights the angle value text when the analog stick is in ESS position (on flat ground)");
if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Enabled"), 0)) {
// gInputViewer.AnalogAngles.Range1.Color
if (ImGui::ColorEdit4("ESS Color", (float*)&range1Color)) {
CVarSetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range1.Color"), vec2Color(range1Color));
}
}
UIWidgets::PaddedSeparator(true, true);
// gInputViewer.AnalogAngles.Range2.Enabled
UIWidgets::EnhancementCheckbox("Highlight Walking Speed Angles",
CVAR_INPUT_VIEWER("AnalogAngles.Range2.Enabled"));
UIWidgets::Tooltip("Highlights the angle value text when the analog stick is at an angle that would "
"produce a walking speed (on flat ground)\n\n"
"Useful for 1.0 Empty Jumpslash Quick Put Away");
if (CVarGetInteger(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Enabled"), 0)) {
// gInputViewer.AnalogAngles.Range2.Color
if (ImGui::ColorEdit4("Walking Speed Color", (float*)&range2Color)) {
CVarSetColor(CVAR_INPUT_VIEWER("AnalogAngles.Range2.Color"), vec2Color(range2Color));
}
}
}
}
ImGui::End();
}
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <libultraship/libultraship.h>
#define CVAR_INPUT_VIEWER(var) "gInputViewer." var
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 Ship::GuiWindow {
public:
using 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 Ship::GuiWindow {
public:
using 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.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 Ship::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, Ship::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, Ship::Direction direction, std::string id);
void DrawStickDirectionLineAddMappingButton(uint8_t port, uint8_t stick, Ship::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<Ship::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<Ship::Direction, std::vector<std::string>>>>
mStickDirectionToMappingIds;
void UpdateBitmaskToMappingIds(uint8_t port);
void UpdateStickDirectionToMappingIds(uint8_t port);
void GetButtonColorsForLUSDeviceIndex(Ship::ShipDeviceIndex 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, Ship::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);
};

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,29 @@
ResourceMgr_UnpatchGfxByName(path, name); \
}
// Not to be confused with tabs, groups are 1:1 with the boxes shown in the UI, grouping them allows us to reset/randomize
// every item in a group at once. If you are looking for tabs they are rendered manually in ImGui in `DrawCosmeticsEditor`
typedef enum {
COSMETICS_GROUP_LINK,
COSMETICS_GROUP_MIRRORSHIELD,
COSMETICS_GROUP_SWORDS,
COSMETICS_GROUP_GLOVES,
COSMETICS_GROUP_EQUIPMENT,
COSMETICS_GROUP_CONSUMABLE,
COSMETICS_GROUP_HUD,
COSMETICS_GROUP_KALEIDO,
COSMETICS_GROUP_TITLE,
COSMETICS_GROUP_NPC,
COSMETICS_GROUP_WORLD,
COSMETICS_GROUP_MAGIC,
COSMETICS_GROUP_ARROWS,
COSMETICS_GROUP_SPIN_ATTACK,
COSMETICS_GROUP_TRAILS,
COSMETICS_GROUP_NAVI,
COSMETICS_GROUP_IVAN,
COSMETICS_GROUP_MAX
} CosmeticGroup;
typedef struct {
const std::string Name;
const std::string ToolTip;
@ -26,10 +49,12 @@ static ImGuiTableColumnFlags FlagsCell = ImGuiTableColumnFlags_WidthStretch | Im
void InitCosmeticsEditor();//Init the menu itself
ImVec4 GetRandomValue(int MaximumPossible);
void CosmeticsEditor_RandomizeAll();
void CosmeticsEditor_RandomizeGroup(CosmeticGroup group);
void CosmeticsEditor_ResetAll();
void CosmeticsEditor_ResetGroup(CosmeticGroup group);
void ApplyOrResetCustomGfxPatches(bool manualChange = true);
class CosmeticsEditorWindow : public LUS::GuiWindow {
class CosmeticsEditorWindow : public Ship::GuiWindow {
public:
using GuiWindow::GuiWindow;

View File

@ -1,5 +1,6 @@
#include <libultraship/bridge.h>
#include <string>
#include "soh/OTRGlobals.h"
extern "C" {
#include <libultraship/libultra.h>
@ -81,7 +82,7 @@ void PatchDekuStickTextureOverflow() {
const char* dlist = gLinkChildLinkDekuStickDL;
int start = 5;
if (!CVarGetInteger("gFixTexturesOOB", 0)) {
if (!CVarGetInteger(CVAR_ENHANCEMENT("FixTexturesOOB"), 0)) {
// Unpatch the other texture fix
for (size_t i = 0; i < 7; i++) {
int instruction = start + (i == 0 ? 0 : i + 1);
@ -121,7 +122,7 @@ void PatchFreezardTextureOverflow() {
char patchNameBuf[24];
// Patch using custom overflowed texture
if (!CVarGetInteger("gFixTexturesOOB", 0)) {
if (!CVarGetInteger(CVAR_ENHANCEMENT("FixTexturesOOB"), 0)) {
// Unpatch the other texture fix
for (size_t i = 0; i < 7; i++) {
int instruction = start + (i == 0 ? 0 : i + 1);
@ -163,7 +164,7 @@ void PatchIronKnuckleTextureOverflow() {
// Until this is solved, Iron Knuckle will be hardcoded to always display with the "authentic" texture fix
// Patch using custom overflowed texture
// if (!CVarGetInteger("gFixTexturesOOB", 0)) {
// if (!CVarGetInteger(CVAR_ENHANCEMENT("FixTexturesOOB"), 0)) {
// Unpatch the other texture fix
for (size_t i = 0; i < 7; i++) {
int instruction = start + (i == 0 ? 0 : i + 1);
@ -222,7 +223,7 @@ void PatchMirroredSoldOutGI() {
G_TX_NOMIRROR | G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD),
};
if (CVarGetInteger("gMirroredWorld", 0)) {
if (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) {
if (mirroredSoldOutVtx == nullptr) {
// Copy the original vertices that we want to modify (4 at the beginning of the resource)
mirroredSoldOutVtx = (Vtx*)malloc(sizeof(Vtx) * 4);
@ -269,7 +270,7 @@ void PatchMirroredSunSongEtching() {
G_TX_NOMIRROR | G_TX_CLAMP, 7, 5, G_TX_NOLOD, G_TX_NOLOD)
};
if (CVarGetInteger("gMirroredWorld", 0)) {
if (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) {
if (mirroredSunSongVtx == nullptr) {
// Copy the original vertices that we want to modify (4 at the beginning of the resource)
mirroredSunSongVtx = (Vtx*)malloc(sizeof(Vtx) * 4);

View File

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

View File

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

View File

@ -38,6 +38,7 @@ typedef enum {
TEXT_CARPET_SALESMAN_1 = 0x6077,
TEXT_CARPET_SALESMAN_2 = 0x6078,
TEXT_MARKET_GUARD_NIGHT = 0x7003,
TEXT_FISHERMAN_LEAVE = 0x409E,
TEXT_SHEIK_NEED_HOOK = 0x700F,
TEXT_SHEIK_HAVE_HOOK = 0x7010,
TEXT_SCRUB_RANDOM = 0x9000,
@ -54,6 +55,22 @@ typedef enum {
TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW = 0x9210,
TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range
TEXT_SARIAS_SONG_CHANNELING_POWER = 0x016D,
TEXT_BEAN_SALESMAN_BUY_FOR_10 = 0x405E,
TEXT_BEAN_SALESMAN_BUY_FOR_20 = 0x405F,
TEXT_BEAN_SALESMAN_BUY_FOR_30 = 0x4060,
TEXT_BEAN_SALESMAN_BUY_FOR_40 = 0x4061,
TEXT_BEAN_SALESMAN_BUY_FOR_50 = 0x4062,
TEXT_BEAN_SALESMAN_BUY_FOR_60 = 0x4063,
TEXT_BEAN_SALESMAN_BUY_FOR_70 = 0x4064,
TEXT_BEAN_SALESMAN_BUY_FOR_80 = 0x4065,
TEXT_BEAN_SALESMAN_BUY_FOR_90 = 0x4066,
TEXT_BEAN_SALESMAN_BUY_FOR_100 = 0x4067,
TEXT_BEAN_SALESMAN_OH_WELL = 0x4068,
TEXT_BEAN_SALESMAN_NOT_ENOUGH_MONEY = 0x4069,
TEXT_BEAN_SALESMAN_SET_A_BEAN_TO_C = 0x406A,
TEXT_BEAN_SALESMAN_SOLD_OUT = 0x406B,
TEXT_BEAN_SALESMAN_WANT_TO_PLANT = 0x406C,
} TextIDs;
#ifdef __cplusplus

File diff suppressed because it is too large Load Diff

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(CVAR_ENHANCEMENT("BetterOwl"), 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 Ship::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,9 +8,11 @@
#include <array>
#include <bit>
#include <map>
#include <unordered_map>
#include <string>
#include <libultraship/bridge.h>
#include <libultraship/libultraship.h>
#include "soh/OTRGlobals.h"
extern "C" {
#include <z64.h>
@ -24,6 +26,7 @@ extern PlayState* gPlayState;
#include "textures/icon_item_24_static/icon_item_24_static.h"
}
#define DEKUNUTS_FLOWER 10
#define DEBUG_ACTOR_NAMETAG_TAG "debug_actor_viewer"
typedef struct {
@ -107,8 +110,785 @@ 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) {
int val = CVarGetInteger("gDebugActorViewerNameTags", ACTORVIEWER_NAMETAGS_NONE);
int val = CVarGetInteger(CVAR_DEVELOPER_TOOLS("ActorViewer.NameTags"), ACTORVIEWER_NAMETAGS_NONE);
auto entry = ActorDB::Instance->RetrieveEntry(actor->id);
std::string tag;
@ -163,6 +943,9 @@ void ActorViewerWindow::DrawElement() {
static std::string filler = "Please select";
static std::vector<Actor*> list;
static u16 lastSceneId = 0;
static char searchString[64] = "";
static s16 currentSelectedInDropdown;
static std::vector<u16> actors;
if (gPlayState != nullptr) {
needs_reset = lastSceneId != gPlayState->sceneNum;
@ -173,6 +956,11 @@ void ActorViewerWindow::DrawElement() {
filler = "Please Select";
list.clear();
needs_reset = false;
for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) {
searchString[i] = 0;
}
currentSelectedInDropdown = -1;
actors.clear();
}
lastSceneId = gPlayState->sceneNum;
if (ImGui::BeginCombo("Actor Type", acMapping[category])) {
@ -316,9 +1104,48 @@ void ActorViewerWindow::DrawElement() {
if (ImGui::TreeNode("New...")) {
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::InputScalar("ID", ImGuiDataType_S16, &newActor.id, &one);
ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one);
if (ImGui::InputScalar("ID", ImGuiDataType_S16, &newActor.id, &one)) {
newActor.params = 0;
}
UIWidgets::EnhancementCheckbox("Advanced mode", CVAR_DEVELOPER_TOOLS("ActorViewer.AdvancedParams"));
UIWidgets::InsertHelpHoverText("Changes the actor specific param menus with a direct input");
if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("ActorViewer.AdvancedParams"), 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);
@ -388,7 +1215,7 @@ void ActorViewerWindow::DrawElement() {
UIWidgets::Spacer(0);
ImGui::Text("Actor Name Tags");
if (UIWidgets::EnhancementCombobox("gDebugActorViewerNameTags", nameTagOptions, ACTORVIEWER_NAMETAGS_NONE)) {
if (UIWidgets::EnhancementCombobox(CVAR_DEVELOPER_TOOLS("ActorViewer.NameTags"), nameTagOptions, ACTORVIEWER_NAMETAGS_NONE)) {
NameTag_RemoveAllByTag(DEBUG_ACTOR_NAMETAG_TAG);
ActorViewer_AddTagForAllActors();
}
@ -401,6 +1228,11 @@ void ActorViewerWindow::DrawElement() {
filler = "Please Select";
list.clear();
needs_reset = false;
for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) {
searchString[i] = 0;
}
currentSelectedInDropdown = -1;
actors.clear();
}
}

View File

@ -2,7 +2,7 @@
#include <libultraship/libultraship.h>
class ActorViewerWindow : public LUS::GuiWindow {
class ActorViewerWindow : public Ship::GuiWindow {
public:
using GuiWindow::GuiWindow;

View File

@ -7,6 +7,7 @@
#include <cmath>
#include <libultraship/bridge.h>
#include <libultraship/libultraship.h>
#include "soh/OTRGlobals.h"
extern "C" {
#include <z64.h>
@ -57,17 +58,17 @@ void ColViewerWindow::DrawElement() {
ImGui::End();
return;
}
UIWidgets::EnhancementCheckbox("Enabled", "gColViewerEnabled");
UIWidgets::EnhancementCheckbox("Enabled", CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"));
UIWidgets::LabeledRightAlignedEnhancementCombobox("Scene", "gColViewerScene", ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Bg Actors", "gColViewerBgActors", ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Col Check", "gColViewerColCheck", ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Waterbox", "gColViewerWaterbox", ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Scene", CVAR_DEVELOPER_TOOLS("ColViewer.Scene"), ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Bg Actors", CVAR_DEVELOPER_TOOLS("ColViewer.BGActors"), ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Col Check", CVAR_DEVELOPER_TOOLS("ColViewer.ColCheck"), ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Waterbox", CVAR_DEVELOPER_TOOLS("ColViewer.Waterbox"), ColRenderSettingNames, COLVIEW_DISABLED);
UIWidgets::EnhancementCheckbox("Apply as decal", "gColViewerDecal");
UIWidgets::EnhancementCheckbox("Apply as decal", CVAR_DEVELOPER_TOOLS("ColViewer.Decal"));
UIWidgets::InsertHelpHoverText("Applies the collision as a decal display. This can be useful if there is z-fighting occuring "
"with the scene geometry, but can cause other artifacts.");
UIWidgets::EnhancementCheckbox("Shaded", "gColViewerShaded");
UIWidgets::EnhancementCheckbox("Shaded", CVAR_DEVELOPER_TOOLS("ColViewer.Shaded"));
UIWidgets::InsertHelpHoverText("Applies the scene's shading to the collision display.");
// This has to be duplicated in both code paths due to the nature of ImGui::IsItemHovered()
@ -75,20 +76,20 @@ void ColViewerWindow::DrawElement() {
if (ImGui::TreeNode("Colors")) {
UIWidgets::InsertHelpHoverText(colorHelpText);
UIWidgets::EnhancementColor("Normal", "gColViewerColorNormal", scene_col, ImVec4(255, 255, 255, 255), false);
UIWidgets::EnhancementColor("Hookshot", "gColViewerColorHookshot", hookshot_col, ImVec4(128, 128, 255, 255),
UIWidgets::EnhancementColor("Normal", CVAR_DEVELOPER_TOOLS("ColViewer.ColorNormal"), scene_col, ImVec4(255, 255, 255, 255), false);
UIWidgets::EnhancementColor("Hookshot", CVAR_DEVELOPER_TOOLS("ColViewer.ColorHookshot"), hookshot_col, ImVec4(128, 128, 255, 255),
false);
UIWidgets::EnhancementColor("Entrance", "gColViewerColorEntrance", entrance_col, ImVec4(0, 255, 0, 255), false);
UIWidgets::EnhancementColor("Special Surface (Grass/Sand/Etc)", "gColViewerColorSpecialSurface",
UIWidgets::EnhancementColor("Entrance", CVAR_DEVELOPER_TOOLS("ColViewer.ColorEntrance"), entrance_col, ImVec4(0, 255, 0, 255), false);
UIWidgets::EnhancementColor("Special Surface (Grass/Sand/Etc)", CVAR_DEVELOPER_TOOLS("ColViewer.ColorSpecialSurface"),
specialSurface_col, ImVec4(192, 255, 192, 255), false);
UIWidgets::EnhancementColor("Interactable (Vines/Crawlspace/Etc)", "gColViewerColorInteractable",
UIWidgets::EnhancementColor("Interactable (Vines/Crawlspace/Etc)", CVAR_DEVELOPER_TOOLS("ColViewer.ColorInteractable"),
interactable_col, ImVec4(192, 0, 192, 255), false);
UIWidgets::EnhancementColor("Slope", "gColViewerColorSlope", slope_col, ImVec4(255, 255, 128, 255), false);
UIWidgets::EnhancementColor("Void", "gColViewerColorVoid", void_col, ImVec4(255, 0, 0, 255), false);
UIWidgets::EnhancementColor("OC", "gColViewerColorOC", oc_col, ImVec4(255, 255, 255, 255), false);
UIWidgets::EnhancementColor("AC", "gColViewerColorAC", ac_col, ImVec4(0, 0, 255, 255), false);
UIWidgets::EnhancementColor("AT", "gColViewerColorAT", at_col, ImVec4(255, 0, 0, 255), false);
UIWidgets::EnhancementColor("Waterbox", "gColViewerColorWaterbox", waterbox_col, ImVec4(0, 0, 255, 255), false);
UIWidgets::EnhancementColor("Slope", CVAR_DEVELOPER_TOOLS("ColViewer.ColorSlope"), slope_col, ImVec4(255, 255, 128, 255), false);
UIWidgets::EnhancementColor("Void", CVAR_DEVELOPER_TOOLS("ColViewer.ColorVoid"), void_col, ImVec4(255, 0, 0, 255), false);
UIWidgets::EnhancementColor("OC", CVAR_DEVELOPER_TOOLS("ColViewer.ColorOC"), oc_col, ImVec4(255, 255, 255, 255), false);
UIWidgets::EnhancementColor("AC", CVAR_DEVELOPER_TOOLS("ColViewer.ColorAC"), ac_col, ImVec4(0, 0, 255, 255), false);
UIWidgets::EnhancementColor("AT", CVAR_DEVELOPER_TOOLS("ColViewer.ColorAT"), at_col, ImVec4(255, 0, 0, 255), false);
UIWidgets::EnhancementColor("Waterbox", CVAR_DEVELOPER_TOOLS("ColViewer.ColorWaterbox"), waterbox_col, ImVec4(0, 0, 255, 255), false);
ImGui::TreePop();
} else {
@ -308,7 +309,7 @@ void InitGfx(std::vector<Gfx>& gfx, ColRenderSetting setting) {
alpha = 0xFF;
}
if (CVarGetInteger("gColViewerDecal", 0) != 0) {
if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Decal"), 0) != 0) {
rm |= ZMODE_DEC;
} else if (setting == ColRenderSetting::Transparent) {
rm |= ZMODE_XLU;
@ -320,7 +321,7 @@ void InitGfx(std::vector<Gfx>& gfx, ColRenderSetting setting) {
gfx.push_back(gsDPSetCycleType(G_CYC_1CYCLE));
gfx.push_back(gsDPSetRenderMode(rm | blc1, rm | blc2));
if (CVarGetInteger("gColViewerShaded", 0) != 0) {
if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Shaded"), 0) != 0) {
gfx.push_back(gsDPSetCombineMode(G_CC_MODULATERGB_PRIM_ENVA, G_CC_MODULATERGB_PRIM_ENVA));
gfx.push_back(gsSPLoadGeometryMode(G_CULL_BACK | G_ZBUFFER | G_LIGHTING));
} else {
@ -333,16 +334,13 @@ void InitGfx(std::vector<Gfx>& gfx, ColRenderSetting setting) {
// Draws a dynapoly structure (scenes or Bg Actors)
void DrawDynapoly(std::vector<Gfx>& dl, CollisionHeader* col, int32_t bgId) {
uint32_t colorR = CVarGetInteger("gColViewerColorNormalR", 255);
uint32_t colorG = CVarGetInteger("gColViewerColorNormalG", 255);
uint32_t colorB = CVarGetInteger("gColViewerColorNormalB", 255);
uint32_t colorA = 255;
Color_RGBA8 color = {255, 255, 255, 255};
uint32_t lastColorR = colorR;
uint32_t lastColorG = colorG;
uint32_t lastColorB = colorB;
uint32_t lastColorR = color.r;
uint32_t lastColorG = color.g;
uint32_t lastColorB = color.b;
dl.push_back(gsDPSetPrimColor(0, 0, colorR, colorG, colorB, colorA));
dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
// This keeps track of if we have processed a poly, but not drawn it yet so we can batch them.
// This saves several hundred commands in larger scenes
@ -352,49 +350,35 @@ void DrawDynapoly(std::vector<Gfx>& dl, CollisionHeader* col, int32_t bgId) {
CollisionPoly* poly = &col->polyList[i];
if (SurfaceType_IsHookshotSurface(&gPlayState->colCtx, poly, bgId)) {
colorR = CVarGetInteger("gColViewerColorHookshotR", 128);
colorG = CVarGetInteger("gColViewerColorHookshotG", 128);
colorB = CVarGetInteger("gColViewerColorHookshotB", 255);
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorHookshot"), { 128, 128, 255, 255 });
} else if (func_80041D94(&gPlayState->colCtx, poly, bgId) > 0x01) {
colorR = CVarGetInteger("gColViewerColorInteractableR", 192);
colorG = CVarGetInteger("gColViewerColorInteractableG", 0);
colorB = CVarGetInteger("gColViewerColorInteractableB", 192);
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorInteractable"), {192, 0, 192, 255});
} else if (func_80041E80(&gPlayState->colCtx, poly, bgId) == 0x0C) {
colorR = CVarGetInteger("gColViewerColorVoidR", 255);
colorG = CVarGetInteger("gColViewerColorVoidG", 0);
colorB = CVarGetInteger("gColViewerColorVoidB", 0);
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorVoid"), { 255, 0, 0, 255 });
} else if (SurfaceType_GetSceneExitIndex(&gPlayState->colCtx, poly, bgId) ||
func_80041E80(&gPlayState->colCtx, poly, bgId) == 0x05) {
colorR = CVarGetInteger("gColViewerColorEntranceR", 0);
colorG = CVarGetInteger("gColViewerColorEntranceG", 255);
colorB = CVarGetInteger("gColViewerColorEntranceB", 0);
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorEntrance"), { 0, 255, 0, 255 });
} else if (func_80041D4C(&gPlayState->colCtx, poly, bgId) != 0 ||
SurfaceType_IsWallDamage(&gPlayState->colCtx, poly, bgId)) {
colorR = CVarGetInteger("gColViewerColorSpecialSurfaceR", 192);
colorG = CVarGetInteger("gColViewerColorSpecialSurfaceG", 255);
colorB = CVarGetInteger("gColViewerColorSpecialSurfaceB", 192);
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSpecialSurface"), { 192, 255, 192, 255 });
} else if (SurfaceType_GetSlope(&gPlayState->colCtx, poly, bgId) == 0x01) {
colorR = CVarGetInteger("gColViewerColorSlopeR", 255);
colorG = CVarGetInteger("gColViewerColorSlopeG", 255);
colorB = CVarGetInteger("gColViewerColorSlopeB", 128);
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorSlope"), { 255, 255, 128, 255 });
} else {
colorR = CVarGetInteger("gColViewerColorNormalR", 255);
colorG = CVarGetInteger("gColViewerColorNormalG", 255);
colorB = CVarGetInteger("gColViewerColorNormalB", 255);
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorNormal"), { 255, 255, 255, 255 });
}
if (colorR != lastColorR || colorG != lastColorG || colorB != lastColorB) {
if (color.r != lastColorR || color.g != lastColorG || color.b != lastColorB) {
// Color changed, flush previous poly
if (previousPoly) {
dl.push_back(gsSPVertex((uintptr_t)&vtxDl.at(vtxDl.size() - 3), 3, 0));
dl.push_back(gsSP1Triangle(0, 1, 2, 0));
previousPoly = false;
}
dl.push_back(gsDPSetPrimColor(0, 0, colorR, colorG, colorB, colorA));
dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
}
lastColorR = colorR;
lastColorG = colorG;
lastColorB = colorB;
lastColorR = color.r;
lastColorG = color.g;
lastColorB = color.b;
Vec3s* va = &col->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)];
Vec3s* vb = &col->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)];
@ -428,9 +412,9 @@ void DrawDynapoly(std::vector<Gfx>& dl, CollisionHeader* col, int32_t bgId) {
// Draws the scene
void DrawSceneCollision() {
ColRenderSetting showSceneColSetting = (ColRenderSetting)CVarGetInteger("gColViewerScene", COLVIEW_DISABLED);
ColRenderSetting showSceneColSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Scene"), COLVIEW_DISABLED);
if (showSceneColSetting == ColRenderSetting::Disabled || !CVarGetInteger("gColViewerEnabled", 0)) {
if (showSceneColSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) {
return;
}
@ -443,8 +427,8 @@ void DrawSceneCollision() {
// Draws all Bg Actors
void DrawBgActorCollision() {
ColRenderSetting showBgActorSetting = (ColRenderSetting)CVarGetInteger("gColViewerBgActors", COLVIEW_DISABLED);
if (showBgActorSetting == ColRenderSetting::Disabled || !CVarGetInteger("gColViewerEnabled", 0)) {
ColRenderSetting showBgActorSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.BGActors"), COLVIEW_DISABLED);
if (showBgActorSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) {
return;
}
@ -568,8 +552,8 @@ void DrawColCheckList(std::vector<Gfx>& dl, Collider** objects, int32_t count) {
// Draws all Col Check objects
void DrawColCheckCollision() {
ColRenderSetting showColCheckSetting = (ColRenderSetting)CVarGetInteger("gColViewerColCheck", COLVIEW_DISABLED);
if (showColCheckSetting == ColRenderSetting::Disabled || !CVarGetInteger("gColViewerEnabled", 0)) {
ColRenderSetting showColCheckSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.ColCheck"), COLVIEW_DISABLED);
if (showColCheckSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) {
return;
}
@ -578,15 +562,14 @@ void DrawColCheckCollision() {
dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH));
CollisionCheckContext& col = gPlayState->colChkCtx;
dl.push_back(gsDPSetPrimColor(0, 0, CVarGetInteger("gColViewerColorOCR", 255), CVarGetInteger("gColViewerColorOCG", 255),
CVarGetInteger("gColViewerColorOCB", 255), 255));
Color_RGBA8 color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorOC"), { 255, 255, 255, 255 });
dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
DrawColCheckList(dl, col.colOC, col.colOCCount);
dl.push_back(gsDPSetPrimColor(0, 0, CVarGetInteger("gColViewerColorACR", 0), CVarGetInteger("gColViewerColorACG", 0),
CVarGetInteger("gColViewerColorACB", 255), 255));
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAC"), { 0, 0, 255, 255 });
dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
DrawColCheckList(dl, col.colAC, col.colACCount);
dl.push_back(gsDPSetPrimColor(0, 0, CVarGetInteger("gColViewerColorATR", 255), CVarGetInteger("gColViewerColorATG", 0),
CVarGetInteger("gColViewerColorATB", 0), 255));
color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorAT"), { 0, 0, 255, 255 });
dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
DrawColCheckList(dl, col.colAT, col.colATCount);
}
@ -621,8 +604,8 @@ extern "C" f32 zdWaterBoxMinY;
// Draws all waterboxes
void DrawWaterboxList() {
ColRenderSetting showWaterboxSetting = (ColRenderSetting)CVarGetInteger("gColViewerWaterbox", COLVIEW_DISABLED);
if (showWaterboxSetting == ColRenderSetting::Disabled || !CVarGetInteger("gColViewerEnabled", 0)) {
ColRenderSetting showWaterboxSetting = (ColRenderSetting)CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Waterbox"), COLVIEW_DISABLED);
if (showWaterboxSetting == ColRenderSetting::Disabled || !CVarGetInteger(CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), 0)) {
return;
}
@ -630,9 +613,9 @@ void DrawWaterboxList() {
InitGfx(dl, showWaterboxSetting);
dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH));
dl.push_back(gsDPSetPrimColor(0, 0, CVarGetInteger("gColViewerColorWaterboxR", 0),
CVarGetInteger("gColViewerColorWaterboxG", 0),
CVarGetInteger("gColViewerColorWaterboxB", 255), 255));
Color_RGBA8 color = CVarGetColor(CVAR_DEVELOPER_TOOLS("ColViewer.ColorWaterbox"), { 0, 0, 255, 255 });
dl.push_back(gsDPSetPrimColor(0, 0, color.r, color.g, color.b, 255));
CollisionHeader* col = gPlayState->colCtx.colHeader;
for (int32_t waterboxIndex = 0; waterboxIndex < col->numWaterBoxes; waterboxIndex++) {
@ -693,7 +676,7 @@ extern "C" void DrawColViewer() {
OPEN_DISPS(gPlayState->state.gfxCtx);
uint8_t mirroredWorld = CVarGetInteger("gMirroredWorld", 0);
uint8_t mirroredWorld = CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0);
// Col viewer needs inverted culling in mirror mode for both OPA and XLU buffers
if (mirroredWorld) {
gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING);

View File

@ -14,7 +14,7 @@ typedef enum {
} ColViewerRenderSetting;
#ifdef __cplusplus
class ColViewerWindow : public LUS::GuiWindow {
class ColViewerWindow : public Ship::GuiWindow {
public:
using GuiWindow::GuiWindow;

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