Merge branch 'develop' into actor-accessibility-experiments

This commit is contained in:
Ryan 2024-01-17 13:47:44 -05:00
commit 61dcbe3812
203 changed files with 14500 additions and 6130 deletions

View file

@ -32,17 +32,6 @@ jobs:
make -j 10 make -j 10
sudo make install sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Install latest SDL_net
if: ${{ !vars.LINUX_RUNNER }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz
tar -xzf SDL2_net-2.2.0.tar.gz
cd SDL2_net-2.2.0
./configure
make -j 10
sudo make install
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
- name: Generate soh.otr - name: Generate soh.otr
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
@ -102,7 +91,7 @@ jobs:
- name: Build SoH - name: Build SoH
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DBUILD_REMOTE_CONTROL=1
cmake --build build-cmake --config Release --parallel 10 cmake --build build-cmake --config Release --parallel 10
mv soh.otr build-cmake/soh mv soh.otr build-cmake/soh
(cd build-cmake && cpack) (cd build-cmake && cpack)
@ -171,7 +160,7 @@ jobs:
- name: Build SoH - name: Build SoH
run: | run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_REMOTE_CONTROL=1
cmake --build build-cmake --config Release -j3 cmake --build build-cmake --config Release -j3
(cd build-cmake && cpack -G External) (cd build-cmake && cpack -G External)
@ -281,16 +270,23 @@ jobs:
with: with:
submodules: true submodules: true
- name: ccache - name: ccache
uses: dcvz/ccache-action@27b9f33213c0079872f064f6b6ba0233dfa16ba2 uses: hendrikmuhs/ccache-action@v1.2
with: with:
key: ${{ runner.os }}-ccache key: ${{ runner.os }}-ccache
- uses: ilammy/msvc-dev-cmd@v1 - name: vcpkg
uses: johnwason/vcpkg-action@v5
with:
pkgs: zlib bzip2 libpng sdl2 sdl2-net glew glfw3
token: ${{ github.token }}
triplet: 'x64-windows-static'
- name: Configure Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1
- name: Build SoH - name: Build SoH
env: env:
VCPKG_ROOT: D:/a/vcpkg VCPKG_ROOT: ${{github.workspace}}/vcpkg
run: | run: |
set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH" set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH"
cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DBUILD_REMOTE_CONTROL=1
cmake --build build-windows --config Release --parallel 10 cmake --build build-windows --config Release --parallel 10
mkdir soh-windows mkdir soh-windows

View file

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

View file

@ -5,20 +5,14 @@ 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") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
project(Ship VERSION 8.0.0 LANGUAGES C CXX) project(Ship VERSION 8.0.4 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Alfa" CACHE STRING "") set(PROJECT_BUILD_NAME "MacReady Echo" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "") set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>) add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/utf-8>) add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/utf-8>)
if (CMAKE_SYSTEM_NAME MATCHES "Windows|Linux")
if(NOT DEFINED BUILD_CROWD_CONTROL)
set(BUILD_CROWD_CONTROL ON)
endif()
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
include(CMake/automate-vcpkg.cmake) include(CMake/automate-vcpkg.cmake)

@ -1 +1 @@
Subproject commit 0d8f5570a8e57f302ec6633d65615ee21ab39454 Subproject commit 04b85b95fab07a394b62dcd28a502a3040f08e0c

View file

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

@ -1 +1 @@
Subproject commit c75ff3653f699cb1a8c017b10e4b3986259d8cf0 Subproject commit 15d57d806e39d7f19783e26acc1a062d402169c7

View file

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

View file

@ -154,7 +154,7 @@ list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/gfx.*")
# handle crowd control removals # handle crowd control removals
list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.cs") list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.cs")
list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak") list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak")
if (!BUILD_CROWD_CONTROL) if (!BUILD_REMOTE_CONTROL)
list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*") list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*")
endif() endif()
@ -328,7 +328,7 @@ endif()
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Boost Boost
URL https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.gz URL https://sourceforge.net/projects/boost/files/boost/1.81.0/boost_1_81_0.tar.gz
URL_HASH SHA256=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6 URL_HASH SHA256=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6
SOURCE_SUBDIR "null" # Set to a nonexistent directory so boost is not built (we don't need to build it) SOURCE_SUBDIR "null" # Set to a nonexistent directory so boost is not built (we don't need to build it)
DOWNLOAD_EXTRACT_TIMESTAMP false # supress timestamp warning, not needed since the url wont change DOWNLOAD_EXTRACT_TIMESTAMP false # supress timestamp warning, not needed since the url wont change
@ -354,7 +354,7 @@ endif()
find_package(SDL2) find_package(SDL2)
set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS}) set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS})
if (BUILD_CROWD_CONTROL) if (BUILD_REMOTE_CONTROL)
find_package(SDL2_net) find_package(SDL2_net)
set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS}) set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
endif() endif()
@ -408,9 +408,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"$<$<CONFIG:Release>:" "$<$<CONFIG:Release>:"
"NDEBUG" "NDEBUG"
">" ">"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"INCLUDE_GAME_PRINTF;" "INCLUDE_GAME_PRINTF;"
"ENABLE_CROWD_CONTROL;"
"UNICODE;" "UNICODE;"
"_UNICODE" "_UNICODE"
STORMLIB_NO_AUTO_LINK STORMLIB_NO_AUTO_LINK
@ -455,7 +454,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
"$<$<CONFIG:Release>:" "$<$<CONFIG:Release>:"
"NDEBUG" "NDEBUG"
">" ">"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"SPDLOG_ACTIVE_LEVEL=0;" "SPDLOG_ACTIVE_LEVEL=0;"
"_CONSOLE;" "_CONSOLE;"
"_CRT_SECURE_NO_WARNINGS;" "_CRT_SECURE_NO_WARNINGS;"
@ -689,7 +688,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"glu32;" "glu32;"
"SDL2::SDL2;" "SDL2::SDL2;"
"SDL2::SDL2main;" "SDL2::SDL2main;"
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net-static>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net-static>"
"glfw;" "glfw;"
"winmm;" "winmm;"
"imm32;" "imm32;"
@ -742,7 +741,7 @@ else()
"ZAPDUtils;" "ZAPDUtils;"
"ZAPDLib;" "ZAPDLib;"
SDL2::SDL2 SDL2::SDL2
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net>" "$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net>"
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
Threads::Threads Threads::Threads
) )

View file

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

View file

@ -79,7 +79,7 @@
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x6238"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x91E8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61E8"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61E8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/>
</File> </File>
</Root> </Root>

View file

@ -1,14 +1,14 @@
<Root> <Root>
<File Name="nes_message_data_static"> <File Name="nes_message_data_static">
<Text Name="nes_message_data_static" CodeOffset="0xF6910"/> <Text Name="nes_message_data_static" CodeOffset="0xF68F0"/>
</File> </File>
<File Name="ger_message_data_static"> <File Name="ger_message_data_static">
<Text Name="ger_message_data_static" CodeOffset="0xF6910" LangOffset="0xFAB38"/> <Text Name="ger_message_data_static" CodeOffset="0xF68F0" LangOffset="0xFAB18"/>
</File> </File>
<File Name="fra_message_data_static"> <File Name="fra_message_data_static">
<Text Name="fra_message_data_static" CodeOffset="0xF6910" LangOffset="0xFCC48"/> <Text Name="fra_message_data_static" CodeOffset="0xF68F0" LangOffset="0xFCC28"/>
</File> </File>
<File Name="staff_message_data_static"> <File Name="staff_message_data_static">
<Text Name="staff_message_data_static" CodeOffset="0xFED58"/> <Text Name="staff_message_data_static" CodeOffset="0xFED38"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1210" RangeStart="0x6238" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1210" RangeStart="0x6238" RangeEnd="0x9238">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x6238"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61E8" RangeEnd="0x91E8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61E8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x8089E470" RangeStart="0x61C8" RangeEnd="0x91C8"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x8089E470" RangeStart="0x61C8" RangeEnd="0x91C8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61C8"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61C8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/>
</File> </File>
</Root> </Root>

View file

@ -8,7 +8,7 @@
<!-- Morpha's Title Card --> <!-- Morpha's Title Card -->
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/> <Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/> <Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
<!-- DLists for Morpha's Core --> <!-- DLists for Morpha's Core -->
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/> <DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/> <DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
@ -69,17 +69,17 @@
<!-- Unused content --> <!-- Unused content -->
<!-- This is the dlist for EnVbBall for some reason. --> <!-- This is the dlist for EnVbBall for some reason. -->
<DList Name="gMorphaDL_000550" Offset="0x550"/> <DList Name="gMorphaDL_000550" Offset="0x550"/>
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/> <DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/> <DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/> <DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938"> <Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
<Vtx/> <Vtx/>
</Array> </Array>
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8"> <Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
<Vtx/> <Vtx/>
</Array> </Array>

View file

@ -1,6 +1,6 @@
<Root> <Root>
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61C8" RangeEnd="0x91C8"> <File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61C8" RangeEnd="0x91C8">
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61C8"/> <Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61C8"/>
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/> <Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/>
</File> </File>
</Root> </Root>

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

@ -132,16 +132,6 @@ typedef enum {
/* 0x40 */ PLAYER_IA_MASK_GERUDO, /* 0x40 */ PLAYER_IA_MASK_GERUDO,
/* 0x41 */ PLAYER_IA_MASK_TRUTH, /* 0x41 */ PLAYER_IA_MASK_TRUTH,
/* 0x42 */ PLAYER_IA_LENS_OF_TRUTH, /* 0x42 */ PLAYER_IA_LENS_OF_TRUTH,
// Upstream TODO: Document why these entries were added
/* 0x43 */ PLAYER_IA_SHIELD_DEKU,
/* 0x44 */ PLAYER_IA_SHIELD_HYLIAN,
/* 0x45 */ PLAYER_IA_SHIELD_MIRROR,
/* 0x46 */ PLAYER_IA_TUNIC_KOKIRI,
/* 0x47 */ PLAYER_IA_TUNIC_GORON,
/* 0x48 */ PLAYER_IA_TUNIC_ZORA,
/* 0x49 */ PLAYER_IA_BOOTS_KOKIRI,
/* 0x4A */ PLAYER_IA_BOOTS_IRON,
/* 0x4B */ PLAYER_IA_BOOTS_HOVER,
/* 0x4C */ PLAYER_IA_MAX /* 0x4C */ PLAYER_IA_MAX
} PlayerItemAction; } PlayerItemAction;
@ -494,8 +484,8 @@ typedef struct {
#define PLAYER_STATE3_RESTORE_NAYRUS_LOVE (1 << 6) // Set by ocarina effects actors when destroyed to signal Nayru's Love may be restored (see `ACTOROVL_ALLOC_ABSOLUTE`) #define PLAYER_STATE3_RESTORE_NAYRUS_LOVE (1 << 6) // Set by ocarina effects actors when destroyed to signal Nayru's Love may be restored (see `ACTOROVL_ALLOC_ABSOLUTE`)
#define PLAYER_STATE3_HOOKSHOT_TRAVELLING (1 << 7) //Travelling to target #define PLAYER_STATE3_HOOKSHOT_TRAVELLING (1 << 7) //Travelling to target
typedef void (*PlayerFunc674)(struct Player*, struct PlayState*); typedef void (*PlayerActionFunc)(struct Player*, struct PlayState*);
typedef s32 (*PlayerFunc82C)(struct Player*, struct PlayState*); typedef s32 (*UpperActionFunc)(struct Player*, struct PlayState*);
typedef void (*PlayerFuncA74)(struct PlayState*, struct Player*); typedef void (*PlayerFuncA74)(struct PlayState*, struct Player*);
typedef struct Player { typedef struct Player {
@ -512,7 +502,7 @@ typedef struct Player {
/* 0x0155 */ char unk_155[0x003]; /* 0x0155 */ char unk_155[0x003];
/* 0x0158 */ u8 modelGroup; /* 0x0158 */ u8 modelGroup;
/* 0x0159 */ u8 nextModelGroup; /* 0x0159 */ u8 nextModelGroup;
/* 0x015A */ s8 unk_15A; /* 0x015A */ s8 itemChangeType;
/* 0x015B */ u8 modelAnimType; /* 0x015B */ u8 modelAnimType;
/* 0x015C */ u8 leftHandType; /* 0x015C */ u8 leftHandType;
/* 0x015D */ u8 rightHandType; /* 0x015D */ u8 rightHandType;
@ -548,11 +538,11 @@ typedef struct Player {
/* 0x043C */ s8 mountSide; /* 0x043C */ s8 mountSide;
/* 0x043D */ char unk_43D[0x003]; /* 0x043D */ char unk_43D[0x003];
/* 0x0440 */ Actor* rideActor; /* 0x0440 */ Actor* rideActor;
/* 0x0444 */ u8 csMode; /* 0x0444 */ u8 csAction;
/* 0x0445 */ u8 prevCsMode; /* 0x0445 */ u8 prevCsAction;
/* 0x0446 */ u8 unk_446; /* 0x0446 */ u8 cueId;
/* 0x0447 */ u8 unk_447; /* 0x0447 */ u8 unk_447;
/* 0x0448 */ Actor* unk_448; /* 0x0448 */ Actor* csActor;
/* 0x044C */ char unk_44C[0x004]; /* 0x044C */ char unk_44C[0x004];
/* 0x0450 */ Vec3f unk_450; /* 0x0450 */ Vec3f unk_450;
/* 0x045C */ Vec3f unk_45C; /* 0x045C */ Vec3f unk_45C;
@ -567,7 +557,7 @@ typedef struct Player {
/* 0x0668 */ char unk_668[0x004]; /* 0x0668 */ char unk_668[0x004];
/* 0x066C */ s32 unk_66C; /* 0x066C */ s32 unk_66C;
/* 0x0670 */ s32 meleeWeaponEffectIndex; /* 0x0670 */ s32 meleeWeaponEffectIndex;
/* 0x0674 */ PlayerFunc674 func_674; /* 0x0674 */ PlayerActionFunc actionFunc;
/* 0x0678 */ PlayerAgeProperties* ageProperties; /* 0x0678 */ PlayerAgeProperties* ageProperties;
/* 0x067C */ u32 stateFlags1; /* 0x067C */ u32 stateFlags1;
/* 0x0680 */ u32 stateFlags2; /* 0x0680 */ u32 stateFlags2;
@ -581,7 +571,7 @@ typedef struct Player {
/* 0x0698 */ f32 targetActorDistance; /* 0x0698 */ f32 targetActorDistance;
/* 0x069C */ char unk_69C[0x004]; /* 0x069C */ char unk_69C[0x004];
/* 0x06A0 */ f32 unk_6A0; /* 0x06A0 */ f32 unk_6A0;
/* 0x06A4 */ f32 unk_6A4; /* 0x06A4 */ f32 closestSecretDistSq;
/* 0x06A8 */ Actor* unk_6A8; /* 0x06A8 */ Actor* unk_6A8;
/* 0x06AC */ s8 unk_6AC; /* 0x06AC */ s8 unk_6AC;
/* 0x06AD */ u8 unk_6AD; /* 0x06AD */ u8 unk_6AD;
@ -596,18 +586,18 @@ typedef struct Player {
/* 0x06C0 */ s16 unk_6C0; /* 0x06C0 */ s16 unk_6C0;
/* 0x06C2 */ s16 unk_6C2; /* 0x06C2 */ s16 unk_6C2;
/* 0x06C4 */ f32 unk_6C4; /* 0x06C4 */ f32 unk_6C4;
/* 0x06C8 */ SkelAnime skelAnime2; /* 0x06C8 */ SkelAnime upperSkelAnime;
/* 0x070C */ Vec3s jointTable2[PLAYER_LIMB_BUF_COUNT]; /* 0x070C */ Vec3s upperJointTable[PLAYER_LIMB_BUF_COUNT];
/* 0x079C */ Vec3s morphTable2[PLAYER_LIMB_BUF_COUNT]; /* 0x079C */ Vec3s upperMorphTable[PLAYER_LIMB_BUF_COUNT];
/* 0x082C */ PlayerFunc82C func_82C; /* 0x082C */ UpperActionFunc upperActionFunc;
/* 0x0830 */ f32 unk_830; /* 0x0830 */ f32 upperAnimBlendWeight;
/* 0x0834 */ s16 unk_834; /* 0x0834 */ s16 unk_834;
/* 0x0836 */ s8 unk_836; /* 0x0836 */ s8 unk_836;
/* 0x0837 */ u8 unk_837; /* 0x0837 */ u8 unk_837;
/* 0x0838 */ f32 linearVelocity; /* 0x0838 */ f32 linearVelocity;
/* 0x083C */ s16 currentYaw; /* 0x083C */ s16 currentYaw;
/* 0x083E */ s16 targetYaw; /* 0x083E */ s16 targetYaw;
/* 0x0840 */ u16 unk_840; /* 0x0840 */ u16 underwaterTimer;
/* 0x0842 */ s8 meleeWeaponAnimation; /* 0x0842 */ s8 meleeWeaponAnimation;
/* 0x0843 */ s8 meleeWeaponState; /* 0x0843 */ s8 meleeWeaponState;
/* 0x0844 */ s8 unk_844; /* 0x0844 */ s8 unk_844;
@ -631,8 +621,8 @@ typedef struct Player {
/* 0x087C */ s16 unk_87C; /* 0x087C */ s16 unk_87C;
/* 0x087E */ s16 unk_87E; /* 0x087E */ s16 unk_87E;
/* 0x0880 */ f32 unk_880; /* 0x0880 */ f32 unk_880;
/* 0x0884 */ f32 wallHeight; // height used to determine whether link can climb or grab a ledge at the top /* 0x0884 */ f32 yDistToLedge; // y distance to ground above an interact wall. LEDGE_DIST_MAX if no ground is found
/* 0x0888 */ f32 wallDistance; // distance to the colliding wall plane /* 0x0888 */ f32 distToInteractWall; // distance to the colliding wall plane
/* 0x088C */ u8 unk_88C; /* 0x088C */ u8 unk_88C;
/* 0x088D */ u8 unk_88D; /* 0x088D */ u8 unk_88D;
/* 0x088E */ u8 unk_88E; /* 0x088E */ u8 unk_88E;
@ -643,17 +633,17 @@ typedef struct Player {
/* 0x0893 */ u8 hoverBootsTimer; /* 0x0893 */ u8 hoverBootsTimer;
/* 0x0894 */ s16 fallStartHeight; // last truncated Y position before falling /* 0x0894 */ s16 fallStartHeight; // last truncated Y position before falling
/* 0x0896 */ s16 fallDistance; // truncated Y distance the player has fallen so far (positive is down) /* 0x0896 */ s16 fallDistance; // truncated Y distance the player has fallen so far (positive is down)
/* 0x0898 */ s16 unk_898; /* 0x0898 */ s16 floorPitch; // angle of the floor slope in the direction of current world yaw (positive for ascending slope)
/* 0x089A */ s16 unk_89A; /* 0x089A */ s16 floorPitchAlt; // the calculation for this value is bugged and doesn't represent anything meaningful
/* 0x089C */ s16 unk_89C; /* 0x089C */ s16 unk_89C;
/* 0x089E */ u16 unk_89E; /* 0x089E */ u16 floorSfxOffset;
/* 0x08A0 */ u8 unk_8A0; /* 0x08A0 */ u8 unk_8A0;
/* 0x08A1 */ u8 unk_8A1; /* 0x08A1 */ u8 unk_8A1;
/* 0x08A2 */ s16 unk_8A2; /* 0x08A2 */ s16 unk_8A2;
/* 0x08A4 */ f32 unk_8A4; /* 0x08A4 */ f32 unk_8A4;
/* 0x08A8 */ f32 unk_8A8; /* 0x08A8 */ f32 unk_8A8;
/* 0x08AC */ f32 windSpeed; // Pushing player, examples include water currents, floor conveyors, climbing sloped surfaces // Upstream TODO: pushedSpeed /* 0x08AC */ f32 pushedSpeed; // Pushing player, examples include water currents, floor conveyors, climbing sloped surfaces
/* 0x08B0 */ s16 windDirection; // Yaw direction of player being pushed // Upstream TODO: pushedYaw /* 0x08B0 */ s16 pushedYaw; // Yaw direction of player being pushed
/* 0x08B4 */ WeaponInfo meleeWeaponInfo[3]; /* 0x08B4 */ WeaponInfo meleeWeaponInfo[3];
/* 0x0908 */ Vec3f bodyPartsPos[PLAYER_BODYPART_MAX]; /* 0x0908 */ Vec3f bodyPartsPos[PLAYER_BODYPART_MAX];
/* 0x09E0 */ MtxF mf_9E0; /* 0x09E0 */ MtxF mf_9E0;

View file

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

View file

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

View file

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

View file

@ -248,11 +248,6 @@ fi
cd "$SNAME" cd "$SNAME"
arch_name="$(uname -m)" "$RESPATH"/soh-macos
launch_arch="arm64"
if [ "${arch_name}" = "x86_64" ] && [ "$(sysctl -in sysctl.proc_translated)" != "1" ]; then
launch_arch="x86_64"
fi
arch -${launch_arch} "$RESPATH"/soh-macos
exit exit

View file

@ -74,7 +74,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
{ ACTOR_EN_BUBBLE, "Shabom" }, { ACTOR_EN_BUBBLE, "Shabom" },
{ ACTOR_DOOR_SHUTTER, "Shutter Door" }, { ACTOR_DOOR_SHUTTER, "Shutter Door" },
{ ACTOR_EN_DODOJR, "Baby Dodongo" }, { ACTOR_EN_DODOJR, "Baby Dodongo" },
{ ACTOR_EN_BDFIRE, "Empty" }, { ACTOR_EN_BDFIRE, "King Dodongo's Fire Breath" },
{ ACTOR_EN_BOOM, "Boomerang" }, { ACTOR_EN_BOOM, "Boomerang" },
{ ACTOR_EN_TORCH2, "Dark Link" }, { ACTOR_EN_TORCH2, "Dark Link" },
{ ACTOR_EN_BILI, "Biri" }, { ACTOR_EN_BILI, "Biri" },
@ -132,7 +132,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
{ ACTOR_BG_TOKI_HIKARI, "Windows (Temple of Time)" }, { ACTOR_BG_TOKI_HIKARI, "Windows (Temple of Time)" },
{ ACTOR_EN_YUKABYUN, "Flying Floor Tile" }, { ACTOR_EN_YUKABYUN, "Flying Floor Tile" },
{ ACTOR_BG_TOKI_SWD, "Master Sword" }, { ACTOR_BG_TOKI_SWD, "Master Sword" },
{ ACTOR_EN_FHG_FIRE, "Empty" }, { ACTOR_EN_FHG_FIRE, "Phantom Ganon's Lighting Attack" },
{ ACTOR_BG_MJIN, "Warp Song Pad" }, { ACTOR_BG_MJIN, "Warp Song Pad" },
{ ACTOR_BG_HIDAN_KOUSI, "Sliding Metal Gate" }, { ACTOR_BG_HIDAN_KOUSI, "Sliding Metal Gate" },
{ ACTOR_DOOR_TOKI, "Door of Time Collision" }, { ACTOR_DOOR_TOKI, "Door of Time Collision" },
@ -439,7 +439,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
{ ACTOR_EN_DAIKU_KAKARIKO, "Carpenters (Kakariko)" }, { ACTOR_EN_DAIKU_KAKARIKO, "Carpenters (Kakariko)" },
{ ACTOR_BG_BOWL_WALL, "Bombchu Bowling Alley Wall" }, { ACTOR_BG_BOWL_WALL, "Bombchu Bowling Alley Wall" },
{ ACTOR_EN_WALL_TUBO, "Bombchu Bowling Alley Bullseyes" }, { ACTOR_EN_WALL_TUBO, "Bombchu Bowling Alley Bullseyes" },
{ ACTOR_EN_PO_DESERT, "Poe Guide (Desert Wasteland)" }, { ACTOR_EN_PO_DESERT, "Poe Guide (Haunted Wasteland)" },
{ ACTOR_EN_CROW, "Guay" }, { ACTOR_EN_CROW, "Guay" },
{ ACTOR_DOOR_KILLER, "Fake Door" }, { ACTOR_DOOR_KILLER, "Fake Door" },
{ ACTOR_BG_SPOT11_OASIS, "Oasis (Desert Colossus)" }, { ACTOR_BG_SPOT11_OASIS, "Oasis (Desert Colossus)" },
@ -548,6 +548,10 @@ int ActorDB::RetrieveId(const std::string& name) {
return entry->second; return entry->second;
} }
int ActorDB::GetEntryCount() {
return db.size();
}
ActorDB::Entry::Entry() { ActorDB::Entry::Entry() {
entry.name = nullptr; entry.name = nullptr;
entry.desc = nullptr; entry.desc = nullptr;

View file

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

View file

@ -400,8 +400,9 @@ void AudioCollection::InitializeShufflePool() {
if (shufflePoolInitialized) return; if (shufflePoolInitialized) return;
for (auto& [seqId, seqInfo] : sequenceMap) { for (auto& [seqId, seqInfo] : sequenceMap) {
if (!seqInfo.canBeUsedAsReplacement) continue;
const std::string cvarKey = "gAudioEditor.Excluded." + seqInfo.sfxKey; const std::string cvarKey = "gAudioEditor.Excluded." + seqInfo.sfxKey;
if (CVarGetInteger(cvarKey.c_str(), 0) && !seqInfo.canBeUsedAsReplacement) { if (CVarGetInteger(cvarKey.c_str(), 0)) {
excludedSequences.insert(&seqInfo); excludedSequences.insert(&seqInfo);
} else { } else {
includedSequences.insert(&seqInfo); includedSequences.insert(&seqInfo);

View file

@ -422,8 +422,8 @@ void AudioEditor::DrawElement() {
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::NewLine(); ImGui::NewLine();
ImGui::PopItemWidth(); ImGui::PopItemWidth();
UIWidgets::EnhancementSliderFloat("Link's voice pitch multiplier: %f", "##linkVoiceFreqMultiplier", UIWidgets::EnhancementSliderFloat("Link's voice pitch multiplier: %.1f %%", "##linkVoiceFreqMultiplier",
"gLinkVoiceFreqMultiplier", 0.4, 2.5, "", 1.0, false, false); "gLinkVoiceFreqMultiplier", 0.4, 2.5, "", 1.0, true, true);
ImGui::SameLine(); ImGui::SameLine();
const std::string resetButton = "Reset##linkVoiceFreqMultiplier"; const std::string resetButton = "Reset##linkVoiceFreqMultiplier";
if (ImGui::Button(resetButton.c_str())) { if (ImGui::Button(resetButton.c_str())) {

View file

@ -4,6 +4,9 @@
#ifdef __cplusplus #ifdef __cplusplus
#include <libultraship/libultraship.h> #include <libultraship/libultraship.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h> #include <ImGui/imgui.h>
class AudioEditor : public LUS::GuiWindow { class AudioEditor : public LUS::GuiWindow {

View file

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

View file

@ -7,6 +7,9 @@
#include <iterator> #include <iterator>
#include <variables.h> #include <variables.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h> #include <ImGui/imgui.h>
#include <ImGui/imgui_internal.h> #include <ImGui/imgui_internal.h>
#include <libultraship/bridge.h> #include <libultraship/bridge.h>
@ -14,6 +17,8 @@
#include <Utils/StringHelper.h> #include <Utils/StringHelper.h>
#include <libultraship/libultraship.h> #include <libultraship/libultraship.h>
#include "macros.h"
#include "../../UIWidgets.hpp" #include "../../UIWidgets.hpp"
namespace GameControlEditor { namespace GameControlEditor {
@ -214,16 +219,6 @@ namespace GameControlEditor {
ImGui::EndTable(); 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) { void DrawCameraControlPanel(GameControlEditorWindow* window) {
if (!ImGui::CollapsingHeader("Camera Controls")) { if (!ImGui::CollapsingHeader("Camera Controls")) {
return; return;
@ -233,6 +228,10 @@ namespace GameControlEditor {
window->BeginGroupPanelPublic("Aiming/First-Person Camera", ImGui::GetContentRegionAvail()); window->BeginGroupPanelPublic("Aiming/First-Person Camera", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Right Stick Aiming", "gRightStickAiming"); UIWidgets::PaddedEnhancementCheckbox("Right Stick Aiming", "gRightStickAiming");
DrawHelpIcon("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming"); DrawHelpIcon("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming");
if (CVarGetInteger("gRightStickAiming", 0)) {
UIWidgets::PaddedEnhancementCheckbox("Allow moving while in first person mode", "gMoveWhileFirstPerson");
DrawHelpIcon("Changes the left stick to move the player while in first person mode");
}
UIWidgets::PaddedEnhancementCheckbox("Invert Aiming X Axis", "gInvertAimingXAxis"); UIWidgets::PaddedEnhancementCheckbox("Invert Aiming X Axis", "gInvertAimingXAxis");
DrawHelpIcon("Inverts the Camera X Axis in:\n-First-Person/C-Up view\n-Weapon Aiming"); 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); UIWidgets::PaddedEnhancementCheckbox("Invert Aiming Y Axis", "gInvertAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
@ -241,17 +240,20 @@ namespace GameControlEditor {
DrawHelpIcon("Inverts the Shield Aiming Y Axis"); DrawHelpIcon("Inverts the Shield Aiming Y Axis");
UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming X Axis", "gInvertShieldAimingXAxis"); UIWidgets::PaddedEnhancementCheckbox("Invert Shield Aiming X Axis", "gInvertShieldAimingXAxis");
DrawHelpIcon("Inverts the Shield Aiming X Axis"); DrawHelpIcon("Inverts the Shield Aiming X Axis");
UIWidgets::PaddedEnhancementCheckbox("Invert Z-Weapon Aiming Y Axis", "gInvertZAimingYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Z-Weapon Aiming");
UIWidgets::PaddedEnhancementCheckbox("Disable Auto-Centering in First-Person View", "gDisableAutoCenterViewFirstPerson"); UIWidgets::PaddedEnhancementCheckbox("Disable Auto-Centering in First-Person View", "gDisableAutoCenterViewFirstPerson");
DrawHelpIcon("Prevents the C-Up view from auto-centering, allowing for Gyro Aiming"); 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 (UIWidgets::PaddedEnhancementCheckbox("Enable Custom Aiming/First-Person sensitivity", "gEnableFirstPersonSensitivity", true, false)) {
if (!CVarGetInteger("gEnableFirstPersonSensitivity", 0)) { if (!CVarGetInteger("gEnableFirstPersonSensitivity", 0)) {
CVarClear("gFirstPersonCameraSensitivity"); CVarClear("gFirstPersonCameraSensitivityX");
CVarClear("gFirstPersonCameraSensitivityY");
} }
} }
if (CVarGetInteger("gEnableFirstPersonSensitivity", 0)) { if (CVarGetInteger("gEnableFirstPersonSensitivity", 0)) {
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %d %%", "##FirstPersonSensitivity Horizontal", UIWidgets::EnhancementSliderFloat("Aiming/First-Person Horizontal Sensitivity: %.0f %%", "##FirstPersonSensitivity Horizontal",
"gFirstPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true); "gFirstPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true);
UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %d %%", "##FirstPersonSensitivity Vertical", UIWidgets::EnhancementSliderFloat("Aiming/First-Person Vertical Sensitivity: %.0f %%", "##FirstPersonSensitivity Vertical",
"gFirstPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true); "gFirstPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true);
} }
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
@ -268,9 +270,9 @@ namespace GameControlEditor {
UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free camera"); DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free camera");
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal", UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %.0f %%", "##ThirdPersonSensitivity Horizontal",
"gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true); "gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical", UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %.0f %%", "##ThirdPersonSensitivity Vertical",
"gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true); "gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist", UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist",
"gFreeCameraDistMax", 100, 900, "", 185, true, false, true); "gFreeCameraDistMax", 100, 900, "", 185, true, false, true);
@ -312,6 +314,7 @@ namespace GameControlEditor {
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select " DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
"certain items."); "certain items.");
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false); 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"); 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)) { if (CVarGetInteger("gEnableWalkModify", 0)) {
@ -319,80 +322,25 @@ namespace GameControlEditor {
window->BeginGroupPanelPublic("Walk Modifier", ImGui::GetContentRegionAvail()); window->BeginGroupPanelPublic("Walk Modifier", ImGui::GetContentRegionAvail());
UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", "gWalkSpeedToggle", true, false); UIWidgets::PaddedEnhancementCheckbox("Toggle modifier instead of holding", "gWalkSpeedToggle", true, false);
UIWidgets::PaddedEnhancementCheckbox("Don't affect jump distance/velocity", "gWalkModifierDoesntChangeJump", 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 1: %.0f %%", "##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); UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %.0f %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
window->EndGroupPanelPublic(0); window->EndGroupPanelPublic(0);
} }
ImGui::EndDisabled();
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL"); UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up"); DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
window->EndGroupPanelPublic(0); 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() { void GameControlEditorWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(465, 430), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(465, 430), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Game Controls Configuration", &mIsVisible)) { if (ImGui::Begin("Game Controls Configuration", &mIsVisible)) {
ImGui::BeginTabBar("##CustomControllers"); DrawOcarinaControlPanel(this);
if (ImGui::BeginTabItem("Generic")) { DrawCameraControlPanel(this);
CurrentPort = 0; DrawDpadControlPanel(this);
ImGui::EndTabItem(); DrawMiscControlPanel(this);
}
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(); ImGui::End();
} }

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1485,12 +1485,13 @@ void Draw_Placements(){
} }
void DrawSillyTab() { void DrawSillyTab() {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (CVarGetInteger("gLetItSnow", 0)) { if (CVarGetInteger("gLetItSnow", 0)) {
if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) { if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
} }
if (UIWidgets::EnhancementSliderFloat("Link Body Scale: %f", "##Link_BodyScale", "gCosmetics.Link_BodyScale.Value", 0.001f, 0.025f, "", 0.01f, true)) { if (UIWidgets::EnhancementSliderFloat("Link Body Scale: %.3fx", "##Link_BodyScale", "gCosmetics.Link_BodyScale.Value", 0.001f, 0.025f, "", 0.01f, true)) {
CVarSetInteger("gCosmetics.Link_BodyScale.Changed", 1); CVarSetInteger("gCosmetics.Link_BodyScale.Changed", 1);
} }
ImGui::SameLine(); ImGui::SameLine();
@ -1505,7 +1506,7 @@ void DrawSillyTab() {
player->actor.scale.z = 0.01f; player->actor.scale.z = 0.01f;
} }
} }
if (UIWidgets::EnhancementSliderFloat("Link Head Scale: %f", "##Link_HeadScale", "gCosmetics.Link_HeadScale.Value", 0.4f, 4.0f, "", 1.0f, false)) { if (UIWidgets::EnhancementSliderFloat("Link Head Scale: %.2fx", "##Link_HeadScale", "gCosmetics.Link_HeadScale.Value", 0.4f, 4.0f, "", 1.0f, false)) {
CVarSetInteger("gCosmetics.Link_HeadScale.Changed", 1); CVarSetInteger("gCosmetics.Link_HeadScale.Changed", 1);
} }
ImGui::SameLine(); ImGui::SameLine();
@ -1514,7 +1515,7 @@ void DrawSillyTab() {
CVarClear("gCosmetics.Link_HeadScale.Changed"); CVarClear("gCosmetics.Link_HeadScale.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
if (UIWidgets::EnhancementSliderFloat("Link Sword Scale: %f", "##Link_SwordScale", "gCosmetics.Link_SwordScale.Value", 1.0f, 2.5f, "", 1.0f, false)) { if (UIWidgets::EnhancementSliderFloat("Link Sword Scale: %.3fx", "##Link_SwordScale", "gCosmetics.Link_SwordScale.Value", 1.0f, 2.5f, "", 1.0f, false)) {
CVarSetInteger("gCosmetics.Link_SwordScale.Changed", 1); CVarSetInteger("gCosmetics.Link_SwordScale.Changed", 1);
} }
ImGui::SameLine(); ImGui::SameLine();
@ -1523,44 +1524,44 @@ void DrawSillyTab() {
CVarClear("gCosmetics.Link_SwordScale.Changed"); CVarClear("gCosmetics.Link_SwordScale.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
UIWidgets::EnhancementSliderFloat("Bunny Hood Length: %f", "##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength", -300.0f, 1000.0f, "", 0.0f, false); UIWidgets::EnhancementSliderFloat("Bunny Hood Length: %.0f", "##BunnyHood_EarLength", "gCosmetics.BunnyHood_EarLength", -300.0f, 1000.0f, "", 0.0f, false);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reset##BunnyHood_EarLength")) { if (ImGui::Button("Reset##BunnyHood_EarLength")) {
CVarClear("gCosmetics.BunnyHood_EarLength"); CVarClear("gCosmetics.BunnyHood_EarLength");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
UIWidgets::EnhancementSliderFloat("Bunny Hood Spread: %f", "##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread", -300.0f, 500.0f, "", 0.0f, false); UIWidgets::EnhancementSliderFloat("Bunny Hood Spread: %.0f", "##BunnyHood_EarSpread", "gCosmetics.BunnyHood_EarSpread", -300.0f, 500.0f, "", 0.0f, false);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reset##BunnyHood_EarSpread")) { if (ImGui::Button("Reset##BunnyHood_EarSpread")) {
CVarClear("gCosmetics.BunnyHood_EarSpread"); CVarClear("gCosmetics.BunnyHood_EarSpread");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
UIWidgets::EnhancementSliderFloat("Goron Neck Length: %f", "##Goron_NeckLength", "gCosmetics.Goron_NeckLength", 0.0f, 5000.0f, "", 0.0f, false); UIWidgets::EnhancementSliderFloat("Goron Neck Length: %.0f", "##Goron_NeckLength", "gCosmetics.Goron_NeckLength", 0.0f, 5000.0f, "", 0.0f, false);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reset##Goron_NeckLength")) { if (ImGui::Button("Reset##Goron_NeckLength")) {
CVarClear("gCosmetics.Goron_NeckLength"); CVarClear("gCosmetics.Goron_NeckLength");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
UIWidgets::EnhancementCheckbox("Unfix Goron Spin", "gUnfixGoronSpin"); UIWidgets::EnhancementCheckbox("Unfix Goron Spin", "gUnfixGoronSpin");
UIWidgets::EnhancementSliderFloat("Fairies Size: %f", "##Fairies_Size", "gCosmetics.Fairies_Size", 0.25f, 5.0f, "", 1.0f, false); UIWidgets::EnhancementSliderFloat("Fairies Size: %.2fx", "##Fairies_Size", "gCosmetics.Fairies_Size", 0.25f, 5.0f, "", 1.0f, false);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reset##Fairies_Size")) { if (ImGui::Button("Reset##Fairies_Size")) {
CVarClear("gCosmetics.Fairies_Size"); CVarClear("gCosmetics.Fairies_Size");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
UIWidgets::EnhancementSliderFloat("N64 Logo Spin Speed: %f", "##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed", 0.25f, 5.0f, "", 1.0f, false); UIWidgets::EnhancementSliderFloat("N64 Logo Spin Speed: %.2fx", "##N64Logo_SpinSpeed", "gCosmetics.N64Logo_SpinSpeed", 0.25f, 5.0f, "", 1.0f, false);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reset##N64Logo_SpinSpeed")) { if (ImGui::Button("Reset##N64Logo_SpinSpeed")) {
CVarClear("gCosmetics.N64Logo_SpinSpeed"); CVarClear("gCosmetics.N64Logo_SpinSpeed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
UIWidgets::EnhancementSliderFloat("Moon Size: %f", "##Moon_Size", "gCosmetics.Moon_Size", 0.5f, 2.0f, "", 1.0f, false); UIWidgets::EnhancementSliderFloat("Moon Size: %.1f %%", "##Moon_Size", "gCosmetics.Moon_Size", 0.5f, 2.0f, "", 1.0f, true);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reset##Moon_Size")) { if (ImGui::Button("Reset##Moon_Size")) {
CVarClear("gCosmetics.Moon_Size"); CVarClear("gCosmetics.Moon_Size");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
if (UIWidgets::EnhancementSliderFloat("Kak Windmill Speed: %f", "##Kak_Windmill_Speed", "gCosmetics.Kak_Windmill_Speed.Value", 100.0f, 6000.0f, "", 100.0f, false)) { if (UIWidgets::EnhancementSliderFloat("Kak Windmill Speed: %.0f", "##Kak_Windmill_Speed", "gCosmetics.Kak_Windmill_Speed.Value", 100.0f, 6000.0f, "", 100.0f, false)) {
CVarSetInteger("gCosmetics.Kak_Windmill_Speed.Changed", 1); CVarSetInteger("gCosmetics.Kak_Windmill_Speed.Changed", 1);
} }
ImGui::SameLine(); ImGui::SameLine();
@ -1569,6 +1570,7 @@ void DrawSillyTab() {
CVarClear("gCosmetics.Kak_Windmill_Speed.Changed"); CVarClear("gCosmetics.Kak_Windmill_Speed.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
} }
ImGui::EndDisabled();
} }
// Copies the RGB values from one cosmetic option to another, multiplied by the passed in amount, this // Copies the RGB values from one cosmetic option to another, multiplied by the passed in amount, this
@ -1767,6 +1769,10 @@ void CosmeticsEditorWindow::DrawElement() {
ImGui::SameLine(); ImGui::SameLine();
UIWidgets::EnhancementCombobox("gCosmetics.DefaultColorScheme", colorSchemes, COLORSCHEME_N64); UIWidgets::EnhancementCombobox("gCosmetics.DefaultColorScheme", colorSchemes, COLORSCHEME_N64);
UIWidgets::EnhancementCheckbox("Advanced Mode", "gCosmetics.AdvancedMode"); UIWidgets::EnhancementCheckbox("Advanced Mode", "gCosmetics.AdvancedMode");
UIWidgets::InsertHelpHoverText(
"Some cosmetic options may not apply if you have any mods that provide custom models for the cosmetic option.\n\n"
"For example, if you have custom Link model, then the Link's Hair color option will most likely not apply."
);
if (CVarGetInteger("gCosmetics.AdvancedMode", 0)) { if (CVarGetInteger("gCosmetics.AdvancedMode", 0)) {
if (ImGui::Button("Lock All Advanced", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) { if (ImGui::Button("Lock All Advanced", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) {
for (auto& [id, cosmeticOption] : cosmeticOptions) { for (auto& [id, cosmeticOption] : cosmeticOptions) {
@ -1787,7 +1793,7 @@ void CosmeticsEditorWindow::DrawElement() {
} }
} }
UIWidgets::EnhancementCheckbox("Sync Rainbow colors", "gCosmetics.RainbowSync"); UIWidgets::EnhancementCheckbox("Sync Rainbow colors", "gCosmetics.RainbowSync");
UIWidgets::EnhancementSliderFloat("Rainbow Speed: %f", "##rainbowSpeed", "gCosmetics.RainbowSpeed", 0.03f, 1.0f, "", 0.6f, false); UIWidgets::EnhancementSliderFloat("Rainbow Speed: %.3f", "##rainbowSpeed", "gCosmetics.RainbowSpeed", 0.03f, 1.0f, "", 0.6f, false, true);
if (ImGui::Button("Randomize All", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) { if (ImGui::Button("Randomize All", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) {
for (auto& [id, cosmeticOption] : cosmeticOptions) { for (auto& [id, cosmeticOption] : cosmeticOptions) {
if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && (!cosmeticOption.advancedOption || CVarGetInteger("gCosmetics.AdvancedMode", 0))) { if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && (!cosmeticOption.advancedOption || CVarGetInteger("gCosmetics.AdvancedMode", 0))) {

View file

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

View file

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

View file

@ -51,6 +51,7 @@ typedef enum {
TEXT_WARP_NOCTURNE_OF_SHADOW = 0x891, TEXT_WARP_NOCTURNE_OF_SHADOW = 0x891,
TEXT_WARP_PRELUDE_OF_LIGHT = 0x892, TEXT_WARP_PRELUDE_OF_LIGHT = 0x892,
TEXT_WARP_RANDOM_REPLACED_TEXT = 0x9200, TEXT_WARP_RANDOM_REPLACED_TEXT = 0x9200,
TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW = 0x9210,
TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range
} TextIDs; } TextIDs;

View file

@ -17,6 +17,10 @@
#include <Window.h> #include <Window.h>
#include <Context.h> #include <Context.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h>
#include <ImGui/imgui_internal.h> #include <ImGui/imgui_internal.h>
#undef PATH_HACK #undef PATH_HACK
#undef Path #undef Path
@ -99,7 +103,7 @@ static bool ActorSpawnHandler(std::shared_ptr<LUS::Console> Console, const std::
static bool KillPlayerHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>&, std::string* output) { static bool KillPlayerHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>&, std::string* output) {
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
effect->parameters[0] = 0; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = 0;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] You've met with a terrible fate, haven't you?"); INFO_MESSAGE("[SOH] You've met with a terrible fate, haven't you?");
@ -130,7 +134,7 @@ static bool SetPlayerHealthHandler(std::shared_ptr<LUS::Console> Console, const
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
effect->parameters[0] = health; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = health;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Player health updated to %d", health); INFO_MESSAGE("[SOH] Player health updated to %d", health);
@ -207,6 +211,7 @@ static bool ResetHandler(std::shared_ptr<LUS::Console> Console, std::vector<std:
return 1; return 1;
} }
gPlayState->gameplayFrames = 0;
SET_NEXT_GAMESTATE(&gPlayState->state, TitleSetup_Init, GameState); SET_NEXT_GAMESTATE(&gPlayState->state, TitleSetup_Init, GameState);
gPlayState->state.running = false; gPlayState->state.running = false;
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnExitGame>(gSaveContext.fileNum); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnExitGame>(gSaveContext.fileNum);
@ -246,8 +251,8 @@ static bool AddAmmoHandler(std::shared_ptr<LUS::Console> Console, const std::vec
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo(); GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo();
effect->parameters[0] = amount; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = amount;
effect->parameters[1] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[1] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -286,8 +291,8 @@ static bool TakeAmmoHandler(std::shared_ptr<LUS::Console> Console, const std::ve
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo(); GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo();
effect->parameters[0] = -amount; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -amount;
effect->parameters[1] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[1] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -396,9 +401,9 @@ static bool EntranceHandler(std::shared_ptr<LUS::Console> Console, const std::ve
} }
gPlayState->nextEntranceIndex = entrance; gPlayState->nextEntranceIndex = entrance;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 11; gPlayState->transitionType = TRANS_TYPE_INSTANT;
gSaveContext.nextTransitionType = 11; gSaveContext.nextTransitionType = TRANS_TYPE_INSTANT;
} }
static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) { static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) {
@ -406,10 +411,10 @@ static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector
gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwchFlags = gPlayState->actorCtx.flags.tempSwch; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwchFlags = gPlayState->actorCtx.flags.tempSwch;
gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = gPlayState->actorCtx.flags.tempCollect; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = gPlayState->actorCtx.flags.tempCollect;
gSaveContext.respawnFlag = 1; gSaveContext.respawnFlag = 1;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex; gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex;
gPlayState->fadeTransition = 2; gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
gSaveContext.nextTransitionType = 2; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK;
} else { } else {
ERROR_MESSAGE("gPlayState == nullptr"); ERROR_MESSAGE("gPlayState == nullptr");
return 1; return 1;
@ -420,9 +425,9 @@ static bool VoidHandler(std::shared_ptr<LUS::Console> Console, const std::vector
static bool ReloadHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) { static bool ReloadHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>& args, std::string* output) {
if (gPlayState != nullptr) { if (gPlayState != nullptr) {
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex; gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 11; gPlayState->transitionType = TRANS_TYPE_INSTANT;
gSaveContext.nextTransitionType = 11; gSaveContext.nextTransitionType = TRANS_TYPE_INSTANT;
} else { } else {
ERROR_MESSAGE("gPlayState == nullptr"); ERROR_MESSAGE("gPlayState == nullptr");
return 1; return 1;
@ -456,9 +461,9 @@ static bool FWHandler(std::shared_ptr<LUS::Console> Console, const std::vector<s
break; break;
case 1: //warp case 1: //warp
if (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0) { if (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0) {
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex; gPlayState->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex;
gPlayState->fadeTransition = 5; gPlayState->transitionType = TRANS_TYPE_FADE_WHITE_FAST;
} else { } else {
ERROR_MESSAGE("Farore's wind not set!"); ERROR_MESSAGE("Farore's wind not set!");
return 1; return 1;
@ -576,7 +581,7 @@ static bool InvisibleHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::InvisibleLink(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::InvisibleLink();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -603,8 +608,8 @@ static bool GiantLinkHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameters[0] = GI_LINK_SIZE_GIANT; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_GIANT;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -631,8 +636,8 @@ static bool MinishLinkHandler(std::shared_ptr<LUS::Console> Console, const std::
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameters[0] = GI_LINK_SIZE_MINISH; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_MINISH;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -665,7 +670,7 @@ static bool AddHeartContainerHandler(std::shared_ptr<LUS::Console> Console, cons
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
effect->parameters[0] = hearts; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = hearts;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Added %d heart containers", hearts); INFO_MESSAGE("[SOH] Added %d heart containers", hearts);
@ -696,7 +701,7 @@ static bool RemoveHeartContainerHandler(std::shared_ptr<LUS::Console> Console, c
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
effect->parameters[0] = -hearts; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -hearts;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Removed %d heart containers", hearts); INFO_MESSAGE("[SOH] Removed %d heart containers", hearts);
@ -716,7 +721,7 @@ static bool GravityHandler(std::shared_ptr<LUS::Console> Console, const std::vec
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity();
try { try {
effect->parameters[0] = LUS::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = LUS::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Gravity value must be a number."); ERROR_MESSAGE("[SOH] Gravity value must be a number.");
return 1; return 1;
@ -746,7 +751,7 @@ static bool NoUIHandler(std::shared_ptr<LUS::Console> Console, const std::vector
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::NoUI(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::NoUI();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -781,7 +786,7 @@ static bool DefenseModifierHandler(std::shared_ptr<LUS::Console> Console, const
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier();
try { try {
effect->parameters[0] = std::stoi(args[1], nullptr, 10); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Defense modifier value must be a number."); ERROR_MESSAGE("[SOH] Defense modifier value must be a number.");
return 1; return 1;
@ -789,7 +794,7 @@ static bool DefenseModifierHandler(std::shared_ptr<LUS::Console> Console, const
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
INFO_MESSAGE("[SOH] Defense modifier set to %d", effect->parameters[0]); INFO_MESSAGE("[SOH] Defense modifier set to %d", dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0]);
return 0; return 0;
} else { } else {
INFO_MESSAGE("[SOH] Command failed: Could not set defense modifier."); INFO_MESSAGE("[SOH] Command failed: Could not set defense modifier.");
@ -811,7 +816,7 @@ static bool DamageHandler(std::shared_ptr<LUS::Console> Console, const std::vect
return 1; return 1;
} }
effect->parameters[0] = -value; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Damage value must be a number."); ERROR_MESSAGE("[SOH] Damage value must be a number.");
return 1; return 1;
@ -841,7 +846,7 @@ static bool HealHandler(std::shared_ptr<LUS::Console> Console, const std::vector
return 1; return 1;
} }
effect->parameters[0] = value; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Damage value must be a number."); ERROR_MESSAGE("[SOH] Damage value must be a number.");
return 1; return 1;
@ -897,7 +902,7 @@ static bool NoZHandler(std::shared_ptr<LUS::Console> Console, const std::vector<
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::DisableZTargeting(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::DisableZTargeting();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -925,7 +930,7 @@ static bool OneHitKOHandler(std::shared_ptr<LUS::Console> Console, const std::ve
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::OneHitKO(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::OneHitKO();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -953,7 +958,7 @@ static bool PacifistHandler(std::shared_ptr<LUS::Console> Console, const std::ve
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::PacifistMode(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::PacifistMode();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -981,8 +986,8 @@ static bool PaperLinkHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameters[0] = GI_LINK_SIZE_PAPER; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_PAPER;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -1010,7 +1015,7 @@ static bool RainstormHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::WeatherRainstorm(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::WeatherRainstorm();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -1038,7 +1043,7 @@ static bool ReverseControlsHandler(std::shared_ptr<LUS::Console> Console, const
return 1; return 1;
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ReverseControls(); RemovableGameInteractionEffect* effect = new GameInteractionEffect::ReverseControls();
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -1061,7 +1066,7 @@ static bool UpdateRupeesHandler(std::shared_ptr<LUS::Console> Console, const std
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees();
try { try {
effect->parameters[0] = std::stoi(args[1], nullptr, 10); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Rupee value must be a number."); ERROR_MESSAGE("[SOH] Rupee value must be a number.");
return 1; return 1;
@ -1085,7 +1090,7 @@ static bool SpeedModifierHandler(std::shared_ptr<LUS::Console> Console, const st
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier();
try { try {
effect->parameters[0] = std::stoi(args[1], nullptr, 10); dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Speed modifier value must be a number."); ERROR_MESSAGE("[SOH] Speed modifier value must be a number.");
return 1; return 1;
@ -1120,7 +1125,7 @@ static bool BootsHandler(std::shared_ptr<LUS::Console> Console, const std::vecto
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots(); GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots();
effect->parameters[0] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -1151,7 +1156,7 @@ static bool GiveShieldHandler(std::shared_ptr<LUS::Console> Console, const std::
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield(); GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield();
effect->parameters[0] = it->second; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -1176,7 +1181,7 @@ static bool TakeShieldHandler(std::shared_ptr<LUS::Console> Console, const std::
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield(); GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield();
effect->parameters[0] = it->second * -1; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second * -1;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -1202,7 +1207,7 @@ static bool KnockbackHandler(std::shared_ptr<LUS::Console> Console, const std::v
return 1; return 1;
} }
effect->parameters[0] = value; dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
ERROR_MESSAGE("[SOH] Knockback value must be a number."); ERROR_MESSAGE("[SOH] Knockback value must be a number.");
return 1; return 1;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,6 @@
#ifndef _ENHANCEMENT_TYPES_H_
#define _ENHANCEMENT_TYPES_H_
typedef enum { typedef enum {
WARP_MODE_OVERRIDE_OFF, WARP_MODE_OVERRIDE_OFF,
WARP_MODE_OVERRIDE_MQ_AS_VANILLA, WARP_MODE_OVERRIDE_MQ_AS_VANILLA,
@ -74,3 +77,5 @@ typedef enum {
DEKU_STICK_UNBREAKABLE, DEKU_STICK_UNBREAKABLE,
DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE, DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE,
} DekuStickType; } DekuStickType;
#endif

View file

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

View file

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

View file

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

View file

@ -5,6 +5,12 @@
#include "GameInteractionEffect.h" #include "GameInteractionEffect.h"
#include "soh/Enhancements/item-tables/ItemTableTypes.h" #include "soh/Enhancements/item-tables/ItemTableTypes.h"
#include <z64.h>
typedef enum {
GI_SCHEME_SAIL,
GI_SCHEME_CROWD_CONTROL,
} GIScheme;
typedef enum { typedef enum {
/* 0x00 */ GI_LINK_SIZE_NORMAL, /* 0x00 */ GI_LINK_SIZE_NORMAL,
@ -52,13 +58,13 @@ typedef enum {
} GIColors; } GIColors;
typedef enum { typedef enum {
/* */ GI_TP_DEST_LINKSHOUSE = 187, /* */ GI_TP_DEST_LINKSHOUSE = ENTR_LINKS_HOUSE_0,
/* */ GI_TP_DEST_MINUET = 1536, /* */ GI_TP_DEST_MINUET = ENTR_SACRED_FOREST_MEADOW_2,
/* */ GI_TP_DEST_BOLERO = 1270, /* */ GI_TP_DEST_BOLERO = ENTR_DEATH_MOUNTAIN_CRATER_4,
/* */ GI_TP_DEST_SERENADE = 1540, /* */ GI_TP_DEST_SERENADE = ENTR_LAKE_HYLIA_8,
/* */ GI_TP_DEST_REQUIEM = 497, /* */ GI_TP_DEST_REQUIEM = ENTR_DESERT_COLOSSUS_5,
/* */ GI_TP_DEST_NOCTURNE = 1384, /* */ GI_TP_DEST_NOCTURNE = ENTR_GRAVEYARD_7,
/* */ GI_TP_DEST_PRELUDE = 1524, /* */ GI_TP_DEST_PRELUDE = ENTR_TEMPLE_OF_TIME_7,
} GITeleportDestinations; } GITeleportDestinations;
#ifdef __cplusplus #ifdef __cplusplus
@ -91,9 +97,15 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
#ifdef __cplusplus #ifdef __cplusplus
#include <thread>
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <string>
#ifdef ENABLE_REMOTE_CONTROL
#include <SDL2/SDL_net.h>
#include <nlohmann/json.hpp>
#endif
#define DEFINE_HOOK(name, type) \ #define DEFINE_HOOK(name, type) \
struct name { \ struct name { \
@ -131,10 +143,24 @@ public:
static void SetPacifistMode(bool active); static void SetPacifistMode(bool active);
}; };
#ifdef ENABLE_REMOTE_CONTROL
bool isRemoteInteractorEnabled;
bool isRemoteInteractorConnected;
void EnableRemoteInteractor();
void DisableRemoteInteractor();
void RegisterRemoteDataHandler(std::function<void(char payload[512])> method);
void RegisterRemoteJsonHandler(std::function<void(nlohmann::json)> method);
void RegisterRemoteConnectedHandler(std::function<void()> method);
void RegisterRemoteDisconnectedHandler(std::function<void()> method);
void TransmitDataToRemote(const char* payload);
void TransmitJsonToRemote(nlohmann::json packet);
#endif
// Effects // Effects
static GameInteractionEffectQueryResult CanApplyEffect(GameInteractionEffectBase* effect); static GameInteractionEffectQueryResult CanApplyEffect(GameInteractionEffectBase* effect);
static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect); static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect);
static GameInteractionEffectQueryResult RemoveEffect(GameInteractionEffectBase* effect); static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
// Game Hooks // Game Hooks
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; }; template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; };
@ -195,6 +221,9 @@ public:
DEFINE_HOOK(OnSetGameLanguage, void()); DEFINE_HOOK(OnSetGameLanguage, void());
DEFINE_HOOK(OnGameStillFrozen, void()); DEFINE_HOOK(OnGameStillFrozen, void());
DEFINE_HOOK(OnFileDropped, void(std::string filePath));
DEFINE_HOOK(OnAssetAltChange, void());
// Helpers // Helpers
static bool IsSaveLoaded(); static bool IsSaveLoaded();
static bool IsGameplayPaused(); static bool IsGameplayPaused();
@ -237,6 +266,21 @@ public:
static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams); static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams);
static GameInteractionEffectQueryResult SpawnActor(uint32_t actorId, int32_t actorParams); static GameInteractionEffectQueryResult SpawnActor(uint32_t actorId, int32_t actorParams);
}; };
private:
#ifdef ENABLE_REMOTE_CONTROL
IPaddress remoteIP;
TCPsocket remoteSocket;
std::thread remoteThreadReceive;
std::function<void(char payload[512])> remoteDataHandler;
std::function<void(nlohmann::json)> remoteJsonHandler;
std::function<void()> remoteConnectedHandler;
std::function<void()> remoteDisconnectedHandler;
void ReceiveFromServer();
void HandleRemoteData(char payload[512]);
void HandleRemoteJson(std::string payload);
#endif
}; };
#endif /* __cplusplus */ #endif /* __cplusplus */

View file

@ -183,7 +183,16 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode) {
void GameInteractor_ExecuteOnSetGameLanguage() { void GameInteractor_ExecuteOnSetGameLanguage() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>(); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
} }
void GameInteractor_ExecuteOnGameStillFrozen() void GameInteractor_ExecuteOnGameStillFrozen()
{ {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameStillFrozen>(); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameStillFrozen>();
} }
// MARK: - System
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnAssetAltChange>(fn);
}

View file

@ -58,7 +58,14 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode);
// MARK: - Game // MARK: - Game
void GameInteractor_ExecuteOnSetGameLanguage(); void GameInteractor_ExecuteOnSetGameLanguage();
void GameInteractor_ExecuteOnGameStillFrozen(); void GameInteractor_ExecuteOnGameStillFrozen();
// MARK: - System
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void));
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

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

View file

@ -0,0 +1,182 @@
#ifdef ENABLE_REMOTE_CONTROL
#include "GameInteractor.h"
#include <spdlog/spdlog.h>
#include <ImGui/imgui.h>
#include <ImGui/imgui_internal.h>
#include <unordered_map>
#include <tuple>
#include <type_traits>
#include <libultraship/libultraship.h>
// MARK: - Remote
void GameInteractor::EnableRemoteInteractor() {
if (isRemoteInteractorEnabled) {
return;
}
if (SDLNet_ResolveHost(&remoteIP, CVarGetString("gRemote.IP", "127.0.0.1"), CVarGetInteger("gRemote.Port", 43384)) == -1) {
SPDLOG_ERROR("[GameInteractor] SDLNet_ResolveHost: {}", SDLNet_GetError());
}
isRemoteInteractorEnabled = true;
// First check if there is a thread running, if so, join it
if (remoteThreadReceive.joinable()) {
remoteThreadReceive.join();
}
remoteThreadReceive = std::thread(&GameInteractor::ReceiveFromServer, this);
}
/**
* Raw data handler
*
* If you are developing a new remote, you should probably use the json methods instead. This
* method requires you to parse the data and ensure packets are complete manually, we cannot
* gaurentee that the data will be complete, or that it will only contain one packet with this
*/
void GameInteractor::RegisterRemoteDataHandler(std::function<void(char payload[512])> method) {
remoteDataHandler = method;
}
/**
* Json handler
*
* This method will be called when a complete json packet is received. All json packets must
* be delimited by a null terminator (\0).
*/
void GameInteractor::RegisterRemoteJsonHandler(std::function<void(nlohmann::json)> method) {
remoteJsonHandler = method;
}
void GameInteractor::RegisterRemoteConnectedHandler(std::function<void()> method) {
remoteConnectedHandler = method;
}
void GameInteractor::RegisterRemoteDisconnectedHandler(std::function<void()> method) {
remoteDisconnectedHandler = method;
}
void GameInteractor::DisableRemoteInteractor() {
if (!isRemoteInteractorEnabled) {
return;
}
isRemoteInteractorEnabled = false;
remoteThreadReceive.join();
remoteDataHandler = nullptr;
remoteJsonHandler = nullptr;
remoteConnectedHandler = nullptr;
remoteDisconnectedHandler = nullptr;
}
void GameInteractor::TransmitDataToRemote(const char* payload) {
SDLNet_TCP_Send(remoteSocket, payload, strlen(payload) + 1);
}
// Appends a newline character to the end of the json payload and sends it to the remote
void GameInteractor::TransmitJsonToRemote(nlohmann::json payload) {
TransmitDataToRemote(payload.dump().c_str());
}
// MARK: - Private
std::string receivedData;
void GameInteractor::ReceiveFromServer() {
while (isRemoteInteractorEnabled) {
while (!isRemoteInteractorConnected && isRemoteInteractorEnabled) {
SPDLOG_TRACE("[GameInteractor] Attempting to make connection to server...");
remoteSocket = SDLNet_TCP_Open(&remoteIP);
if (remoteSocket) {
isRemoteInteractorConnected = true;
SPDLOG_INFO("[GameInteractor] Connection to server established!");
if (remoteConnectedHandler) {
remoteConnectedHandler();
}
break;
}
}
SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(1);
if (remoteSocket) {
SDLNet_TCP_AddSocket(socketSet, remoteSocket);
}
// Listen to socket messages
while (isRemoteInteractorConnected && remoteSocket && isRemoteInteractorEnabled) {
// 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("[GameInteractor] SDLNet_CheckSockets: {}", SDLNet_GetError());
break;
}
if (socketsReady == 0) {
continue;
}
char remoteDataReceived[512];
memset(remoteDataReceived, 0, sizeof(remoteDataReceived));
int len = SDLNet_TCP_Recv(remoteSocket, &remoteDataReceived, sizeof(remoteDataReceived));
if (!len || !remoteSocket || len == -1) {
SPDLOG_ERROR("[GameInteractor] SDLNet_TCP_Recv: {}", SDLNet_GetError());
break;
}
HandleRemoteData(remoteDataReceived);
receivedData.append(remoteDataReceived, len);
// Proess all complete packets
size_t delimiterPos = receivedData.find('\0');
while (delimiterPos != std::string::npos) {
// Extract the complete packet until the delimiter
std::string packet = receivedData.substr(0, delimiterPos);
// Remove the packet (including the delimiter) from the received data
receivedData.erase(0, delimiterPos + 1);
HandleRemoteJson(packet);
// Find the next delimiter
delimiterPos = receivedData.find('\0');
}
}
if (isRemoteInteractorConnected) {
SDLNet_TCP_Close(remoteSocket);
isRemoteInteractorConnected = false;
if (remoteDisconnectedHandler) {
remoteDisconnectedHandler();
}
SPDLOG_INFO("[GameInteractor] Ending receiving thread...");
}
}
}
void GameInteractor::HandleRemoteData(char payload[512]) {
if (remoteDataHandler) {
remoteDataHandler(payload);
return;
}
}
void GameInteractor::HandleRemoteJson(std::string payload) {
nlohmann::json jsonPayload;
try {
jsonPayload = nlohmann::json::parse(payload);
} catch (const std::exception& e) {
SPDLOG_ERROR("[GameInteractor] Failed to parse json: \n{}\n{}\n", payload, e.what());
return;
}
if (remoteJsonHandler) {
remoteJsonHandler(jsonPayload);
return;
}
}
#endif

View file

@ -0,0 +1,471 @@
#ifdef ENABLE_REMOTE_CONTROL
#include "GameInteractor_Sail.h"
#include <libultraship/bridge.h>
#include <libultraship/libultraship.h>
#include <nlohmann/json.hpp>
template <class DstType, class SrcType>
bool IsType(const SrcType* src) {
return dynamic_cast<const DstType*>(src) != nullptr;
}
void GameInteractorSail::Enable() {
if (isEnabled) {
return;
}
isEnabled = true;
GameInteractor::Instance->EnableRemoteInteractor();
GameInteractor::Instance->RegisterRemoteJsonHandler([&](nlohmann::json payload) {
HandleRemoteJson(payload);
});
GameInteractor::Instance->RegisterRemoteConnectedHandler([&]() {
RegisterHooks();
});
}
void GameInteractorSail::Disable() {
if (!isEnabled) {
return;
}
isEnabled = false;
GameInteractor::Instance->DisableRemoteInteractor();
}
void GameInteractorSail::HandleRemoteJson(nlohmann::json payload) {
SPDLOG_INFO("[GameInteractorSail] Received payload: \n{}", payload.dump());
nlohmann::json responsePayload;
responsePayload["type"] = "result";
responsePayload["status"] = "failure";
try {
if (!payload.contains("id")) {
SPDLOG_ERROR("[GameInteractorSail] Received payload without ID");
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
responsePayload["id"] = payload["id"];
if (!payload.contains("type")) {
SPDLOG_ERROR("[GameInteractorSail] Received payload without type");
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
std::string payloadType = payload["type"].get<std::string>();
if (payloadType == "command") {
if (!payload.contains("command")) {
SPDLOG_ERROR("[GameInteractorSail] Received command payload without command");
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
std::string command = payload["command"].get<std::string>();
std::reinterpret_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch(command);
responsePayload["status"] = "success";
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
} else if (payloadType == "effect") {
if (!payload.contains("effect") || !payload["effect"].contains("type")) {
SPDLOG_ERROR("[GameInteractorSail] Received effect payload without effect type");
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
std::string effectType = payload["effect"]["type"].get<std::string>();
// Special case for "command" effect, so we can also run commands from the `simple_twitch_sail` script
if (effectType == "command") {
if (!payload["effect"].contains("command")) {
SPDLOG_ERROR("[GameInteractorSail] Received command effect payload without command");
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
std::string command = payload["effect"]["command"].get<std::string>();
std::reinterpret_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch(command);
responsePayload["status"] = "success";
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
if (effectType != "apply" && effectType != "remove") {
SPDLOG_ERROR("[GameInteractorSail] Received effect payload with unknown effect type: {}", effectType);
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
if (!GameInteractor::IsSaveLoaded()) {
responsePayload["status"] = "try_again";
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
GameInteractionEffectBase* giEffect = EffectFromJson(payload["effect"]);
if (giEffect) {
GameInteractionEffectQueryResult result;
if (effectType == "remove") {
if (IsType<RemovableGameInteractionEffect>(giEffect)) {
result = dynamic_cast<RemovableGameInteractionEffect*>(giEffect)->Remove();
} else {
result = GameInteractionEffectQueryResult::NotPossible;
}
} else {
result = giEffect->Apply();
}
if (result == GameInteractionEffectQueryResult::Possible) {
responsePayload["status"] = "success";
} else if (result == GameInteractionEffectQueryResult::TemporarilyNotPossible) {
responsePayload["status"] = "try_again";
}
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
} else {
SPDLOG_ERROR("[GameInteractorSail] Unknown payload type: {}", payloadType);
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
return;
}
// If we get here, something went wrong, send the failure response
SPDLOG_ERROR("[GameInteractorSail] Failed to handle remote JSON, sending failure response");
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
} catch (const std::exception& e) {
SPDLOG_ERROR("[GameInteractorSail] Exception handling remote JSON: {}", e.what());
} catch (...) {
SPDLOG_ERROR("[GameInteractorSail] Unknown exception handling remote JSON");
}
}
GameInteractionEffectBase* GameInteractorSail::EffectFromJson(nlohmann::json payload) {
if (!payload.contains("name")) {
return nullptr;
}
std::string name = payload["name"].get<std::string>();
if (name == "SetSceneFlag") {
auto effect = new GameInteractionEffect::SetSceneFlag();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
effect->parameters[2] = payload["parameters"][2].get<int32_t>();
}
return effect;
} else if (name == "UnsetSceneFlag") {
auto effect = new GameInteractionEffect::UnsetSceneFlag();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
effect->parameters[2] = payload["parameters"][2].get<int32_t>();
}
return effect;
} else if (name == "SetFlag") {
auto effect = new GameInteractionEffect::SetFlag();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
}
return effect;
} else if (name == "UnsetFlag") {
auto effect = new GameInteractionEffect::UnsetFlag();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
}
return effect;
} else if (name == "ModifyHeartContainers") {
auto effect = new GameInteractionEffect::ModifyHeartContainers();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "FillMagic") {
return new GameInteractionEffect::FillMagic();
} else if (name == "EmptyMagic") {
return new GameInteractionEffect::EmptyMagic();
} else if (name == "ModifyRupees") {
auto effect = new GameInteractionEffect::ModifyRupees();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "NoUI") {
return new GameInteractionEffect::NoUI();
} else if (name == "ModifyGravity") {
auto effect = new GameInteractionEffect::ModifyGravity();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "ModifyHealth") {
auto effect = new GameInteractionEffect::ModifyHealth();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "SetPlayerHealth") {
auto effect = new GameInteractionEffect::SetPlayerHealth();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "FreezePlayer") {
return new GameInteractionEffect::FreezePlayer();
} else if (name == "BurnPlayer") {
return new GameInteractionEffect::BurnPlayer();
} else if (name == "ElectrocutePlayer") {
return new GameInteractionEffect::ElectrocutePlayer();
} else if (name == "KnockbackPlayer") {
auto effect = new GameInteractionEffect::KnockbackPlayer();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "ModifyLinkSize") {
auto effect = new GameInteractionEffect::ModifyLinkSize();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "InvisibleLink") {
return new GameInteractionEffect::InvisibleLink();
} else if (name == "PacifistMode") {
return new GameInteractionEffect::PacifistMode();
} else if (name == "DisableZTargeting") {
return new GameInteractionEffect::DisableZTargeting();
} else if (name == "WeatherRainstorm") {
return new GameInteractionEffect::WeatherRainstorm();
} else if (name == "ReverseControls") {
return new GameInteractionEffect::ReverseControls();
} else if (name == "ForceEquipBoots") {
auto effect = new GameInteractionEffect::ForceEquipBoots();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "ModifyRunSpeedModifier") {
auto effect = new GameInteractionEffect::ModifyRunSpeedModifier();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "OneHitKO") {
return new GameInteractionEffect::OneHitKO();
} else if (name == "ModifyDefenseModifier") {
auto effect = new GameInteractionEffect::ModifyDefenseModifier();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "GiveOrTakeShield") {
auto effect = new GameInteractionEffect::GiveOrTakeShield();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "TeleportPlayer") {
auto effect = new GameInteractionEffect::TeleportPlayer();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "ClearAssignedButtons") {
auto effect = new GameInteractionEffect::ClearAssignedButtons();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "SetTimeOfDay") {
auto effect = new GameInteractionEffect::SetTimeOfDay();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "SetCollisionViewer") {
return new GameInteractionEffect::SetCollisionViewer();
} else if (name == "SetCosmeticsColor") {
auto effect = new GameInteractionEffect::SetCosmeticsColor();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
}
return effect;
} else if (name == "RandomizeCosmetics") {
return new GameInteractionEffect::RandomizeCosmetics();
} else if (name == "PressButton") {
auto effect = new GameInteractionEffect::PressButton();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "PressRandomButton") {
auto effect = new GameInteractionEffect::PressRandomButton();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
}
return effect;
} else if (name == "AddOrTakeAmmo") {
auto effect = new GameInteractionEffect::AddOrTakeAmmo();
if (payload.contains("parameters")) {
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
}
return effect;
} else if (name == "RandomBombFuseTimer") {
return new GameInteractionEffect::RandomBombFuseTimer();
} else if (name == "DisableLedgeGrabs") {
return new GameInteractionEffect::DisableLedgeGrabs();
} else if (name == "RandomWind") {
return new GameInteractionEffect::RandomWind();
} else if (name == "RandomBonks") {
return new GameInteractionEffect::RandomBonks();
} else if (name == "PlayerInvincibility") {
return new GameInteractionEffect::PlayerInvincibility();
} else if (name == "SlipperyFloor") {
return new GameInteractionEffect::SlipperyFloor();
} else {
SPDLOG_INFO("[GameInteractorSail] Unknown effect name: {}", name);
return nullptr;
}
}
// Workaround until we have a way to unregister hooks
static bool hasRegisteredHooks = false;
void GameInteractorSail::RegisterHooks() {
if (hasRegisteredHooks) {
return;
}
hasRegisteredHooks = true;
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>([](int32_t sceneNum) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnTransitionEnd";
payload["hook"]["sceneNum"] = sceneNum;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnLoadGame";
payload["hook"]["fileNum"] = fileNum;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](int32_t fileNum) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnExitGame";
payload["hook"]["fileNum"] = fileNum;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>([](GetItemEntry itemEntry) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnItemReceive";
payload["hook"]["tableId"] = itemEntry.tableId;
payload["hook"]["getItemId"] = itemEntry.getItemId;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnEnemyDefeat>([](void* refActor) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
Actor* actor = (Actor*)refActor;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnEnemyDefeat";
payload["hook"]["actorId"] = actor->id;
payload["hook"]["params"] = actor->params;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
Actor* actor = (Actor*)refActor;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnActorInit";
payload["hook"]["actorId"] = actor->id;
payload["hook"]["params"] = actor->params;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>([](int16_t flagType, int16_t flag) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnFlagSet";
payload["hook"]["flagType"] = flagType;
payload["hook"]["flag"] = flag;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagUnset>([](int16_t flagType, int16_t flag) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnFlagUnset";
payload["hook"]["flagType"] = flagType;
payload["hook"]["flag"] = flag;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>([](int16_t sceneNum, int16_t flagType, int16_t flag) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnSceneFlagSet";
payload["hook"]["flagType"] = flagType;
payload["hook"]["flag"] = flag;
payload["hook"]["sceneNum"] = sceneNum;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagUnset>([](int16_t sceneNum, int16_t flagType, int16_t flag) {
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
nlohmann::json payload;
payload["id"] = std::rand();
payload["type"] = "hook";
payload["hook"]["type"] = "OnSceneFlagUnset";
payload["hook"]["flagType"] = flagType;
payload["hook"]["flag"] = flag;
payload["hook"]["sceneNum"] = sceneNum;
GameInteractor::Instance->TransmitJsonToRemote(payload);
});
}
#endif

View file

@ -0,0 +1,29 @@
#ifdef ENABLE_REMOTE_CONTROL
#ifdef __cplusplus
#include <SDL2/SDL_net.h>
#include <cstdint>
#include <thread>
#include <memory>
#include <map>
#include <vector>
#include <iostream>
#include <chrono>
#include <future>
#include "./GameInteractor.h"
class GameInteractorSail {
private:
bool isEnabled;
void HandleRemoteJson(nlohmann::json payload);
GameInteractionEffectBase* EffectFromJson(nlohmann::json payload);
void RegisterHooks();
public:
static GameInteractorSail* Instance;
void Enable();
void Disable();
};
#endif
#endif

View file

@ -618,7 +618,7 @@ void DrawGameplayStatsOptionsTab() {
} }
void GameplayStatsWindow::DrawElement() { void GameplayStatsWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_Appearing); ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Gameplay Stats", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) { if (!ImGui::Begin("Gameplay Stats", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::End(); ImGui::End();
return; return;

View file

@ -30,10 +30,13 @@ typedef enum GetItemCategory {
} GetItemCategory; } GetItemCategory;
#define GET_ITEM(itemId, objectId, drawId, textId, field, chestAnim, itemCategory, modIndex, getItemId) \ #define GET_ITEM(itemId, objectId, drawId, textId, field, chestAnim, itemCategory, modIndex, getItemId) \
{ itemId, field, (chestAnim != CHEST_ANIM_SHORT ? 1 : -1) * (drawId + 1), textId, objectId, modIndex, getItemId, drawId, true, ITEM_FROM_NPC, itemCategory, NULL } { itemId, field, (chestAnim != CHEST_ANIM_SHORT ? 1 : -1) * (drawId + 1), textId, objectId, modIndex, modIndex, getItemId, drawId, true, ITEM_FROM_NPC, itemCategory, NULL }
#define GET_ITEM_CUSTOM_TABLE(itemId, objectId, drawId, textId, field, chestAnim, itemCategory, modIndex, tableId, getItemId) \
{ itemId, field, (chestAnim != CHEST_ANIM_SHORT ? 1 : -1) * (drawId + 1), textId, objectId, modIndex, tableId, getItemId, drawId, true, ITEM_FROM_NPC, itemCategory, NULL }
#define GET_ITEM_NONE \ #define GET_ITEM_NONE \
{ ITEM_NONE, 0, 0, 0, 0, 0, 0, 0, false, ITEM_FROM_NPC, ITEM_CATEGORY_JUNK, NULL } { ITEM_NONE, 0, 0, 0, 0, 0, 0, 0, 0, false, ITEM_FROM_NPC, ITEM_CATEGORY_JUNK, NULL }
typedef struct PlayState PlayState; typedef struct PlayState PlayState;
typedef struct GetItemEntry GetItemEntry; typedef struct GetItemEntry GetItemEntry;
@ -46,7 +49,8 @@ typedef struct GetItemEntry {
/* 0x02 */ int16_t gi; // defines the draw id and chest opening animation /* 0x02 */ int16_t gi; // defines the draw id and chest opening animation
/* 0x03 */ uint16_t textId; /* 0x03 */ uint16_t textId;
/* 0x04 */ uint16_t objectId; /* 0x04 */ uint16_t objectId;
/* 0x06 */ uint16_t modIndex; // 0 = Vanilla, 1 = Randomizer, future mods will increment up? /* 0x06 */ uint16_t modIndex; // Primarily used for determining whether to use Item_Give or Randomizer_Item_Give
/* 0x07 */ uint16_t tableId; // GetItemEntry table this entry is in (usually the same as modIndex, but not always)
/* 0x08 */ int16_t getItemId; /* 0x08 */ int16_t getItemId;
/* 0x0A */ uint16_t gid; // Stores the GID value unmodified for future reference. /* 0x0A */ uint16_t gid; // Stores the GID value unmodified for future reference.
/* 0x0C */ uint16_t collectable; // determines whether the item can be collected on the overworld. Will be true in most cases. /* 0x0C */ uint16_t collectable; // determines whether the item can be collected on the overworld. Will be true in most cases.

View file

@ -26,29 +26,42 @@
extern "C" { extern "C" {
#include <z64.h> #include <z64.h>
#include "align_asset_macro.h"
#include "macros.h" #include "macros.h"
#include "functions.h" #include "functions.h"
#include "variables.h" #include "variables.h"
#include "functions.h" #include "functions.h"
void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction);
void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
extern SaveContext gSaveContext; extern SaveContext gSaveContext;
extern PlayState* gPlayState; extern PlayState* gPlayState;
extern void Overlay_DisplayText(float duration, const char* text); extern void Overlay_DisplayText(float duration, const char* text);
uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
} }
bool performDelayedSave = false;
bool performSave = false; // GreyScaleEndDlist
#define dgEndGrayscaleAndEndDlistDL "__OTR__helpers/cosmetics/gEndGrayscaleAndEndDlistDL"
static const ALIGN_ASSET(2) char gEndGrayscaleAndEndDlistDL[] = dgEndGrayscaleAndEndDlistDL;
// This is used for the Temple of Time Medalions' color
#define dtokinoma_room_0DL_007A70 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007A70"
static const ALIGN_ASSET(2) char tokinoma_room_0DL_007A70[] = dtokinoma_room_0DL_007A70;
#define dtokinoma_room_0DL_007FD0 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007FD0"
static const ALIGN_ASSET(2) char tokinoma_room_0DL_007FD0[] = dtokinoma_room_0DL_007FD0;
// TODO: When there's more uses of something like this, create a new GI::RawAction? // TODO: When there's more uses of something like this, create a new GI::RawAction?
void ReloadSceneTogglingLinkAge() { void ReloadSceneTogglingLinkAge() {
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex; gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 42; // Fade Out gPlayState->transitionType = TRANS_TYPE_CIRCLE(TCA_WAVE, TCC_WHITE, TCS_FAST); // Fade Out
gSaveContext.nextTransitionType = 42; gSaveContext.nextTransitionType = TRANS_TYPE_CIRCLE(TCA_WAVE, TCC_WHITE, TCS_FAST);
gPlayState->linkAgeOnLoad ^= 1; // toggle linkAgeOnLoad gPlayState->linkAgeOnLoad ^= 1; // toggle linkAgeOnLoad
} }
void RegisterInfiniteMoney() { void RegisterInfiniteMoney() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteMoney", 0) != 0) { if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) { if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
@ -59,6 +72,7 @@ void RegisterInfiniteMoney() {
void RegisterInfiniteHealth() { void RegisterInfiniteHealth() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteHealth", 0) != 0) { if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
if (gSaveContext.health < gSaveContext.healthCapacity) { if (gSaveContext.health < gSaveContext.healthCapacity) {
gSaveContext.health = gSaveContext.healthCapacity; gSaveContext.health = gSaveContext.healthCapacity;
@ -69,6 +83,7 @@ void RegisterInfiniteHealth() {
void RegisterInfiniteAmmo() { void RegisterInfiniteAmmo() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) { if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
// Deku Sticks // Deku Sticks
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) { if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
@ -105,6 +120,7 @@ void RegisterInfiniteAmmo() {
void RegisterInfiniteMagic() { void RegisterInfiniteMagic() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteMagic", 0) != 0) { if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) { if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30; gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
@ -115,6 +131,7 @@ void RegisterInfiniteMagic() {
void RegisterInfiniteNayrusLove() { void RegisterInfiniteNayrusLove() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gInfiniteNayru", 0) != 0) { if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
gSaveContext.nayrusLoveTimer = 0x44B; gSaveContext.nayrusLoveTimer = 0x44B;
} }
@ -123,7 +140,7 @@ void RegisterInfiniteNayrusLove() {
void RegisterMoonJumpOnL() { void RegisterMoonJumpOnL() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) { if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
@ -138,7 +155,7 @@ void RegisterMoonJumpOnL() {
void RegisterInfiniteISG() { void RegisterInfiniteISG() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gEzISG", 0) != 0) { if (CVarGetInteger("gEzISG", 0) != 0) {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
@ -150,7 +167,7 @@ void RegisterInfiniteISG() {
//Permanent quick put away (QPA) glitched damage value //Permanent quick put away (QPA) glitched damage value
void RegisterEzQPA() { void RegisterEzQPA() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gEzQPA", 0) != 0) { if (CVarGetInteger("gEzQPA", 0) != 0) {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
@ -162,7 +179,7 @@ void RegisterEzQPA() {
void RegisterUnrestrictedItems() { void RegisterUnrestrictedItems() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return; if (!GameInteractor::IsSaveLoaded()) return;
if (CVarGetInteger("gNoRestrictItems", 0) != 0) { if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong; u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
@ -190,14 +207,16 @@ void RegisterFreezeTime() {
/// Switches Link's age and respawns him at the last entrance he entered. /// Switches Link's age and respawns him at the last entrance he entered.
void RegisterSwitchAge() { void RegisterSwitchAge() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) {
CVarClear("gSwitchAge");
return;
}
static bool warped = false; static bool warped = false;
static Vec3f playerPos; static Vec3f playerPos;
static int16_t playerYaw; static int16_t playerYaw;
static RoomContext* roomCtx; static RoomContext* roomCtx;
static s32 roomNum; static s32 roomNum;
if (!gPlayState) return;
if (CVarGetInteger("gSwitchAge", 0) && !warped) { if (CVarGetInteger("gSwitchAge", 0) && !warped) {
playerPos = GET_PLAYER(gPlayState)->actor.world.pos; playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y; playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
@ -207,8 +226,8 @@ void RegisterSwitchAge() {
warped = true; warped = true;
} }
if (warped && gPlayState->sceneLoadFlag != 0x0014 && if (warped && gPlayState->transitionTrigger != TRANS_TRIGGER_START &&
gSaveContext.nextTransitionType == 255) { gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT) {
GET_PLAYER(gPlayState)->actor.shape.rot.y = playerYaw; GET_PLAYER(gPlayState)->actor.shape.rot.y = playerYaw;
GET_PLAYER(gPlayState)->actor.world.pos = playerPos; GET_PLAYER(gPlayState)->actor.world.pos = playerPos;
if (roomNum != roomCtx->curRoom.num) { if (roomNum != roomCtx->curRoom.num) {
@ -217,7 +236,7 @@ void RegisterSwitchAge() {
func_80097534(gPlayState, roomCtx); // load map for new room (unloading the previous room) func_80097534(gPlayState, roomCtx); // load map for new room (unloading the previous room)
} }
warped = false; warped = false;
CVarSetInteger("gSwitchAge", 0); CVarClear("gSwitchAge");
} }
}); });
} }
@ -226,7 +245,8 @@ void RegisterSwitchAge() {
void RegisterOcarinaTimeTravel() { void RegisterOcarinaTimeTravel() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
if (!gPlayState) { if (!GameInteractor::IsSaveLoaded()) {
CVarClear("gTimeTravel");
return; return;
} }
@ -258,14 +278,12 @@ void RegisterOcarinaTimeTravel() {
void AutoSave(GetItemEntry itemEntry) { void AutoSave(GetItemEntry itemEntry) {
u8 item = itemEntry.itemId; u8 item = itemEntry.itemId;
bool performSave = false;
// Don't autosave immediately after buying items from shops to prevent getting them for free! // Don't autosave immediately after buying items from shops to prevent getting them for free!
// Don't autosave in the Chamber of Sages since resuming from that map breaks the game // Don't autosave in the Chamber of Sages since resuming from that map breaks the game
// Don't autosave during the Ganon fight when picking up the Master Sword // Don't autosave during the Ganon fight when picking up the Master Sword
// Don't autosave in the fishing pond to prevent getting rod on B outside of the pond
// Don't autosave in the bombchu bowling alley to prevent having chus on B outside of the minigame
// Don't autosave in grottos since resuming from grottos breaks the game.
if ((CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF) && (gPlayState != NULL) && (gSaveContext.pendingSale == ITEM_NONE) && if ((CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF) && (gPlayState != NULL) && (gSaveContext.pendingSale == ITEM_NONE) &&
(gPlayState->gameplayFrames > 60 && gSaveContext.cutsceneIndex < 0xFFF0) && (gPlayState->sceneNum != SCENE_GANON_BOSS)) { (gPlayState->gameplayFrames > 60 && gSaveContext.cutsceneIndex < 0xFFF0) && (gPlayState->sceneNum != SCENE_GANON_BOSS) && (gPlayState->sceneNum != SCENE_CHAMBER_OF_THE_SAGES)) {
if (((CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS) || (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_ALL_ITEMS)) && (item != ITEM_NONE)) { if (((CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS) || (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_ALL_ITEMS)) && (item != ITEM_NONE)) {
// Autosave for all items // Autosave for all items
performSave = true; performSave = true;
@ -326,25 +344,9 @@ void AutoSave(GetItemEntry itemEntry) {
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) { CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
performSave = true; performSave = true;
} }
if (gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN || gPlayState->sceneNum == SCENE_GROTTOS || if (performSave) {
gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_FISHING_POND ||
gPlayState->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) {
if (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_MAJOR_ITEMS ||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS ||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
performSave = false;
return;
}
if (performSave) {
performSave = false;
performDelayedSave = true;
}
return;
}
if (performSave || performDelayedSave) {
Play_PerformSave(gPlayState); Play_PerformSave(gPlayState);
performSave = false; performSave = false;
performDelayedSave = false;
} }
} }
} }
@ -412,6 +414,52 @@ void RegisterShadowTag() {
}); });
} }
static bool hasAffectedHealth = false;
void UpdatePermanentHeartLossState() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!CVarGetInteger("gPermanentHeartLoss", 0) && hasAffectedHealth) {
uint8_t heartContainers = gSaveContext.sohStats.heartContainers; // each worth 16 health
uint8_t heartPieces = gSaveContext.sohStats.heartPieces; // each worth 4 health, but only in groups of 4
uint8_t startingHealth = 16 * 3;
uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4);
gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity);
gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity);
hasAffectedHealth = false;
}
}
void RegisterPermanentHeartLoss() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int16_t fileNum) {
hasAffectedHealth = false;
UpdatePermanentHeartLossState();
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>([]() {
if (!CVarGetInteger("gPermanentHeartLoss", 0) || !GameInteractor::IsSaveLoaded()) return;
if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) {
gSaveContext.healthCapacity -= 16;
gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity);
hasAffectedHealth = true;
}
});
};
void RegisterDeleteFileOnDeath() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!CVarGetInteger("gDeleteFileOnDeath", 0) || !GameInteractor::IsSaveLoaded() || &gPlayState->gameOverCtx == NULL || &gPlayState->pauseCtx == NULL) return;
if (gPlayState->gameOverCtx.state == GAMEOVER_DEATH_MENU && gPlayState->pauseCtx.state == 9) {
SaveManager::Instance->DeleteZeldaFile(gSaveContext.fileNum);
hasAffectedHealth = false;
std::reinterpret_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset");
}
});
}
struct DayTimeGoldSkulltulas { struct DayTimeGoldSkulltulas {
uint16_t scene; uint16_t scene;
uint16_t room; uint16_t room;
@ -648,10 +696,10 @@ void RegisterTriforceHunt() {
// Warp to credits // Warp to credits
if (GameInteractor::State::TriforceHuntCreditsWarpActive) { if (GameInteractor::State::TriforceHuntCreditsWarpActive) {
gPlayState->nextEntranceIndex = 0x6B; gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0xFFF2; gSaveContext.nextCutsceneIndex = 0xFFF2;
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 3; gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
GameInteractor::State::TriforceHuntCreditsWarpActive = 0; GameInteractor::State::TriforceHuntCreditsWarpActive = 0;
} }
@ -664,16 +712,19 @@ void RegisterTriforceHunt() {
triforcePieceScale = 0.0f; triforcePieceScale = 0.0f;
GameInteractor::State::TriforceHuntPieceGiven = 0; GameInteractor::State::TriforceHuntPieceGiven = 0;
} }
}
});
}
uint8_t currentPieces = gSaveContext.triforcePiecesCollected; void RegisterGrantGanonsBossKey() {
uint8_t requiredPieces = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>([]() {
// Triforce Hunt needs the check if the player isn't being teleported to the credits scene.
// Give Boss Key when player loads back into the savefile. if (!GameInteractor::IsGameplayPaused() && IS_RANDO &&
if (currentPieces >= requiredPieces && gPlayState->sceneLoadFlag != 0x14 && Flags_GetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY) && gPlayState->transitionTrigger != TRANS_TRIGGER_START &&
(1 << 0 & gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER]) == 0) { (1 << 0 & gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER]) == 0) {
GetItemEntry getItemEntry = ItemTableManager::Instance->RetrieveItemEntry(MOD_RANDOMIZER, RG_GANONS_CASTLE_BOSS_KEY); GetItemEntry getItemEntry =
ItemTableManager::Instance->RetrieveItemEntry(MOD_RANDOMIZER, RG_GANONS_CASTLE_BOSS_KEY);
GiveItemEntryWithoutActor(gPlayState, getItemEntry); GiveItemEntryWithoutActor(gPlayState, getItemEntry);
}
} }
}); });
} }
@ -1045,8 +1096,16 @@ void RegisterRandomizedEnemySizes() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor); Actor* actor = static_cast<Actor*>(refActor);
// Only apply to enemies and bosses. Exclude the wobbly platforms in Jabu because they need to act like platforms. // Exclude wobbly platforms in Jabu because they need to act like platforms.
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || actor->id == ACTOR_EN_BROB) { // Exclude Dead Hand hands and Bongo Bongo main body because they make the fights (near) impossible.
uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || (actor->id == ACTOR_BOSS_SST && actor->params == -1);
// Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger.
uint8_t smallOnlyEnemy = actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD ||
actor->id == ACTOR_BOSS_FD2 || actor->id == ACTOR_EN_DH;
// Only apply to enemies and bosses.
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || excludedEnemy) {
return; return;
} }
@ -1055,9 +1114,8 @@ void RegisterRandomizedEnemySizes() {
uint8_t bigActor = rand() % 2; uint8_t bigActor = rand() % 2;
// Big actor. Dodongo and Volvagia are always smaller because they're impossible when bigger. // Big actor
if (bigActor && actor->id != ACTOR_BOSS_DODONGO && actor->id != ACTOR_BOSS_FD && if (bigActor && !smallOnlyEnemy) {
actor->id != ACTOR_BOSS_FD2) {
randomNumber = rand() % 200; randomNumber = rand() % 200;
// Between 100% and 300% size. // Between 100% and 300% size.
randomScale = 1.0f + (randomNumber / 100); randomScale = 1.0f + (randomNumber / 100);
@ -1069,6 +1127,99 @@ void RegisterRandomizedEnemySizes() {
} }
Actor_SetScale(actor, actor->scale.z * randomScale); Actor_SetScale(actor, actor->scale.z * randomScale);
if (CVarGetInteger("gEnemySizeScalesHealth", 0) && (actor->category == ACTORCAT_ENEMY)) {
// Scale the health based on a smaller factor than randomScale
float healthScalingFactor = 0.8f; // Adjust this factor as needed
float scaledHealth = actor->colChkInfo.health * (randomScale * healthScalingFactor);
// Ensure the scaled health doesn't go below zero
actor->colChkInfo.health = fmax(scaledHealth, 1.0f);
} else {
return;
}
});
}
void PatchToTMedallions() {
// TODO: Refactor the DemoEffect_UpdateJewelAdult and DemoEffect_UpdateJewelChild from z_demo_effect
// effects to take effect in there
if (CVarGetInteger("gToTMedallionsColors", 0)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_StartGrayscale", 7, gsSPGrayscale(true));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_StartGrayscale", 7, gsSPGrayscale(true));
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue", 16, gsDPSetGrayscaleColor(0, 161, 255, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue", 16, gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange", 45, gsDPSetGrayscaleColor(255, 135, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange", 45, gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_LIGHT)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow", 69, gsDPSetGrayscaleColor(255, 255, 0, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow", 16, gsDPSetGrayscaleColor(255, 255, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow", 69, gsDPSetGrayscaleColor(255, 255, 255, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow", 16, gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeGreen", 94, gsDPSetGrayscaleColor(0, 255, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeGreen", 94, gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed", 118, gsDPSetGrayscaleColor(255, 0, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed", 118, gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple", 142, gsDPSetGrayscaleColor(212, 0, 255, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple", 27, gsDPSetGrayscaleColor(212, 0, 255, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple", 142, gsDPSetGrayscaleColor(255, 255, 255, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple", 27, gsDPSetGrayscaleColor(255, 255, 255, 255));
}
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_EndGrayscaleAndEndDlist", 160, gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_EndGrayscaleAndEndDlist", 51, gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL));
} else {
// Unpatch everything
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_StartGrayscale");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_StartGrayscale");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_EndGrayscaleAndEndDlist");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_EndGrayscaleAndEndDlist");
}
}
void RegisterToTMedallions() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>([](GetItemEntry _unused) {
if (!CVarGetInteger("gToTMedallionsColors", 0) || !gPlayState || gPlayState->sceneNum != SCENE_TEMPLE_OF_TIME) {
return;
}
PatchToTMedallions();
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) {
if (!CVarGetInteger("gToTMedallionsColors", 0) || sceneNum != SCENE_TEMPLE_OF_TIME) {
return;
}
PatchToTMedallions();
}); });
} }
@ -1090,15 +1241,19 @@ void InitMods() {
RegisterDaytimeGoldSkultullas(); RegisterDaytimeGoldSkultullas();
RegisterRupeeDash(); RegisterRupeeDash();
RegisterShadowTag(); RegisterShadowTag();
RegisterPermanentHeartLoss();
RegisterDeleteFileOnDeath();
RegisterHyperBosses(); RegisterHyperBosses();
RegisterHyperEnemies(); RegisterHyperEnemies();
RegisterBonkDamage(); RegisterBonkDamage();
RegisterMenuPathFix(); RegisterMenuPathFix();
RegisterMirrorModeHandler(); RegisterMirrorModeHandler();
RegisterTriforceHunt(); RegisterTriforceHunt();
RegisterGrantGanonsBossKey();
RegisterEnemyDefeatCounts(); RegisterEnemyDefeatCounts();
RegisterAltTrapTypes(); RegisterAltTrapTypes();
RegisterRandomizerSheikSpawn(); RegisterRandomizerSheikSpawn();
RegisterRandomizedEnemySizes(); RegisterRandomizedEnemySizes();
RegisterToTMedallions();
NameTag_RegisterHooks(); NameTag_RegisterHooks();
} }

View file

@ -9,6 +9,8 @@ extern "C" {
void UpdateDirtPathFixState(int32_t sceneNum); void UpdateDirtPathFixState(int32_t sceneNum);
void UpdateMirrorModeState(int32_t sceneNum); void UpdateMirrorModeState(int32_t sceneNum);
void PatchToTMedallions();
void UpdatePermanentHeartLossState();
void InitMods(); void InitMods();
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -136,6 +136,7 @@ const std::vector<const char*> enhancementsCvars = {
"gInjectItemCounts", "gInjectItemCounts",
"gDayGravePull", "gDayGravePull",
"gDampeAllNight", "gDampeAllNight",
"gSkipSwimDeepEndAnim",
"gSkipScarecrow", "gSkipScarecrow",
"gBlueFireArrows", "gBlueFireArrows",
"gSunlightArrows", "gSunlightArrows",
@ -217,6 +218,32 @@ const std::vector<const char*> enhancementsCvars = {
"gFixTexturesOOB", "gFixTexturesOOB",
"gIvanCoopModeEnabled", "gIvanCoopModeEnabled",
"gEnemySpawnsOverWaterboxes", "gEnemySpawnsOverWaterboxes",
"gTreeStickDrops",
"gShadowTag",
"gRandomizedEnemySizes",
"gRandomizedEnemies",
"gMirroredWorldMode",
"gMirroredWorld",
"gHyperEnemies",
"gHookshotableReticle",
"gHideBunnyHood",
"gFixVineFall",
"gFileSelectMoreInfo",
"gEnemyHealthBar",
"gBushDropFix",
"gAllDogsRichard",
"gAddTraps.enabled",
"gAddTraps.Ammo",
"gAddTraps.Bomb",
"gAddTraps.Burn",
"gAddTraps.Ice",
"gAddTraps.Kill",
"gAddTraps.Knock",
"gAddTraps.Shock",
"gAddTraps.Speed",
"gAddTraps.Tele",
"gAddTraps.Void",
"gToTMedallionsColors",
}; };
const std::vector<const char*> cheatCvars = { const std::vector<const char*> cheatCvars = {
@ -269,7 +296,23 @@ const std::vector<const char*> cheatCvars = {
"gNoRedeadFreeze", "gNoRedeadFreeze",
"gBombTimerMultiplier", "gBombTimerMultiplier",
"gNoFishDespawn", "gNoFishDespawn",
"gNoBugsDespawn" "gNoBugsDespawn",
"gWalkModifierDoesntChangeJump",
"gStatsEnabled",
"gSaveStatesEnabled",
"gSaveStatePromise",
"gRegEditEnabled",
"gPreset0",
"gPreset1",
"gDekuStickCheat",
"gDebugWarpScreenTranslation",
"gDebugSaveFileMode",
"gCosmetics.Link_BodyScale.Changed",
"gCosmetics.Link_BodyScale.Value",
"gCosmetics.Link_HeadScale.Changed",
"gCosmetics.Link_HeadScale.Value",
"gCosmetics.Link_SwordScale.Changed",
"gCosmetics.Link_SwordScale.Value",
}; };
const std::vector<const char*> randomizerCvars = { const std::vector<const char*> randomizerCvars = {
@ -399,6 +442,15 @@ const std::vector<const char*> randomizerCvars = {
"gRandomizeGregHint", "gRandomizeGregHint",
"gRandoManualSeedEntry", "gRandoManualSeedEntry",
"gRandomizerSettingsEnabled", "gRandomizerSettingsEnabled",
"gRandomizeTriforceHuntTotalPieces",
"gRandomizeTriforceHuntRequiredPieces",
"gRandomizeTriforceHunt",
"gRandomizeShuffleMasterSword",
"gRandomizeSariaHint",
"gRandomizeRupeeNames",
"gRandomizeFrogsHint",
"gRandoRelevantNavi",
"gRandoQuestItemFanfares",
}; };
const std::vector<PresetEntry> vanillaPlusPresetEntries = { const std::vector<PresetEntry> vanillaPlusPresetEntries = {
@ -740,6 +792,9 @@ const std::vector<PresetEntry> randomizerPresetEntries = {
// Chest size & texture matches contents // Chest size & texture matches contents
PRESET_ENTRY_S32("gChestSizeAndTextureMatchesContents", CSMC_BOTH), PRESET_ENTRY_S32("gChestSizeAndTextureMatchesContents", CSMC_BOTH),
// Color Temple of Time's Medallions
PRESET_ENTRY_S32("gToTMedallionsColors", 1),
// Pause link animation (0 to 16) // Pause link animation (0 to 16)
PRESET_ENTRY_S32("gPauseLiveLink", 16), PRESET_ENTRY_S32("gPauseLiveLink", 16),
// Frames to wait // Frames to wait

View file

@ -913,6 +913,8 @@ void VanillaFill() {
ShuffleAllEntrances(); ShuffleAllEntrances();
printf("\x1b[7;32HDone"); printf("\x1b[7;32HDone");
} }
// Populate the playthrough for entrances so they are placed in the spoiler log
GeneratePlaythrough();
//Finish up //Finish up
CreateItemOverrides(); CreateItemOverrides();
CreateEntranceOverrides(); CreateEntranceOverrides();

View file

@ -24,11 +24,11 @@ void LocationTable_Init() {
//Lost Woods //Lost Woods
locationTable[LW_NEAR_SHORTCUTS_GROTTO_CHEST] = ItemLocation::Chest (RC_LW_NEAR_SHORTCUTS_GROTTO_CHEST, 0x3E, 0x14, "LW Near Shortcuts Grotto Chest", LW_NEAR_SHORTCUTS_GROTTO_CHEST, BLUE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_NEAR_SHORTCUTS_GROTTO_CHEST] = ItemLocation::Chest (RC_LW_NEAR_SHORTCUTS_GROTTO_CHEST, 0x3E, 0x14, "LW Near Shortcuts Grotto Chest", LW_NEAR_SHORTCUTS_GROTTO_CHEST, BLUE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_SKULL_KID] = ItemLocation::Base (RC_LW_SKULL_KID, 0x5B, "LW Skull Kid", LW_SKULL_KID, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(30), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_SKULL_KID] = ItemLocation::Base (RC_LW_SKULL_KID, 0x5B, "LW Skull Kid", LW_SKULL_KID, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(22), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_TRADE_COJIRO] = ItemLocation::Base (RC_LW_TRADE_COJIRO, 0x5B, "LW Trade Cojiro", LW_TRADE_COJIRO, ODD_MUSHROOM, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TRADE_COJIRO] = ItemLocation::Base (RC_LW_TRADE_COJIRO, 0x5B, "LW Trade Cojiro", LW_TRADE_COJIRO, ODD_MUSHROOM, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_TRADE_ODD_POTION] = ItemLocation::Base (RC_LW_TRADE_ODD_POTION, 0x5B, "LW Trade Odd Potion", LW_TRADE_ODD_POTION, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(57), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TRADE_ODD_POTION] = ItemLocation::Base (RC_LW_TRADE_ODD_POTION, 0x5B, "LW Trade Odd Potion", LW_TRADE_ODD_POTION, POACHERS_SAW, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(49), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_OCARINA_MEMORY_GAME] = ItemLocation::Base (RC_LW_OCARINA_MEMORY_GAME, 0x5B, "LW Ocarina Memory Game", LW_OCARINA_MEMORY_GAME, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(31), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_OCARINA_MEMORY_GAME] = ItemLocation::Base (RC_LW_OCARINA_MEMORY_GAME, 0x5B, "LW Ocarina Memory Game", LW_OCARINA_MEMORY_GAME, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(23), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_TARGET_IN_WOODS] = ItemLocation::Base (RC_LW_TARGET_IN_WOODS, 0x5B, "LW Target in Woods", LW_TARGET_IN_WOODS, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(21), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TARGET_IN_WOODS] = ItemLocation::Base (RC_LW_TARGET_IN_WOODS, 0x5B, "LW Target in Woods", LW_TARGET_IN_WOODS, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, 0x5B, "LW Deku Scrub Near Deku Theater Right",LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, 0x5B, "LW Deku Scrub Near Deku Theater Right",LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, 0x5B, "LW Deku Scrub Near Deku Theater Left", LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, BUY_DEKU_STICK_1, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, 0x5B, "LW Deku Scrub Near Deku Theater Left", LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, BUY_DEKU_STICK_1, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[LW_DEKU_SCRUB_NEAR_BRIDGE] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_BRIDGE, 0x5B, "LW Deku Scrub Near Bridge", LW_DEKU_SCRUB_NEAR_BRIDGE, PROGRESSIVE_STICK_UPGRADE, {Category::cDekuScrub, Category::cDekuScrubUpgrades}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_NEAR_BRIDGE] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_BRIDGE, 0x5B, "LW Deku Scrub Near Bridge", LW_DEKU_SCRUB_NEAR_BRIDGE, PROGRESSIVE_STICK_UPGRADE, {Category::cDekuScrub, Category::cDekuScrubUpgrades}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
@ -53,7 +53,7 @@ void LocationTable_Init() {
//Lake Hylia //Lake Hylia
locationTable[LH_CHILD_FISHING] = ItemLocation::Base (RC_LH_CHILD_FISHING, 0x49, "LH Child Fishing", LH_CHILD_FISHING, PIECE_OF_HEART, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_CHILD_FISHING] = ItemLocation::Base (RC_LH_CHILD_FISHING, 0x49, "LH Child Fishing", LH_CHILD_FISHING, PIECE_OF_HEART, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_ADULT_FISHING] = ItemLocation::Base (RC_LH_ADULT_FISHING, 0x49, "LH Adult Fishing", LH_ADULT_FISHING, PROGRESSIVE_SCALE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_ADULT_FISHING] = ItemLocation::Base (RC_LH_ADULT_FISHING, 0x49, "LH Adult Fishing", LH_ADULT_FISHING, PROGRESSIVE_SCALE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_LAB_DIVE] = ItemLocation::Base (RC_LH_LAB_DIVE, 0x38, "LH Lab Dive", LH_LAB_DIVE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(24), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_LAB_DIVE] = ItemLocation::Base (RC_LH_LAB_DIVE, 0x38, "LH Lab Dive", LH_LAB_DIVE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(16), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_TRADE_FROG] = ItemLocation::Base (RC_LH_TRADE_FROG, 0x38, "LH Lab Trade Eyeball Frog", LH_TRADE_FROG, EYEDROPS, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_TRADE_FROG] = ItemLocation::Base (RC_LH_TRADE_FROG, 0x38, "LH Lab Trade Eyeball Frog", LH_TRADE_FROG, EYEDROPS, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_UNDERWATER_ITEM] = ItemLocation::Base (RC_LH_UNDERWATER_ITEM, 0x57, "LH Underwater Item", LH_UNDERWATER_ITEM, RUTOS_LETTER, {}, SpoilerCollectionCheck::EventChkInf(0x31), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_UNDERWATER_ITEM] = ItemLocation::Base (RC_LH_UNDERWATER_ITEM, 0x57, "LH Underwater Item", LH_UNDERWATER_ITEM, RUTOS_LETTER, {}, SpoilerCollectionCheck::EventChkInf(0x31), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
locationTable[LH_SUN] = ItemLocation::Base (RC_LH_SUN, 0x57, "LH Sun", LH_SUN, FIRE_ARROWS, {}, SpoilerCollectionCheck::Chest(0x57, 0x1F), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_SUN] = ItemLocation::Base (RC_LH_SUN, 0x57, "LH Sun", LH_SUN, FIRE_ARROWS, {}, SpoilerCollectionCheck::Chest(0x57, 0x1F), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
@ -73,7 +73,7 @@ void LocationTable_Init() {
//Gerudo Fortress //Gerudo Fortress
locationTable[GF_CHEST] = ItemLocation::Chest (RC_GF_CHEST, 0x5D, 0x00, "GF Chest", GF_CHEST, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_CHEST] = ItemLocation::Chest (RC_GF_CHEST, 0x5D, 0x00, "GF Chest", GF_CHEST, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_HBA_1000_POINTS] = ItemLocation::Base (RC_GF_HBA_1000_POINTS, 0x5D, "GF HBA 1000 Points", GF_HBA_1000_POINTS, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x08), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_HBA_1000_POINTS] = ItemLocation::Base (RC_GF_HBA_1000_POINTS, 0x5D, "GF HBA 1000 Points", GF_HBA_1000_POINTS, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x08), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_HBA_1500_POINTS] = ItemLocation::Base (RC_GF_HBA_1500_POINTS, 0x5D, "GF HBA 1500 Points", GF_HBA_1500_POINTS, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::ItemGetInf(7), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_HBA_1500_POINTS] = ItemLocation::Base (RC_GF_HBA_1500_POINTS, 0x5D, "GF HBA 1500 Points", GF_HBA_1500_POINTS, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::ItemGetInf(15), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_GERUDO_MEMBERSHIP_CARD] = ItemLocation::Base (RC_GF_GERUDO_MEMBERSHIP_CARD, 0x0C, "GF Gerudo Membership Card", GF_GERUDO_MEMBERSHIP_CARD, GERUDO_MEMBERSHIP_CARD, {}, SpoilerCollectionCheck::GerudoToken(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_GERUDO_MEMBERSHIP_CARD] = ItemLocation::Base (RC_GF_GERUDO_MEMBERSHIP_CARD, 0x0C, "GF Gerudo Membership Card", GF_GERUDO_MEMBERSHIP_CARD, GERUDO_MEMBERSHIP_CARD, {}, SpoilerCollectionCheck::GerudoToken(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_NORTH_F1_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F1_CARPENTER, 0x0C, 0x0C, "GF North F1 Carpenter", GF_NORTH_F1_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_NORTH_F1_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F1_CARPENTER, 0x0C, 0x0C, "GF North F1 Carpenter", GF_NORTH_F1_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[GF_NORTH_F2_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F2_CARPENTER, 0x0C, 0x0A, "GF North F2 Carpenter", GF_NORTH_F2_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GF_NORTH_F2_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F2_CARPENTER, 0x0C, 0x0A, "GF North F2 Carpenter", GF_NORTH_F2_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
@ -90,12 +90,12 @@ void LocationTable_Init() {
locationTable[COLOSSUS_DEKU_SCRUB_GROTTO_FRONT] = ItemLocation::GrottoScrub(RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, 0xFD, "Colossus Deku Scrub Grotto Front", COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, BUY_GREEN_POTION, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[COLOSSUS_DEKU_SCRUB_GROTTO_FRONT] = ItemLocation::GrottoScrub(RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, 0xFD, "Colossus Deku Scrub Grotto Front", COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, BUY_GREEN_POTION, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
//Market //Market
locationTable[MARKET_TREASURE_CHEST_GAME_REWARD] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_REWARD, 0x10, "MK Treasure Chest Game Reward", MARKET_TREASURE_CHEST_GAME_REWARD, TREASURE_GAME_HEART, {}, SpoilerCollectionCheck::ItemGetInf(19), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_TREASURE_CHEST_GAME_REWARD] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_REWARD, 0x10, "MK Treasure Chest Game Reward", MARKET_TREASURE_CHEST_GAME_REWARD, TREASURE_GAME_HEART, {}, SpoilerCollectionCheck::ItemGetInf(27), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_BOMBCHU_BOWLING_FIRST_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, 0x4B, "MK Bombchu Bowling First Prize", MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, PROGRESSIVE_BOMB_BAG, {}, SpoilerCollectionCheck::ItemGetInf(25), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_BOMBCHU_BOWLING_FIRST_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, 0x4B, "MK Bombchu Bowling First Prize", MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, PROGRESSIVE_BOMB_BAG, {}, SpoilerCollectionCheck::ItemGetInf(17), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_BOMBCHU_BOWLING_SECOND_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, 0x4B, "MK Bombchu Bowling Second Prize", MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(26), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_BOMBCHU_BOWLING_SECOND_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, 0x4B, "MK Bombchu Bowling Second Prize", MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(18), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_BOMBCHU_BOWLING_BOMBCHUS] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_BOMBCHUS, 0x4B, "MK Bombchu Bowling Bombchus", NONE, BOMBCHU_DROP, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_BOMBCHU_BOWLING_BOMBCHUS] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_BOMBCHUS, 0x4B, "MK Bombchu Bowling Bombchus", NONE, BOMBCHU_DROP, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_LOST_DOG] = ItemLocation::Base (RC_MARKET_LOST_DOG, 0x35, "MK Lost Dog", MARKET_LOST_DOG, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x09), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_LOST_DOG] = ItemLocation::Base (RC_MARKET_LOST_DOG, 0x35, "MK Lost Dog", MARKET_LOST_DOG, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x09), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_MARKET_SHOOTING_GALLERY_REWARD, 0x42, "MK Shooting Gallery", MARKET_SHOOTING_GALLERY_REWARD, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(5), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_MARKET_SHOOTING_GALLERY_REWARD, 0x42, "MK Shooting Gallery", MARKET_SHOOTING_GALLERY_REWARD, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(13), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_10_BIG_POES] = ItemLocation::Base (RC_MARKET_10_BIG_POES, 0x4D, "MK 10 Big Poes", MARKET_10_BIG_POES, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_10_BIG_POES] = ItemLocation::Base (RC_MARKET_10_BIG_POES, 0x4D, "MK 10 Big Poes", MARKET_10_BIG_POES, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_1] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_1, 0x10, 0x01, "MK Chest Game First Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_1, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_1] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_1, 0x10, 0x01, "MK Chest Game First Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_1, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_2] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_2, 0x10, 0x03, "MK Chest Game Second Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_2, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_2] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_2, 0x10, 0x03, "MK Chest Game Second Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_2, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
@ -115,14 +115,14 @@ void LocationTable_Init() {
locationTable[KAK_30_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_30_GOLD_SKULLTULA_REWARD, 0x50, "Kak 30 Gold Skulltula Reward", KAK_30_GOLD_SKULLTULA_REWARD, PROGRESSIVE_WALLET, {}, SpoilerCollectionCheck::EventChkInf(0xDC), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_30_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_30_GOLD_SKULLTULA_REWARD, 0x50, "Kak 30 Gold Skulltula Reward", KAK_30_GOLD_SKULLTULA_REWARD, PROGRESSIVE_WALLET, {}, SpoilerCollectionCheck::EventChkInf(0xDC), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_40_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_40_GOLD_SKULLTULA_REWARD, 0x50, "Kak 40 Gold Skulltula Reward", KAK_40_GOLD_SKULLTULA_REWARD, BOMBCHU_10, {}, SpoilerCollectionCheck::EventChkInf(0xDD), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_40_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_40_GOLD_SKULLTULA_REWARD, 0x50, "Kak 40 Gold Skulltula Reward", KAK_40_GOLD_SKULLTULA_REWARD, BOMBCHU_10, {}, SpoilerCollectionCheck::EventChkInf(0xDD), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_50_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_50_GOLD_SKULLTULA_REWARD, 0x50, "Kak 50 Gold Skulltula Reward", KAK_50_GOLD_SKULLTULA_REWARD, PIECE_OF_HEART, {}, SpoilerCollectionCheck::EventChkInf(0xDE), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_50_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_50_GOLD_SKULLTULA_REWARD, 0x50, "Kak 50 Gold Skulltula Reward", KAK_50_GOLD_SKULLTULA_REWARD, PIECE_OF_HEART, {}, SpoilerCollectionCheck::EventChkInf(0xDE), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_100_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_100_GOLD_SKULLTULA_REWARD, 0x50, "Kak 100 Gold Skulltula Reward", KAK_100_GOLD_SKULLTULA_REWARD, HUGE_RUPEE, {}, SpoilerCollectionCheck::EventChkInf(0xDF), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_100_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_100_GOLD_SKULLTULA_REWARD, 0x50, "Kak 100 Gold Skulltula Reward", KAK_100_GOLD_SKULLTULA_REWARD, HUGE_RUPEE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(21), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(56), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(48), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cMerchant}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cMerchant}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {}, SpoilerCollectionCheck::ItemGetInf(36), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, POCKET_EGG, {}, SpoilerCollectionCheck::ItemGetInf(44), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::ItemGetInf(4), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::ItemGetInf(12), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(38), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(46), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_IMPAS_HOUSE_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_IMPAS_HOUSE_FREESTANDING_POH, 0x37, 0x01, "Kak Impas House Freestanding PoH", KAK_IMPAS_HOUSE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_IMPAS_HOUSE_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_IMPAS_HOUSE_FREESTANDING_POH, 0x37, 0x01, "Kak Impas House Freestanding PoH", KAK_IMPAS_HOUSE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_WINDMILL_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_WINDMILL_FREESTANDING_POH, 0x48, 0x01, "Kak Windmill Freestanding PoH", KAK_WINDMILL_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_WINDMILL_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_WINDMILL_FREESTANDING_POH, 0x48, 0x01, "Kak Windmill Freestanding PoH", KAK_WINDMILL_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
@ -133,7 +133,7 @@ void LocationTable_Init() {
locationTable[GRAVEYARD_HOOKSHOT_CHEST] = ItemLocation::Chest (RC_GRAVEYARD_HOOKSHOT_CHEST, 0x48, 0x00, "GY Hookshot Chest", GRAVEYARD_HOOKSHOT_CHEST, PROGRESSIVE_HOOKSHOT, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_HOOKSHOT_CHEST] = ItemLocation::Chest (RC_GRAVEYARD_HOOKSHOT_CHEST, 0x48, 0x00, "GY Hookshot Chest", GRAVEYARD_HOOKSHOT_CHEST, PROGRESSIVE_HOOKSHOT, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[GRAVEYARD_DAMPE_RACE_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, 0x48, 0x07, "GY Dampe Race Freestanding PoH", GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_DAMPE_RACE_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, 0x48, 0x07, "GY Dampe Race Freestanding PoH", GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[GRAVEYARD_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_FREESTANDING_POH, 0x53, 0x04, "GY Freestanding PoH", GRAVEYARD_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_FREESTANDING_POH, 0x53, 0x04, "GY Freestanding PoH", GRAVEYARD_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {}, SpoilerCollectionCheck::Gravedigger(0x53, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {}, SpoilerCollectionCheck::Gravedigger(0x53, 0x19), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
//Death Mountain //Death Mountain
locationTable[DMT_CHEST] = ItemLocation::Chest (RC_DMT_CHEST, 0x60, 0x01, "DMT Chest", DMT_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); locationTable[DMT_CHEST] = ItemLocation::Chest (RC_DMT_CHEST, 0x60, 0x01, "DMT Chest", DMT_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN);
@ -191,7 +191,7 @@ void LocationTable_Init() {
locationTable[ZF_BOTTOM_FREESTANDING_POH] = ItemLocation::Collectable(RC_ZF_BOTTOM_FREESTANDING_POH, 0x59, 0x14, "ZF Bottom Freestanding PoH", ZF_BOTTOM_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); locationTable[ZF_BOTTOM_FREESTANDING_POH] = ItemLocation::Collectable(RC_ZF_BOTTOM_FREESTANDING_POH, 0x59, 0x14, "ZF Bottom Freestanding PoH", ZF_BOTTOM_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN);
//Lon Lon Ranch //Lon Lon Ranch
locationTable[LLR_TALONS_CHICKENS] = ItemLocation::Base (RC_LLR_TALONS_CHICKENS, 0x4C, "LLR Talons Chickens", LLR_TALONS_CHICKENS, BOTTLE_WITH_MILK, {}, SpoilerCollectionCheck::ItemGetInf(10), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_TALONS_CHICKENS] = ItemLocation::Base (RC_LLR_TALONS_CHICKENS, 0x4C, "LLR Talons Chickens", LLR_TALONS_CHICKENS, BOTTLE_WITH_MILK, {}, SpoilerCollectionCheck::ItemGetInf(2), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
locationTable[LLR_FREESTANDING_POH] = ItemLocation::Collectable(RC_LLR_FREESTANDING_POH, 0x4C, 0x01, "LLR Freestanding PoH", LLR_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_FREESTANDING_POH] = ItemLocation::Collectable(RC_LLR_FREESTANDING_POH, 0x4C, 0x01, "LLR Freestanding PoH", LLR_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
locationTable[LLR_DEKU_SCRUB_GROTTO_LEFT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_LEFT, 0xFC, "LLR Deku Scrub Grotto Left", LLR_DEKU_SCRUB_GROTTO_LEFT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_DEKU_SCRUB_GROTTO_LEFT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_LEFT, 0xFC, "LLR Deku Scrub Grotto Left", LLR_DEKU_SCRUB_GROTTO_LEFT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
locationTable[LLR_DEKU_SCRUB_GROTTO_RIGHT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_RIGHT, 0xFC, "LLR Deku Scrub Grotto Right", LLR_DEKU_SCRUB_GROTTO_RIGHT, BUY_BOMBS_535, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); locationTable[LLR_DEKU_SCRUB_GROTTO_RIGHT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_RIGHT, 0xFC, "LLR Deku Scrub Grotto Right", LLR_DEKU_SCRUB_GROTTO_RIGHT, BUY_BOMBS_535, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
@ -492,7 +492,7 @@ void LocationTable_Init() {
locationTable[GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_THIRD_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_THIRD_CHEST, 0x0B, 0x09, "Gerudo Training Grounds MQ Maze Path Third Chest", GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_THIRD_CHEST, TREASURE_GAME_GREEN_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND); locationTable[GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_THIRD_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_THIRD_CHEST, 0x0B, 0x09, "Gerudo Training Grounds MQ Maze Path Third Chest", GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_THIRD_CHEST, TREASURE_GAME_GREEN_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND);
locationTable[GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_SECOND_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_SECOND_CHEST, 0x0B, 0x0A, "Gerudo Training Grounds MQ Maze Path Second Chest", GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_SECOND_CHEST, RED_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND); locationTable[GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_SECOND_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_SECOND_CHEST, 0x0B, 0x0A, "Gerudo Training Grounds MQ Maze Path Second Chest", GERUDO_TRAINING_GROUNDS_MQ_MAZE_PATH_SECOND_CHEST, RED_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND);
locationTable[GERUDO_TRAINING_GROUNDS_MQ_HIDDEN_CEILING_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_HIDDEN_CEILING_CHEST, 0x0B, 0x0B, "Gerudo Training Grounds MQ Hidden Ceiling Chest", GERUDO_TRAINING_GROUNDS_MQ_HIDDEN_CEILING_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND); locationTable[GERUDO_TRAINING_GROUNDS_MQ_HIDDEN_CEILING_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_HIDDEN_CEILING_CHEST, 0x0B, 0x0B, "Gerudo Training Grounds MQ Hidden Ceiling Chest", GERUDO_TRAINING_GROUNDS_MQ_HIDDEN_CEILING_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND);
locationTable[GERUDO_TRAINING_GROUNDS_MQ_UNDERWATER_SILVER_RUPEE_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_UNDERWATER_SILVER_RUPEE_CHEST, 0x0B, 0x0D, "Gerudo Training Grounds MQ Underwater Silver Rupee Chest",GERUDO_TRAINING_GROUNDS_MQ_UNDERWATER_SILVER_RUPEE_CHEST, TREASURE_GAME_GREEN_RUPEE, {Category::cVanillaSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND); locationTable[GERUDO_TRAINING_GROUNDS_MQ_UNDERWATER_SILVER_RUPEE_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_UNDERWATER_SILVER_RUPEE_CHEST, 0x0B, 0x0D, "Gerudo Training Grounds MQ Underwater Silver Rupee Chest",GERUDO_TRAINING_GROUNDS_MQ_UNDERWATER_SILVER_RUPEE_CHEST, GERUDO_TRAINING_GROUNDS_SMALL_KEY, {Category::cVanillaSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND);
locationTable[GERUDO_TRAINING_GROUNDS_MQ_HEAVY_BLOCK_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_HEAVY_BLOCK_CHEST, 0x0B, 0x02, "Gerudo Training Grounds MQ Heavy Block Chest", GERUDO_TRAINING_GROUNDS_MQ_HEAVY_BLOCK_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND); locationTable[GERUDO_TRAINING_GROUNDS_MQ_HEAVY_BLOCK_CHEST] = ItemLocation::Chest (RC_GERUDO_TRAINING_GROUND_MQ_HEAVY_BLOCK_CHEST, 0x0B, 0x02, "Gerudo Training Grounds MQ Heavy Block Chest", GERUDO_TRAINING_GROUNDS_MQ_HEAVY_BLOCK_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_TRAINING_GROUND);
//Ganons Castle Shared //Ganons Castle Shared
@ -918,7 +918,7 @@ void LocationTable_Init() {
locationTable[DMC_UPPER_GROTTO_GOSSIP_STONE] = ItemLocation::HintStone(RC_DMC_UPPER_GROTTO_GOSSIP_STONE, "DMC Upper Grotto Gossip Stone"); locationTable[DMC_UPPER_GROTTO_GOSSIP_STONE] = ItemLocation::HintStone(RC_DMC_UPPER_GROTTO_GOSSIP_STONE, "DMC Upper Grotto Gossip Stone");
locationTable[GANONDORF_HINT] = ItemLocation::OtherHint(RC_GANONDORF_HINT, "Ganondorf Hint"); locationTable[GANONDORF_HINT] = ItemLocation::OtherHint(RC_GANONDORF_HINT, "Ganondorf Hint");
locationTable[TRIFORCE_COMPLETED] = ItemLocation::Reward (RC_TRIFORCE_COMPLETED, 0xFF, "Completed Triforce", NONE, TRIFORCE_COMPLETED, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_NO_GROUP); locationTable[TRIFORCE_COMPLETED] = ItemLocation::Reward (RC_TRIFORCE_COMPLETED, 0xFF, "Completed Triforce", NONE, NONE, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_NO_GROUP);
for (int i = NONE; i != KEY_ENUM_MAX; i++) for (int i = NONE; i != KEY_ENUM_MAX; i++)
locationLookupTable.insert(std::make_pair(locationTable[i].GetRandomizerCheck(), static_cast<Key>(i))); locationLookupTable.insert(std::make_pair(locationTable[i].GetRandomizerCheck(), static_cast<Key>(i)));

View file

@ -104,7 +104,7 @@ void AreaTable_Init_DeathMountain() {
Entrance(DEATH_MOUNTAIN_TRAIL, {[]{return true;}}), Entrance(DEATH_MOUNTAIN_TRAIL, {[]{return true;}}),
Entrance(GC_WOODS_WARP, {[]{return GCWoodsWarpOpen;}}), Entrance(GC_WOODS_WARP, {[]{return GCWoodsWarpOpen;}}),
Entrance(GC_SHOP, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && (CanBlastOrSmash || GoronBracelet || GoronCityChildFire || CanUse(BOW)));}}), Entrance(GC_SHOP, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && (CanBlastOrSmash || GoronBracelet || GoronCityChildFire || CanUse(BOW)));}}),
Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || GCDaruniasDoorOpenChild;}}), Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && GCDaruniasDoorOpenChild);}}),
Entrance(GC_GROTTO_PLATFORM, {[]{return IsAdult && ((CanPlay(SongOfTime) && ((EffectiveHealth > 2) || CanUse(GORON_TUNIC) || CanUse(LONGSHOT) || CanUse(NAYRUS_LOVE))) || (EffectiveHealth > 1 && CanUse(GORON_TUNIC) && CanUse(HOOKSHOT)) || (CanUse(NAYRUS_LOVE) && CanUse(HOOKSHOT)) || (EffectiveHealth > 2 && CanUse(HOOKSHOT) && LogicGoronCityGrotto));}}), Entrance(GC_GROTTO_PLATFORM, {[]{return IsAdult && ((CanPlay(SongOfTime) && ((EffectiveHealth > 2) || CanUse(GORON_TUNIC) || CanUse(LONGSHOT) || CanUse(NAYRUS_LOVE))) || (EffectiveHealth > 1 && CanUse(GORON_TUNIC) && CanUse(HOOKSHOT)) || (CanUse(NAYRUS_LOVE) && CanUse(HOOKSHOT)) || (EffectiveHealth > 2 && CanUse(HOOKSHOT) && LogicGoronCityGrotto));}}),
}); });

View file

@ -22,6 +22,14 @@ std::vector<std::string> presetEntries;
Option* currentSetting; Option* currentSetting;
} // namespace } // namespace
static void RestoreOverrides() {
if (Settings::Logic.Is(LOGIC_VANILLA)) {
for (auto overridePair : Settings::vanillaLogicOverrides) {
overridePair.first->RestoreDelayedOption();
}
}
}
std::string GenerateRandomizer(std::unordered_map<RandomizerSettingKey, uint8_t> cvarSettings, std::set<RandomizerCheck> excludedLocations, std::set<RandomizerTrick> enabledTricks, std::string GenerateRandomizer(std::unordered_map<RandomizerSettingKey, uint8_t> cvarSettings, std::set<RandomizerCheck> excludedLocations, std::set<RandomizerTrick> enabledTricks,
std::string seedString) { std::string seedString) {
@ -52,20 +60,17 @@ std::string GenerateRandomizer(std::unordered_map<RandomizerSettingKey, uint8_t>
printf("\n\nFailed to generate after 5 tries.\nPress B to go back to the menu.\nA different seed might be " printf("\n\nFailed to generate after 5 tries.\nPress B to go back to the menu.\nA different seed might be "
"successful."); "successful.");
SPDLOG_DEBUG("\nRANDOMIZATION FAILED COMPLETELY. PLZ FIX\n"); SPDLOG_DEBUG("\nRANDOMIZATION FAILED COMPLETELY. PLZ FIX\n");
RestoreOverrides();
return ""; return "";
} else { } else {
printf("\n\nError %d with fill.\nPress Select to exit or B to go back to the menu.\n", ret); printf("\n\nError %d with fill.\nPress Select to exit or B to go back to the menu.\n", ret);
RestoreOverrides();
return ""; return "";
} }
} }
// Restore settings that were set to a specific value for vanilla logic RestoreOverrides();
if (Settings::Logic.Is(LOGIC_VANILLA)) {
for (Option* setting : Settings::vanillaLogicDefaults) {
setting->RestoreDelayedOption();
}
Settings::Keysanity.RestoreDelayedOption();
}
std::ostringstream fileNameStream; std::ostringstream fileNameStream;
for (int i = 0; i < Settings::hashIconIndexes.size(); i++) { for (int i = 0; i < Settings::hashIconIndexes.size(); i++) {
if (i) { if (i) {

View file

@ -1,5 +1,6 @@
#include "playthrough.hpp" #include "playthrough.hpp"
#include <libultraship/libultraship.h>
#include <boost_custom/container_hash/hash_32.hpp> #include <boost_custom/container_hash/hash_32.hpp>
#include "custom_messages.hpp" #include "custom_messages.hpp"
#include "fill.hpp" #include "fill.hpp"
@ -8,6 +9,7 @@
#include "random.hpp" #include "random.hpp"
#include "spoiler_log.hpp" #include "spoiler_log.hpp"
#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "variables.h"
namespace Playthrough { namespace Playthrough {
@ -39,6 +41,10 @@ int Playthrough_Init(uint32_t seed, std::unordered_map<RandomizerSettingKey, uin
} }
} }
if (CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
settingsStr += (char*)gBuildVersion;
}
uint32_t finalHash = boost::hash_32<std::string>{}(std::to_string(Settings::seed) + settingsStr); uint32_t finalHash = boost::hash_32<std::string>{}(std::to_string(Settings::seed) + settingsStr);
Random_Init(finalHash); Random_Init(finalHash);
Settings::hash = std::to_string(finalHash); Settings::hash = std::to_string(finalHash);

View file

@ -2060,21 +2060,65 @@ namespace Settings {
} }
//Options that should be saved, set to default, then restored after finishing when vanilla logic is enabled //Options that should be saved, set to default, then restored after finishing when vanilla logic is enabled
std::vector<Option *> vanillaLogicDefaults = { std::vector<std::pair<Option*, uint8_t>> vanillaLogicOverrides = {
&LinksPocketItem, { &OpenForest, OPENFOREST_CLOSED },
&ShuffleRewards, { &OpenKakariko, OPENKAKARIKO_CLOSED },
&ShuffleSongs, { &OpenDoorOfTime, OPENDOOROFTIME_CLOSED },
&Shopsanity, { &ZorasFountain, ZORASFOUNTAIN_NORMAL },
&ShopsanityPrices, { &GerudoFortress, GERUDOFORTRESS_NORMAL },
&ShopsanityPricesAffordable, { &Bridge, RAINBOWBRIDGE_VANILLA },
&Scrubsanity, { &RandomGanonsTrials, OFF },
&ShuffleCows, { &GanonsTrialsCount, 6 },
&ShuffleMagicBeans,
&ShuffleMerchants, { &StartingAge, AGE_CHILD },
&ShuffleFrogSongRupees, { &TriforceHunt, TRIFORCE_HUNT_OFF },
&ShuffleAdultTradeQuest,
&Shuffle100GSReward, { &ShuffleRewards, REWARDSHUFFLE_END_OF_DUNGEON },
&GossipStoneHints, { &LinksPocketItem, LINKSPOCKETITEM_DUNGEON_REWARD },
{ &ShuffleSongs, SONGSHUFFLE_SONG_LOCATIONS },
{ &Shopsanity, SHOPSANITY_OFF },
{ &Tokensanity, TOKENSANITY_OFF },
{ &Scrubsanity, SCRUBSANITY_OFF },
{ &ShuffleCows, OFF },
{ &ShuffleKokiriSword, OFF },
{ &ShuffleMasterSword, OFF },
{ &ShuffleOcarinas, OFF },
{ &ShuffleWeirdEgg, OFF },
{ &ShuffleGerudoToken, OFF },
{ &ShuffleMagicBeans, OFF },
{ &ShuffleMerchants, SHUFFLEMERCHANTS_OFF },
{ &ShuffleFrogSongRupees, SHUFFLEFROGSONGRUPEES_OFF },
{ &ShuffleAdultTradeQuest, SHUFFLEADULTTRADEQUEST_OFF },
{ &ShuffleChestMinigame, SHUFFLECHESTMINIGAME_OFF },
{ &Shuffle100GSReward, OFF },
{ &MapsAndCompasses, MAPSANDCOMPASSES_VANILLA },
{ &Keysanity, KEYSANITY_ANY_DUNGEON }, // Set small keys to any dungeon so FiT basement door will be locked
{ &GerudoKeys, GERUDOKEYS_VANILLA },
{ &BossKeysanity, BOSSKEYSANITY_VANILLA },
{ &GanonsBossKey, GANONSBOSSKEY_VANILLA },
{ &KeyRings, KEYRINGS_OFF },
{ &StartingOcarina, OFF },
{ &SkipChildStealth, DONT_SKIP },
{ &SkipTowerEscape, DONT_SKIP },
{ &SkipEponaRace, DONT_SKIP },
{ &GossipStoneHints, HINTS_NO_HINTS },
{ &AltarHintText, HINTS_NO_HINTS },
{ &LightArrowHintText, HINTS_NO_HINTS },
{ &DampeHintText, HINTS_NO_HINTS },
{ &GregHintText, HINTS_NO_HINTS },
{ &SariaHintText, HINTS_NO_HINTS },
{ &FrogsHintText, HINTS_NO_HINTS },
{ &WarpSongHints, HINTS_NO_HINTS },
{ &Kak10GSHintText, HINTS_NO_HINTS },
{ &Kak20GSHintText, HINTS_NO_HINTS },
{ &Kak30GSHintText, HINTS_NO_HINTS },
{ &Kak40GSHintText, HINTS_NO_HINTS },
{ &Kak50GSHintText, HINTS_NO_HINTS },
{ &ScrubHintText, HINTS_NO_HINTS },
}; };
// Randomizes all settings in a category if chosen // Randomizes all settings in a category if chosen
@ -2207,6 +2251,9 @@ namespace Settings {
case RO_LOGIC_NO_LOGIC: case RO_LOGIC_NO_LOGIC:
Logic.SetSelectedIndex(2); Logic.SetSelectedIndex(2);
break; break;
case RO_LOGIC_VANILLA:
Logic.SetSelectedIndex(3);
break;
} }
LocationsReachable.SetSelectedIndex(cvarSettings[RSK_ALL_LOCATIONS_REACHABLE]); LocationsReachable.SetSelectedIndex(cvarSettings[RSK_ALL_LOCATIONS_REACHABLE]);
@ -2428,6 +2475,15 @@ namespace Settings {
// RANDOTODO implement chest shuffle with keysanity // RANDOTODO implement chest shuffle with keysanity
// ShuffleChestMinigame.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_CHEST_MINIGAME]); // ShuffleChestMinigame.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_CHEST_MINIGAME]);
if (Logic.Is(LOGIC_VANILLA)) {
LACSCondition = LACSCONDITION_VANILLA;
skipChildZelda = false;
for (auto overridePair : vanillaLogicOverrides) {
overridePair.first->SetDelayedOption();
overridePair.first->SetSelectedIndex(overridePair.second);
}
}
RandomizeAllSettings(true); //now select any random options instead of just hiding them RandomizeAllSettings(true); //now select any random options instead of just hiding them
//shuffle the dungeons and then set MQ for as many as necessary //shuffle the dungeons and then set MQ for as many as necessary
@ -2594,16 +2650,6 @@ namespace Settings {
} else { } else {
LACSCondition = LACSCONDITION_VANILLA; LACSCondition = LACSCONDITION_VANILLA;
} }
//If vanilla logic, we want to set all settings which unnecessarily modify vanilla behavior to off
if (Logic.Is(LOGIC_VANILLA)) {
for (Option* setting : vanillaLogicDefaults) {
setting->SetDelayedOption();
setting->SetSelectedIndex(0);
}
Keysanity.SetDelayedOption();
Keysanity.SetSelectedIndex(3); //Set small keys to any dungeon so FiT basement door will be locked
}
} }
//If this is an option menu, return the options //If this is an option menu, return the options

View file

@ -188,6 +188,11 @@ typedef enum {
SHUFFLEMERCHANTS_HINTS, SHUFFLEMERCHANTS_HINTS,
} ShuffleMerchantsSetting; } ShuffleMerchantsSetting;
typedef enum {
SHUFFLEFROGSONGRUPEES_OFF,
SHUFFLEFROGSONGRUPEES_ON,
} ShuffleFrogSongRupeesSetting;
typedef enum { typedef enum {
SHUFFLEADULTTRADEQUEST_OFF, SHUFFLEADULTTRADEQUEST_OFF,
SHUFFLEADULTTRADEQUEST_ON, SHUFFLEADULTTRADEQUEST_ON,
@ -1173,5 +1178,5 @@ void UpdateSettings(std::unordered_map<RandomizerSettingKey, uint8_t> cvarSettin
extern std::vector<Menu *> mainMenu; extern std::vector<Menu *> mainMenu;
extern std::vector<Option *> vanillaLogicDefaults; extern std::vector<std::pair<Option*, uint8_t>> vanillaLogicOverrides;
} }

View file

@ -214,7 +214,7 @@ extern "C" void Randomizer_DrawTriforcePiece(PlayState* play, GetItemEntry getIt
Gfx_SetupDL_25Xlu(play->state.gfxCtx); Gfx_SetupDL_25Xlu(play->state.gfxCtx);
uint16_t current = gSaveContext.triforcePiecesCollected; uint8_t current = gSaveContext.triforcePiecesCollected;
Matrix_Scale(0.035f, 0.035f, 0.035f, MTXMODE_APPLY); Matrix_Scale(0.035f, 0.035f, 0.035f, MTXMODE_APPLY);
@ -238,8 +238,8 @@ extern "C" void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry get
Gfx_SetupDL_25Xlu(play->state.gfxCtx); Gfx_SetupDL_25Xlu(play->state.gfxCtx);
uint16_t current = gSaveContext.triforcePiecesCollected; uint8_t current = gSaveContext.triforcePiecesCollected;
uint16_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED); uint8_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED);
Matrix_Scale(triforcePieceScale, triforcePieceScale, triforcePieceScale, MTXMODE_APPLY); Matrix_Scale(triforcePieceScale, triforcePieceScale, triforcePieceScale, MTXMODE_APPLY);

View file

@ -12,6 +12,10 @@
#include "3drando/rando_main.hpp" #include "3drando/rando_main.hpp"
#include "3drando/random.hpp" #include "3drando/random.hpp"
#include "../../UIWidgets.hpp" #include "../../UIWidgets.hpp"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h>
#include <ImGui/imgui_internal.h> #include <ImGui/imgui_internal.h>
#include "../custom-message/CustomMessageTypes.h" #include "../custom-message/CustomMessageTypes.h"
#include "../item-tables/ItemTableManager.h" #include "../item-tables/ItemTableManager.h"
@ -64,7 +68,7 @@ const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi";
const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap"; const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap";
const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints"; const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints";
static const char* englishRupeeNames[170] = { static const char* englishRupeeNames[171] = {
"[P]", "Bad RNG Rolls", "Bananas", "Beanbean Coins", "Beans", "[P]", "Bad RNG Rolls", "Bananas", "Beanbean Coins", "Beans",
"Beli", "Bells", "Berries", "Bison Dollars", "Bitcoin", "Beli", "Bells", "Berries", "Bison Dollars", "Bitcoin",
"Blue Essence", "Bolts", "Bones", "Boondollars", "Bottle Caps", "Blue Essence", "Bolts", "Bones", "Boondollars", "Bottle Caps",
@ -77,28 +81,29 @@ static const char* englishRupeeNames[170] = {
"Dollarydoos", "Dosh", "Doubloons", "Dwarfbucks", "Emeralds", "Dollarydoos", "Dosh", "Doubloons", "Dwarfbucks", "Emeralds",
"Energon", "Eris", "Ether", "Euro", "Experience", "Energon", "Eris", "Ether", "Euro", "Experience",
"Extinction Points", "Floopies", "Flurbos", "FPS", "Friends", "Extinction Points", "Floopies", "Flurbos", "FPS", "Friends",
"Frog Coins", "Gald", "Gekz", "Gems", "Gil", "Frog Coins", "Gald", "Gekz", "Gems", "Geo",
"Glimmer", "Glitches", "Gold", "Gold Dragons", "Goober Dollars", "Gil", "Glimmer", "Glitches", "Gold", "Gold Dragons",
"Green Herbs", "Greg Siblings", "Gummybears", "Hell", "Hylian Loaches", "Goober Dollars", "Green Herbs", "Greg Siblings", "Gummybears", "Hell",
"Ice Traps", "ISK", "Jiggies", "KF7 Ammo", "Kinstones", "Hylian Loaches", "Ice Traps", "ISK", "Jiggies", "KF7 Ammo",
"Kremcoins", "Kroner", "Leaves ", "Lemmings", "Lien", "Kinstones", "Kremcoins", "Kroner", "Leaves ", "Lemmings",
"Lira", "Lumber", "Lungmen Dollars", "Macca", "Mana", "Lien", "Lira", "Lumber", "Lungmen Dollars", "Macca",
"Mann Co. Keys", "Meat", "Meat Stacks", "Medaparts", "Meseta", "Mana", "Mann Co. Keys", "Meat", "Meat Stacks", "Medaparts",
"Mesetas", "Minerals", "Monopoly Money", "Moons", "Mora", "Meseta", "Mesetas", "Minerals", "Monopoly Money", "Moons",
"Mumbo Tokens", "Munny", "Mushrooms", "Mysteries", "Neopoints", "Mora", "Mumbo Tokens", "Munny", "Mushrooms", "Mysteries",
"Notes", "Nuyen", "Orbs", "Pix", "Pixels", "Neopoints", "Notes", "Nuyen", "Orbs", "Pix",
"Platinum", "Pokédollars", "Pokémon", "Poko", "Pokos", "Pixels", "Platinum", "Pokédollars", "Pokémon", "Poko",
"Potch", "Pounds", "Power Pellets", "Primogems", "Réals", "Pokos", "Potch", "Pounds", "Power Pellets", "Primogems",
"Refined Metal", "Remote Mines", "Retweets", "Rhinu", "Rings", "Réals", "Refined Metal", "Remote Mines", "Retweets", "Rhinu",
"Riot Points", "Robux", "Rubies", "Rubles", "Runite Ore", "Rings", "Riot Points", "Robux", "Rubies", "Rubles",
"Rupees", "Saint Quartz", "Septims", "Shekels", "Shillings", "Runite Ore", "Rupees", "Saint Quartz", "Septims", "Shekels",
"Silver", "Simoleons", "Smackaroos", "Social Credit", "Souls", "Shillings", "Silver", "Simoleons", "Smackaroos", "Social Credit",
"Spent Casings", "Spice", "Spondulicks", "Spoons", "Star Bits", "Souls", "Spent Casings", "Spice", "Spondulicks", "Spoons",
"Star Chips", "Stars", "Stones of Jordan", "Store Credit", "Strawbs", "Star Bits", "Star Chips", "Stars", "Stones of Jordan", "Store Credit",
"Studs", "Super Sea Snails", "Talent", "Teef", "Telecrystals", "Strawbs", "Studs", "Super Sea Snails", "Talent", "Teef",
"Tiberium", "TokKul", "Toys", "Turnips", "Upvotes", "Telecrystals", "Tiberium", "TokKul", "Toys", "Turnips",
"V-Bucks", "Vespene Gas", "Watts", "Widgets", "Woolongs", "Upvotes", "V-Bucks", "Vespene Gas", "Watts", "Widgets",
"World Dollars", "Wumpa Fruit", "Yen", "Zenny", "Zorkmids" "Woolongs", "World Dollars", "Wumpa Fruit", "Yen", "Zenny",
"Zorkmids"
}; };
static const char* germanRupeeNames[41] = { static const char* germanRupeeNames[41] = {
@ -222,9 +227,9 @@ std::unordered_map<s16, s16> getItemIdToItemId = {
}; };
std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEnum = { std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEnum = {
{ "Detailed Logic Settings:Logic", RSK_LOGIC_RULES }, { "Logic Options:Logic", RSK_LOGIC_RULES },
{ "Detailed Logic Settings:Night GSs Expect Sun's", RSK_SKULLS_SUNS_SONG }, { "Logic Options:Night GSs Expect Sun's", RSK_SKULLS_SUNS_SONG },
{ "Detailed Logic Settings:All Locations Reachable", RSK_ALL_LOCATIONS_REACHABLE }, { "Logic Options:All Locations Reachable", RSK_ALL_LOCATIONS_REACHABLE },
{ "Item Pool Settings:Item Pool", RSK_ITEM_POOL }, { "Item Pool Settings:Item Pool", RSK_ITEM_POOL },
{ "Item Pool Settings:Ice Traps", RSK_ICE_TRAPS }, { "Item Pool Settings:Ice Traps", RSK_ICE_TRAPS },
{ "Open Settings:Forest", RSK_FOREST }, { "Open Settings:Forest", RSK_FOREST },
@ -360,29 +365,12 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Shuffle Dungeon Quest:Ganon's Castle", RSK_MQ_GANONS_CASTLE }, { "Shuffle Dungeon Quest:Ganon's Castle", RSK_MQ_GANONS_CASTLE },
}; };
std::string sanitize(std::string stringValue) {
// Add backslashes.
for (auto i = stringValue.begin();;) {
auto const pos = std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
if (pos == stringValue.end()) {
break;
}
i = std::next(stringValue.insert(pos, '\\'), 2);
}
// Removes others.
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) {
return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end());
return stringValue;
}
#pragma optimize("", off) #pragma optimize("", off)
#pragma GCC push_options #pragma GCC push_options
#pragma GCC optimize ("O0") #pragma GCC optimize ("O0")
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
if (strcmp(spoilerFileName, "") != 0) { if (strcmp(spoilerFileName, "") != 0) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) { if (!spoilerFileStream) {
return false; return false;
} else { } else {
@ -496,6 +484,13 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
"Zu {{location}}?\x1B&%gOK&No%w\x02", "Zu {{location}}?\x1B&%gOK&No%w\x02",
"Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02")); "Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02"));
// Bow Shooting Gallery reminder
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW,
CustomMessage("Come back when you have your own&bow and you'll get a %rdifferent prize%w!",
"Komm wieder sobald du deinen eigenen&Bogen hast, um einen %rspeziellen Preis%w zu&erhalten!",
"J'aurai %rune autre récompense%w pour toi&lorsque tu auras ton propre arc."));
// Lake Hylia water level system
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN, CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN,
CustomMessage("Water level control system.&Keep away!", CustomMessage("Water level control system.&Keep away!",
"Wasserstand Kontrollsystem&Finger weg!", "Wasserstand Kontrollsystem&Finger weg!",
@ -659,7 +654,7 @@ void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
} }
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
return; return;
@ -692,6 +687,8 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
gSaveContext.randoSettings[index].value = RO_LOGIC_GLITCHLESS; gSaveContext.randoSettings[index].value = RO_LOGIC_GLITCHLESS;
} else if (it.value() == "No Logic") { } else if (it.value() == "No Logic") {
gSaveContext.randoSettings[index].value = RO_LOGIC_NO_LOGIC; gSaveContext.randoSettings[index].value = RO_LOGIC_NO_LOGIC;
} else if (it.value() == "Vanilla") {
gSaveContext.randoSettings[index].value = RO_LOGIC_VANILLA;
} }
break; break;
case RSK_FOREST: case RSK_FOREST:
@ -1293,7 +1290,7 @@ std::string FormatJsonHintText(std::string jsonHint) {
} }
void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) { void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
return; return;
@ -1394,7 +1391,7 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
} }
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) { void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) { if (!spoilerFileStream) {
return; return;
} }
@ -1415,7 +1412,7 @@ void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
} }
void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) { void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) { if (!spoilerFileStream) {
return; return;
} }
@ -1495,7 +1492,7 @@ int16_t Randomizer::GetVanillaMerchantPrice(RandomizerCheck check) {
} }
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) { void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
return; return;
@ -1558,7 +1555,7 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
} }
void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent) { void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) { if (!spoilerFileStream) {
return; return;
} }
@ -2547,6 +2544,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
{ RC_LH_CHILD_FISHING, RAND_INF_CHILD_FISHING }, { RC_LH_CHILD_FISHING, RAND_INF_CHILD_FISHING },
{ RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING }, { RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING },
{ RC_MARKET_10_BIG_POES, RAND_INF_10_BIG_POES }, { RC_MARKET_10_BIG_POES, RAND_INF_10_BIG_POES },
{ RC_KAK_100_GOLD_SKULLTULA_REWARD, RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD },
}; };
RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) { RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) {
@ -2715,7 +2713,7 @@ ShopItemIdentity Randomizer::IdentifyShopItem(s32 sceneNum, u8 slotIndex) {
RandomizerCheckObject rcObject = GetCheckObjectFromActor(ACTOR_EN_GIRLA, RandomizerCheckObject rcObject = GetCheckObjectFromActor(ACTOR_EN_GIRLA,
// Bazaar (SHOP1) scene is reused, so if entering from Kak use debug scene to identify // Bazaar (SHOP1) scene is reused, so if entering from Kak use debug scene to identify
(sceneNum == SCENE_BAZAAR && gSaveContext.entranceIndex == 0xB7) ? SCENE_TEST01 : sceneNum, slotIndex); (sceneNum == SCENE_BAZAAR && gSaveContext.entranceIndex == ENTR_BAZAAR_0) ? SCENE_TEST01 : sceneNum, slotIndex);
if (rcObject.rc != RC_UNKNOWN_CHECK) { if (rcObject.rc != RC_UNKNOWN_CHECK) {
shopItemIdentity.randomizerInf = rcToRandomizerInf[rcObject.rc]; shopItemIdentity.randomizerInf = rcToRandomizerInf[rcObject.rc];
@ -3069,7 +3067,7 @@ void RandomizerSettingsWindow::DrawElement() {
// Randomizer settings // Randomizer settings
// Logic Settings // Logic Settings
static const char* randoLogicRules[2] = { "Glitchless", "No logic" }; static const char* randoLogicRules[3] = { "Glitchless", "No logic", "Vanilla" };
// Open Settings // Open Settings
static const char* randoForest[3] = { "Closed", "Closed Deku", "Open" }; static const char* randoForest[3] = { "Closed", "Closed Deku", "Open" };
@ -3143,7 +3141,9 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
} }
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
DrawPresetSelector(PRESET_TYPE_RANDOMIZER); DrawPresetSelector(PRESET_TYPE_RANDOMIZER);
ImGui::EndDisabled();
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, ""); UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, "");
@ -3166,13 +3166,17 @@ void RandomizerSettingsWindow::DrawElement() {
} }
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gRandomizerDontGenerateSpoiler", 0) && gSaveContext.gameMode != GAMEMODE_FILE_SELECT);
if (ImGui::Button("Generate Randomizer")) { if (ImGui::Button("Generate Randomizer")) {
GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : ""); GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : "");
} }
ImGui::EndDisabled();
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
std::string spoilerfilepath = CVarGetString("gSpoilerLog", ""); if (!CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str()); std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
}
// RANDOTODO settings presets // RANDOTODO settings presets
// std::string presetfilepath = CVarGetString("gLoadedPreset", ""); // std::string presetfilepath = CVarGetString("gLoadedPreset", "");
@ -3180,6 +3184,8 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
ImGuiWindow* window = ImGui::GetCurrentWindow(); ImGuiWindow* window = ImGui::GetCurrentWindow();
static ImVec2 cellPadding(8.0f, 8.0f); static ImVec2 cellPadding(8.0f, 8.0f);
@ -3195,6 +3201,7 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::PopItemFlag(); ImGui::PopItemFlag();
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::BeginDisabled(CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA);
// COLUMN 1 - Area Access // COLUMN 1 - Area Access
ImGui::TableNextColumn(); ImGui::TableNextColumn();
window->DC.CurrLineTextBaseOffset = 0.0f; window->DC.CurrLineTextBaseOffset = 0.0f;
@ -3489,6 +3496,8 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
ImGui::EndDisabled();
// Master Quest Dungeons // Master Quest Dungeons
if (OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal()) { if (OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal()) {
ImGui::PushItemWidth(-FLT_MIN); ImGui::PushItemWidth(-FLT_MIN);
@ -3541,6 +3550,8 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
} }
ImGui::BeginDisabled(CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA);
// Triforce Hunt // Triforce Hunt
UIWidgets::EnhancementCheckbox("Triforce Hunt", "gRandomizeTriforceHunt"); UIWidgets::EnhancementCheckbox("Triforce Hunt", "gRandomizeTriforceHunt");
UIWidgets::InsertHelpHoverText( UIWidgets::InsertHelpHoverText(
@ -3575,6 +3586,7 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
ImGui::EndChild(); ImGui::EndChild();
ImGui::EndDisabled();
// COLUMN 3 - Shuffle Entrances // COLUMN 3 - Shuffle Entrances
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -3736,6 +3748,7 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
ImGui::BeginDisabled(CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA);
if (ImGui::BeginTabItem("Items")) { if (ImGui::BeginTabItem("Items")) {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
if (ImGui::BeginTable("tableRandoStartingInventory", 3, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { if (ImGui::BeginTable("tableRandoStartingInventory", 3, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
@ -4321,7 +4334,9 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::PopStyleVar(1); ImGui::PopStyleVar(1);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
ImGui::EndDisabled();
ImGui::BeginDisabled(CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA);
if (ImGui::BeginTabItem("Gameplay")) { if (ImGui::BeginTabItem("Gameplay")) {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
if (ImGui::BeginTable("tableRandoGameplay", 3, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { if (ImGui::BeginTable("tableRandoGameplay", 3, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
@ -4591,7 +4606,9 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::PopStyleVar(1); ImGui::PopStyleVar(1);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
ImGui::EndDisabled();
ImGui::BeginDisabled(CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA);
static bool locationsTabOpen = false; static bool locationsTabOpen = false;
if (ImGui::BeginTabItem("Locations")) { if (ImGui::BeginTabItem("Locations")) {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
@ -4710,6 +4727,7 @@ void RandomizerSettingsWindow::DrawElement() {
} else { } else {
locationsTabOpen = false; locationsTabOpen = false;
} }
ImGui::EndDisabled();
static bool tricksTabOpen = false; static bool tricksTabOpen = false;
if (ImGui::BeginTabItem("Tricks/Glitches")) { if (ImGui::BeginTabItem("Tricks/Glitches")) {
@ -4743,10 +4761,12 @@ void RandomizerSettingsWindow::DrawElement() {
"\n" "\n"
//"Glitched - Glitches may be required to beat the game. You can disable and enable glitches below.\n" //"Glitched - Glitches may be required to beat the game. You can disable and enable glitches below.\n"
//"\n" //"\n"
"No logic - Item placement is completely random. MAY BE IMPOSSIBLE TO BEAT." "No logic - Item placement is completely random. MAY BE IMPOSSIBLE TO BEAT.\n"
"\n"
"Vanilla - Places all items and dungeon rewards in their vanilla locations."
); );
UIWidgets::EnhancementCombobox("gRandomizeLogicRules", randoLogicRules, RO_LOGIC_GLITCHLESS); UIWidgets::EnhancementCombobox("gRandomizeLogicRules", randoLogicRules, RO_LOGIC_GLITCHLESS);
if (CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC) { if (CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_GLITCHLESS) {
ImGui::SameLine(); ImGui::SameLine();
UIWidgets::EnhancementCheckbox(Settings::LocationsReachable.GetName().c_str(), "gRandomizeAllLocationsReachable", false, "", UIWidgets::CheckboxGraphics::Cross, RO_GENERIC_ON); UIWidgets::EnhancementCheckbox(Settings::LocationsReachable.GetName().c_str(), "gRandomizeAllLocationsReachable", false, "", UIWidgets::CheckboxGraphics::Cross, RO_GENERIC_ON);
UIWidgets::InsertHelpHoverText( UIWidgets::InsertHelpHoverText(
@ -4757,6 +4777,10 @@ void RandomizerSettingsWindow::DrawElement() {
"will be guaranteed reachable." "will be guaranteed reachable."
); );
} }
if (CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA) {
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Heads up! This will disable all rando settings except for entrance shuffle and starter items");
}
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
@ -4770,6 +4794,8 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::EndTable(); ImGui::EndTable();
} }
ImGui::BeginDisabled(CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA);
// Tricks // Tricks
static std::unordered_map<RandomizerTrickArea, bool> areaTreeDisabled { static std::unordered_map<RandomizerTrickArea, bool> areaTreeDisabled {
{RTAREA_GENERAL, true}, {RTAREA_GENERAL, true},
@ -5142,6 +5168,7 @@ void RandomizerSettingsWindow::DrawElement() {
} }
ImGui::EndTable(); ImGui::EndTable();
} }
ImGui::EndDisabled();
ImGui::PopStyleVar(1); ImGui::PopStyleVar(1);
ImGui::EndTabItem(); ImGui::EndTabItem();
} else { } else {
@ -5163,13 +5190,14 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
window->DC.CurrLineTextBaseOffset = 0.0f; window->DC.CurrLineTextBaseOffset = 0.0f;
ImGui::BeginChild("ChildStartingEquipment", ImVec2(0, -8)); ImGui::BeginChild("ChildStartingEquipment", ImVec2(0, -8));
// Don't display this option if Dungeon Rewards are Shuffled to End of Dungeon. ImGui::BeginDisabled(
// TODO: Show this but disabled when we have options for disabled Comboboxes. CVarGetInteger("gRandomizeShuffleDungeonReward", RO_DUNGEON_REWARDS_END_OF_DUNGEON) == RO_DUNGEON_REWARDS_END_OF_DUNGEON ||
if (CVarGetInteger("gRandomizeShuffleDungeonReward", RO_DUNGEON_REWARDS_END_OF_DUNGEON) != RO_DUNGEON_REWARDS_END_OF_DUNGEON) { CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA
ImGui::Text("%s", Settings::LinksPocketItem.GetName().c_str()); );
UIWidgets::EnhancementCombobox("gRandomizeLinksPocket", randoLinksPocket, RO_LINKS_POCKET_DUNGEON_REWARD); ImGui::Text("%s", Settings::LinksPocketItem.GetName().c_str());
UIWidgets::PaddedSeparator(); UIWidgets::EnhancementCombobox("gRandomizeLinksPocket", randoLinksPocket, RO_LINKS_POCKET_DUNGEON_REWARD);
} UIWidgets::PaddedSeparator();
ImGui::EndDisabled();
UIWidgets::EnhancementCheckbox(Settings::StartingKokiriSword.GetName().c_str(), "gRandomizeStartingKokiriSword"); UIWidgets::EnhancementCheckbox(Settings::StartingKokiriSword.GetName().c_str(), "gRandomizeStartingKokiriSword");
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
@ -5184,8 +5212,10 @@ void RandomizerSettingsWindow::DrawElement() {
window->DC.CurrLineTextBaseOffset = 0.0f; window->DC.CurrLineTextBaseOffset = 0.0f;
ImGui::BeginChild("ChildStartingItems", ImVec2(0, -8)); ImGui::BeginChild("ChildStartingItems", ImVec2(0, -8));
ImGui::BeginDisabled(CVarGetInteger("gRandomizeLogicRules", RO_LOGIC_GLITCHLESS) == RO_LOGIC_VANILLA);
UIWidgets::EnhancementCheckbox(Settings::StartingOcarina.GetName().c_str(), "gRandomizeStartingOcarina"); UIWidgets::EnhancementCheckbox(Settings::StartingOcarina.GetName().c_str(), "gRandomizeStartingOcarina");
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
ImGui::EndDisabled();
UIWidgets::EnhancementCheckbox(Settings::StartingConsumables.GetName().c_str(), "gRandomizeStartingConsumables"); UIWidgets::EnhancementCheckbox(Settings::StartingConsumables.GetName().c_str(), "gRandomizeStartingConsumables");
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
UIWidgets::EnhancementSliderInt("Gold Skulltula Tokens: %d", "##RandoStartingSkulltulaToken", "gRandomizeStartingSkulltulaToken", 0, 100, "", 0); UIWidgets::EnhancementSliderInt("Gold Skulltula Tokens: %d", "##RandoStartingSkulltulaToken", "gRandomizeStartingSkulltulaToken", 0, 100, "", 0);
@ -5224,6 +5254,8 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::EndTabBar(); ImGui::EndTabBar();
} }
ImGui::EndDisabled();
if (disableEditingRandoSettings) { if (disableEditingRandoSettings) {
UIWidgets::ReEnableComponent(""); UIWidgets::ReEnableComponent("");
@ -5548,9 +5580,9 @@ void CreateTriforcePieceMessages() {
CustomMessage Randomizer::GetTriforcePieceMessage() { CustomMessage Randomizer::GetTriforcePieceMessage() {
// Item is only given after the textbox, so reflect that inside the textbox. // Item is only given after the textbox, so reflect that inside the textbox.
uint16_t current = gSaveContext.triforcePiecesCollected + 1; uint8_t current = gSaveContext.triforcePiecesCollected + 1;
uint16_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED); uint8_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED);
uint16_t remaining = required - current; uint8_t remaining = required - current;
float percentageCollected = (float)current / (float)required; float percentageCollected = (float)current / (float)required;
uint8_t messageIndex; uint8_t messageIndex;
@ -6128,30 +6160,30 @@ class ExtendedVanillaTableInvalidItemIdException: public std::exception {
void InitRandoItemTable() { void InitRandoItemTable() {
// These entries have ItemIDs from vanilla, but not GetItemIDs or entries in the old sGetItemTable // These entries have ItemIDs from vanilla, but not GetItemIDs or entries in the old sGetItemTable
static GetItemEntry extendedVanillaGetItemTable[] = { static GetItemEntry extendedVanillaGetItemTable[] = {
GET_ITEM(ITEM_MEDALLION_LIGHT, OBJECT_GI_MEDAL, GID_MEDALLION_LIGHT, 0x40, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_LIGHT_MEDALLION), GET_ITEM_CUSTOM_TABLE(ITEM_MEDALLION_LIGHT, OBJECT_GI_MEDAL, GID_MEDALLION_LIGHT, 0x40, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_LIGHT_MEDALLION),
GET_ITEM(ITEM_MEDALLION_FOREST, OBJECT_GI_MEDAL, GID_MEDALLION_FOREST, 0x3E, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_FOREST_MEDALLION), GET_ITEM_CUSTOM_TABLE(ITEM_MEDALLION_FOREST, OBJECT_GI_MEDAL, GID_MEDALLION_FOREST, 0x3E, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_FOREST_MEDALLION),
GET_ITEM(ITEM_MEDALLION_FIRE, OBJECT_GI_MEDAL, GID_MEDALLION_FIRE, 0x3C, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_FIRE_MEDALLION), GET_ITEM_CUSTOM_TABLE(ITEM_MEDALLION_FIRE, OBJECT_GI_MEDAL, GID_MEDALLION_FIRE, 0x3C, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_FIRE_MEDALLION),
GET_ITEM(ITEM_MEDALLION_WATER, OBJECT_GI_MEDAL, GID_MEDALLION_WATER, 0x3D, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_WATER_MEDALLION), GET_ITEM_CUSTOM_TABLE(ITEM_MEDALLION_WATER, OBJECT_GI_MEDAL, GID_MEDALLION_WATER, 0x3D, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_WATER_MEDALLION),
GET_ITEM(ITEM_MEDALLION_SHADOW, OBJECT_GI_MEDAL, GID_MEDALLION_SHADOW, 0x41, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SHADOW_MEDALLION), GET_ITEM_CUSTOM_TABLE(ITEM_MEDALLION_SHADOW, OBJECT_GI_MEDAL, GID_MEDALLION_SHADOW, 0x41, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_SHADOW_MEDALLION),
GET_ITEM(ITEM_MEDALLION_SPIRIT, OBJECT_GI_MEDAL, GID_MEDALLION_SPIRIT, 0x3F, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SPIRIT_MEDALLION), GET_ITEM_CUSTOM_TABLE(ITEM_MEDALLION_SPIRIT, OBJECT_GI_MEDAL, GID_MEDALLION_SPIRIT, 0x3F, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_SPIRIT_MEDALLION),
GET_ITEM(ITEM_KOKIRI_EMERALD, OBJECT_GI_JEWEL, GID_KOKIRI_EMERALD, 0x80, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_KOKIRI_EMERALD), GET_ITEM_CUSTOM_TABLE(ITEM_KOKIRI_EMERALD, OBJECT_GI_JEWEL, GID_KOKIRI_EMERALD, 0x80, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_KOKIRI_EMERALD),
GET_ITEM(ITEM_GORON_RUBY, OBJECT_GI_JEWEL, GID_GORON_RUBY, 0x81, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_GORON_RUBY), GET_ITEM_CUSTOM_TABLE(ITEM_GORON_RUBY, OBJECT_GI_JEWEL, GID_GORON_RUBY, 0x81, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_GORON_RUBY),
GET_ITEM(ITEM_ZORA_SAPPHIRE, OBJECT_GI_JEWEL, GID_ZORA_SAPPHIRE, 0x82, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_ZORA_SAPPHIRE), GET_ITEM_CUSTOM_TABLE(ITEM_ZORA_SAPPHIRE, OBJECT_GI_JEWEL, GID_ZORA_SAPPHIRE, 0x82, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_ZORA_SAPPHIRE),
GET_ITEM(ITEM_SONG_LULLABY, OBJECT_GI_MELODY, GID_SONG_ZELDA, 0xD4, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_ZELDAS_LULLABY), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_LULLABY, OBJECT_GI_MELODY, GID_SONG_ZELDA, 0xD4, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_ZELDAS_LULLABY),
GET_ITEM(ITEM_SONG_SUN, OBJECT_GI_MELODY, GID_SONG_SUN, 0xD3, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SUNS_SONG), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_SUN, OBJECT_GI_MELODY, GID_SONG_SUN, 0xD3, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_SUNS_SONG),
GET_ITEM(ITEM_SONG_EPONA, OBJECT_GI_MELODY, GID_SONG_EPONA, 0xD2, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_EPONAS_SONG), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_EPONA, OBJECT_GI_MELODY, GID_SONG_EPONA, 0xD2, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_EPONAS_SONG),
GET_ITEM(ITEM_SONG_STORMS, OBJECT_GI_MELODY, GID_SONG_STORM, 0xD6, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SONG_OF_STORMS), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_STORMS, OBJECT_GI_MELODY, GID_SONG_STORM, 0xD6, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_SONG_OF_STORMS),
GET_ITEM(ITEM_SONG_TIME, OBJECT_GI_MELODY, GID_SONG_TIME, 0xD5, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SONG_OF_TIME), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_TIME, OBJECT_GI_MELODY, GID_SONG_TIME, 0xD5, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_SONG_OF_TIME),
GET_ITEM(ITEM_SONG_SARIA, OBJECT_GI_MELODY, GID_SONG_SARIA, 0xD1, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SARIAS_SONG), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_SARIA, OBJECT_GI_MELODY, GID_SONG_SARIA, 0xD1, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_SARIAS_SONG),
GET_ITEM(ITEM_SONG_MINUET, OBJECT_GI_MELODY, GID_SONG_MINUET, 0x73, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_MINUET_OF_FOREST), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_MINUET, OBJECT_GI_MELODY, GID_SONG_MINUET, 0x73, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_MINUET_OF_FOREST),
GET_ITEM(ITEM_SONG_BOLERO, OBJECT_GI_MELODY, GID_SONG_BOLERO, 0x74, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_BOLERO_OF_FIRE), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_BOLERO, OBJECT_GI_MELODY, GID_SONG_BOLERO, 0x74, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_BOLERO_OF_FIRE),
GET_ITEM(ITEM_SONG_SERENADE, OBJECT_GI_MELODY, GID_SONG_SERENADE, 0x75, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SERENADE_OF_WATER), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_SERENADE, OBJECT_GI_MELODY, GID_SONG_SERENADE, 0x75, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_SERENADE_OF_WATER),
GET_ITEM(ITEM_SONG_NOCTURNE, OBJECT_GI_MELODY, GID_SONG_NOCTURNE, 0x77, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_NOCTURNE_OF_SHADOW), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_NOCTURNE, OBJECT_GI_MELODY, GID_SONG_NOCTURNE, 0x77, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_NOCTURNE_OF_SHADOW),
GET_ITEM(ITEM_SONG_REQUIEM, OBJECT_GI_MELODY, GID_SONG_REQUIEM, 0x76, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_REQUIEM_OF_SPIRIT), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_REQUIEM, OBJECT_GI_MELODY, GID_SONG_REQUIEM, 0x76, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_REQUIEM_OF_SPIRIT),
GET_ITEM(ITEM_SONG_PRELUDE, OBJECT_GI_MELODY, GID_SONG_PRELUDE, 0x78, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_PRELUDE_OF_LIGHT), GET_ITEM_CUSTOM_TABLE(ITEM_SONG_PRELUDE, OBJECT_GI_MELODY, GID_SONG_PRELUDE, 0x78, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, TABLE_RANDOMIZER, RG_PRELUDE_OF_LIGHT),
}; };
// These do not have ItemIDs or GetItemIDs from vanilla, so I'm using their // These do not have ItemIDs or GetItemIDs from vanilla, so I'm using their

View file

@ -9,7 +9,14 @@
// This should probably go in a less rando-specific location // This should probably go in a less rando-specific location
// but the best location will probably be in the modding engine // but the best location will probably be in the modding engine
// which doesn't exist yet. // which doesn't exist yet.
typedef enum { MOD_NONE, MOD_RANDOMIZER } ModIndex; typedef enum {
MOD_NONE,
MOD_RANDOMIZER
} ModIndex;
typedef enum {
TABLE_VANILLA = MOD_NONE,
TABLE_RANDOMIZER = MOD_RANDOMIZER
} TableIndex;
typedef struct { typedef struct {
char tex[512]; char tex[512];
@ -1767,6 +1774,7 @@ typedef enum {
RO_LOGIC_GLITCHLESS, RO_LOGIC_GLITCHLESS,
//RO_LOGIC_GLITCHED, //RO_LOGIC_GLITCHED,
RO_LOGIC_NO_LOGIC, RO_LOGIC_NO_LOGIC,
RO_LOGIC_VANILLA,
} RandoOptionLogic; } RandoOptionLogic;
// MQ Dungeons // MQ Dungeons

View file

@ -39,7 +39,7 @@ std::map<RandomizerCheck, RandomizerCheckObject> rcObjects = {
RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x01, GI_STICKS_1, "Deku Scrub Near Deku Theater Left", "LW Deku Scrub Near Deku Theater Left", false), RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x01, GI_STICKS_1, "Deku Scrub Near Deku Theater Left", "LW Deku Scrub Near Deku Theater Left", false),
RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x09, GI_STICK_UPGRADE_20, "Deku Scrub Near Bridge", "LW Deku Scrub Near Bridge", true), RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x09, GI_STICK_UPGRADE_20, "Deku Scrub Near Bridge", "LW Deku Scrub Near Bridge", true),
RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_REAR, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x03,0xF5), GI_SEEDS_30, "Deku Scrub Grotto Rear", "LW Deku Scrub Grotto Rear", false), RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_REAR, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x03,0xF5), GI_SEEDS_30, "Deku Scrub Grotto Rear", "LW Deku Scrub Grotto Rear", false),
RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_FRONT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x0A,0xF5), GI_NUT_UPGRADE_30, "Deku Scrub Grotto Front", "LW Deku Scrub Grotto Front", false), RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_FRONT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x0A,0xF5), GI_NUT_UPGRADE_30, "Deku Scrub Grotto Front", "LW Deku Scrub Grotto Front", true),
RC_OBJECT(RC_DEKU_THEATER_SKULL_MASK, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Skull Mask", "Deku Theater Skull Mask", true), RC_OBJECT(RC_DEKU_THEATER_SKULL_MASK, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Skull Mask", "Deku Theater Skull Mask", true),
RC_OBJECT(RC_DEKU_THEATER_MASK_OF_TRUTH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Mask of Truth", "Deku Theater Mask of Truth", true), RC_OBJECT(RC_DEKU_THEATER_MASK_OF_TRUTH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Mask of Truth", "Deku Theater Mask of Truth", true),
RC_OBJECT(RC_LW_GS_BEAN_PATCH_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_LOST_WOODS, ACTOR_EN_SI, SCENE_LOST_WOODS, 27905, GI_SKULL_TOKEN, "GS Bean Patch Near Bridge", "LW GS Bean Patch Near Bridge", true), RC_OBJECT(RC_LW_GS_BEAN_PATCH_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_LOST_WOODS, ACTOR_EN_SI, SCENE_LOST_WOODS, 27905, GI_SKULL_TOKEN, "GS Bean Patch Near Bridge", "LW GS Bean Patch Near Bridge", true),
@ -238,7 +238,7 @@ std::map<RandomizerCheck, RandomizerCheckObject> rcObjects = {
RC_OBJECT(RC_GRAVEYARD_HOOKSHOT_CHEST, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_BOX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 4352, GI_HOOKSHOT, "Hookshot Chest", "GY Hookshot Chest", true), RC_OBJECT(RC_GRAVEYARD_HOOKSHOT_CHEST, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_BOX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 4352, GI_HOOKSHOT, "Hookshot Chest", "GY Hookshot Chest", true),
RC_OBJECT(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_WINDMILL_AND_DAMPES_GRAVE, 1798, GI_HEART_PIECE, "Dampe Race Freestanding PoH", "GY Dampe Race Freestanding PoH", true), RC_OBJECT(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_WINDMILL_AND_DAMPES_GRAVE, 1798, GI_HEART_PIECE, "Dampe Race Freestanding PoH", "GY Dampe Race Freestanding PoH", true),
RC_OBJECT(RC_GRAVEYARD_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 1030, GI_HEART_PIECE, "Freestanding PoH", "GY Freestanding PoH", true), RC_OBJECT(RC_GRAVEYARD_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 1030, GI_HEART_PIECE, "Freestanding PoH", "GY Freestanding PoH", true),
RC_OBJECT(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 7942, GI_HEART_PIECE, "Dampe Gravedigging Tour", "GY Dampe Gravedigging Tour", true), RC_OBJECT(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 6406, GI_HEART_PIECE, "Dampe Gravedigging Tour", "GY Dampe Gravedigging Tour", true),
RC_OBJECT(RC_GRAVEYARD_GS_WALL, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 20608, GI_SKULL_TOKEN, "GS Wall", "Graveyard GS Wall", true), RC_OBJECT(RC_GRAVEYARD_GS_WALL, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 20608, GI_SKULL_TOKEN, "GS Wall", "Graveyard GS Wall", true),
RC_OBJECT(RC_GRAVEYARD_GS_BEAN_PATCH, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 28673, GI_SKULL_TOKEN, "GS Bean Patch", "Graveyard GS Bean Patch", true), RC_OBJECT(RC_GRAVEYARD_GS_BEAN_PATCH, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 28673, GI_SKULL_TOKEN, "GS Bean Patch", "Graveyard GS Bean Patch", true),
RC_OBJECT(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCVORMQ_BOTH, RCTYPE_SONG_LOCATION, RCAREA_GRAVEYARD, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, "Song from Composers Grave", "Song from Composers Grave", true), RC_OBJECT(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCVORMQ_BOTH, RCTYPE_SONG_LOCATION, RCAREA_GRAVEYARD, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, "Song from Composers Grave", "Song from Composers Grave", true),
@ -783,7 +783,7 @@ std::map<RandomizerCheckArea, std::string> rcAreaNames = {
{ RCAREA_LAKE_HYLIA, "Lake Hylia"}, { RCAREA_LAKE_HYLIA, "Lake Hylia"},
{ RCAREA_GERUDO_VALLEY, "Gerudo Valley"}, { RCAREA_GERUDO_VALLEY, "Gerudo Valley"},
{ RCAREA_GERUDO_FORTRESS, "Gerudo Fortress"}, { RCAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
{ RCAREA_WASTELAND, "Desert Wasteland"}, { RCAREA_WASTELAND, "Haunted Wasteland"},
{ RCAREA_DESERT_COLOSSUS, "Desert Colossus"}, { RCAREA_DESERT_COLOSSUS, "Desert Colossus"},
{ RCAREA_MARKET, "Hyrule Market"}, { RCAREA_MARKET, "Hyrule Market"},
{ RCAREA_HYRULE_CASTLE, "Hyrule Castle"}, { RCAREA_HYRULE_CASTLE, "Hyrule Castle"},

View file

@ -73,20 +73,12 @@ bool showLinksPocket;
bool fortressFast; bool fortressFast;
bool fortressNormal; bool fortressNormal;
bool bypassRandoCheck = true;
// persistent during gameplay // persistent during gameplay
bool initialized; bool initialized;
bool doAreaScroll; bool doAreaScroll;
bool previousShowHidden = false; bool previousShowHidden = false;
bool hideShopRightChecks = true; bool hideShopRightChecks = true;
bool alwaysShowGS = false;
bool checkCollected = false;
int checkLoops = 0;
int checkCounter = 0;
u16 savedFrames = 0;
bool messageCloseCheck = false;
bool pendingSaleCheck = false;
bool transitionCheck = false;
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 }, std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 }, { SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
@ -115,15 +107,13 @@ std::map<RandomizerCheckArea, std::vector<RandomizerCheckObject>> checksByArea;
bool areasFullyChecked[RCAREA_INVALID]; bool areasFullyChecked[RCAREA_INVALID];
u32 areasSpoiled = 0; u32 areasSpoiled = 0;
bool showVOrMQ; bool showVOrMQ;
s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)" s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)"
bool optCollapseAll; // A bool that will collapse all checks once s8 areaCheckTotals[RCAREA_INVALID];
bool optCollapseAll; // A bool that will collapse all checks once
bool optExpandAll; // A bool that will expand all checks once bool optExpandAll; // A bool that will expand all checks once
RandomizerCheck lastItemGetCheck = RC_UNKNOWN_CHECK;
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK; RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
RandomizerCheckArea previousArea = RCAREA_INVALID; RandomizerCheckArea previousArea = RCAREA_INVALID;
RandomizerCheckArea currentArea = RCAREA_INVALID; RandomizerCheckArea currentArea = RCAREA_INVALID;
std::vector<RandomizerCheckArea> checkAreas;
std::vector<GetItemEntry> itemsReceived;
OSContPad* trackerButtonsPressed; OSContPad* trackerButtonsPressed;
void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0); void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0);
@ -194,10 +184,6 @@ Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; // Green
std::vector<uint32_t> buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L, std::vector<uint32_t> buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L,
BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
void SetLastItemGetRC(RandomizerCheck rc) {
lastItemGetCheck = rc;
}
void DefaultCheckData(RandomizerCheck rc) { void DefaultCheckData(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED; gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED;
gSaveContext.checkTrackerData[rc].skipped = 0; gSaveContext.checkTrackerData[rc].skipped = 0;
@ -240,6 +226,26 @@ void TrySetAreas() {
} }
} }
void RecalculateAreaTotals() {
for (auto [rcArea, rcObjects] : checksByArea) {
if (rcArea == RCAREA_INVALID) {
return;
}
areaChecksGotten[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
for (auto rcObj : rcObjects) {
if (!IsVisibleInCheckTracker(rcObj)) {
continue;
}
areaCheckTotals[rcArea]++;
if (gSaveContext.checkTrackerData[rcObj.rc].skipped || gSaveContext.checkTrackerData[rcObj.rc].status == RCSHOW_COLLECTED
|| gSaveContext.checkTrackerData[rcObj.rc].status == RCSHOW_SAVED) {
areaChecksGotten[rcArea]++;
}
}
}
}
void SetCheckCollected(RandomizerCheck rc) { void SetCheckCollected(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED; gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED;
RandomizerCheckObject rcObj; RandomizerCheckObject rcObj;
@ -248,13 +254,12 @@ void SetCheckCollected(RandomizerCheck rc) {
} else { } else {
rcObj = RandomizerCheckObjects::GetAllRCObjects().find(rc)->second; rcObj = RandomizerCheckObjects::GetAllRCObjects().find(rc)->second;
} }
if (!gSaveContext.checkTrackerData[rc].skipped) { if (IsVisibleInCheckTracker(rcObj)) {
areaChecksGotten[rcObj.rcArea]++; if (!gSaveContext.checkTrackerData[rc].skipped) {
} else { areaChecksGotten[rcObj.rcArea]++;
gSaveContext.checkTrackerData[rc].skipped = false; } else {
} gSaveContext.checkTrackerData[rc].skipped = false;
if (!checkAreas.empty()) { }
checkAreas.erase(checkAreas.begin());
} }
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
@ -360,32 +365,19 @@ bool vector_contains_scene(std::vector<SceneID> vec, const int16_t scene) {
std::vector<SceneID> skipScenes = {SCENE_GANON_BOSS, SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, SCENE_GANON_BOSS, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR}; std::vector<SceneID> skipScenes = {SCENE_GANON_BOSS, SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, SCENE_GANON_BOSS, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR};
bool EvaluateCheck(RandomizerCheckObject rco) { void ClearAreaChecksAndTotals() {
if (HasItemBeenCollected(rco.rc) && gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_COLLECTED && for (auto& [rcArea, vec] : checksByArea) {
gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_SAVED) { vec.clear();
SetCheckCollected(rco.rc); areaChecksGotten[rcArea] = 0;
return true; areaCheckTotals[rcArea] = 0;
}
return false;
}
bool CheckByArea(RandomizerCheckArea area = RCAREA_INVALID) {
if (area == RCAREA_INVALID) {
area = checkAreas.front();
}
if (area != RCAREA_INVALID) {
auto areaChecks = checksByArea.find(area)->second;
if (checkCounter >= areaChecks.size()) {
checkCounter = 0;
checkLoops++;
}
auto rco = areaChecks.at(checkCounter);
return EvaluateCheck(rco);
} }
} }
void SetShopSeen(uint32_t sceneNum, bool prices) { void SetShopSeen(uint32_t sceneNum, bool prices) {
RandomizerCheck start = startingShopItem.find(sceneNum)->second; RandomizerCheck start = startingShopItem.find(sceneNum)->second;
if (sceneNum == SCENE_POTION_SHOP_KAKARIKO && !LINK_IS_ADULT) {
return;
}
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) { if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) {
start = RC_KAK_BAZAAR_ITEM_1; start = RC_KAK_BAZAAR_ITEM_1;
} }
@ -452,10 +444,64 @@ bool HasItemBeenCollected(RandomizerCheck rc) {
return false; return false;
} }
void CheckTrackerDialogClosed() { void CheckTrackerLoadGame(int32_t fileNum) {
if (messageCloseCheck) { LoadSettings();
messageCloseCheck = false; TrySetAreas();
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc];
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET || !RandomizerCheckObjects::GetAllRCObjects().contains(rc)) {
continue;
}
RandomizerCheckObject realRcObj;
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
realRcObj = RCO_RAORU;
} else {
realRcObj = rcObj;
}
checksByArea.find(realRcObj.rcArea)->second.push_back(realRcObj);
if (IsVisibleInCheckTracker(realRcObj)) {
areaCheckTotals[realRcObj.rcArea]++;
if (rcTrackerData.status == RCSHOW_COLLECTED || rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
areaChecksGotten[realRcObj.rcArea]++;
}
}
if (areaChecksGotten[realRcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(realRcObj.rcArea)) {
areasSpoiled |= (1 << realRcObj.rcArea);
}
} }
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) {
s8 startingAge = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_AGE);
RandomizerCheckArea startingArea;
switch (startingAge) {
case RO_AGE_CHILD:
startingArea = RCAREA_KOKIRI_FOREST;
break;
case RO_AGE_ADULT:
startingArea = RCAREA_MARKET;
break;
default:
startingArea = RCAREA_KOKIRI_FOREST;
break;
}
RandomizerCheckObject linksPocket = { RC_LINKS_POCKET, RCVORMQ_BOTH, RCTYPE_LINKS_POCKET, startingArea, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, false, "Link's Pocket", "Link's Pocket" };
checksByArea.find(startingArea)->second.push_back(linksPocket);
areaChecksGotten[startingArea]++;
areaCheckTotals[startingArea]++;
}
showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_RANDOM_NUMBER ||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12));
LinksPocket();
SongFromImpa();
GiftFromSages();
initialized = true;
UpdateAllOrdering();
UpdateInventoryChecks();
} }
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) { void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
@ -479,10 +525,6 @@ void CheckTrackerTransition(uint32_t sceneNum) {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded()) {
return; return;
} }
gSaveContext;
if (transitionCheck) {
transitionCheck = false;
}
doAreaScroll = true; doAreaScroll = true;
previousArea = currentArea; previousArea = currentArea;
currentArea = GetCheckArea(); currentArea = GetCheckArea();
@ -503,28 +545,12 @@ void CheckTrackerFrame() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded()) {
return; return;
} }
if (!checkAreas.empty() && !transitionCheck && !messageCloseCheck && !pendingSaleCheck) { // TODO: Move to OnAmmoChange hook once it gets added.
for (int i = 0; i < 10; i++) { if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED &&
if (CheckByArea()) { gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) {
checkCounter = 0; if (BEANS_BOUGHT >= 10) {
break; SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
} else {
checkCounter++;
}
} }
if (checkLoops > 15) {
checkAreas.erase(checkAreas.begin());
checkLoops = 0;
}
}
if (savedFrames > 0 && !pendingSaleCheck && !messageCloseCheck) {
savedFrames--;
}
}
void CheckTrackerSaleEnd(GetItemEntry giEntry) {
if (pendingSaleCheck) {
pendingSaleCheck = false;
} }
} }
@ -536,7 +562,7 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
// Vanilla special item checks // Vanilla special item checks
if (!IS_RANDO) { if (!IS_RANDO) {
if (giEntry.itemId == ITEM_SHIELD_DEKU) { if (giEntry.itemId == ITEM_SHIELD_DEKU) {
SetCheckCollected(RC_KF_SHOP_ITEM_3); SetCheckCollected(RC_KF_SHOP_ITEM_1);
return; return;
}else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) { }else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) {
SetCheckCollected(RC_QUEEN_GOHMA); SetCheckCollected(RC_QUEEN_GOHMA);
@ -565,16 +591,19 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
} else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) { } else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) {
SetCheckCollected(RC_GIFT_FROM_SAGES); SetCheckCollected(RC_GIFT_FROM_SAGES);
return; return;
} else if (giEntry.itemId == ITEM_SONG_LULLABY) {
SetCheckCollected(RC_SONG_FROM_IMPA);
return;
} else if (giEntry.itemId == ITEM_SONG_EPONA) { } else if (giEntry.itemId == ITEM_SONG_EPONA) {
SetCheckCollected(RC_SONG_FROM_MALON); SetCheckCollected(RC_SONG_FROM_MALON);
return; return;
} else if (giEntry.itemId == ITEM_SONG_SARIA) { } else if (giEntry.itemId == ITEM_SONG_SARIA) {
SetCheckCollected(RC_SONG_FROM_SARIA); SetCheckCollected(RC_SONG_FROM_SARIA);
return; return;
} else if (giEntry.itemId == ITEM_SONG_SUN) { } else if (giEntry.itemId == ITEM_BEAN) {
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
return;
} else if (giEntry.itemId == ITEM_BRACELET) {
SetCheckCollected(RC_GC_DARUNIAS_JOY);
return;
}/* else if (giEntry.itemId == ITEM_SONG_SUN) {
SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB); SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB);
return; return;
} else if (giEntry.itemId == ITEM_SONG_TIME) { } else if (giEntry.itemId == ITEM_SONG_TIME) {
@ -601,42 +630,127 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
} else if (giEntry.itemId == ITEM_SONG_PRELUDE) { } else if (giEntry.itemId == ITEM_SONG_PRELUDE) {
SetCheckCollected(RC_SHEIK_AT_TEMPLE); SetCheckCollected(RC_SHEIK_AT_TEMPLE);
return; return;
} else if (giEntry.itemId == ITEM_BRACELET) { }*/
SetCheckCollected(RC_GC_DARUNIAS_JOY); }
return; }
} else if (giEntry.itemId == ITEM_LETTER_ZELDA) {
SetCheckCollected(RC_HC_ZELDAS_LETTER); void CheckTrackerSceneFlagSet(int16_t sceneNum, int16_t flagType, int32_t flag) {
return; if (flagType != FLAG_SCENE_TREASURE && flagType != FLAG_SCENE_COLLECTIBLE) {
} else if (giEntry.itemId == ITEM_WEIRD_EGG) { return;
SetCheckCollected(RC_HC_MALON_EGG); }
return; if (sceneNum == SCENE_GRAVEYARD && flag == 0x19 && flagType == FLAG_SCENE_COLLECTIBLE) { // Gravedigging tour special case
} else if (giEntry.itemId == ITEM_BEAN) { SetCheckCollected(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR);
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN); return;
}
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
if (!IsVisibleInCheckTracker(rcObj)) {
continue;
}
SpoilerCollectionCheckType checkMatchType = flagType == FLAG_SCENE_TREASURE ? SpoilerCollectionCheckType::SPOILER_CHK_CHEST : SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE;
SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
if (scCheck.scene == sceneNum && scCheck.flag == flag && scCheck.type == checkMatchType) {
SetCheckCollected(rc);
return; return;
} }
} }
auto checkArea = GetCheckArea(); }
if (gSaveContext.pendingSale != ITEM_NONE) {
pendingSaleCheck = true; void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
checkAreas.push_back(checkArea); SpoilerCollectionCheckType checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_NONE;
switch (flagType) {
case FLAG_GS_TOKEN:
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA;
break;
case FLAG_EVENT_CHECK_INF:
if ((flag == EVENTCHKINF_CARPENTERS_FREE(0) || flag == EVENTCHKINF_CARPENTERS_FREE(1) ||
flag == EVENTCHKINF_CARPENTERS_FREE(2) || flag == EVENTCHKINF_CARPENTERS_FREE(3))
&& GET_EVENTCHKINF_CARPENTERS_FREE_ALL()) {
SetCheckCollected(RC_GF_GERUDO_MEMBERSHIP_CARD);
return;
}
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF;
break;
case FLAG_INF_TABLE:
if (flag == INFTABLE_190) {
SetCheckCollected(RC_GF_HBA_1000_POINTS);
return;
} else if (flag == INFTABLE_11E) {
SetCheckCollected(RC_GC_ROLLING_GORON_AS_CHILD);
return;
} else if (flag == INFTABLE_GORON_CITY_DOORS_UNLOCKED) {
SetCheckCollected(RC_GC_ROLLING_GORON_AS_ADULT);
return;
} else if (flag == INFTABLE_139) {
SetCheckCollected(RC_ZD_KING_ZORA_THAWED);
return;
} else if (flag == INFTABLE_191) {
SetCheckCollected(RC_MARKET_LOST_DOG);
return;
}
if (!IS_RANDO) {
if (flag == INFTABLE_192) {
SetCheckCollected(RC_LW_DEKU_SCRUB_NEAR_BRIDGE);
return;
} else if (flag == INFTABLE_193) {
SetCheckCollected(RC_LW_DEKU_SCRUB_GROTTO_FRONT);
return;
}
}
break;
case FLAG_ITEM_GET_INF:
if (!IS_RANDO) {
if (flag == ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE) {
SetCheckCollected(RC_DEKU_THEATER_SKULL_MASK);
return;
} else if (flag == ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) {
SetCheckCollected(RC_DEKU_THEATER_MASK_OF_TRUTH);
return;
} else if (flag == ITEMGETINF_0B) {
SetCheckCollected(RC_HF_DEKU_SCRUB_GROTTO);
return;
}
}
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF;
break;
case FLAG_RANDOMIZER_INF:
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF;
break;
}
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_NONE) {
return; return;
} }
if (scene == SCENE_DESERT_COLOSSUS && (gSaveContext.entranceIndex == 485 || gSaveContext.entranceIndex == 489)) { for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
checkAreas.push_back(RCAREA_SPIRIT_TEMPLE); if ((!IS_RANDO && ((rcObj.vOrMQ == RCVORMQ_MQ && !IS_MASTER_QUEST) ||
return; (rcObj.vOrMQ == RCVORMQ_VANILLA && IS_MASTER_QUEST))) ||
} (IS_RANDO && ((OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
if (GET_PLAYER(gPlayState) == nullptr) { rcObj.vOrMQ == RCVORMQ_VANILLA) ||
transitionCheck = true; !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
return; rcObj.vOrMQ == RCVORMQ_MQ))) {
} continue;
if (gPlayState->msgCtx.msgMode != MSGMODE_NONE) { }
checkAreas.push_back(checkArea); SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
messageCloseCheck = true; SpoilerCollectionCheckType scCheckType = scCheck.type;
return; if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF &&
} (scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT ||
if (IS_RANDO || (!IS_RANDO && giEntry.getItemCategory != ITEM_CATEGORY_JUNK)) { scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
checkAreas.push_back(checkArea); scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW ||
checkCollected = true; scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SCRUB ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF)) {
if (flag == OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc)) {
SetCheckCollected(rc);
return;
}
continue;
}
int16_t checkFlag = scCheck.flag;
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA) {
checkFlag = rcObj.actorParams;
}
if (checkFlag == flag && scCheck.type == checkMatchType) {
SetCheckCollected(rc);
return;
}
} }
} }
@ -654,7 +768,7 @@ void InitTrackerData(bool isDebug) {
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) { void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) { SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) { if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
if (gameSave || savedFrames > 0) { if (gameSave) {
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED; gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
UpdateAllOrdering(); UpdateAllOrdering();
UpdateInventoryChecks(); UpdateInventoryChecks();
@ -673,15 +787,9 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveTrackerData(saveContext, sectionID, fullSave); SaveTrackerData(saveContext, sectionID, fullSave);
if (fullSave) {
savedFrames = 40;
}
} }
void LoadFile() { void LoadFile() {
Teardown();
LoadSettings();
TrySetAreas();
SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) { SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) {
SaveManager::Instance->LoadStruct("", [&]() { SaveManager::Instance->LoadStruct("", [&]() {
SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status); SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status);
@ -689,79 +797,27 @@ void LoadFile() {
SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price); SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price);
SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem); SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem);
}); });
RandomizerCheckTrackerData entry = gSaveContext.checkTrackerData[i];
RandomizerCheck rc = static_cast<RandomizerCheck>(i);
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX ||
!RandomizerCheckObjects::GetAllRCObjects().contains(rc))
return;
RandomizerCheckObject entry2;
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
entry2 = RCO_RAORU;
} else {
entry2 = RandomizerCheckObjects::GetAllRCObjects().find(rc)->second;
}
if (!IsVisibleInCheckTracker(entry2)) return;
checksByArea.find(entry2.rcArea)->second.push_back(entry2);
if (entry.status == RCSHOW_SAVED || entry.skipped) {
areaChecksGotten[entry2.rcArea]++;
}
if (areaChecksGotten[entry2.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2.rcArea)) {
areasSpoiled |= (1 << entry2.rcArea);
}
}); });
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) {
s8 startingAge = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_AGE);
RandomizerCheckArea startingArea;
switch (startingAge) {
case RO_AGE_CHILD:
startingArea = RCAREA_KOKIRI_FOREST;
break;
case RO_AGE_ADULT:
startingArea = RCAREA_MARKET;
break;
default:
startingArea = RCAREA_KOKIRI_FOREST;
break;
}
RandomizerCheckObject linksPocket = { RC_LINKS_POCKET, RCVORMQ_BOTH, RCTYPE_LINKS_POCKET, startingArea, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, false, "Link's Pocket", "Link's Pocket" };
checksByArea.find(startingArea)->second.push_back(linksPocket);
areaChecksGotten[startingArea]++;
}
showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_RANDOM_NUMBER ||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12));
LinksPocket();
SongFromImpa();
GiftFromSages();
initialized = true;
UpdateAllOrdering();
UpdateInventoryChecks();
} }
void Teardown() { void Teardown() {
initialized = false; initialized = false;
for (auto& [rcArea, vec] : checksByArea) { ClearAreaChecksAndTotals();
vec.clear();
areaChecksGotten[rcArea] = 0;
}
checksByArea.clear(); checksByArea.clear();
areasSpoiled = 0; areasSpoiled = 0;
checkCollected = false;
checkLoops = 0;
lastLocationChecked = RC_UNKNOWN_CHECK; lastLocationChecked = RC_UNKNOWN_CHECK;
} }
void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) { void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) {
auto area = RandomizerCheckObjects::GetAllRCObjects().find(static_cast<RandomizerCheck>(check))->second.rcArea; auto area = RandomizerCheckObjects::GetAllRCObjects().find(static_cast<RandomizerCheck>(check))->second.rcArea;
if (!gSaveContext.checkTrackerData[check].skipped && data.skipped) { if ((!gSaveContext.checkTrackerData[check].skipped && data.skipped) ||
((gSaveContext.checkTrackerData[check].status != RCSHOW_COLLECTED && gSaveContext.checkTrackerData[check].status != RCSHOW_SAVED) &&
(data.status == RCSHOW_COLLECTED || data.status == RCSHOW_SAVED))) {
areaChecksGotten[area]++; areaChecksGotten[area]++;
} else if (gSaveContext.checkTrackerData[check].skipped && !data.skipped) { } else if ((gSaveContext.checkTrackerData[check].skipped && !data.skipped) ||
((gSaveContext.checkTrackerData[check].status == RCSHOW_COLLECTED || gSaveContext.checkTrackerData[check].status == RCSHOW_SAVED) &&
(data.status != RCSHOW_COLLECTED && data.status != RCSHOW_SAVED))) {
areaChecksGotten[area]--; areaChecksGotten[area]--;
} }
gSaveContext.checkTrackerData[check] = data; gSaveContext.checkTrackerData[check] = data;
@ -874,8 +930,7 @@ void CheckTrackerWindow::DrawElement() {
for (auto& [rcArea, objs] : checksByArea) { for (auto& [rcArea, objs] : checksByArea) {
RandomizerCheckArea thisArea = currentArea; RandomizerCheckArea thisArea = currentArea;
const int areaChecksTotal = static_cast<int>(objs.size()); thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaCheckTotals[rcArea]);
thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaChecksTotal);
//Last Area needs to be cleaned up //Last Area needs to be cleaned up
if (lastArea != RCAREA_INVALID && doDraw) { if (lastArea != RCAREA_INVALID && doDraw) {
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
@ -912,26 +967,33 @@ void CheckTrackerWindow::DrawElement() {
stemp = RandomizerCheckObjects::GetRCAreaName(rcArea) + "##TreeNode"; stemp = RandomizerCheckObjects::GetRCAreaName(rcArea) + "##TreeNode";
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f,
mainColor.b / 255.0f, mainColor.a / 255.0f)); mainColor.b / 255.0f, mainColor.a / 255.0f));
if (doingCollapseOrExpand) if (doingCollapseOrExpand) {
ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always); ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always);
else } else {
ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once); ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once);
}
doDraw = ImGui::TreeNode(stemp.c_str()); doDraw = ImGui::TreeNode(stemp.c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::SameLine(); ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f,
extraColor.b / 255.0f, extraColor.a / 255.0f)); extraColor.b / 255.0f, extraColor.a / 255.0f));
isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0); isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0) || !IS_RANDO ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_NONE ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SELECTION ||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12);
if (isThisAreaSpoiled) { if (isThisAreaSpoiled) {
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) { if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(DungeonSceneLookupByArea(rcArea))) if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(
ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaChecksTotal); DungeonSceneLookupByArea(rcArea))) {
else ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaChecksTotal); } else {
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
}
} else { } else {
ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaChecksTotal); ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
} }
} else { } else {
ImGui::Text("???"); ImGui::Text("???");
@ -945,11 +1007,13 @@ void CheckTrackerWindow::DrawElement() {
doAreaScroll = false; doAreaScroll = false;
} }
for (auto rco : objs) { for (auto rco : objs) {
if (doDraw && isThisAreaSpoiled && IsVisibleInCheckTracker(rco)) if (IsVisibleInCheckTracker(rco) && doDraw && isThisAreaSpoiled) {
DrawLocation(rco); DrawLocation(rco);
}
} }
if (doDraw) if (doDraw) {
ImGui::TreePop(); ImGui::TreePop();
}
} }
areaMask <<= 1; areaMask <<= 1;
} }
@ -1056,7 +1120,6 @@ void LoadSettings() {
showLinksPocket = IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled showLinksPocket = IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING
:false; :false;
hideShopRightChecks = IS_RANDO ? CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1) : false;
if (IS_RANDO) { if (IS_RANDO) {
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) { switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) {
@ -1099,7 +1162,7 @@ void LoadSettings() {
} }
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) { bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
if (IS_RANDO) { if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LOGIC_RULES) != RO_LOGIC_VANILLA) {
return return
(rcObj.rcArea != RCAREA_INVALID) && // don't show Invalid locations (rcObj.rcArea != RCAREA_INVALID) && // don't show Invalid locations
(rcObj.rcType != RCTYPE_GOSSIP_STONE) && //TODO: Don't show hints until tracker supports them (rcObj.rcType != RCTYPE_GOSSIP_STONE) && //TODO: Don't show hints until tracker supports them
@ -1120,7 +1183,7 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
) && ) &&
(rcObj.rcType != RCTYPE_MERCHANT || showMerchants) && (rcObj.rcType != RCTYPE_MERCHANT || showMerchants) &&
(rcObj.rcType != RCTYPE_OCARINA || showOcarinas) && (rcObj.rcType != RCTYPE_OCARINA || showOcarinas) &&
(rcObj.rcType != RCTYPE_SKULL_TOKEN || (rcObj.rcType != RCTYPE_SKULL_TOKEN || alwaysShowGS ||
(showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) || (showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) ||
(showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea)) (showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea))
) && ) &&
@ -1148,8 +1211,8 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
} }
else if (rcObj.vanillaCompletion) { else if (rcObj.vanillaCompletion) {
return (rcObj.vOrMQ == RCVORMQ_BOTH || return (rcObj.vOrMQ == RCVORMQ_BOTH ||
rcObj.vOrMQ == RCVORMQ_MQ && OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) || (rcObj.vOrMQ == RCVORMQ_MQ && IS_MASTER_QUEST) ||
rcObj.vOrMQ == RCVORMQ_VANILLA && !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) || (rcObj.vOrMQ == RCVORMQ_VANILLA && !IS_MASTER_QUEST) ||
rcObj.rc == RC_GIFT_FROM_SAGES) && rcObj.rc != RC_LINKS_POCKET; rcObj.rc == RC_GIFT_FROM_SAGES) && rcObj.rc != RC_LINKS_POCKET;
} }
return false; return false;
@ -1169,8 +1232,9 @@ void UpdateAreaFullyChecked(RandomizerCheckArea area) {
void UpdateAreas(RandomizerCheckArea area) { void UpdateAreas(RandomizerCheckArea area) {
areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size(); areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size();
if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area)) if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area)) {
areasSpoiled |= (1 << area); areasSpoiled |= (1 << area);
}
} }
void UpdateAllOrdering() { void UpdateAllOrdering() {
@ -1198,30 +1262,36 @@ bool CompareChecks(RandomizerCheckObject i, RandomizerCheckObject j) {
bool iSaved = iShow.status == RCSHOW_SAVED; bool iSaved = iShow.status == RCSHOW_SAVED;
bool jCollected = jShow.status == RCSHOW_COLLECTED || jShow.status == RCSHOW_SAVED; bool jCollected = jShow.status == RCSHOW_COLLECTED || jShow.status == RCSHOW_SAVED;
bool jSaved = jShow.status == RCSHOW_SAVED; bool jSaved = jShow.status == RCSHOW_SAVED;
if (!iCollected && jCollected)
return true;
else if (iCollected && !jCollected)
return false;
if (!iSaved && jSaved) if (!iCollected && jCollected) {
return true; return true;
else if (iSaved && !jSaved) } else if (iCollected && !jCollected) {
return false; return false;
}
if (!iShow.skipped && jShow.skipped) if (!iSaved && jSaved) {
return true; return true;
else if (iShow.skipped && !jShow.skipped) } else if (iSaved && !jSaved) {
return false; return false;
}
if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType)) if (!iShow.skipped && jShow.skipped) {
return true; return true;
else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType)) } else if (iShow.skipped && !jShow.skipped) {
return false; return false;
}
if (i.rc < j.rc) if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType)) {
return true; return true;
else if (i.rc > j.rc) } else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType)) {
return false; return false;
}
if (i.rc < j.rc) {
return true;
} else if (i.rc > j.rc) {
return false;
}
return false; return false;
} }
@ -1239,47 +1309,54 @@ void DrawLocation(RandomizerCheckObject rcObj) {
RandomizerCheckStatus status = checkData.status; RandomizerCheckStatus status = checkData.status;
bool skipped = checkData.skipped; bool skipped = checkData.skipped;
if (status == RCSHOW_COLLECTED) { if (status == RCSHOW_COLLECTED) {
if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) {
return; return;
}
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default) : mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default) :
CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default); CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default); extraColor = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default);
} else if (status == RCSHOW_SAVED) { } else if (status == RCSHOW_SAVED) {
if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) {
return; return;
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default) : }
CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default); mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default) :
CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default); extraColor = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default);
} else if (skipped) { } else if (skipped) {
if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) {
return; return;
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default) : }
CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default); mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default) :
CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default); extraColor = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default);
} else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) { } else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) {
if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) {
return; return;
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default) : }
CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default); mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default) :
CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default); extraColor = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default);
} else if (status == RCSHOW_SCUMMED) { } else if (status == RCSHOW_SCUMMED) {
if (!showHidden && CVarGetInteger("gCheckTrackerKnownHide", 0)) if (!showHidden && CVarGetInteger("gCheckTrackerKnownHide", 0)) {
return; return;
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) : }
CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default); mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) :
CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default); extraColor = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default);
} else if (status == RCSHOW_UNCHECKED) { } else if (status == RCSHOW_UNCHECKED) {
if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) {
return; return;
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default) : }
CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default); mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default) :
CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default); extraColor = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default);
} }
//Main Text //Main Text
txt = rcObj.rcShortName; txt = rcObj.rcShortName;
if (lastLocationChecked == rcObj.rc) if (lastLocationChecked == rcObj.rc) {
txt = "* " + txt; txt = "* " + txt;
}
// Draw button - for Skipped/Seen/Scummed/Unchecked only // Draw button - for Skipped/Seen/Scummed/Unchecked only
if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) { if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) {
@ -1354,8 +1431,9 @@ void DrawLocation(RandomizerCheckObject rcObj) {
break; break;
} }
} }
if (txt == "" && skipped) if (txt == "" && skipped) {
txt = "Skipped"; //TODO language txt = "Skipped"; // TODO language
}
if (txt != "") { if (txt != "") {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f));
@ -1381,8 +1459,9 @@ int hue = 0;
void RainbowTick() { void RainbowTick() {
float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat("gCosmetics.RainbowSpeed", 0.6f)); float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat("gCosmetics.RainbowSpeed", 0.6f));
for (auto& cvar : rainbowCVars) { for (auto& cvar : rainbowCVars) {
if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) {
continue; continue;
}
Color_RGBA8 newColor; Color_RGBA8 newColor;
newColor.r = sin(freqHue + 0) * 127 + 128; newColor.r = sin(freqHue + 0) * 127 + 128;
@ -1489,8 +1568,16 @@ void CheckTrackerSettingsWindow::DrawElement() {
} }
UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers"); UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers");
UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked."); UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.");
UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true); if (UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true)) {
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops. Requires save reload."); hideShopRightChecks = !hideShopRightChecks;
RecalculateAreaTotals();
}
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops.");
if (UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "")) {
alwaysShowGS = !alwaysShowGS;
RecalculateAreaTotals();
}
UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings.");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -1533,14 +1620,19 @@ void CheckTrackerWindow::InitElement() {
SaveManager::Instance->AddInitFunction(InitTrackerData); SaveManager::Instance->AddInitFunction(InitTrackerData);
sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1); sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1);
SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile); SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>(CheckTrackerLoadGame);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) {
Teardown(); Teardown();
}); });
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSaleEnd>(CheckTrackerSaleEnd);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(CheckTrackerSceneFlagSet);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(CheckTrackerFlagSet);
hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1);
alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0);
LocationTable_Init(); LocationTable_Init();
} }

View file

@ -48,10 +48,7 @@ void Teardown();
void UpdateAllOrdering(); void UpdateAllOrdering();
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj); bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
void InitTrackerData(bool isDebug); void InitTrackerData(bool isDebug);
void SetLastItemGetRC(RandomizerCheck rc);
RandomizerCheckArea GetCheckArea(); RandomizerCheckArea GetCheckArea();
void CheckTrackerDialogClosed();
void ToggleShopRightChecks();
void UpdateCheck(uint32_t, RandomizerCheckTrackerData); void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
} // namespace CheckTracker } // namespace CheckTracker

View file

@ -17,10 +17,24 @@ extern PlayState* gPlayState;
//Overwrite the dynamic exit for the OGC Fairy Fountain to be 0x3E8 instead //Overwrite the dynamic exit for the OGC Fairy Fountain to be 0x3E8 instead
//of 0x340 (0x340 will stay as the exit for the HC Fairy Fountain -> Castle Grounds) //of 0x340 (0x340 will stay as the exit for the HC Fairy Fountain -> Castle Grounds)
s16 dynamicExitList[] = { 0x045B, 0x0482, 0x03E8, 0x044B, 0x02A2, 0x0201, 0x03B8, 0x04EE, 0x03C0, 0x0463, 0x01CD, 0x0394, 0x0340, 0x057C }; s16 dynamicExitList[] = {
// OGC Fairy HC Fairy ENTR_DEATH_MOUNTAIN_TRAIL_4,
ENTR_DEATH_MOUNTAIN_CRATER_3,
ENTR_POTION_SHOP_KAKARIKO_1, // OGC Fairy -- ENTR_POTION_SHOP_KAKARIKO_1 unused
ENTR_KAKARIKO_VILLAGE_9,
ENTR_MARKET_DAY_5,
ENTR_KAKARIKO_VILLAGE_3,
ENTR_MARKET_DAY_6,
ENTR_KAKARIKO_VILLAGE_11,
ENTR_BACK_ALLEY_DAY_2,
ENTR_KAKARIKO_VILLAGE_10,
ENTR_MARKET_DAY_8,
ENTR_ZORAS_FOUNTAIN_5,
ENTR_HYRULE_CASTLE_2, // HC Fairy
ENTR_DESERT_COLOSSUS_7
};
// Warp Song indices array : 0x53C33C = { 0x0600, 0x04F6, 0x0604, 0x01F1, 0x0568, 0x05F4 } // Warp Song indices array : 0x53C33C = { ENTR_SACRED_FOREST_MEADOW_2, ENTR_DEATH_MOUNTAIN_CRATER_4, ENTR_LAKE_HYLIA_8, ENTR_DESERT_COLOSSUS_5, ENTR_GRAVEYARD_7, ENTR_TEMPLE_OF_TIME_7 }
// Owl Flights : 0x492064 and 0x492080 // Owl Flights : 0x492064 and 0x492080
@ -47,15 +61,15 @@ typedef struct {
} DungeonEntranceInfo; } DungeonEntranceInfo;
static DungeonEntranceInfo dungeons[] = { static DungeonEntranceInfo dungeons[] = {
//entryway exit, boss, reverse,bluewarp,dungeon scene, boss scene //entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene
{ DEKU_TREE_ENTRANCE, 0x0209, 0x040F, 0x0252, 0x0457, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS }, { DEKU_TREE_ENTRANCE, ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, ENTR_KOKIRI_FOREST_11, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS },
{ DODONGOS_CAVERN_ENTRANCE, 0x0242, 0x040B, 0x00C5, 0x047A, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS }, { DODONGOS_CAVERN_ENTRANCE, ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, ENTR_DEATH_MOUNTAIN_TRAIL_5, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS },
{ JABU_JABUS_BELLY_ENTRANCE, 0x0221, 0x0301, 0x0407, 0x010E, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS }, { JABU_JABUS_BELLY_ENTRANCE, ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, ENTR_ZORAS_FOUNTAIN_0, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS },
{ FOREST_TEMPLE_ENTRANCE, 0x0215, 0x000C, 0x024E, 0x0608, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS }, { FOREST_TEMPLE_ENTRANCE, ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, ENTR_SACRED_FOREST_MEADOW_3, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS },
{ FIRE_TEMPLE_ENTRANCE, 0x024A, 0x0305, 0x0175, 0x0564, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS }, { FIRE_TEMPLE_ENTRANCE, ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, ENTR_DEATH_MOUNTAIN_CRATER_5, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS },
{ WATER_TEMPLE_ENTRANCE, 0x021D, 0x0417, 0x0423, 0x060C, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS }, { WATER_TEMPLE_ENTRANCE, ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, ENTR_LAKE_HYLIA_9, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS },
{ SPIRIT_TEMPLE_ENTRANCE, 0x01E1, 0x008D, 0x02F5, 0x0610, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS }, { SPIRIT_TEMPLE_ENTRANCE, ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, ENTR_DESERT_COLOSSUS_8, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS },
{ SHADOW_TEMPLE_ENTRANCE, 0x0205, 0x0413, 0x02B2, 0x0580, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS }, { SHADOW_TEMPLE_ENTRANCE, ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, ENTR_GRAVEYARD_8, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS },
}; };
//These variables store the new entrance indices for dungeons so that //These variables store the new entrance indices for dungeons so that
@ -76,7 +90,7 @@ static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
static s8 hasCopiedEntranceTable = 0; static s8 hasCopiedEntranceTable = 0;
static s8 hasModifiedEntranceTable = 0; static s8 hasModifiedEntranceTable = 0;
void Entrance_SetEntranceDiscovered(u16 entranceIndex); void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance);
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) { u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0 return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
@ -84,18 +98,18 @@ u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
} }
static void Entrance_SeparateOGCFairyFountainExit(void) { static void Entrance_SeparateOGCFairyFountainExit(void) {
//Overwrite unused entrance 0x03E8 with values from 0x0340 to use it as the //Overwrite unused entrance 0x03E8 (ENTR_POTION_SHOP_KAKARIKO_1) with values from 0x0340 (ENTR_HYRULE_CASTLE_2) to use it as the
//exit from OGC Great Fairy Fountain -> Castle Grounds //exit from OGC Great Fairy Fountain -> Castle Grounds
for (size_t i = 0; i < 4; ++i) { for (size_t i = 0; i < 4; ++i) {
gEntranceTable[0x3E8 + i] = gEntranceTable[0x340 + i]; gEntranceTable[ENTR_POTION_SHOP_KAKARIKO_1 + i] = gEntranceTable[ENTR_HYRULE_CASTLE_2 + i];
} }
} }
static void Entrance_SeparateAdultSpawnAndPrelude() { static void Entrance_SeparateAdultSpawnAndPrelude() {
// Overwrite unused entrance 0x0282 with values from 0x05F4 to use it as the // Overwrite unused entrance 0x0282 (ENTR_HYRULE_FIELD_10) with values from 0x05F4 (ENTR_TEMPLE_OF_TIME_7) to use it as the
// Adult Spawn index and separate it from Prelude of Light // Adult Spawn index and separate it from Prelude of Light
for (size_t i = 0; i < 4; ++i) { for (size_t i = 0; i < 4; ++i) {
gEntranceTable[0x282 + i] = gEntranceTable[0x5F4 + i]; gEntranceTable[ENTR_HYRULE_FIELD_10 + i] = gEntranceTable[ENTR_TEMPLE_OF_TIME_7 + i];
} }
} }
@ -123,14 +137,14 @@ void Entrance_Init(void) {
// Skip Child Stealth if given by settings // Skip Child Stealth if given by settings
if (Randomizer_GetSettingValue(RSK_SKIP_CHILD_STEALTH)) { if (Randomizer_GetSettingValue(RSK_SKIP_CHILD_STEALTH)) {
gEntranceTable[0x07A].scene = 0x4A; gEntranceTable[ENTR_CASTLE_COURTYARD_GUARDS_DAY_0].scene = SCENE_CASTLE_COURTYARD_ZELDA;
gEntranceTable[0x07A].spawn = 0x00; gEntranceTable[ENTR_CASTLE_COURTYARD_GUARDS_DAY_0].spawn = 0;
gEntranceTable[0x07A].field = 0x0183; gEntranceTable[ENTR_CASTLE_COURTYARD_GUARDS_DAY_0].field = ENTRANCE_INFO_FIELD(false, false, TRANS_TYPE_FADE_WHITE, TRANS_TYPE_FADE_WHITE);
} }
// Delete the title card and add a fade in for Hyrule Field from Ocarina of Time cutscene // Delete the title card and add a fade in for Hyrule Field from Ocarina of Time cutscene
for (index = 0x50F; index < 0x513; ++index) { for (index = ENTR_HYRULE_FIELD_16; index <= ENTR_HYRULE_FIELD_16_3; ++index) {
gEntranceTable[index].field = 0x010B; gEntranceTable[index].field = ENTRANCE_INFO_FIELD(false, false, TRANS_TYPE_FADE_BLACK, TRANS_TYPE_INSTANT);
} }
Entrance_SeparateOGCFairyFountainExit(); Entrance_SeparateOGCFairyFountainExit();
@ -206,10 +220,10 @@ void Entrance_Init(void) {
} }
//Override both land and water entrances for Hyrule Field -> ZR Front and vice versa //Override both land and water entrances for Hyrule Field -> ZR Front and vice versa
if (originalIndex == 0x00EA) { //Hyrule Field -> ZR Front land entrance if (originalIndex == ENTR_ZORAS_RIVER_0) { //Hyrule Field -> ZR Front land entrance
entranceOverrideTable[0x01D9] = overrideIndex; entranceOverrideTable[ENTR_ZORAS_RIVER_3] = overrideIndex;
} else if (originalIndex == 0x0181) { //ZR Front -> Hyrule Field land entrance } else if (originalIndex == ENTR_HYRULE_FIELD_2) { //ZR Front -> Hyrule Field land entrance
entranceOverrideTable[0x0311] = overrideIndex; entranceOverrideTable[ENTR_HYRULE_FIELD_14] = overrideIndex;
} }
} }
@ -228,11 +242,11 @@ void Entrance_Init(void) {
s16 indicesToSilenceBackgroundMusic[2] = { s16 indicesToSilenceBackgroundMusic[2] = {
// The lost woods music playing near the GC Woods Warp keeps playing // The lost woods music playing near the GC Woods Warp keeps playing
// in the next area if the bvackground music is allowed to keep playing // in the next area if the bvackground music is allowed to keep playing
entranceOverrideTable[0x04D6], // Goron City -> Lost Woods override entranceOverrideTable[ENTR_LOST_WOODS_6], // Goron City -> Lost Woods override
// If Malon is singing at night, then her singing will be transferred // If Malon is singing at night, then her singing will be transferred
// to the next area if it allows the background music to keep playing // to the next area if it allows the background music to keep playing
entranceOverrideTable[0x025A], // Castle Grounds -> Market override entranceOverrideTable[ENTR_MARKET_DAY_1], // Castle Grounds -> Market override
}; };
for (size_t j = 0; j < sizeof(indicesToSilenceBackgroundMusic) / sizeof(s16); j++) { for (size_t j = 0; j < sizeof(indicesToSilenceBackgroundMusic) / sizeof(s16); j++) {
@ -241,7 +255,7 @@ void Entrance_Init(void) {
for (s16 i = 0; i < 4; i++) { for (s16 i = 0; i < 4; i++) {
// Zero out the bit in the field which tells the game to keep playing // Zero out the bit in the field which tells the game to keep playing
// background music for all four scene setups at each index // background music for all four scene setups at each index
gEntranceTable[override + i].field &= ~0x8000; gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG;
} }
} }
} }
@ -263,7 +277,7 @@ s16 Entrance_GetOverride(s16 index) {
s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) { s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) {
// When entering Spirit Temple, clear temp flags so they don't carry over to the randomized dungeon // When entering Spirit Temple, clear temp flags so they don't carry over to the randomized dungeon
if (nextEntranceIndex == 0x0082 && Entrance_GetOverride(nextEntranceIndex) != nextEntranceIndex && if (nextEntranceIndex == ENTR_SPIRIT_TEMPLE_0 && Entrance_GetOverride(nextEntranceIndex) != nextEntranceIndex &&
gPlayState != NULL) { gPlayState != NULL) {
gPlayState->actorCtx.flags.tempSwch = 0; gPlayState->actorCtx.flags.tempSwch = 0;
gPlayState->actorCtx.flags.tempCollect = 0; gPlayState->actorCtx.flags.tempCollect = 0;
@ -272,40 +286,40 @@ s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) {
// Exiting through the crawl space from Hyrule Castle courtyard is the same exit as leaving Ganon's castle // Exiting through the crawl space from Hyrule Castle courtyard is the same exit as leaving Ganon's castle
// Don't override the entrance if we came from the Castle courtyard (day and night scenes) // Don't override the entrance if we came from the Castle courtyard (day and night scenes)
if (gPlayState != NULL && (gPlayState->sceneNum == SCENE_CASTLE_COURTYARD_GUARDS_DAY || gPlayState->sceneNum == SCENE_CASTLE_COURTYARD_GUARDS_NIGHT) && if (gPlayState != NULL && (gPlayState->sceneNum == SCENE_CASTLE_COURTYARD_GUARDS_DAY || gPlayState->sceneNum == SCENE_CASTLE_COURTYARD_GUARDS_NIGHT) &&
nextEntranceIndex == 0x023D) { nextEntranceIndex == ENTR_HYRULE_CASTLE_1) {
return nextEntranceIndex; return nextEntranceIndex;
} }
Entrance_SetEntranceDiscovered(nextEntranceIndex); Entrance_SetEntranceDiscovered(nextEntranceIndex, false);
EntranceTracker_SetLastEntranceOverride(nextEntranceIndex); EntranceTracker_SetLastEntranceOverride(nextEntranceIndex);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex)); return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex));
} }
s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) { s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) {
Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex]); Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex], false);
EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]); EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex])); return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex]));
} }
u32 Entrance_SceneAndSpawnAre(u8 scene, u8 spawn) { u32 Entrance_SceneAndSpawnAre(u8 scene, u8 spawn) {
s16 computedEntranceIndex; s16 entranceIndex;
// Adjust the entrance to account for the exact scene/spawn combination for child/adult and day/night // Adjust the entrance to account for the exact scene/spawn combination for child/adult and day/night
if (!IS_DAY) { if (!IS_DAY) {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
computedEntranceIndex = gSaveContext.entranceIndex + 1; entranceIndex = gSaveContext.entranceIndex + 1;
} else { } else {
computedEntranceIndex = gSaveContext.entranceIndex + 3; entranceIndex = gSaveContext.entranceIndex + 3;
} }
} else { } else {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
computedEntranceIndex = gSaveContext.entranceIndex; entranceIndex = gSaveContext.entranceIndex;
} else { } else {
computedEntranceIndex = gSaveContext.entranceIndex + 2; entranceIndex = gSaveContext.entranceIndex + 2;
} }
} }
EntranceInfo currentEntrance = gEntranceTable[computedEntranceIndex]; EntranceInfo currentEntrance = gEntranceTable[entranceIndex];
return currentEntrance.scene == scene && currentEntrance.spawn == spawn; return currentEntrance.scene == scene && currentEntrance.spawn == spawn;
} }
@ -325,32 +339,32 @@ void Entrance_SetGameOverEntrance(void) {
//Set the current entrance depending on which entrance the player last came through //Set the current entrance depending on which entrance the player last came through
switch (gSaveContext.entranceIndex) { switch (gSaveContext.entranceIndex) {
case 0x040F : //Deku Tree Boss Room case ENTR_DEKU_TREE_BOSS_0 : //Deku Tree Boss Room
gSaveContext.entranceIndex = newDekuTreeEntrance; gSaveContext.entranceIndex = newDekuTreeEntrance;
return; return;
case 0x040B : //Dodongos Cavern Boss Room case ENTR_DODONGOS_CAVERN_BOSS_0 : //Dodongos Cavern Boss Room
gSaveContext.entranceIndex = newDodongosCavernEntrance; gSaveContext.entranceIndex = newDodongosCavernEntrance;
return; return;
case 0x0301 : //Jabu Jabus Belly Boss Room case ENTR_JABU_JABU_BOSS_0 : //Jabu Jabus Belly Boss Room
gSaveContext.entranceIndex = newJabuJabusBellyEntrance; gSaveContext.entranceIndex = newJabuJabusBellyEntrance;
return; return;
case 0x000C : //Forest Temple Boss Room case ENTR_FOREST_TEMPLE_BOSS_0 : //Forest Temple Boss Room
gSaveContext.entranceIndex = newForestTempleEntrance; gSaveContext.entranceIndex = newForestTempleEntrance;
return; return;
case 0x0305 : //Fire Temple Boss Room case ENTR_FIRE_TEMPLE_BOSS_0 : //Fire Temple Boss Room
gSaveContext.entranceIndex = newFireTempleEntrance; gSaveContext.entranceIndex = newFireTempleEntrance;
return; return;
case 0x0417 : //Water Temple Boss Room case ENTR_WATER_TEMPLE_BOSS_0 : //Water Temple Boss Room
gSaveContext.entranceIndex = newWaterTempleEntrance; gSaveContext.entranceIndex = newWaterTempleEntrance;
return; return;
case 0x008D : //Spirit Temple Boss Room case ENTR_SPIRIT_TEMPLE_BOSS_0 : //Spirit Temple Boss Room
gSaveContext.entranceIndex = newSpiritTempleEntrance; gSaveContext.entranceIndex = newSpiritTempleEntrance;
return; return;
case 0x0413 : //Shadow Temple Boss Room case ENTR_SHADOW_TEMPLE_BOSS_0 : //Shadow Temple Boss Room
gSaveContext.entranceIndex = newShadowTempleEntrance; gSaveContext.entranceIndex = newShadowTempleEntrance;
return; return;
case 0x041F : //Ganondorf Boss Room case ENTR_GANONDORF_BOSS_0 : //Ganondorf Boss Room
gSaveContext.entranceIndex = 0x041B; // Inside Ganon's Castle -> Ganon's Tower Climb gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb
return; return;
} }
} }
@ -393,42 +407,42 @@ void Entrance_SetSavewarpEntrance(void) {
} else if (scene == SCENE_INSIDE_GANONS_CASTLE) { } else if (scene == SCENE_INSIDE_GANONS_CASTLE) {
gSaveContext.entranceIndex = GANONS_CASTLE_ENTRANCE; gSaveContext.entranceIndex = GANONS_CASTLE_ENTRANCE;
} else if (scene == SCENE_GANONS_TOWER || scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || scene == SCENE_GANON_BOSS || scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) { } else if (scene == SCENE_GANONS_TOWER || scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || scene == SCENE_GANON_BOSS || scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) {
gSaveContext.entranceIndex = 0x041B; // Inside Ganon's Castle -> Ganon's Tower Climb gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb
} else if (scene == SCENE_THIEVES_HIDEOUT) { // Theives hideout } else if (scene == SCENE_THIEVES_HIDEOUT) { // Theives hideout
gSaveContext.entranceIndex = 0x0486; // Gerudo Fortress -> Thieve's Hideout spawn 0 gSaveContext.entranceIndex = ENTR_THIEVES_HIDEOUT_0; // Gerudo Fortress -> Thieve's Hideout spawn 0
} else if (scene == SCENE_LINKS_HOUSE) { } else if (scene == SCENE_LINKS_HOUSE) {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE);
} else if (LINK_IS_CHILD) { } else if (LINK_IS_CHILD) {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); // Child Overworld Spawn gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); // Child Overworld Spawn
} else { } else {
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(0x0282); // Adult Overworld Spawn (Normally 0x5F4, but 0x282 has been repurposed to differentiate from Prelude which also uses 0x5F4) gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_HYRULE_FIELD_10); // Adult Overworld Spawn (Normally 0x5F4 (ENTR_TEMPLE_OF_TIME_7), but 0x282 (ENTR_HYRULE_FIELD_10) has been repurposed to differentiate from Prelude which also uses 0x5F4)
} }
} }
void Entrance_SetWarpSongEntrance(void) { void Entrance_SetWarpSongEntrance(void) {
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 5; gPlayState->transitionType = TRANS_TYPE_FADE_WHITE_FAST;
switch (gPlayState->msgCtx.lastPlayedSong) { switch (gPlayState->msgCtx.lastPlayedSong) {
case 0: case OCARINA_SONG_MINUET:
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0600); // Minuet gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_SACRED_FOREST_MEADOW_2); // Minuet
break; break;
case 1: case OCARINA_SONG_BOLERO:
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x04F6); // Bolero gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DEATH_MOUNTAIN_CRATER_4); // Bolero
break; break;
case 2: case OCARINA_SONG_SERENADE:
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0604); // Serenade gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_LAKE_HYLIA_8); // Serenade
break; break;
case 3: case OCARINA_SONG_REQUIEM:
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x01F1); // Requiem gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DESERT_COLOSSUS_5); // Requiem
break; break;
case 4: case OCARINA_SONG_NOCTURNE:
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0568); // Nocturne gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_GRAVEYARD_7); // Nocturne
break; break;
case 5: case OCARINA_SONG_PRELUDE:
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x05F4); // Prelude gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_TEMPLE_OF_TIME_7); // Prelude
break; break;
default: default:
gPlayState->sceneLoadFlag = 0; // if something goes wrong, the animation plays normally gPlayState->transitionTrigger = TRANS_TRIGGER_OFF; // if something goes wrong, the animation plays normally
} }
// If one of the warp songs happens to lead to a grotto return, then we // If one of the warp songs happens to lead to a grotto return, then we
@ -451,28 +465,28 @@ void Entrance_OverrideBlueWarp(void) {
switch (gPlayState->sceneNum) { switch (gPlayState->sceneNum) {
case SCENE_DEKU_TREE_BOSS: // Ghoma boss room case SCENE_DEKU_TREE_BOSS: // Ghoma boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0457); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_KOKIRI_FOREST_11);
return; return;
case SCENE_DODONGOS_CAVERN_BOSS: // King Dodongo boss room case SCENE_DODONGOS_CAVERN_BOSS: // King Dodongo boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x047A); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DEATH_MOUNTAIN_TRAIL_5);
return; return;
case SCENE_JABU_JABU_BOSS: // Barinade boss room case SCENE_JABU_JABU_BOSS: // Barinade boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x010E); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_ZORAS_FOUNTAIN_0);
return; return;
case SCENE_FOREST_TEMPLE_BOSS: // Phantom Ganon boss room case SCENE_FOREST_TEMPLE_BOSS: // Phantom Ganon boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0608); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_SACRED_FOREST_MEADOW_3);
return; return;
case SCENE_FIRE_TEMPLE_BOSS: // Volvagia boss room case SCENE_FIRE_TEMPLE_BOSS: // Volvagia boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0564); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DEATH_MOUNTAIN_CRATER_5);
return; return;
case SCENE_WATER_TEMPLE_BOSS: // Morpha boss room case SCENE_WATER_TEMPLE_BOSS: // Morpha boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x060C); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_LAKE_HYLIA_9);
return; return;
case SCENE_SPIRIT_TEMPLE_BOSS: // Twinrova boss room case SCENE_SPIRIT_TEMPLE_BOSS: // Twinrova boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0610); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DESERT_COLOSSUS_8);
return; return;
case SCENE_SHADOW_TEMPLE_BOSS: // Bongo-Bongo boss room case SCENE_SHADOW_TEMPLE_BOSS: // Bongo-Bongo boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(0x0580); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_GRAVEYARD_8);
return; return;
} }
} }
@ -481,8 +495,8 @@ void Entrance_OverrideCutsceneEntrance(u16 cutsceneCmd) {
switch (cutsceneCmd) { switch (cutsceneCmd) {
case 24: // Dropping a fish for Jabu Jabu case 24: // Dropping a fish for Jabu Jabu
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(newJabuJabusBellyEntrance); gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(newJabuJabusBellyEntrance);
gPlayState->sceneLoadFlag = 0x14; gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->fadeTransition = 2; gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
// In case Jabu's mouth leads to a grotto return // In case Jabu's mouth leads to a grotto return
Grotto_ForceGrottoReturnOnSpecialEntrance(); Grotto_ForceGrottoReturnOnSpecialEntrance();
break; break;
@ -494,9 +508,10 @@ void Entrance_EnableFW(void) {
// Leave restriction in Tower Collapse Interior, Castle Collapse, Treasure Box Shop, Tower Collapse Exterior, // Leave restriction in Tower Collapse Interior, Castle Collapse, Treasure Box Shop, Tower Collapse Exterior,
// Grottos area, Fishing Pond, Ganon Battle and for states that disable buttons. // Grottos area, Fishing Pond, Ganon Battle and for states that disable buttons.
if (!false /* farores wind anywhere */ || if (!false /* farores wind anywhere */ ||
gPlayState->sceneNum == 14 || gPlayState->sceneNum == 15 || (gPlayState->sceneNum == 16 && !false /* shuffled chest mini game */) || gPlayState->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || gPlayState->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE ||
gPlayState->sceneNum == 26 || gPlayState->sceneNum == 62 || gPlayState->sceneNum == 73 || (gPlayState->sceneNum == SCENE_TREASURE_BOX_SHOP && !false /* shuffled chest mini game */) ||
gPlayState->sceneNum == 79 || gPlayState->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR || gPlayState->sceneNum == SCENE_GROTTOS ||
gPlayState->sceneNum == SCENE_FISHING_POND || gPlayState->sceneNum == SCENE_GANON_BOSS ||
gSaveContext.eventInf[0] & 0x1 || // Ingo's Minigame state gSaveContext.eventInf[0] & 0x1 || // Ingo's Minigame state
player->stateFlags1 & 0x08A02000 || // Swimming, riding horse, Down A, hanging from a ledge player->stateFlags1 & 0x08A02000 || // Swimming, riding horse, Down A, hanging from a ledge
player->stateFlags2 & 0x00040000 // Blank A player->stateFlags2 & 0x00040000 // Blank A
@ -505,8 +520,8 @@ void Entrance_EnableFW(void) {
return; return;
} }
for (int i = 1; i < 5; i++) { for (size_t i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
if (gSaveContext.equips.buttonItems[i] == 13) { if (gSaveContext.equips.buttonItems[i] == ITEM_FARORES_WIND) {
gSaveContext.buttonStatus[i] = BTN_ENABLED; gSaveContext.buttonStatus[i] = BTN_ENABLED;
} }
} }
@ -520,38 +535,38 @@ void Entrance_HandleEponaState(void) {
//unset the Epona flag to avoid Master glitch, and restore temp B. //unset the Epona flag to avoid Master glitch, and restore temp B.
if (Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES) && (player->stateFlags1 & PLAYER_STATE1_ON_HORSE)) { if (Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES) && (player->stateFlags1 & PLAYER_STATE1_ON_HORSE)) {
// Allow Master glitch to be performed on the Thieves Hideout entrance // Allow Master glitch to be performed on the Thieves Hideout entrance
if (entrance == Entrance_GetOverride(0x0496)) { // Gerudo Fortress -> Theives Hideout if (entrance == Entrance_GetOverride(ENTR_THIEVES_HIDEOUT_4)) { // Gerudo Fortress -> Theives Hideout
return; return;
} }
static const s16 validEponaEntrances[] = { static const s16 validEponaEntrances[] = {
0x0102, // Hyrule Field -> Lake Hylia ENTR_LAKE_HYLIA_0, // Hyrule Field -> Lake Hylia
0x0189, // Lake Hylia -> Hyrule Field ENTR_HYRULE_FIELD_4, // Lake Hylia -> Hyrule Field
0x0309, // LH Fishing Hole -> LH Fishing Island ENTR_LAKE_HYLIA_6, // LH Fishing Hole -> LH Fishing Island
0x03CC, // LH Lab -> Lake Hylia ENTR_LAKE_HYLIA_4, // LH Lab -> Lake Hylia
0x0117, // Hyrule Field -> Gerudo Valley ENTR_GERUDO_VALLEY_0, // Hyrule Field -> Gerudo Valley
0x018D, // Gerudo Valley -> Hyrule Field ENTR_HYRULE_FIELD_5, // Gerudo Valley -> Hyrule Field
0x0157, // Hyrule Field -> Lon Lon Ranch ENTR_LON_LON_RANCH_0, // Hyrule Field -> Lon Lon Ranch
0x01F9, // Lon Lon Ranch -> Hyrule Field ENTR_HYRULE_FIELD_6, // Lon Lon Ranch -> Hyrule Field
0x01FD, // Market Entrance -> Hyrule Field ENTR_HYRULE_FIELD_7, // Market Entrance -> Hyrule Field
0x0181, // ZR Front -> Hyrule Field ENTR_HYRULE_FIELD_2, // ZR Front -> Hyrule Field
0x0185, // LW Bridge -> Hyrule Field ENTR_HYRULE_FIELD_3, // LW Bridge -> Hyrule Field
0x0129, // GV Fortress Side -> Gerudo Fortress ENTR_GERUDOS_FORTRESS_0, // GV Fortress Side -> Gerudo Fortress
0x022D, // Gerudo Fortress -> GV Fortress Side ENTR_GERUDO_VALLEY_3, // Gerudo Fortress -> GV Fortress Side
0x03D0, // GV Carpenter Tent -> GV Fortress Side ENTR_GERUDO_VALLEY_4, // GV Carpenter Tent -> GV Fortress Side
0x042F, // LLR Stables -> Lon Lon Ranch ENTR_LON_LON_RANCH_5, // LLR Stables -> Lon Lon Ranch
0x05D4, // LLR Tower -> Lon Lon Ranch ENTR_LON_LON_RANCH_10, // LLR Tower -> Lon Lon Ranch
0x0378, // LLR Talons House -> Lon Lon Ranch ENTR_LON_LON_RANCH_4, // LLR Talons House -> Lon Lon Ranch
0x028A, // LLR Southern Fence Jump ENTR_HYRULE_FIELD_11, // LLR Southern Fence Jump
0x028E, // LLR Western Fence Jump ENTR_HYRULE_FIELD_12, // LLR Western Fence Jump
0x0292, // LLR Eastern Fence Jump ENTR_HYRULE_FIELD_13, // LLR Eastern Fence Jump
0x0476, // LLR Front Gate Jump ENTR_HYRULE_FIELD_15, // LLR Front Gate Jump
// The following indices currently aren't randomized, but we'll list // The following indices currently aren't randomized, but we'll list
// them in case they ever are. They're all Theives Hideout -> Gerudo Fortress // them in case they ever are. They're all Theives Hideout -> Gerudo Fortress
0x231, ENTR_GERUDOS_FORTRESS_1,
0x235, ENTR_GERUDOS_FORTRESS_2,
0x239, ENTR_GERUDOS_FORTRESS_3,
0x2BA, ENTR_GERUDOS_FORTRESS_5,
}; };
for (size_t i = 0; i < ARRAY_COUNT(validEponaEntrances); i++) { for (size_t i = 0; i < ARRAY_COUNT(validEponaEntrances); i++) {
// If the entrance is equal to any of the valid ones, return and // If the entrance is equal to any of the valid ones, return and
@ -583,15 +598,15 @@ void Entrance_OverrideWeatherState() {
} }
// Hyrule Market // Hyrule Market
if (gSaveContext.entranceIndex == 0x01FD) { // Hyrule Field by Market Entrance if (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_7) { // Hyrule Field by Market Entrance
gWeatherMode = 1; gWeatherMode = 1;
return; return;
} }
// Lon Lon Ranch (No Epona) // Lon Lon Ranch (No Epona)
if (!Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED)){ // if you don't have Epona if (!Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED)){ // if you don't have Epona
switch (gSaveContext.entranceIndex) { switch (gSaveContext.entranceIndex) {
case 0x0157: // Lon Lon Ranch from HF case ENTR_LON_LON_RANCH_0: // Lon Lon Ranch from HF
case 0x01F9: // Hyrule Field from LLR case ENTR_HYRULE_FIELD_6: // Hyrule Field from LLR
gWeatherMode = 2; gWeatherMode = 2;
return; return;
} }
@ -599,15 +614,15 @@ void Entrance_OverrideWeatherState() {
// Water Temple // Water Temple
if (!Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP)) { // have not beaten Water Temple if (!Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP)) { // have not beaten Water Temple
switch (gSaveContext.entranceIndex) { switch (gSaveContext.entranceIndex) {
case 0x019D: // Zora River from behind waterfall case ENTR_ZORAS_RIVER_2: // Zora River from behind waterfall
case 0x01DD: // Zora River from LW water shortcut case ENTR_ZORAS_RIVER_4: // Zora River from LW water shortcut
case 0x04DA: // Lost Woods water shortcut from ZR case ENTR_LOST_WOODS_7: // Lost Woods water shortcut from ZR
gWeatherMode = 3; gWeatherMode = 3;
return; return;
} }
switch (gPlayState->sceneNum) { switch (gPlayState->sceneNum) {
case 88: // Zora's Domain case SCENE_ZORAS_DOMAIN: // Zora's Domain
case 89: // Zora's Fountain case SCENE_ZORAS_FOUNTAIN: // Zora's Fountain
gWeatherMode = 3; gWeatherMode = 3;
return; return;
} }
@ -616,13 +631,13 @@ void Entrance_OverrideWeatherState() {
if (((gSaveContext.inventory.questItems & 0x7) == 0x7) && // Have forest, fire, and water medallion if (((gSaveContext.inventory.questItems & 0x7) == 0x7) && // Have forest, fire, and water medallion
!(gSaveContext.sceneFlags[24].clear & 0x02)) { // have not beaten Bongo Bongo !(gSaveContext.sceneFlags[24].clear & 0x02)) { // have not beaten Bongo Bongo
switch (gPlayState->sceneNum) { switch (gPlayState->sceneNum) {
case 82: // Kakariko case SCENE_KAKARIKO_VILLAGE: // Kakariko
case 83: // Graveyard case SCENE_GRAVEYARD: // Graveyard
gPlayState->envCtx.gloomySkyMode = 2; gPlayState->envCtx.gloomySkyMode = 2;
switch (gSaveContext.entranceIndex) { switch (gSaveContext.entranceIndex) {
case 0x00DB: // Kakariko from HF case ENTR_KAKARIKO_VILLAGE_0: // Kakariko from HF
case 0x0191: // Kakariko from Death Mountain Trail case ENTR_KAKARIKO_VILLAGE_1: // Kakariko from Death Mountain Trail
case 0x0205: // Graveyard from Shadow Temple case ENTR_GRAVEYARD_1: // Graveyard from Shadow Temple
break; break;
default: default:
gWeatherMode = 5; gWeatherMode = 5;
@ -632,21 +647,21 @@ void Entrance_OverrideWeatherState() {
} }
// Death Mountain Cloudy // Death Mountain Cloudy
if (!Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP)) { // have not beaten Fire Temple if (!Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP)) { // have not beaten Fire Temple
if (gPlayState->nextEntranceIndex == 0x04D6) { // Lost Woods Goron City Shortcut if (gPlayState->nextEntranceIndex == ENTR_LOST_WOODS_6) { // Lost Woods Goron City Shortcut
gWeatherMode = 2; gWeatherMode = 2;
return; return;
} }
switch (gPlayState->sceneNum) { switch (gPlayState->sceneNum) {
case 82: // Kakariko case SCENE_KAKARIKO_VILLAGE: // Kakariko
case 83: // Graveyard case SCENE_GRAVEYARD: // Graveyard
case 96: // Death Mountain Trail case SCENE_DEATH_MOUNTAIN_TRAIL: // Death Mountain Trail
case 97: // Death Mountain Crater case SCENE_DEATH_MOUNTAIN_CRATER: // Death Mountain Crater
if (!gPlayState->envCtx.gloomySkyMode) { if (!gPlayState->envCtx.gloomySkyMode) {
gPlayState->envCtx.gloomySkyMode = 1; gPlayState->envCtx.gloomySkyMode = 1;
} }
switch (gSaveContext.entranceIndex) { switch (gSaveContext.entranceIndex) {
case 0x00DB: // Kakariko from HF case ENTR_KAKARIKO_VILLAGE_0: // Kakariko from HF
case 0x0195: // Kakariko from Graveyard case ENTR_KAKARIKO_VILLAGE_2: // Kakariko from Graveyard
break; break;
default: default:
gWeatherMode = 2; gWeatherMode = 2;
@ -661,13 +676,13 @@ void Entrance_OverrideWeatherState() {
// Child should always be thrown in the stream when caught in the valley, and placed at the fortress entrance from valley when caught in the fortress // Child should always be thrown in the stream when caught in the valley, and placed at the fortress entrance from valley when caught in the fortress
void Entrance_OverrideGeurdoGuardCapture(void) { void Entrance_OverrideGeurdoGuardCapture(void) {
if (LINK_IS_CHILD) { if (LINK_IS_CHILD) {
gPlayState->nextEntranceIndex = 0x1A5; // Geurdo Valley thrown out gPlayState->nextEntranceIndex = ENTR_GERUDO_VALLEY_1; // Geurdo Valley thrown out
} }
if ((LINK_IS_CHILD || Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) && if ((LINK_IS_CHILD || Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) &&
gPlayState->nextEntranceIndex == 0x1A5) { // Geurdo Valley thrown out gPlayState->nextEntranceIndex == ENTR_GERUDO_VALLEY_1) { // Geurdo Valley thrown out
if (gPlayState->sceneNum != 0x5A) { // Geurdo Valley if (gPlayState->sceneNum != SCENE_GERUDO_VALLEY) { // Geurdo Valley
gPlayState->nextEntranceIndex = 0x129; // Gerudo Fortress gPlayState->nextEntranceIndex = ENTR_GERUDOS_FORTRESS_0; // Gerudo Fortress
} }
} }
} }
@ -784,7 +799,7 @@ u8 Entrance_GetIsEntranceDiscovered(u16 entranceIndex) {
return 0; return 0;
} }
void Entrance_SetEntranceDiscovered(u16 entranceIndex) { void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) {
// Skip if already set to save time from setting the connected entrance or // Skip if already set to save time from setting the connected entrance or
// if this entrance is outside of the randomized entrance range (i.e. is a dynamic entrance) // if this entrance is outside of the randomized entrance range (i.e. is a dynamic entrance)
if (entranceIndex > MAX_ENTRANCE_RANDO_USED_INDEX || Entrance_GetIsEntranceDiscovered(entranceIndex)) { if (entranceIndex > MAX_ENTRANCE_RANDO_USED_INDEX || Entrance_GetIsEntranceDiscovered(entranceIndex)) {
@ -796,14 +811,20 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) { if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex)); u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit; gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit;
// Set connected
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) { // Set reverse entrance when not decoupled
if (entranceIndex == gSaveContext.entranceOverrides[i].index) { if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination); for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
break; if (entranceIndex == gSaveContext.entranceOverrides[i].index) {
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination, true);
break;
}
} }
} }
} }
// Save entrancesDiscovered
Save_SaveSection(SECTION_ID_ENTRANCES); // Save entrancesDiscovered when it is not the reversed entrance
if (!isReversedEntrance) {
Save_SaveSection(SECTION_ID_ENTRANCES);
}
} }

View file

@ -6,32 +6,78 @@
//Entrance Table Data: https://wiki.cloudmodding.com/oot/Entrance_Table_(Data) //Entrance Table Data: https://wiki.cloudmodding.com/oot/Entrance_Table_(Data)
//Accessed June 2021, published content date at the time was 14 March 2020, at 21:47 //Accessed June 2021, published content date at the time was 14 March 2020, at 21:47
#define ENTRANCE_TABLE_SIZE 0x0614 #define ENTRANCE_TABLE_SIZE ENTR_MAX
#define DEKU_TREE_ENTRANCE 0x0000 #define DEKU_TREE_ENTRANCE ENTR_DEKU_TREE_0
#define DODONGOS_CAVERN_ENTRANCE 0x0004 #define DODONGOS_CAVERN_ENTRANCE ENTR_DODONGOS_CAVERN_0
#define JABU_JABUS_BELLY_ENTRANCE 0x0028 #define JABU_JABUS_BELLY_ENTRANCE ENTR_JABU_JABU_0
#define FOREST_TEMPLE_ENTRANCE 0x169 #define FOREST_TEMPLE_ENTRANCE ENTR_FOREST_TEMPLE_0
#define FIRE_TEMPLE_ENTRANCE 0x165 #define FIRE_TEMPLE_ENTRANCE ENTR_FIRE_TEMPLE_0
#define WATER_TEMPLE_ENTRANCE 0x0010 #define WATER_TEMPLE_ENTRANCE ENTR_WATER_TEMPLE_0
#define SPIRIT_TEMPLE_ENTRANCE 0x0082 #define SPIRIT_TEMPLE_ENTRANCE ENTR_SPIRIT_TEMPLE_0
#define SHADOW_TEMPLE_ENTRANCE 0x0037 #define SHADOW_TEMPLE_ENTRANCE ENTR_SHADOW_TEMPLE_0
#define BOTTOM_OF_THE_WELL_ENTRANCE 0x0098 #define BOTTOM_OF_THE_WELL_ENTRANCE ENTR_BOTTOM_OF_THE_WELL_0
#define GERUDO_TRAINING_GROUNDS_ENTRANCE 0x0008 #define GERUDO_TRAINING_GROUNDS_ENTRANCE ENTR_GERUDO_TRAINING_GROUND_0
#define ICE_CAVERN_ENTRANCE 0x0088 #define ICE_CAVERN_ENTRANCE ENTR_ICE_CAVERN_0
#define GANONS_CASTLE_ENTRANCE 0x0467 #define GANONS_CASTLE_ENTRANCE ENTR_INSIDE_GANONS_CASTLE_0
#define LINK_HOUSE_SAVEWARP_ENTRANCE 0x00BB #define LINK_HOUSE_SAVEWARP_ENTRANCE ENTR_LINKS_HOUSE_0
#define ENTRANCE_RANDO_GROTTO_LOAD_START 0x0700 #define ENTRANCE_RANDO_GROTTO_LOAD_START 0x0700
#define ENTRANCE_RANDO_GROTTO_EXIT_START 0x0800 #define ENTRANCE_RANDO_GROTTO_EXIT_START 0x0800
#define MAX_ENTRANCE_RANDO_USED_INDEX 0x0820 #define MAX_ENTRANCE_RANDO_USED_INDEX 0x0820
typedef enum {
/* 0x00 */ GROTTO_COLOSSUS_OFFSET,
/* 0x01 */ GROTTO_LH_OFFSET,
/* 0x02 */ GROTTO_ZR_STORMS_OFFSET,
/* 0x03 */ GROTTO_ZR_FAIRY_OFFSET,
/* 0x04 */ GROTTO_ZR_OPEN_OFFSET,
/* 0x05 */ GROTTO_DMC_HAMMER_OFFSET,
/* 0x06 */ GROTTO_DMC_UPPER_OFFSET,
/* 0x07 */ GROTTO_GORON_CITY_OFFSET,
/* 0x08 */ GROTTO_DMT_STORMS_OFFSET,
/* 0x09 */ GROTTO_DMT_COW_OFFSET,
/* 0x0A */ GROTTO_KAK_OPEN_OFFSET,
/* 0x0B */ GROTTO_KAK_REDEAD_OFFSET,
/* 0x0C */ GROTTO_HC_STORMS_OFFSET,
/* 0x0D */ GROTTO_HF_TEKTITE_OFFSET,
/* 0x0E */ GROTTO_HF_NEAR_KAK_OFFSET,
/* 0x0F */ GROTTO_HF_FAIRY_OFFSET,
/* 0x10 */ GROTTO_HF_NEAR_MARKET_OFFSET,
/* 0x11 */ GROTTO_HF_COW_OFFSET,
/* 0x12 */ GROTTO_HF_INSIDE_FENCE_OFFSET,
/* 0x13 */ GROTTO_HF_OPEN_OFFSET,
/* 0x14 */ GROTTO_HF_SOUTHEAST_OFFSET,
/* 0x15 */ GROTTO_LLR_OFFSET,
/* 0x16 */ GROTTO_SFM_WOLFOS_OFFSET,
/* 0x17 */ GROTTO_SFM_STORMS_OFFSET,
/* 0x18 */ GROTTO_SFM_FAIRY_OFFSET,
/* 0x19 */ GROTTO_LW_SCRUBS_OFFSET,
/* 0x1A */ GROTTO_LW_NEAR_SHORTCUTS_OFFSET,
/* 0x1B */ GROTTO_KF_STORMS_OFFSET,
/* 0x1C */ GROTTO_ZD_STORMS_OFFSET,
/* 0x1D */ GROTTO_GF_STORMS_OFFSET,
/* 0x1E */ GROTTO_GV_STORMS_OFFSET,
/* 0x1F */ GROTTO_GV_OCTOROK_OFFSET,
/* 0x20 */ GROTTO_LW_DEKU_THEATRE_OFFSET,
/* 0x21 */ GROTTO_OFFSET_MAX,
} GrottoEntranceOffsets;
#define ENTRANCE_RANDO_GROTTO_LOAD(index) ENTRANCE_RANDO_GROTTO_LOAD_START + index
#define ENTRANCE_RANDO_GROTTO_EXIT(index) ENTRANCE_RANDO_GROTTO_EXIT_START + index
#define ENTRANCE_OVERRIDES_MAX_COUNT 259 // 11 one-way entrances + 124 two-way entrances (x2) #define ENTRANCE_OVERRIDES_MAX_COUNT 259 // 11 one-way entrances + 124 two-way entrances (x2)
#define SHUFFLEABLE_BOSS_COUNT 8 #define SHUFFLEABLE_BOSS_COUNT 8
#define SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT 66 // Max entrance rando index is 0x0820, (2080 / 32 == 65) + 1 #define SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT 66 // Max entrance rando index is 0x0820, (2080 / 32 == 65) + 1
#define SAVEFILE_SCENES_DISCOVERED_IDX_COUNT 4 // Max scene ID is 0x6E, (110 / 32 == 3) + 1 #define SAVEFILE_SCENES_DISCOVERED_IDX_COUNT 4 // Max scene ID is 0x6E, (110 / 32 == 3) + 1
#define ENTRANCE_INFO_FIELD(continueBgm, displayTitleCard, endTransType, startTransType) \
(((continueBgm) ? ENTRANCE_INFO_CONTINUE_BGM_FLAG : 0) | \
((displayTitleCard) ? ENTRANCE_INFO_DISPLAY_TITLE_CARD_FLAG : 0) | \
(((endTransType) << ENTRANCE_INFO_END_TRANS_TYPE_SHIFT) & ENTRANCE_INFO_END_TRANS_TYPE_MASK) | \
(((startTransType) << ENTRANCE_INFO_START_TRANS_TYPE_SHIFT) & ENTRANCE_INFO_START_TRANS_TYPE_MASK))
typedef struct { typedef struct {
int16_t index; int16_t index;
int16_t destination; int16_t destination;
@ -63,7 +109,7 @@ void Entrance_OverrideSpawnScene(int32_t sceneNum, int32_t spawn);
int32_t Entrance_OverrideSpawnSceneRoom(int32_t sceneNum, int32_t spawn, int32_t room); int32_t Entrance_OverrideSpawnSceneRoom(int32_t sceneNum, int32_t spawn, int32_t room);
void Entrance_EnableFW(void); void Entrance_EnableFW(void);
uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex); uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex);
void Entrance_SetEntranceDiscovered(uint16_t entranceIndex); void Entrance_SetEntranceDiscovered(uint16_t entranceIndex, uint8_t isReversedEntrance);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -67,305 +67,306 @@ static std::string groupTypeNames[] = {
}; };
// Entrance data for the tracker taken from the 3ds rando entrance tracker, and supplemented with scene/spawn info and meta search tags // Entrance data for the tracker taken from the 3ds rando entrance tracker, and supplemented with scene/spawn info and meta search tags
// ENTR_HYRULE_FIELD_10 and ENTR_POTION_SHOP_KAKARIKO_1 have been repurposed for entrance randomizer
const EntranceData entranceData[] = { const EntranceData entranceData[] = {
//index, reverse, scenes (and spawns), source name, destination name, source group, destination group, type, metaTag, oneExit //index, reverse, scenes (and spawns), source name, destination name, source group, destination group, type, metaTag, oneExit
{ 0x00BB, -1, SINGLE_SCENE_INFO(0x34), "Child Spawn", "Link's House", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_LINKS_HOUSE_0, -1, SINGLE_SCENE_INFO(SCENE_LINKS_HOUSE), "Child Spawn", "Link's House", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0282, -1, SINGLE_SCENE_INFO(0x43), "Adult Spawn", "Temple of Time", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_HYRULE_FIELD_10, -1, SINGLE_SCENE_INFO(SCENE_TEMPLE_OF_TIME), "Adult Spawn", "Temple of Time", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0600, -1, {{ -1 }}, "Minuet of Forest", "SFM Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_SACRED_FOREST_MEADOW_2, -1, {{ -1 }}, "Minuet of Forest", "SFM Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x04F6, -1, {{ -1 }}, "Bolero of Fire", "DMC Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_DEATH_MOUNTAIN_CRATER_4, -1, {{ -1 }}, "Bolero of Fire", "DMC Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0604, -1, {{ -1 }}, "Serenade of Water", "Lake Hylia Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_LAKE_HYLIA_8, -1, {{ -1 }}, "Serenade of Water", "Lake Hylia Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x01F1, -1, {{ -1 }}, "Requiem of Spirit", "Desert Colossus Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_DESERT_COLOSSUS_5, -1, {{ -1 }}, "Requiem of Spirit", "Desert Colossus Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0568, -1, {{ -1 }}, "Nocturne of Shadow", "Graveyard Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_GRAVEYARD_7, -1, {{ -1 }}, "Nocturne of Shadow", "Graveyard Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x05F4, -1, {{ -1 }}, "Prelude of Light", "Temple of Time Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_TEMPLE_OF_TIME_7, -1, {{ -1 }}, "Prelude of Light", "Temple of Time Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0554, -1, SINGLE_SCENE_INFO(0x60), "DMT Owl Flight", "Kakariko Village Owl Drop", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_KAKARIKO_VILLAGE_14, -1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT Owl Flight", "Kakariko Village Owl Drop", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x027E, -1, SINGLE_SCENE_INFO(0x57), "LH Owl Flight", "Hyrule Field Owl Drop", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY}, { ENTR_HYRULE_FIELD_9, -1, SINGLE_SCENE_INFO(SCENE_LAKE_HYLIA), "LH Owl Flight", "Hyrule Field Owl Drop", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
// Kokiri Forest // Kokiri Forest
{ 0x05E0, 0x020D, SINGLE_SCENE_INFO(0x55), "KF", "Lost Woods Bridge", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_LOST_WOODS_9, ENTR_KOKIRI_FOREST_2, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "Lost Woods Bridge", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x011E, 0x0286, SINGLE_SCENE_INFO(0x55), "KF", "Lost Woods", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_LOST_WOODS_0, ENTR_KOKIRI_FOREST_6, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "Lost Woods", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0272, 0x0211, SINGLE_SCENE_INFO(0x55), "KF", "Link's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_LINKS_HOUSE_1, ENTR_KOKIRI_FOREST_3, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "Link's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0433, 0x0443, SINGLE_SCENE_INFO(0x55), "KF", "Mido's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_MIDOS_HOUSE_0, ENTR_KOKIRI_FOREST_9, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "Mido's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0437, 0x0447, SINGLE_SCENE_INFO(0x55), "KF", "Saria's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_SARIAS_HOUSE_0, ENTR_KOKIRI_FOREST_10, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "Saria's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x009C, 0x033C, SINGLE_SCENE_INFO(0x55), "KF", "House of Twins", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_TWINS_HOUSE_0, ENTR_KOKIRI_FOREST_8, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "House of Twins", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x00C9, 0x026A, SINGLE_SCENE_INFO(0x55), "KF", "Know-It-All House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_KNOW_IT_ALL_BROS_HOUSE_0, ENTR_KOKIRI_FOREST_5, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "Know-It-All House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x00C1, 0x0266, SINGLE_SCENE_INFO(0x55), "KF", "KF Shop", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_KOKIRI_SHOP_0, ENTR_KOKIRI_FOREST_4, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "KF Shop", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x071B, 0x081B, SINGLE_SCENE_INFO(0x55), "KF", "KF Storms Grotto", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "KF Storms Grotto", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0000, 0x0209, SINGLE_SCENE_INFO(0x55), "KF", "Deku Tree", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_DEKU_TREE_0, ENTR_KOKIRI_FOREST_1, SINGLE_SCENE_INFO(SCENE_KOKIRI_FOREST), "KF", "Deku Tree", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0211, 0x0272, SINGLE_SCENE_INFO(0x34), "Link's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""}, { ENTR_KOKIRI_FOREST_3, ENTR_LINKS_HOUSE_1, SINGLE_SCENE_INFO(SCENE_LINKS_HOUSE), "Link's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x0443, 0x0433, SINGLE_SCENE_INFO(0x28), "Mido's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""}, { ENTR_KOKIRI_FOREST_9, ENTR_MIDOS_HOUSE_0, SINGLE_SCENE_INFO(SCENE_MIDOS_HOUSE), "Mido's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x0447, 0x0437, SINGLE_SCENE_INFO(0x29), "Saria's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""}, { ENTR_KOKIRI_FOREST_10, ENTR_SARIAS_HOUSE_0, SINGLE_SCENE_INFO(SCENE_SARIAS_HOUSE), "Saria's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x033C, 0x009C, SINGLE_SCENE_INFO(0x27), "House of Twins", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""}, { ENTR_KOKIRI_FOREST_8, ENTR_TWINS_HOUSE_0, SINGLE_SCENE_INFO(SCENE_TWINS_HOUSE), "House of Twins", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x026A, 0x00C9, SINGLE_SCENE_INFO(0x26), "Know-It-All House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""}, { ENTR_KOKIRI_FOREST_5, ENTR_KNOW_IT_ALL_BROS_HOUSE_0, SINGLE_SCENE_INFO(SCENE_KNOW_IT_ALL_BROS_HOUSE), "Know-It-All House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x0266, 0x00C1, SINGLE_SCENE_INFO(0x2D), "KF Shop", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""}, { ENTR_KOKIRI_FOREST_4, ENTR_KOKIRI_SHOP_0, SINGLE_SCENE_INFO(SCENE_KOKIRI_SHOP), "KF Shop", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x081B, 0x071B, {{ 0x3E, 0x00 }}, "KF Storms Grotto", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "KF Storms Grotto", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0209, 0x0000, SINGLE_SCENE_INFO(0x00), "Deku Tree", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, ""}, { ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, ""},
{ 0x040F, 0x0252, SINGLE_SCENE_INFO(0x00), "Deku Tree Boss Door", "Gohma", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree Boss Door", "Gohma", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0252, 0x040F, SINGLE_SCENE_INFO(0x11), "Gohma", "Deku Tree Boss Door", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_DEKU_TREE_1, ENTR_DEKU_TREE_BOSS_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE_BOSS), "Gohma", "Deku Tree Boss Door", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
// Lost Woods // Lost Woods
{ 0x020D, 0x05E0, SINGLE_SCENE_INFO(0x5B), "Lost Woods Bridge", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_KOKIRI_FOREST_2, ENTR_LOST_WOODS_9, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods Bridge", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0185, 0x04DE, SINGLE_SCENE_INFO(0x5B), "Lost Woods Bridge", "Hyrule Field", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "lw,hf"}, { ENTR_HYRULE_FIELD_3, ENTR_LOST_WOODS_8, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods Bridge", "Hyrule Field", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "lw,hf"},
{ 0x0286, 0x011E, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_KOKIRI_FOREST_6, ENTR_LOST_WOODS_0, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x04E2, 0x04D6, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "Goron City", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "lw,gc"}, { ENTR_GORON_CITY_3, ENTR_LOST_WOODS_6, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods", "Goron City", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "lw,gc"},
{ 0x01DD, 0x04DA, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "ZR", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_ZORAS_RIVER_4, ENTR_LOST_WOODS_7, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods", "ZR", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x00FC, 0x01A9, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "SFM", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_SACRED_FOREST_MEADOW_0, ENTR_LOST_WOODS_1, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods", "SFM", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x071A, 0x081A, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "LW Near Shortcuts Grotto", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET), SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods", "LW Near Shortcuts Grotto", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,chest", 1},
{ 0x0719, 0x0819, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "LW Scrubs Grotto", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET), SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods", "LW Scrubs Grotto", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw", 1},
{ 0x0720, 0x0820, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "Deku Theater", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,mask,stage", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET), SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods", "Deku Theater", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,mask,stage", 1},
{ 0x081A, 0x071A, {{ 0x3E, 0x00 }}, "LW Near Shortcuts Grotto", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "LW Near Shortcuts Grotto", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,chest"},
{ 0x0819, 0x0719, {{ 0x3E, 0x07 }}, "LW Scrubs Grotto", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET), {{ SCENE_GROTTOS, 0x07 }}, "LW Scrubs Grotto", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw"},
{ 0x0820, 0x0720, {{ 0x3E, 0x0C }}, "Deku Theater", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,mask,stage"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET), {{ SCENE_GROTTOS, 0x0C }}, "Deku Theater", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,mask,stage"},
// Sacred Forest Meadow // Sacred Forest Meadow
{ 0x01A9, 0x00FC, SINGLE_SCENE_INFO(0x56), "SFM", "Lost Woods", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_LOST_WOODS_1, ENTR_SACRED_FOREST_MEADOW_0, SINGLE_SCENE_INFO(SCENE_SACRED_FOREST_MEADOW), "SFM", "Lost Woods", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0716, 0x0816, SINGLE_SCENE_INFO(0x56), "SFM", "SFM Wolfos Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET), SINGLE_SCENE_INFO(SCENE_SACRED_FOREST_MEADOW), "SFM", "SFM Wolfos Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0718, 0x0818, SINGLE_SCENE_INFO(0x56), "SFM", "SFM Fairy Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET), SINGLE_SCENE_INFO(SCENE_SACRED_FOREST_MEADOW), "SFM", "SFM Fairy Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0717, 0x0817, SINGLE_SCENE_INFO(0x56), "SFM", "SFM Storms Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_SACRED_FOREST_MEADOW), "SFM", "SFM Storms Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0169, 0x0215, SINGLE_SCENE_INFO(0x56), "SFM", "Forest Temple", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FOREST_TEMPLE_0, ENTR_SACRED_FOREST_MEADOW_1, SINGLE_SCENE_INFO(SCENE_SACRED_FOREST_MEADOW), "SFM", "Forest Temple", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0816, 0x0716, {{ 0x3E, 0x08 }}, "SFM Wolfos Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET), {{ SCENE_GROTTOS, 0x08 }}, "SFM Wolfos Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO},
{ 0x0818, 0x0718, {{ 0x3C, 0x00 }}, "SFM Fairy Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET), {{ SCENE_FAIRYS_FOUNTAIN, 0x00 }}, "SFM Fairy Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO},
{ 0x0817, 0x0717, {{ 0x3E, 0x0A }}, "SFM Storms Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET), {{ SCENE_GROTTOS, 0x0A }}, "SFM Storms Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x0215, 0x0169, SINGLE_SCENE_INFO(0x03), "Forest Temple", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON}, { ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON},
{ 0x000C, 0x024E, SINGLE_SCENE_INFO(0x03), "Forest Temple Boss Door", "Phantom Ganon", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple Boss Door", "Phantom Ganon", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x024E, 0x000C, SINGLE_SCENE_INFO(0x14), "Phantom Ganon", "Forest Temple Boss Door", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FOREST_TEMPLE_1, ENTR_FOREST_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE_BOSS), "Phantom Ganon", "Forest Temple Boss Door", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
// Kakariko Village // Kakariko Village
{ 0x017D, 0x00DB, SINGLE_SCENE_INFO(0x52), "Kakariko", "Hyrule Field", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_HYRULE_FIELD_1, ENTR_KAKARIKO_VILLAGE_0, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Hyrule Field", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x00E4, 0x0195, SINGLE_SCENE_INFO(0x52), "Kakariko", "Graveyard", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_OVERWORLD}, { ENTR_GRAVEYARD_0, ENTR_KAKARIKO_VILLAGE_2, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Graveyard", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_OVERWORLD},
{ 0x013D, 0x0191, SINGLE_SCENE_INFO(0x52), "Kakariko", "DMT", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD}, { ENTR_DEATH_MOUNTAIN_TRAIL_0, ENTR_KAKARIKO_VILLAGE_1, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "DMT", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD},
{ 0x02FD, 0x0349, SINGLE_SCENE_INFO(0x52), "Kakariko", "Carpenter Boss House", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0, ENTR_KAKARIKO_VILLAGE_6, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Carpenter Boss House", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0550, 0x04EE, SINGLE_SCENE_INFO(0x52), "Kakariko", "House of Skulltula", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_HOUSE_OF_SKULLTULA_0, ENTR_KAKARIKO_VILLAGE_11, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "House of Skulltula", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x039C, 0x0345, SINGLE_SCENE_INFO(0x52), "Kakariko", "Impa's House Front", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_IMPAS_HOUSE_0, ENTR_KAKARIKO_VILLAGE_5, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Impa's House Front", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x05C8, 0x05DC, SINGLE_SCENE_INFO(0x52), "Kakariko", "Impa's House Back", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "cow", 1}, { ENTR_IMPAS_HOUSE_1, ENTR_KAKARIKO_VILLAGE_15, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Impa's House Back", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "cow", 1},
{ 0x0453, 0x0351, SINGLE_SCENE_INFO(0x52), "Kakariko", "Windmill", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_WINDMILL_AND_DAMPES_GRAVE_1, ENTR_KAKARIKO_VILLAGE_8, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Windmill", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x003B, 0x0463, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Shooting Gallery", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "adult", 1}, { ENTR_SHOOTING_GALLERY_0, ENTR_KAKARIKO_VILLAGE_10, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Kak Shooting Gallery", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "adult", 1},
{ 0x0072, 0x034D, SINGLE_SCENE_INFO(0x52), "Kakariko", "Granny's Potion Shop", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_POTION_SHOP_GRANNY_0, ENTR_KAKARIKO_VILLAGE_7, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Granny's Potion Shop", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x00B7, 0x0201, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Bazaar", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "shop", 1}, { ENTR_BAZAAR_0, ENTR_KAKARIKO_VILLAGE_3, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Kak Bazaar", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "shop", 1},
{ 0x0384, 0x044B, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Potion Shop Front", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_POTION_SHOP_KAKARIKO_0, ENTR_KAKARIKO_VILLAGE_9, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Kak Potion Shop Front", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x03EC, 0x04FF, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Potion Shop Back", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_POTION_SHOP_KAKARIKO_2, ENTR_KAKARIKO_VILLAGE_12, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Kak Potion Shop Back", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x070A, 0x080A, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Open Grotto", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET), SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Kak Open Grotto", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x070B, 0x080B, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Redead Grotto", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET), SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Kak Redead Grotto", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0098, 0x02A6, SINGLE_SCENE_INFO(0x52), "Kakariko", "Bottom of the Well", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_DUNGEON, "botw", 1}, { ENTR_BOTTOM_OF_THE_WELL_0, ENTR_KAKARIKO_VILLAGE_4, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Bottom of the Well", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_DUNGEON, "botw", 1},
{ 0x0349, 0x02FD, SINGLE_SCENE_INFO(0x2A), "Carpenter Boss House", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_6, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0, SINGLE_SCENE_INFO(SCENE_KAKARIKO_CENTER_GUEST_HOUSE), "Carpenter Boss House", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x04EE, 0x0550, SINGLE_SCENE_INFO(0x50), "House of Skulltula", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_11, ENTR_HOUSE_OF_SKULLTULA_0, SINGLE_SCENE_INFO(SCENE_HOUSE_OF_SKULLTULA), "House of Skulltula", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x0345, 0x039C, SINGLE_SCENE_INFO(0x37), "Impa's House Front", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_5, ENTR_IMPAS_HOUSE_0, SINGLE_SCENE_INFO(SCENE_IMPAS_HOUSE), "Impa's House Front", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x05DC, 0x05C8, SINGLE_SCENE_INFO(0x37), "Impa's House Back", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "cow"}, { ENTR_KAKARIKO_VILLAGE_15, ENTR_IMPAS_HOUSE_1, SINGLE_SCENE_INFO(SCENE_IMPAS_HOUSE), "Impa's House Back", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "cow"},
{ 0x0351, 0x0453, SINGLE_SCENE_INFO(0x48), "Windmill", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_8, ENTR_WINDMILL_AND_DAMPES_GRAVE_1, SINGLE_SCENE_INFO(SCENE_WINDMILL_AND_DAMPES_GRAVE), "Windmill", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x0463, 0x003B, {{ 0x42, 0x00 }}, "Kak Shooting Gallery", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_10, ENTR_SHOOTING_GALLERY_0, {{ SCENE_SHOOTING_GALLERY, 0x00 }}, "Kak Shooting Gallery", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x034D, 0x0072, SINGLE_SCENE_INFO(0x4E), "Granny's Potion Shop", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_7, ENTR_POTION_SHOP_GRANNY_0, SINGLE_SCENE_INFO(SCENE_POTION_SHOP_GRANNY), "Granny's Potion Shop", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x0201, 0x00B7, {{ 0x2C, 0x00 }}, "Kak Bazaar", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "shop"}, { ENTR_KAKARIKO_VILLAGE_3, ENTR_BAZAAR_0, {{ SCENE_BAZAAR, 0x00 }}, "Kak Bazaar", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "shop"},
{ 0x044B, 0x0384, SINGLE_SCENE_INFO(0x30), "Kak Potion Shop Front", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_9, ENTR_POTION_SHOP_KAKARIKO_0, SINGLE_SCENE_INFO(SCENE_POTION_SHOP_KAKARIKO), "Kak Potion Shop Front", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x04FF, 0x03EC, SINGLE_SCENE_INFO(0x30), "Kak Potion Shop Back", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR}, { ENTR_KAKARIKO_VILLAGE_12, ENTR_POTION_SHOP_KAKARIKO_2, SINGLE_SCENE_INFO(SCENE_POTION_SHOP_KAKARIKO), "Kak Potion Shop Back", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x080A, 0x070A, {{ 0x3E, 0x00 }}, "Kak Open Grotto", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "Kak Open Grotto", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x080B, 0x070B, {{ 0x3E, 0x03 }}, "Kak Redead Grotto", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET), {{ SCENE_GROTTOS, 0x03 }}, "Kak Redead Grotto", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x02A6, 0x0098, SINGLE_SCENE_INFO(0x08), "Bottom of the Well", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_DUNGEON, "botw"}, { ENTR_KAKARIKO_VILLAGE_4, ENTR_BOTTOM_OF_THE_WELL_0, SINGLE_SCENE_INFO(SCENE_BOTTOM_OF_THE_WELL), "Bottom of the Well", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_DUNGEON, "botw"},
// The Graveyard // The Graveyard
{ 0x0195, 0x00E4, SINGLE_SCENE_INFO(0x53), "Graveyard", "Kakariko", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD}, { ENTR_KAKARIKO_VILLAGE_2, ENTR_GRAVEYARD_0, SINGLE_SCENE_INFO(SCENE_GRAVEYARD), "Graveyard", "Kakariko", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD},
{ 0x030D, 0x0355, SINGLE_SCENE_INFO(0x53), "Graveyard", "Dampe's Shack", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_GRAVEKEEPERS_HUT_0, ENTR_GRAVEYARD_2, SINGLE_SCENE_INFO(SCENE_GRAVEYARD), "Graveyard", "Dampe's Shack", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x004B, 0x035D, SINGLE_SCENE_INFO(0x53), "Graveyard", "Shield Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0, ENTR_GRAVEYARD_4, SINGLE_SCENE_INFO(SCENE_GRAVEYARD), "Graveyard", "Shield Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x031C, 0x0361, SINGLE_SCENE_INFO(0x53), "Graveyard", "Heart Piece Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTR_REDEAD_GRAVE_0, ENTR_GRAVEYARD_5, SINGLE_SCENE_INFO(SCENE_GRAVEYARD), "Graveyard", "Heart Piece Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x002D, 0x050B, SINGLE_SCENE_INFO(0x53), "Graveyard", "Composer's Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTR_ROYAL_FAMILYS_TOMB_0, ENTR_GRAVEYARD_6, SINGLE_SCENE_INFO(SCENE_GRAVEYARD), "Graveyard", "Composer's Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x044F, 0x0359, SINGLE_SCENE_INFO(0x53), "Graveyard", "Dampe's Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "race", 1}, { ENTR_WINDMILL_AND_DAMPES_GRAVE_0, ENTR_GRAVEYARD_3, SINGLE_SCENE_INFO(SCENE_GRAVEYARD), "Graveyard", "Dampe's Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "race", 1},
{ 0x0037, 0x0205, SINGLE_SCENE_INFO(0x53), "Graveyard", "Shadow Temple", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_SHADOW_TEMPLE_0, ENTR_GRAVEYARD_1, SINGLE_SCENE_INFO(SCENE_GRAVEYARD), "Graveyard", "Shadow Temple", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0355, 0x030D, SINGLE_SCENE_INFO(0x3A), "Dampe's Shack", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_INTERIOR}, { ENTR_GRAVEYARD_2, ENTR_GRAVEKEEPERS_HUT_0, SINGLE_SCENE_INFO(SCENE_GRAVEKEEPERS_HUT), "Dampe's Shack", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_INTERIOR},
{ 0x035D, 0x004B, SINGLE_SCENE_INFO(0x40), "Shield Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO}, { ENTR_GRAVEYARD_4, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0, SINGLE_SCENE_INFO(SCENE_GRAVE_WITH_FAIRYS_FOUNTAIN), "Shield Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO},
{ 0x0361, 0x031C, SINGLE_SCENE_INFO(0x3F), "Heart Piece Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO}, { ENTR_GRAVEYARD_5, ENTR_REDEAD_GRAVE_0, SINGLE_SCENE_INFO(SCENE_REDEAD_GRAVE), "Heart Piece Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO},
{ 0x050B, 0x002D, SINGLE_SCENE_INFO(0x41), "Composer's Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO}, { ENTR_GRAVEYARD_6, ENTR_ROYAL_FAMILYS_TOMB_0, SINGLE_SCENE_INFO(SCENE_ROYAL_FAMILYS_TOMB), "Composer's Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO},
{ 0x0359, 0x044F, SINGLE_SCENE_INFO(0x48), "Dampe's Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "race"}, { ENTR_GRAVEYARD_3, ENTR_WINDMILL_AND_DAMPES_GRAVE_0, SINGLE_SCENE_INFO(SCENE_WINDMILL_AND_DAMPES_GRAVE), "Dampe's Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "race"},
{ 0x0205, 0x0037, SINGLE_SCENE_INFO(0x07), "Shadow Temple", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON}, { ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON},
{ 0x0413, 0x02B2, SINGLE_SCENE_INFO(0x07), "Shadow Temple Boss Door", "Bongo-Bongo", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple Boss Door", "Bongo-Bongo", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x02B2, 0x0413, SINGLE_SCENE_INFO(0x18), "Bongo-Bongo", "Shadow Temple Boss Door", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_SHADOW_TEMPLE_1, ENTR_SHADOW_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE_BOSS), "Bongo-Bongo", "Shadow Temple Boss Door", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
// Death Mountain Trail // Death Mountain Trail
{ 0x0191, 0x013D, SINGLE_SCENE_INFO(0x60), "DMT", "Kakariko", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD}, { ENTR_GORON_CITY_0, ENTR_DEATH_MOUNTAIN_TRAIL_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x014D, 0x01B9, SINGLE_SCENE_INFO(0x60), "DMT", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"}, { ENTR_KAKARIKO_VILLAGE_1, ENTR_DEATH_MOUNTAIN_TRAIL_0, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "Kakariko", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD},
{ 0x0147, 0x01BD, SINGLE_SCENE_INFO(0x60), "DMT", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_OVERWORLD}, { ENTR_DEATH_MOUNTAIN_CRATER_0, ENTR_DEATH_MOUNTAIN_TRAIL_2, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_OVERWORLD},
{ 0x0315, 0x045B, SINGLE_SCENE_INFO(0x60), "DMT", "DMT Great Fairy Fountain", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_0, ENTR_DEATH_MOUNTAIN_TRAIL_4, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "DMT Great Fairy Fountain", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0708, 0x0808, SINGLE_SCENE_INFO(0x60), "DMT", "DMT Storms Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "DMT Storms Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0709, 0x0809, SINGLE_SCENE_INFO(0x60), "DMT", "DMT Cow Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET), SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "DMT Cow Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0004, 0x0242, SINGLE_SCENE_INFO(0x60), "DMT", "Dodongo's Cavern", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1}, { ENTR_DODONGOS_CAVERN_0, ENTR_DEATH_MOUNTAIN_TRAIL_3, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "Dodongo's Cavern", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ 0x045B, 0x0315, {{ 0x3B, 0x00 }}, "DMT Great Fairy Fountain", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_INTERIOR}, { ENTR_DEATH_MOUNTAIN_TRAIL_4, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_0, {{ SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 0x00 }}, "DMT Great Fairy Fountain", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_INTERIOR},
{ 0x0808, 0x0708, {{ 0x3E, 0x00 }}, "DMT Storms Grotto", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "DMT Storms Grotto", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0809, 0x0709, {{ 0x3E, 0x0D }}, "DMT Cow Grotto", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET), {{ SCENE_GROTTOS, 0x0D }}, "DMT Cow Grotto", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO},
{ 0x0242, 0x0004, SINGLE_SCENE_INFO(0x01), "Dodongo's Cavern", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc"}, { ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc"},
{ 0x040B, 0x00C5, SINGLE_SCENE_INFO(0x01), "Dodongo's Cavern Boss Door", "King Dodongo", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1}, { ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern Boss Door", "King Dodongo", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ 0x00C5, 0x040B, SINGLE_SCENE_INFO(0x12), "King Dodongo", "Dodongo's Cavern Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1}, { ENTR_DODONGOS_CAVERN_1, ENTR_DODONGOS_CAVERN_BOSS_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN_BOSS), "King Dodongo", "Dodongo's Cavern Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
// Death Mountain Crater // Death Mountain Crater
{ 0x01C1, 0x0246, SINGLE_SCENE_INFO(0x61), "DMC", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"}, { ENTR_GORON_CITY_1, ENTR_DEATH_MOUNTAIN_CRATER_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x01BD, 0x0147, SINGLE_SCENE_INFO(0x61), "DMC", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD}, { ENTR_DEATH_MOUNTAIN_TRAIL_2, ENTR_DEATH_MOUNTAIN_CRATER_0, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD},
{ 0x04BE, 0x0482, SINGLE_SCENE_INFO(0x61), "DMC", "DMC Great Fairy Fountain", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_1, ENTR_DEATH_MOUNTAIN_CRATER_3, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "DMC Great Fairy Fountain", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0706, 0x0806, SINGLE_SCENE_INFO(0x61), "DMC", "DMC Upper Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET), SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "DMC Upper Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0705, 0x0805, SINGLE_SCENE_INFO(0x61), "DMC", "DMC Hammer Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET), SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "DMC Hammer Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0165, 0x024A, SINGLE_SCENE_INFO(0x61), "DMC", "Fire Temple", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FIRE_TEMPLE_0, ENTR_DEATH_MOUNTAIN_CRATER_2, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "Fire Temple", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0482, 0x04BE, {{ 0x3B, 0x01 }}, "DMC Great Fairy Fountain", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_INTERIOR}, { ENTR_DEATH_MOUNTAIN_CRATER_3, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_1, {{ SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 0x01 }}, "DMC Great Fairy Fountain", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_INTERIOR},
{ 0x0806, 0x0706, {{ 0x3E, 0x00 }}, "DMC Upper Grotto", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "DMC Upper Grotto", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0805, 0x0705, {{ 0x3E, 0x04 }}, "DMC Hammer Grotto", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET), {{ SCENE_GROTTOS, 0x04 }}, "DMC Hammer Grotto", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x024A, 0x0165, SINGLE_SCENE_INFO(0x04), "Fire Temple", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON}, { ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON},
{ 0x0305, 0x0175, SINGLE_SCENE_INFO(0x04), "Fire Temple Boss Door", "Volvagia", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple Boss Door", "Volvagia", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0175, 0x0305, SINGLE_SCENE_INFO(0x15), "Volvagia", "Fire Temple Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FIRE_TEMPLE_1, ENTR_FIRE_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE_BOSS), "Volvagia", "Fire Temple Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
// Goron City // Goron City
{ 0x01B9, 0x014D, SINGLE_SCENE_INFO(0x62), "Goron City", "DMT", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD, "gc"}, { ENTR_DEATH_MOUNTAIN_TRAIL_1, ENTR_GORON_CITY_0, SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "DMT", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x0246, 0x01C1, SINGLE_SCENE_INFO(0x62), "Goron City", "DMC", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_OVERWORLD, "gc"}, { ENTR_DEATH_MOUNTAIN_CRATER_1, ENTR_GORON_CITY_1, SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "DMC", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x04D6, 0x04E2, SINGLE_SCENE_INFO(0x62), "Goron City", "Lost Woods", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "gc,lw"}, { ENTR_LOST_WOODS_6, ENTR_GORON_CITY_3, SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "Lost Woods", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "gc,lw"},
{ 0x037C, 0x03FC, SINGLE_SCENE_INFO(0x62), "Goron City", "Goron Shop", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_INTERIOR, "gc", 1}, { ENTR_GORON_SHOP_0, ENTR_GORON_CITY_2, SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "Goron Shop", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_INTERIOR, "gc", 1},
{ 0x0707, 0x0807, SINGLE_SCENE_INFO(0x62), "Goron City", "Goron City Grotto", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_GROTTO, "gc,scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET), SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "Goron City Grotto", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_GROTTO, "gc,scrubs", 1},
{ 0x03FC, 0x037C, SINGLE_SCENE_INFO(0x2E), "Goron Shop", "Goron City", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_INTERIOR, "gc"}, { ENTR_GORON_CITY_2, ENTR_GORON_SHOP_0, SINGLE_SCENE_INFO(SCENE_GORON_SHOP), "Goron Shop", "Goron City", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_INTERIOR, "gc"},
{ 0x0807, 0x0707, {{ 0x3E, 0x04 }}, "Goron City Grotto", "Goron City", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_GROTTO, "gc,scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET), {{ SCENE_GROTTOS, 0x04 }}, "Goron City Grotto", "Goron City", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_GROTTO, "gc,scrubs"},
// Zora's River // Zora's River
{ 0x0181, 0x00EA, SINGLE_SCENE_INFO(0x54), "ZR", "Hyrule Field", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_HYRULE_FIELD_2, ENTR_ZORAS_RIVER_0, SINGLE_SCENE_INFO(SCENE_ZORAS_RIVER), "ZR", "Hyrule Field", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x04DA, 0x01DD, SINGLE_SCENE_INFO(0x54), "ZR", "Lost Woods", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"}, { ENTR_LOST_WOODS_7, ENTR_ZORAS_RIVER_4, SINGLE_SCENE_INFO(SCENE_ZORAS_RIVER), "ZR", "Lost Woods", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0108, 0x019D, SINGLE_SCENE_INFO(0x54), "ZR", "Zora's Domain", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD}, { ENTR_ZORAS_DOMAIN_0, ENTR_ZORAS_RIVER_2, SINGLE_SCENE_INFO(SCENE_ZORAS_RIVER), "ZR", "Zora's Domain", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD},
{ 0x0702, 0x0802, SINGLE_SCENE_INFO(0x54), "ZR", "ZR Storms Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_ZORAS_RIVER), "ZR", "ZR Storms Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0703, 0x0803, SINGLE_SCENE_INFO(0x54), "ZR", "ZR Fairy Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET), SINGLE_SCENE_INFO(SCENE_ZORAS_RIVER), "ZR", "ZR Fairy Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0704, 0x0804, SINGLE_SCENE_INFO(0x54), "ZR", "ZR Open Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET), SINGLE_SCENE_INFO(SCENE_ZORAS_RIVER), "ZR", "ZR Open Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0802, 0x0702, {{ 0x3E, 0x0A }}, "ZR Storms Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET), {{ SCENE_GROTTOS, 0x0A }}, "ZR Storms Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x0803, 0x0703, {{ 0x3C, 0x00 }}, "ZR Fairy Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET), {{ SCENE_FAIRYS_FOUNTAIN, 0x00 }}, "ZR Fairy Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO},
{ 0x0804, 0x0704, {{ 0x3E, 0x00 }}, "ZR Open Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "ZR Open Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "chest"},
// Zora's Domain // Zora's Domain
{ 0x019D, 0x0108, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "ZR", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD}, { ENTR_ZORAS_RIVER_2, ENTR_ZORAS_DOMAIN_0, SINGLE_SCENE_INFO(SCENE_ZORAS_DOMAIN), "Zora's Domain", "ZR", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD},
{ 0x0560, 0x0328, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "Lake Hylia", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "lh"}, { ENTR_LAKE_HYLIA_7, ENTR_ZORAS_DOMAIN_4, SINGLE_SCENE_INFO(SCENE_ZORAS_DOMAIN), "Zora's Domain", "Lake Hylia", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x0225, 0x01A1, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "ZF", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_OVERWORLD}, { ENTR_ZORAS_FOUNTAIN_2, ENTR_ZORAS_DOMAIN_1, SINGLE_SCENE_INFO(SCENE_ZORAS_DOMAIN), "Zora's Domain", "ZF", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_OVERWORLD},
{ 0x0380, 0x03C4, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "Zora Shop", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_ZORA_SHOP_0, ENTR_ZORAS_DOMAIN_2, SINGLE_SCENE_INFO(SCENE_ZORAS_DOMAIN), "Zora's Domain", "Zora Shop", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x071C, 0x081C, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "ZD Storms Grotto", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_GROTTO, "fairy", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_ZORAS_DOMAIN), "Zora's Domain", "ZD Storms Grotto", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_GROTTO, "fairy", 1},
{ 0x03C4, 0x0380, SINGLE_SCENE_INFO(0x2F), "Zora Shop", "Zora's Domain", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_INTERIOR}, { ENTR_ZORAS_DOMAIN_2, ENTR_ZORA_SHOP_0, SINGLE_SCENE_INFO(SCENE_ZORA_SHOP), "Zora Shop", "Zora's Domain", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_INTERIOR},
{ 0x081C, 0x071C, {{ 0x3C, 0x00 }}, "ZD Storms Grotto", "Zora's Domain", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_GROTTO, "fairy"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET), {{ SCENE_FAIRYS_FOUNTAIN, 0x00 }}, "ZD Storms Grotto", "Zora's Domain", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_GROTTO, "fairy"},
// Zora's Fountain // Zora's Fountain
{ 0x01A1, 0x0225, SINGLE_SCENE_INFO(0x59), "ZF", "Zora's Domain", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD}, { ENTR_ZORAS_DOMAIN_1, ENTR_ZORAS_FOUNTAIN_2, SINGLE_SCENE_INFO(SCENE_ZORAS_FOUNTAIN), "ZF", "Zora's Domain", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD},
{ 0x0371, 0x0394, SINGLE_SCENE_INFO(0x59), "ZF", "ZF Great Fairy Fountain", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_0, ENTR_ZORAS_FOUNTAIN_5, SINGLE_SCENE_INFO(SCENE_ZORAS_FOUNTAIN), "ZF", "ZF Great Fairy Fountain", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0028, 0x0221, SINGLE_SCENE_INFO(0x59), "ZF", "Jabu Jabu's Belly", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_JABU_JABU_0, ENTR_ZORAS_FOUNTAIN_1, SINGLE_SCENE_INFO(SCENE_ZORAS_FOUNTAIN), "ZF", "Jabu Jabu's Belly", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0088, 0x03D4, SINGLE_SCENE_INFO(0x59), "ZF", "Ice Cavern", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_ICE_CAVERN_0, ENTR_ZORAS_FOUNTAIN_3, SINGLE_SCENE_INFO(SCENE_ZORAS_FOUNTAIN), "ZF", "Ice Cavern", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0394, 0x0371, {{ 0x3D, 0x00 }}, "ZF Great Fairy Fountain", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_INTERIOR}, { ENTR_ZORAS_FOUNTAIN_5, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_0, {{ SCENE_GREAT_FAIRYS_FOUNTAIN_SPELLS, 0x00 }}, "ZF Great Fairy Fountain", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_INTERIOR},
{ 0x0221, 0x0028, SINGLE_SCENE_INFO(0x02), "Jabu Jabu's Belly", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON}, { ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
{ 0x0301, 0x0407, SINGLE_SCENE_INFO(0x02), "Jabu Jabu's Belly Boss Door", "Barinade", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly Boss Door", "Barinade", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0407, 0x0301, SINGLE_SCENE_INFO(0x13), "Barinade", "Jabu Jabu's Belly Boss Door", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_JABU_JABU_1, ENTR_JABU_JABU_BOSS_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU_BOSS), "Barinade", "Jabu Jabu's Belly Boss Door", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x03D4, 0x0088, SINGLE_SCENE_INFO(0x09), "Ice Cavern", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON}, { ENTR_ZORAS_FOUNTAIN_3, ENTR_ICE_CAVERN_0, SINGLE_SCENE_INFO(SCENE_ICE_CAVERN), "Ice Cavern", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
// Hyrule Field // Hyrule Field
{ 0x04DE, 0x0185, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Lost Woods Bridge", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "hf,lw"}, { ENTR_LOST_WOODS_8, ENTR_HYRULE_FIELD_3, SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "Lost Woods Bridge", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "hf,lw"},
{ 0x0276, 0x01FD, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Market Entrance", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_MARKET_ENTRANCE_DAY_1, ENTR_HYRULE_FIELD_7, SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "Market Entrance", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0157, 0x01F9, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Lon Lon Ranch", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_OVERWORLD, "hf,llr"}, { ENTR_LON_LON_RANCH_0, ENTR_HYRULE_FIELD_6, SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "Lon Lon Ranch", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_OVERWORLD, "hf,llr"},
{ 0x00DB, 0x017D, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Kakariko", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_KAKARIKO_VILLAGE_0, ENTR_HYRULE_FIELD_1, SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "Kakariko", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x00EA, 0x0181, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "ZR", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_ZORAS_RIVER_0, ENTR_HYRULE_FIELD_2, SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "ZR", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0102, 0x0189, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Lake Hylia", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "hf,lh"}, { ENTR_LAKE_HYLIA_0, ENTR_HYRULE_FIELD_4, SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "Lake Hylia", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "hf,lh"},
{ 0x0117, 0x018D, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "GV", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_GERUDO_VALLEY_0, ENTR_HYRULE_FIELD_5, SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "GV", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0710, 0x0810, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Near Market Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Near Market Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x070E, 0x080E, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Near Kak Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "spider", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Near Kak Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "spider", 1},
{ 0x070D, 0x080D, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Tektite Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "water", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Tektite Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "water", 1},
{ 0x070F, 0x080F, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Fairy Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Fairy Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0711, 0x0811, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Cow Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "webbed", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_COW_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_COW_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Cow Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "webbed", 1},
{ 0x0713, 0x0813, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Open Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Open Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0712, 0x0812, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Inside Fence Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Inside Fence Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0714, 0x0814, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Southeast Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_FIELD), "Hyrule Field", "HF Southeast Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0810, 0x0710, {{ 0x3E, 0x00 }}, "HF Near Market Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "HF Near Market Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO},
{ 0x080E, 0x070E, {{ 0x3E, 0x01 }}, "HF Near Kak Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "spider"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET), {{ SCENE_GROTTOS, 0x01 }}, "HF Near Kak Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "spider"},
{ 0x080D, 0x070D, {{ 0x3E, 0x0B }}, "HF Tektite Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "water"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET), {{ SCENE_GROTTOS, 0x0B }}, "HF Tektite Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "water"},
{ 0x080F, 0x070F, {{ 0x3C, 0x00 }}, "HF Fairy Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET), {{ SCENE_FAIRYS_FOUNTAIN, 0x00 }}, "HF Fairy Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO},
{ 0x0811, 0x0711, {{ 0x3E, 0x05 }}, "HF Cow Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "webbed"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_COW_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_COW_OFFSET), {{ SCENE_GROTTOS, 0x05 }}, "HF Cow Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "webbed"},
{ 0x0813, 0x0713, {{ 0x3E, 0x00 }}, "HF Open Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "HF Open Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0812, 0x0712, {{ 0x3E, 0x02 }}, "HF Inside Fence Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "srubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET), {{ SCENE_GROTTOS, 0x02 }}, "HF Inside Fence Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "srubs"},
{ 0x0814, 0x0714, {{ 0x3E, 0x00 }}, "HF Southeast Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET), {{ SCENE_GROTTOS, 0x00 }}, "HF Southeast Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest"},
// Lon Lon Ranch // Lon Lon Ranch
{ 0x01F9, 0x0157, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "Hyrule Field", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_HYRULE_FIELD_6, ENTR_LON_LON_RANCH_0, SINGLE_SCENE_INFO(SCENE_LON_LON_RANCH), "Lon Lon Ranch", "Hyrule Field", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x004F, 0x0378, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "Talon's House", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "llr", 1}, { ENTR_LON_LON_BUILDINGS_0, ENTR_LON_LON_RANCH_4, SINGLE_SCENE_INFO(SCENE_LON_LON_RANCH), "Lon Lon Ranch", "Talon's House", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "llr", 1},
{ 0x02F9, 0x042F, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "LLR Stables", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow", 1}, { ENTR_STABLE_0, ENTR_LON_LON_RANCH_5, SINGLE_SCENE_INFO(SCENE_LON_LON_RANCH), "Lon Lon Ranch", "LLR Stables", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow", 1},
{ 0x05D0, 0x05D4, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "LLR Tower", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow", 1}, { ENTR_LON_LON_BUILDINGS_1, ENTR_LON_LON_RANCH_10, SINGLE_SCENE_INFO(SCENE_LON_LON_RANCH), "Lon Lon Ranch", "LLR Tower", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow", 1},
{ 0x0715, 0x0815, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "LLR Grotto", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_GROTTO, "scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LLR_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LLR_OFFSET), SINGLE_SCENE_INFO(SCENE_LON_LON_RANCH), "Lon Lon Ranch", "LLR Grotto", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0378, 0x004F, {{ 0x4C, 0x00 }}, "Talon's House", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "llr"}, { ENTR_LON_LON_RANCH_4, ENTR_LON_LON_BUILDINGS_0, {{ SCENE_LON_LON_BUILDINGS, 0x00 }}, "Talon's House", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "llr"},
{ 0x042F, 0x02F9, SINGLE_SCENE_INFO(0x36), "LLR Stables", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow"}, { ENTR_LON_LON_RANCH_5, ENTR_STABLE_0, SINGLE_SCENE_INFO(SCENE_STABLE), "LLR Stables", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow"},
{ 0x05D4, 0x05D0, {{ 0x4C, 0x01 }}, "LLR Tower", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow"}, { ENTR_LON_LON_RANCH_10, ENTR_LON_LON_BUILDINGS_1, {{ SCENE_LON_LON_BUILDINGS, 0x01 }}, "LLR Tower", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow"},
{ 0x0815, 0x0715, {{ 0x3E, 0x04 }}, "LLR Grotto", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_GROTTO, "scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LLR_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LLR_OFFSET), {{ SCENE_GROTTOS, 0x04 }}, "LLR Grotto", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_GROTTO, "scrubs"},
// Lake Hylia // Lake Hylia
{ 0x0189, 0x0102, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Hyrule Field", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "lh"}, { ENTR_HYRULE_FIELD_4, ENTR_LAKE_HYLIA_0, SINGLE_SCENE_INFO(SCENE_LAKE_HYLIA), "Lake Hylia", "Hyrule Field", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x0328, 0x0560, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Zora's Domain", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD, "lh"}, { ENTR_ZORAS_DOMAIN_4, ENTR_LAKE_HYLIA_7, SINGLE_SCENE_INFO(SCENE_LAKE_HYLIA), "Lake Hylia", "Zora's Domain", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x0043, 0x03CC, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "LH Lab", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh", 1}, { ENTR_LAKESIDE_LABORATORY_0, ENTR_LAKE_HYLIA_4, SINGLE_SCENE_INFO(SCENE_LAKE_HYLIA), "Lake Hylia", "LH Lab", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh", 1},
{ 0x045F, 0x0309, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Fishing Hole", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh", 1}, { ENTR_FISHING_POND_0, ENTR_LAKE_HYLIA_6, SINGLE_SCENE_INFO(SCENE_LAKE_HYLIA), "Lake Hylia", "Fishing Hole", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh", 1},
{ 0x0701, 0x0801, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "LH Grotto", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_GROTTO, "scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LH_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LH_OFFSET), SINGLE_SCENE_INFO(SCENE_LAKE_HYLIA), "Lake Hylia", "LH Grotto", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0010, 0x021D, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Water Temple", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1}, { ENTR_WATER_TEMPLE_0, ENTR_LAKE_HYLIA_2, SINGLE_SCENE_INFO(SCENE_LAKE_HYLIA), "Lake Hylia", "Water Temple", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
{ 0x03CC, 0x0043, SINGLE_SCENE_INFO(0x38), "LH Lab", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh"}, { ENTR_LAKE_HYLIA_4, ENTR_LAKESIDE_LABORATORY_0, SINGLE_SCENE_INFO(SCENE_LAKESIDE_LABORATORY), "LH Lab", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh"},
{ 0x0309, 0x045F, SINGLE_SCENE_INFO(0x49), "Fishing Hole", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh"}, { ENTR_LAKE_HYLIA_6, ENTR_FISHING_POND_0, SINGLE_SCENE_INFO(SCENE_FISHING_POND), "Fishing Hole", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh"},
{ 0x0801, 0x0701, {{ 0x3E, 0x04 }}, "LH Grotto", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_GROTTO, "lh,scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_LH_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_LH_OFFSET), {{ SCENE_GROTTOS, 0x04 }}, "LH Grotto", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_GROTTO, "lh,scrubs"},
{ 0x021D, 0x0010, SINGLE_SCENE_INFO(0x05), "Water Temple", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh"}, { ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh"},
{ 0x0417, 0x0423, SINGLE_SCENE_INFO(0x05), "Water Temple Boss Door", "Morpha", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1}, { ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple Boss Door", "Morpha", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
{ 0x0423, 0x0417, SINGLE_SCENE_INFO(0x16), "Morpha", "Water Temple Boss Door", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1}, { ENTR_WATER_TEMPLE_1, ENTR_WATER_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE_BOSS), "Morpha", "Water Temple Boss Door", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
// Gerudo Area // Gerudo Area
{ 0x018D, 0x0117, SINGLE_SCENE_INFO(0x5A), "GV", "Hyrule Field", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_HYRULE_FIELD_5, ENTR_GERUDO_VALLEY_0, SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "Hyrule Field", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0129, 0x022D, SINGLE_SCENE_INFO(0x5A), "GV", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"}, { ENTR_GERUDOS_FORTRESS_0, ENTR_GERUDO_VALLEY_3, SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"},
{ 0x0219, -1, SINGLE_SCENE_INFO(0x5A), "GV", "Lake Hylia", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "lh"}, { ENTR_LAKE_HYLIA_1, -1, SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "Lake Hylia", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x03A0, 0x03D0, SINGLE_SCENE_INFO(0x5A), "GV", "Carpenters' Tent", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_CARPENTERS_TENT_0, ENTR_GERUDO_VALLEY_4, SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "Carpenters' Tent", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x071F, 0x081F, SINGLE_SCENE_INFO(0x5A), "GV", "GV Octorok Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET), SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "GV Octorok Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x071E, 0x081E, SINGLE_SCENE_INFO(0x5A), "GV", "GV Storms Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "GV Storms Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x022D, 0x0129, SINGLE_SCENE_INFO(0x5D), "GF", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"}, { ENTR_GERUDO_VALLEY_3, ENTR_GERUDOS_FORTRESS_0, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"},
{ 0x0130, 0x03AC, SINGLE_SCENE_INFO(0x5D), "GF", "Haunted Wasteland", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"}, { ENTR_HAUNTED_WASTELAND_0, ENTR_GERUDOS_FORTRESS_15, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF", "Haunted Wasteland", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"},
{ 0x071D, 0x081D, SINGLE_SCENE_INFO(0x5D), "GF", "GF Storms Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "gerudo fortress", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF", "GF Storms Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "gerudo fortress", 1},
{ 0x0008, 0x03A8, SINGLE_SCENE_INFO(0x5D), "GF", "Gerudo Training Grounds", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_DUNGEON, "gerudo fortress,gtg", 1}, { ENTR_GERUDO_TRAINING_GROUND_0, ENTR_GERUDOS_FORTRESS_14, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF", "Gerudo Training Grounds", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_DUNGEON, "gerudo fortress,gtg", 1},
{ 0x03D0, 0x03A0, SINGLE_SCENE_INFO(0x39), "Carpenters' Tent", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_INTERIOR}, { ENTR_GERUDO_VALLEY_4, ENTR_CARPENTERS_TENT_0, SINGLE_SCENE_INFO(SCENE_CARPENTERS_TENT), "Carpenters' Tent", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_INTERIOR},
{ 0x081F, 0x071F, {{ 0x3E, 0x06 }}, "GV Octorok Grotto", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET), {{ SCENE_GROTTOS, 0x06 }}, "GV Octorok Grotto", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO},
{ 0x081E, 0x071E, {{ 0x3E, 0x0A }}, "GV Storms Grotto", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET), {{ SCENE_GROTTOS, 0x0A }}, "GV Storms Grotto", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x081D, 0x071D, {{ 0x3C, 0x00 }}, "GF Storms Grotto", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "gerudo fortress"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET), {{ SCENE_FAIRYS_FOUNTAIN, 0x00 }}, "GF Storms Grotto", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "gerudo fortress"},
{ 0x03A8, 0x0008, SINGLE_SCENE_INFO(0x0B), "Gerudo Training Grounds", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_DUNGEON, "gerudo fortress,gtg"}, { ENTR_GERUDOS_FORTRESS_14, ENTR_GERUDO_TRAINING_GROUND_0, SINGLE_SCENE_INFO(SCENE_GERUDO_TRAINING_GROUND), "Gerudo Training Grounds", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_DUNGEON, "gerudo fortress,gtg"},
// The Wasteland // The Wasteland
{ 0x03AC, 0x0130, SINGLE_SCENE_INFO(0x5E), "Haunted Wasteland", "GF", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "hw,gerudo fortress"}, { ENTR_GERUDOS_FORTRESS_15, ENTR_HAUNTED_WASTELAND_0, SINGLE_SCENE_INFO(SCENE_HAUNTED_WASTELAND), "Haunted Wasteland", "GF", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "hw,gerudo fortress"},
{ 0x0123, 0x0365, SINGLE_SCENE_INFO(0x5E), "Haunted Wasteland", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"}, { ENTR_DESERT_COLOSSUS_0, ENTR_HAUNTED_WASTELAND_1, SINGLE_SCENE_INFO(SCENE_HAUNTED_WASTELAND), "Haunted Wasteland", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"},
{ 0x0365, 0x0123, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Haunted Wasteland", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"}, { ENTR_HAUNTED_WASTELAND_1, ENTR_DESERT_COLOSSUS_0, SINGLE_SCENE_INFO(SCENE_DESERT_COLOSSUS), "Desert Colossus", "Haunted Wasteland", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"},
{ 0x0588, 0x057C, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Colossus Great Fairy Fountain", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc", 1}, { ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_2, ENTR_DESERT_COLOSSUS_7, SINGLE_SCENE_INFO(SCENE_DESERT_COLOSSUS), "Desert Colossus", "Colossus Great Fairy Fountain", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc", 1},
{ 0x0700, 0x0800, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Colossus Grotto", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_GROTTO, "dc,scrubs", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET), SINGLE_SCENE_INFO(SCENE_DESERT_COLOSSUS), "Desert Colossus", "Colossus Grotto", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_GROTTO, "dc,scrubs", 1},
{ 0x0082, 0x01E1, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Spirit Temple", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc", 1}, { ENTR_SPIRIT_TEMPLE_0, ENTR_DESERT_COLOSSUS_1, SINGLE_SCENE_INFO(SCENE_DESERT_COLOSSUS), "Desert Colossus", "Spirit Temple", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ 0x057C, 0x0588, {{ 0x3D, 0x02 }}, "Colossus Great Fairy Fountain", "Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc"}, { ENTR_DESERT_COLOSSUS_7, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_2, {{ SCENE_GREAT_FAIRYS_FOUNTAIN_SPELLS, 0x02 }}, "Colossus Great Fairy Fountain", "Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc"},
{ 0x0800, 0x0700, {{ 0x3E, 0x0A }}, "Colossus Grotto", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_GROTTO, "dc,scrubs"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET), {{ SCENE_GROTTOS, 0x0A }}, "Colossus Grotto", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_GROTTO, "dc,scrubs"},
{ 0x01E1, 0x0082, SINGLE_SCENE_INFO(0x06), "Spirit Temple", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc"}, { ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc"},
{ 0x008D, 0x02F5, SINGLE_SCENE_INFO(0x06), "Spirit Temple Boss Door", "Twinrova", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple Boss Door", "Twinrova", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x02F5, 0x008D, SINGLE_SCENE_INFO(0x17), "Twinrova", "Spirit Temple Boss Door", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_SPIRIT_TEMPLE_1, ENTR_SPIRIT_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE_BOSS), "Twinrova", "Spirit Temple Boss Door", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1},
// Market // Market
{ 0x01FD, 0x0276, {SCENE_NO_SPAWN(0x1B), SCENE_NO_SPAWN(0x1C), SCENE_NO_SPAWN(0x1D)}, "Market Entrance", "Hyrule Field", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, { ENTR_HYRULE_FIELD_7, ENTR_MARKET_ENTRANCE_DAY_1, {SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_DAY), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_RUINS)}, "Market Entrance", "Hyrule Field", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x00B1, 0x0033, {SCENE_NO_SPAWN(0x1B), SCENE_NO_SPAWN(0x1C), SCENE_NO_SPAWN(0x1D)}, "Market Entrance", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD}, { ENTR_MARKET_DAY_0, ENTR_MARKET_ENTRANCE_DAY_0, {SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_DAY), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_RUINS)}, "Market Entrance", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD},
{ 0x007E, 0x026E, {SCENE_NO_SPAWN(0x1B), SCENE_NO_SPAWN(0x1C), SCENE_NO_SPAWN(0x1D)}, "Market Entrance", "Guard House", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "pots,poe", 1}, { ENTR_MARKET_GUARD_HOUSE_0, ENTR_MARKET_ENTRANCE_DAY_2, {SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_DAY), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_RUINS)}, "Market Entrance", "Guard House", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "pots,poe", 1},
{ 0x0033, 0x00B1, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Market Entrance", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD}, { ENTR_MARKET_ENTRANCE_DAY_0, ENTR_MARKET_DAY_0, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "Market Entrance", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD},
{ 0x0138, 0x025A, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "HC Grounds / OGC", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_OVERWORLD, "outside ganon's castle"}, { ENTR_HYRULE_CASTLE_0, ENTR_MARKET_DAY_1, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "HC Grounds / OGC", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_OVERWORLD, "outside ganon's castle"},
{ 0x0171, 0x025E, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Outside Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD}, { ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_0, ENTR_MARKET_DAY_2, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "Outside Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD},
{ 0x016D, 0x01CD, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "MK Shooting Gallery", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "child", 1}, { ENTR_SHOOTING_GALLERY_1, ENTR_MARKET_DAY_8, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "MK Shooting Gallery", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "child", 1},
{ 0x0507, 0x03BC, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Bombchu Bowling", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_BOMBCHU_BOWLING_ALLEY_0, ENTR_MARKET_DAY_7, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "Bombchu Bowling", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0063, 0x01D5, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Treasure Chest Game", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_TREASURE_BOX_SHOP_0, ENTR_MARKET_DAY_10, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "Treasure Chest Game", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x043B, 0x0067, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Man-in-Green's House", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_BACK_ALLEY_HOUSE_0, ENTR_BACK_ALLEY_DAY_3, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "Man-in-Green's House", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0530, 0x01D1, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Mask Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_HAPPY_MASK_SHOP_0, ENTR_MARKET_DAY_9, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "Mask Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x052C, 0x03B8, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "MK Bazaar", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "shop", 1}, { ENTR_BAZAAR_1, ENTR_MARKET_DAY_6, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "MK Bazaar", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "shop", 1},
{ 0x0388, 0x02A2, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "MK Potion Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_POTION_SHOP_MARKET_0, ENTR_MARKET_DAY_5, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "MK Potion Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0528, 0x03C0, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Bombchu Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_BOMBCHU_SHOP_1, ENTR_BACK_ALLEY_DAY_2, {SCENE_NO_SPAWN(SCENE_MARKET_DAY), SCENE_NO_SPAWN(SCENE_MARKET_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_RUINS), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_DAY), SCENE_NO_SPAWN(SCENE_BACK_ALLEY_NIGHT)}, "Market", "Bombchu Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x026E, 0x007E, {{ 0x4D }}, "Guard House", "Market Entrance", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "pots,poe"}, { ENTR_MARKET_ENTRANCE_DAY_2, ENTR_MARKET_GUARD_HOUSE_0, {{ SCENE_MARKET_GUARD_HOUSE }}, "Guard House", "Market Entrance", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "pots,poe"},
{ 0x01CD, 0x016D, {{ 0x42, 0x01 }}, "MK Shooting Gallery", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR}, { ENTR_MARKET_DAY_8, ENTR_SHOOTING_GALLERY_1, {{ SCENE_SHOOTING_GALLERY, 0x01 }}, "MK Shooting Gallery", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x03BC, 0x0507, SINGLE_SCENE_INFO(0x4B), "Bombchu Bowling", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR}, { ENTR_MARKET_DAY_7, ENTR_BOMBCHU_BOWLING_ALLEY_0, SINGLE_SCENE_INFO(SCENE_BOMBCHU_BOWLING_ALLEY), "Bombchu Bowling", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x01D5, 0x0063, SINGLE_SCENE_INFO(0x10), "Treasure Chest Game", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR}, { ENTR_MARKET_DAY_10, ENTR_TREASURE_BOX_SHOP_0, SINGLE_SCENE_INFO(SCENE_TREASURE_BOX_SHOP), "Treasure Chest Game", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x0067, 0x043B, SINGLE_SCENE_INFO(0x2B), "Man-in-Green's House", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR}, { ENTR_BACK_ALLEY_DAY_3, ENTR_BACK_ALLEY_HOUSE_0, SINGLE_SCENE_INFO(SCENE_BACK_ALLEY_HOUSE), "Man-in-Green's House", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x01D1, 0x0530, SINGLE_SCENE_INFO(0x33), "Mask Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR}, { ENTR_MARKET_DAY_9, ENTR_HAPPY_MASK_SHOP_0, SINGLE_SCENE_INFO(SCENE_HAPPY_MASK_SHOP), "Mask Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x03B8, 0x052C, {{ 0x2C, 0x01 }}, "MK Bazaar", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "shop"}, { ENTR_MARKET_DAY_6, ENTR_BAZAAR_1, {{ SCENE_BAZAAR, 0x01 }}, "MK Bazaar", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "shop"},
{ 0x02A2, 0x0388, SINGLE_SCENE_INFO(0x31), "MK Potion Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR}, { ENTR_MARKET_DAY_5, ENTR_POTION_SHOP_MARKET_0, SINGLE_SCENE_INFO(SCENE_POTION_SHOP_MARKET), "MK Potion Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x03C0, 0x0528, SINGLE_SCENE_INFO(0x32), "Bombchu Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR}, { ENTR_BACK_ALLEY_DAY_2, ENTR_BOMBCHU_SHOP_1, SINGLE_SCENE_INFO(SCENE_BOMBCHU_SHOP), "Bombchu Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x025E, 0x0171, {SCENE_NO_SPAWN(0x23), SCENE_NO_SPAWN(0x24), SCENE_NO_SPAWN(0x25)}, "Outside Temple of Time", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "tot"}, { ENTR_MARKET_DAY_2, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_0, {SCENE_NO_SPAWN(SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY), SCENE_NO_SPAWN(SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT), SCENE_NO_SPAWN(SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS)}, "Outside Temple of Time", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "tot"},
{ 0x0053, 0x0472, {SCENE_NO_SPAWN(0x23), SCENE_NO_SPAWN(0x24), SCENE_NO_SPAWN(0x25)}, "Outside Temple of Time", "Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "tot", 1}, { ENTR_TEMPLE_OF_TIME_0, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_1, {SCENE_NO_SPAWN(SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY), SCENE_NO_SPAWN(SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT), SCENE_NO_SPAWN(SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS)}, "Outside Temple of Time", "Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "tot", 1},
{ 0x0472, 0x0053, SINGLE_SCENE_INFO(0x43), "Temple of Time", "Outside Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "tot"}, { ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_1, ENTR_TEMPLE_OF_TIME_0, SINGLE_SCENE_INFO(SCENE_TEMPLE_OF_TIME), "Temple of Time", "Outside Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "tot"},
// Hyrule Castle // Hyrule Castle
{ 0x025A, 0x0138, {SCENE_NO_SPAWN(0x5F), SCENE_NO_SPAWN(0x64)}, "HC Grounds / OGC", "Market", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "outside ganon's castle"}, { ENTR_MARKET_DAY_1, ENTR_HYRULE_CASTLE_0, {SCENE_NO_SPAWN(SCENE_HYRULE_CASTLE), SCENE_NO_SPAWN(SCENE_OUTSIDE_GANONS_CASTLE)}, "HC Grounds / OGC", "Market", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "outside ganon's castle"},
{ 0x0578, 0x0340, SINGLE_SCENE_INFO(0x5F), "HC Grounds", "HC Great Fairy Fountain", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "", 1}, { ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_1, ENTR_HYRULE_CASTLE_2, SINGLE_SCENE_INFO(SCENE_HYRULE_CASTLE), "HC Grounds", "HC Great Fairy Fountain", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x070C, 0x080C, SINGLE_SCENE_INFO(0x5F), "HC Grounds", "HC Storms Grotto", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_GROTTO, "bombable", 1}, { ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET), SINGLE_SCENE_INFO(SCENE_HYRULE_CASTLE), "HC Grounds", "HC Storms Grotto", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_GROTTO, "bombable", 1},
{ 0x0340, 0x0578, {{ 0x3D, 0x01 }}, "HC Great Fairy Fountain", "HC Grounds", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR}, { ENTR_HYRULE_CASTLE_2, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_1, {{ SCENE_GREAT_FAIRYS_FOUNTAIN_SPELLS, 0x01 }}, "HC Great Fairy Fountain", "HC Grounds", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR},
{ 0x080C, 0x070C, {{ 0x3E, 0x09 }}, "HC Storms Grotto", "HC Grounds", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_GROTTO, "bombable"}, { ENTRANCE_RANDO_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET), ENTRANCE_RANDO_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET), {{ SCENE_GROTTOS, 0x09 }}, "HC Storms Grotto", "HC Grounds", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_GROTTO, "bombable"},
{ 0x04C2, 0x03E8, SINGLE_SCENE_INFO(0x64), "OGC", "OGC Great Fairy Fountain", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "outside ganon's castle", 1}, { ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_2, ENTR_POTION_SHOP_KAKARIKO_1, SINGLE_SCENE_INFO(SCENE_OUTSIDE_GANONS_CASTLE), "OGC", "OGC Great Fairy Fountain", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "outside ganon's castle", 1},
{ 0x0467, 0x023D, SINGLE_SCENE_INFO(0x64), "OGC", "Ganon's Castle", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc", 1}, { ENTR_INSIDE_GANONS_CASTLE_0, ENTR_HYRULE_CASTLE_1, SINGLE_SCENE_INFO(SCENE_OUTSIDE_GANONS_CASTLE), "OGC", "Ganon's Castle", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc", 1},
{ 0x03E8, 0x04C2, {{ 0x3B, 0x02 }}, "OGC Great Fairy Fountain", "OGC", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "outside ganon's castle"}, { ENTR_POTION_SHOP_KAKARIKO_1, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_2, {{ SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 0x02 }}, "OGC Great Fairy Fountain", "OGC", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "outside ganon's castle"},
{ 0x023D, 0x0467, SINGLE_SCENE_INFO(0x0D), "Ganon's Castle", "OGC", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc"} { ENTR_HYRULE_CASTLE_1, ENTR_INSIDE_GANONS_CASTLE_0, SINGLE_SCENE_INFO(SCENE_INSIDE_GANONS_CASTLE), "Ganon's Castle", "OGC", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc"}
}; };
// Check if Link is in the area and return that scene/entrance for tracking // Check if Link is in the area and return that scene/entrance for tracking
@ -411,9 +412,9 @@ bool IsEntranceDiscovered(s16 index) {
if (!isDiscovered) { if (!isDiscovered) {
// If the pair included one of the hyrule field <-> zora's river entrances, // If the pair included one of the hyrule field <-> zora's river entrances,
// the randomizer will have also overriden the water-based entrances, so check those too // the randomizer will have also overriden the water-based entrances, so check those too
if ((index == 0x00EA && Entrance_GetIsEntranceDiscovered(0x01D9)) || (index == 0x01D9 && Entrance_GetIsEntranceDiscovered(0x00EA))) { if ((index == ENTR_ZORAS_RIVER_0 && Entrance_GetIsEntranceDiscovered(ENTR_ZORAS_RIVER_3)) || (index == ENTR_ZORAS_RIVER_3 && Entrance_GetIsEntranceDiscovered(ENTR_ZORAS_RIVER_0))) {
isDiscovered = true; isDiscovered = true;
} else if ((index == 0x0181 && Entrance_GetIsEntranceDiscovered(0x0311)) || (index == 0x0311 && Entrance_GetIsEntranceDiscovered(0x0181))) { } else if ((index == ENTR_HYRULE_FIELD_2 && Entrance_GetIsEntranceDiscovered(ENTR_HYRULE_FIELD_14)) || (index == ENTR_HYRULE_FIELD_14 && Entrance_GetIsEntranceDiscovered(ENTR_HYRULE_FIELD_2))) {
isDiscovered = true; isDiscovered = true;
} }
} }

View file

@ -11,76 +11,76 @@ extern PlayState* gPlayState;
// Information necessary for entering each grotto // Information necessary for entering each grotto
static const GrottoLoadInfo grottoLoadTable[NUM_GROTTOS] = { static const GrottoLoadInfo grottoLoadTable[NUM_GROTTOS] = {
{.entranceIndex = 0x05BC, .content = 0xFD, .scene = 0x5C}, // Desert Colossus -> Colossus Grotto {.entranceIndex = ENTR_GROTTOS_10, .content = 0xFD, .scene = SCENE_DESERT_COLOSSUS}, // Desert Colossus -> Colossus Grotto
{.entranceIndex = 0x05A4, .content = 0xEF, .scene = 0x57}, // Lake Hylia -> LH Grotto {.entranceIndex = ENTR_GROTTOS_4, .content = 0xEF, .scene = SCENE_LAKE_HYLIA}, // Lake Hylia -> LH Grotto
{.entranceIndex = 0x05BC, .content = 0xEB, .scene = 0x54}, // Zora River -> ZR Storms Grotto {.entranceIndex = ENTR_GROTTOS_10, .content = 0xEB, .scene = SCENE_ZORAS_RIVER}, // Zora River -> ZR Storms Grotto
{.entranceIndex = 0x036D, .content = 0xE6, .scene = 0x54}, // Zora River -> ZR Fairy Grotto {.entranceIndex = ENTR_FAIRYS_FOUNTAIN_0, .content = 0xE6, .scene = SCENE_ZORAS_RIVER}, // Zora River -> ZR Fairy Grotto
{.entranceIndex = 0x003F, .content = 0x29, .scene = 0x54}, // Zora River -> ZR Open Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x29, .scene = SCENE_ZORAS_RIVER}, // Zora River -> ZR Open Grotto
{.entranceIndex = 0x05A4, .content = 0xF9, .scene = 0x61}, // DMC Lower Nearby -> DMC Hammer Grotto {.entranceIndex = ENTR_GROTTOS_4, .content = 0xF9, .scene = SCENE_DEATH_MOUNTAIN_CRATER}, // DMC Lower Nearby -> DMC Hammer Grotto
{.entranceIndex = 0x003F, .content = 0x7A, .scene = 0x61}, // DMC Upper Nearby -> DMC Upper Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x7A, .scene = SCENE_DEATH_MOUNTAIN_CRATER}, // DMC Upper Nearby -> DMC Upper Grotto
{.entranceIndex = 0x05A4, .content = 0xFB, .scene = 0x62}, // GC Grotto Platform -> GC Grotto {.entranceIndex = ENTR_GROTTOS_4, .content = 0xFB, .scene = SCENE_GORON_CITY}, // GC Grotto Platform -> GC Grotto
{.entranceIndex = 0x003F, .content = 0x57, .scene = 0x60}, // Death Mountain -> DMT Storms Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x57, .scene = SCENE_DEATH_MOUNTAIN_TRAIL}, // Death Mountain -> DMT Storms Grotto
{.entranceIndex = 0x05FC, .content = 0xF8, .scene = 0x60}, // Death Mountain Summit -> DMT Cow Grotto {.entranceIndex = ENTR_GROTTOS_13, .content = 0xF8, .scene = SCENE_DEATH_MOUNTAIN_TRAIL}, // Death Mountain Summit -> DMT Cow Grotto
{.entranceIndex = 0x003F, .content = 0x28, .scene = 0x52}, // Kak Backyard -> Kak Open Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x28, .scene = SCENE_KAKARIKO_VILLAGE}, // Kak Backyard -> Kak Open Grotto
{.entranceIndex = 0x05A0, .content = 0xE7, .scene = 0x52}, // Kakariko Village -> Kak Redead Grotto {.entranceIndex = ENTR_GROTTOS_3, .content = 0xE7, .scene = SCENE_KAKARIKO_VILLAGE}, // Kakariko Village -> Kak Redead Grotto
{.entranceIndex = 0x05B8, .content = 0xF6, .scene = 0x5F}, // Hyrule Castle Grounds -> HC Storms Grotto {.entranceIndex = ENTR_GROTTOS_9, .content = 0xF6, .scene = SCENE_HYRULE_CASTLE}, // Hyrule Castle Grounds -> HC Storms Grotto
{.entranceIndex = 0x05C0, .content = 0xE1, .scene = 0x51}, // Hyrule Field -> HF Tektite Grotto {.entranceIndex = ENTR_GROTTOS_11, .content = 0xE1, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Tektite Grotto
{.entranceIndex = 0x0598, .content = 0xE5, .scene = 0x51}, // Hyrule Field -> HF Near Kak Grotto {.entranceIndex = ENTR_GROTTOS_1, .content = 0xE5, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Near Kak Grotto
{.entranceIndex = 0x036D, .content = 0xFF, .scene = 0x51}, // Hyrule Field -> HF Fairy Grotto {.entranceIndex = ENTR_FAIRYS_FOUNTAIN_0, .content = 0xFF, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Fairy Grotto
{.entranceIndex = 0x003F, .content = 0x00, .scene = 0x51}, // Hyrule Field -> HF Near Market Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x00, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Near Market Grotto
{.entranceIndex = 0x05A8, .content = 0xE4, .scene = 0x51}, // Hyrule Field -> HF Cow Grotto {.entranceIndex = ENTR_GROTTOS_5, .content = 0xE4, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Cow Grotto
{.entranceIndex = 0x059C, .content = 0xE6, .scene = 0x51}, // Hyrule Field -> HF Inside Fence Grotto {.entranceIndex = ENTR_GROTTOS_2, .content = 0xE6, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Inside Fence Grotto
{.entranceIndex = 0x003F, .content = 0x03, .scene = 0x51}, // Hyrule Field -> HF Open Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x03, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Open Grotto
{.entranceIndex = 0x003F, .content = 0x22, .scene = 0x51}, // Hyrule Field -> HF Southeast Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x22, .scene = SCENE_HYRULE_FIELD}, // Hyrule Field -> HF Southeast Grotto
{.entranceIndex = 0x05A4, .content = 0xFC, .scene = 0x63}, // Lon Lon Ranch -> LLR Grotto {.entranceIndex = ENTR_GROTTOS_4, .content = 0xFC, .scene = SCENE_LON_LON_RANCH}, // Lon Lon Ranch -> LLR Grotto
{.entranceIndex = 0x05B4, .content = 0xED, .scene = 0x56}, // SFM Entryway -> SFM Wolfos Grotto {.entranceIndex = ENTR_GROTTOS_8, .content = 0xED, .scene = SCENE_SACRED_FOREST_MEADOW}, // SFM Entryway -> SFM Wolfos Grotto
{.entranceIndex = 0x05BC, .content = 0xEE, .scene = 0x56}, // Sacred Forest Meadow -> SFM Storms Grotto {.entranceIndex = ENTR_GROTTOS_10, .content = 0xEE, .scene = SCENE_SACRED_FOREST_MEADOW}, // Sacred Forest Meadow -> SFM Storms Grotto
{.entranceIndex = 0x036D, .content = 0xFF, .scene = 0x56}, // Sacred Forest Meadow -> SFM Fairy Grotto {.entranceIndex = ENTR_FAIRYS_FOUNTAIN_0, .content = 0xFF, .scene = SCENE_SACRED_FOREST_MEADOW}, // Sacred Forest Meadow -> SFM Fairy Grotto
{.entranceIndex = 0x05B0, .content = 0xF5, .scene = 0x5B}, // LW Beyond Mido -> LW Scrubs Grotto {.entranceIndex = ENTR_GROTTOS_7, .content = 0xF5, .scene = SCENE_LOST_WOODS}, // LW Beyond Mido -> LW Scrubs Grotto
{.entranceIndex = 0x003F, .content = 0x14, .scene = 0x5B}, // Lost Woods -> LW Near Shortcuts Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x14, .scene = SCENE_LOST_WOODS}, // Lost Woods -> LW Near Shortcuts Grotto
{.entranceIndex = 0x003F, .content = 0x2C, .scene = 0x55}, // Kokiri Forest -> KF Storms Grotto {.entranceIndex = ENTR_GROTTOS_0, .content = 0x2C, .scene = SCENE_KOKIRI_FOREST}, // Kokiri Forest -> KF Storms Grotto
{.entranceIndex = 0x036D, .content = 0xFF, .scene = 0x58}, // Zoras Domain -> ZD Storms Grotto {.entranceIndex = ENTR_FAIRYS_FOUNTAIN_0, .content = 0xFF, .scene = SCENE_ZORAS_DOMAIN}, // Zoras Domain -> ZD Storms Grotto
{.entranceIndex = 0x036D, .content = 0xFF, .scene = 0x5D}, // Gerudo Fortress -> GF Storms Grotto {.entranceIndex = ENTR_FAIRYS_FOUNTAIN_0, .content = 0xFF, .scene = SCENE_GERUDOS_FORTRESS}, // Gerudo Fortress -> GF Storms Grotto
{.entranceIndex = 0x05BC, .content = 0xF0, .scene = 0x5A}, // GV Fortress Side -> GV Storms Grotto {.entranceIndex = ENTR_GROTTOS_10, .content = 0xF0, .scene = SCENE_GERUDO_VALLEY}, // GV Fortress Side -> GV Storms Grotto
{.entranceIndex = 0x05AC, .content = 0xF2, .scene = 0x5A}, // GV Grotto Ledge -> GV Octorok Grotto {.entranceIndex = ENTR_GROTTOS_6, .content = 0xF2, .scene = SCENE_GERUDO_VALLEY}, // GV Grotto Ledge -> GV Octorok Grotto
{.entranceIndex = 0x05C4, .content = 0xF3, .scene = 0x5B}, // LW Beyond Mido -> Deku Theater {.entranceIndex = ENTR_GROTTOS_12, .content = 0xF3, .scene = SCENE_LOST_WOODS}, // LW Beyond Mido -> Deku Theater
}; };
// Information necessary for setting up returning from a grotto // Information necessary for setting up returning from a grotto
static const GrottoReturnInfo grottoReturnTable[NUM_GROTTOS] = { static const GrottoReturnInfo grottoReturnTable[NUM_GROTTOS] = {
{.entranceIndex = 0x0123, .room = 0x00, .angle = 0xA71C, .pos = {.x = 62.5078f, .y = -32.0f, .z = -1296.2f}}, // Colossus Grotto -> Desert Colossus {.entranceIndex = ENTR_DESERT_COLOSSUS_0, .room = 0x00, .angle = 0xA71C, .pos = {.x = 62.5078f, .y = -32.0f, .z = -1296.2f}}, // Colossus Grotto -> Desert Colossus
{.entranceIndex = 0x0102, .room = 0x00, .angle = 0x0000, .pos = {.x = -3039.34f, .y = -1033.0f, .z = 6080.74f}}, // LH Grotto -> Lake Hylia {.entranceIndex = ENTR_LAKE_HYLIA_0, .room = 0x00, .angle = 0x0000, .pos = {.x = -3039.34f, .y = -1033.0f, .z = 6080.74f}}, // LH Grotto -> Lake Hylia
{.entranceIndex = 0x00EA, .room = 0x00, .angle = 0x0000, .pos = {.x = -1630.05f, .y = 100.0f, .z = -132.104f}}, // ZR Storms Grotto -> Zora River {.entranceIndex = ENTR_ZORAS_RIVER_0, .room = 0x00, .angle = 0x0000, .pos = {.x = -1630.05f, .y = 100.0f, .z = -132.104f}}, // ZR Storms Grotto -> Zora River
{.entranceIndex = 0x00EA, .room = 0x00, .angle = 0xE000, .pos = {.x = 649.507f, .y = 570.0f, .z = -346.853f}}, // ZR Fairy Grotto -> Zora River {.entranceIndex = ENTR_ZORAS_RIVER_0, .room = 0x00, .angle = 0xE000, .pos = {.x = 649.507f, .y = 570.0f, .z = -346.853f}}, // ZR Fairy Grotto -> Zora River
{.entranceIndex = 0x00EA, .room = 0x00, .angle = 0x8000, .pos = {.x = 362.29f, .y = 570.0f, .z = 111.48f}}, // ZR Open Grotto -> Zora River {.entranceIndex = ENTR_ZORAS_RIVER_0, .room = 0x00, .angle = 0x8000, .pos = {.x = 362.29f, .y = 570.0f, .z = 111.48f}}, // ZR Open Grotto -> Zora River
{.entranceIndex = 0x0246, .room = 0x01, .angle = 0x31C7, .pos = {.x = -1666.73f, .y = 721.0f, .z = -459.21f}}, // DMC Hammer Grotto -> DMC Lower Local {.entranceIndex = ENTR_DEATH_MOUNTAIN_CRATER_1, .room = 0x01, .angle = 0x31C7, .pos = {.x = -1666.73f, .y = 721.0f, .z = -459.21f}}, // DMC Hammer Grotto -> DMC Lower Local
{.entranceIndex = 0x0147, .room = 0x01, .angle = 0x238E, .pos = {.x = 63.723f, .y = 1265.0f, .z = 1791.39f}}, // DMC Upper Grotto -> DMC Upper Local {.entranceIndex = ENTR_DEATH_MOUNTAIN_CRATER_0, .room = 0x01, .angle = 0x238E, .pos = {.x = 63.723f, .y = 1265.0f, .z = 1791.39f}}, // DMC Upper Grotto -> DMC Upper Local
{.entranceIndex = 0x014D, .room = 0x03, .angle = 0x0000, .pos = {.x = 1104.73f, .y = 580.0f, .z = -1159.95f}}, // GC Grotto -> GC Grotto Platform {.entranceIndex = ENTR_GORON_CITY_0, .room = 0x03, .angle = 0x0000, .pos = {.x = 1104.73f, .y = 580.0f, .z = -1159.95f}}, // GC Grotto -> GC Grotto Platform
{.entranceIndex = 0x01B9, .room = 0x00, .angle = 0x8000, .pos = {.x = -387.584f, .y = 1386.0f, .z = -1213.05f}}, // DMT Storms Grotto -> Death Mountain {.entranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_1, .room = 0x00, .angle = 0x8000, .pos = {.x = -387.584f, .y = 1386.0f, .z = -1213.05f}}, // DMT Storms Grotto -> Death Mountain
{.entranceIndex = 0x01B9, .room = 0x00, .angle = 0x8000, .pos = {.x = -691.022f, .y = 1946.0f, .z = -312.969f}}, // DMT Cow Grotto -> Death Mountain Summit {.entranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_1, .room = 0x00, .angle = 0x8000, .pos = {.x = -691.022f, .y = 1946.0f, .z = -312.969f}}, // DMT Cow Grotto -> Death Mountain Summit
{.entranceIndex = 0x00DB, .room = 0x00, .angle = 0x0000, .pos = {.x = 855.238f, .y = 80.0f, .z = -234.095f}}, // Kak Open Grotto -> Kak Backyard {.entranceIndex = ENTR_KAKARIKO_VILLAGE_0, .room = 0x00, .angle = 0x0000, .pos = {.x = 855.238f, .y = 80.0f, .z = -234.095f}}, // Kak Open Grotto -> Kak Backyard
{.entranceIndex = 0x00DB, .room = 0x00, .angle = 0x0000, .pos = {.x = -401.873f, .y = 0.0f, .z = 402.792f}}, // Kak Redead Grotto -> Kakariko Village {.entranceIndex = ENTR_KAKARIKO_VILLAGE_0, .room = 0x00, .angle = 0x0000, .pos = {.x = -401.873f, .y = 0.0f, .z = 402.792f}}, // Kak Redead Grotto -> Kakariko Village
{.entranceIndex = 0x0138, .room = 0x00, .angle = 0x9555, .pos = {.x = 1009.02f, .y = 1571.0f, .z = 855.532f}}, // HC Storms Grotto -> Castle Grounds {.entranceIndex = ENTR_HYRULE_CASTLE_0, .room = 0x00, .angle = 0x9555, .pos = {.x = 1009.02f, .y = 1571.0f, .z = 855.532f}}, // HC Storms Grotto -> Castle Grounds
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0x1555, .pos = {.x = -4949.58f, .y = -300.0f, .z = 2837.59f}}, // HF Tektite Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0x1555, .pos = {.x = -4949.58f, .y = -300.0f, .z = 2837.59f}}, // HF Tektite Grotto -> Hyrule Field
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0xC000, .pos = {.x = 2050.6f, .y = 20.0f, .z = -160.397f}}, // HF Near Kak Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0xC000, .pos = {.x = 2050.6f, .y = 20.0f, .z = -160.397f}}, // HF Near Kak Grotto -> Hyrule Field
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0x0000, .pos = {.x = -4447.66f, .y = -300.0f, .z = -393.191f}}, // HF Fairy Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0x0000, .pos = {.x = -4447.66f, .y = -300.0f, .z = -393.191f}}, // HF Fairy Grotto -> Hyrule Field
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0xE000, .pos = {.x = -1446.56f, .y = 0.0f, .z = 830.775f}}, // HF Near Market Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0xE000, .pos = {.x = -1446.56f, .y = 0.0f, .z = 830.775f}}, // HF Near Market Grotto -> Hyrule Field
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0x0000, .pos = {.x = -7874.07f, .y = -300.0f, .z = 6921.31f}}, // HF Cow Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0x0000, .pos = {.x = -7874.07f, .y = -300.0f, .z = 6921.31f}}, // HF Cow Grotto -> Hyrule Field
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0xEAAB, .pos = {.x = -4989.13f, .y = -700.0f, .z = 13821.1f}}, // HF Inside Fence Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0xEAAB, .pos = {.x = -4989.13f, .y = -700.0f, .z = 13821.1f}}, // HF Inside Fence Grotto -> Hyrule Field
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0x8000, .pos = {.x = -4032.61f, .y = -700.0f, .z = 13831.5f}}, // HF Open Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0x8000, .pos = {.x = -4032.61f, .y = -700.0f, .z = 13831.5f}}, // HF Open Grotto -> Hyrule Field
{.entranceIndex = 0x01F9, .room = 0x00, .angle = 0x9555, .pos = {.x = -288.313f, .y = -500.0f, .z = 12320.2f}}, // HF Southeast Grotto -> Hyrule Field {.entranceIndex = ENTR_HYRULE_FIELD_6, .room = 0x00, .angle = 0x9555, .pos = {.x = -288.313f, .y = -500.0f, .z = 12320.2f}}, // HF Southeast Grotto -> Hyrule Field
{.entranceIndex = 0x0157, .room = 0x00, .angle = 0xAAAB, .pos = {.x = 1775.92f, .y = 0.0f, .z = 1486.82f}}, // LLR Grotto -> Lon Lon Ranch {.entranceIndex = ENTR_LON_LON_RANCH_0, .room = 0x00, .angle = 0xAAAB, .pos = {.x = 1775.92f, .y = 0.0f, .z = 1486.82f}}, // LLR Grotto -> Lon Lon Ranch
{.entranceIndex = 0x00FC, .room = 0x00, .angle = 0x8000, .pos = {.x = -189.861f, .y = 0.0f, .z = 1898.09f}}, // SFM Wolfos Grotto -> SFM Entryway {.entranceIndex = ENTR_SACRED_FOREST_MEADOW_0, .room = 0x00, .angle = 0x8000, .pos = {.x = -189.861f, .y = 0.0f, .z = 1898.09f}}, // SFM Wolfos Grotto -> SFM Entryway
{.entranceIndex = 0x00FC, .room = 0x00, .angle = 0xAAAB, .pos = {.x = 314.853f, .y = 480.0f, .z = -2300.39f}}, // SFM Storms Grotto -> Sacred Forest Meadow {.entranceIndex = ENTR_SACRED_FOREST_MEADOW_0, .room = 0x00, .angle = 0xAAAB, .pos = {.x = 314.853f, .y = 480.0f, .z = -2300.39f}}, // SFM Storms Grotto -> Sacred Forest Meadow
{.entranceIndex = 0x00FC, .room = 0x00, .angle = 0x0000, .pos = {.x = 55.034f, .y = 0.0f, .z = 250.595f}}, // SFM Fairy Grotto -> Sacred Forest Meadow {.entranceIndex = ENTR_SACRED_FOREST_MEADOW_0, .room = 0x00, .angle = 0x0000, .pos = {.x = 55.034f, .y = 0.0f, .z = 250.595f}}, // SFM Fairy Grotto -> Sacred Forest Meadow
{.entranceIndex = 0x01A9, .room = 0x08, .angle = 0x2000, .pos = {.x = 691.994f, .y = 0.0f, .z = -2502.2f}}, // LW Scrubs Grotto -> LW Beyond Mido {.entranceIndex = ENTR_LOST_WOODS_1, .room = 0x08, .angle = 0x2000, .pos = {.x = 691.994f, .y = 0.0f, .z = -2502.2f}}, // LW Scrubs Grotto -> LW Beyond Mido
{.entranceIndex = 0x011E, .room = 0x02, .angle = 0xE000, .pos = {.x = 905.755f, .y = 0.0f, .z = -901.43f}}, // LW Near Shortcuts Grotto -> Lost Woods {.entranceIndex = ENTR_LOST_WOODS_0, .room = 0x02, .angle = 0xE000, .pos = {.x = 905.755f, .y = 0.0f, .z = -901.43f}}, // LW Near Shortcuts Grotto -> Lost Woods
{.entranceIndex = 0x0286, .room = 0x00, .angle = 0x4000, .pos = {.x = -507.065f, .y = 380.0f, .z = -1220.43f}}, // KF Storms Grotto -> Kokiri Forest {.entranceIndex = ENTR_KOKIRI_FOREST_6, .room = 0x00, .angle = 0x4000, .pos = {.x = -507.065f, .y = 380.0f, .z = -1220.43f}}, // KF Storms Grotto -> Kokiri Forest
{.entranceIndex = 0x0108, .room = 0x01, .angle = 0xD555, .pos = {.x = -855.68f, .y = 14.0f, .z = -474.422f}}, // ZD Storms Grotto -> Zoras Domain {.entranceIndex = ENTR_ZORAS_DOMAIN_0, .room = 0x01, .angle = 0xD555, .pos = {.x = -855.68f, .y = 14.0f, .z = -474.422f}}, // ZD Storms Grotto -> Zoras Domain
{.entranceIndex = 0x0129, .room = 0x00, .angle = 0x4000, .pos = {.x = 380.521f, .y = 333.0f, .z = -1560.74f}}, // GF Storms Grotto -> Gerudo Fortress {.entranceIndex = ENTR_GERUDOS_FORTRESS_0, .room = 0x00, .angle = 0x4000, .pos = {.x = 380.521f, .y = 333.0f, .z = -1560.74f}}, // GF Storms Grotto -> Gerudo Fortress
{.entranceIndex = 0x022D, .room = 0x00, .angle = 0x9555, .pos = {.x = -1326.34f, .y = 15.0f, .z = -983.994f}}, // GV Storms Grotto -> GV Fortress Side {.entranceIndex = ENTR_GERUDO_VALLEY_3, .room = 0x00, .angle = 0x9555, .pos = {.x = -1326.34f, .y = 15.0f, .z = -983.994f}}, // GV Storms Grotto -> GV Fortress Side
{.entranceIndex = 0x0117, .room = 0x00, .angle = 0x8000, .pos = {.x = 291.513f, .y = -555.0f, .z = 1478.39f}}, // GV Octorok Grotto -> GV Grotto Ledge {.entranceIndex = ENTR_GERUDO_VALLEY_0, .room = 0x00, .angle = 0x8000, .pos = {.x = 291.513f, .y = -555.0f, .z = 1478.39f}}, // GV Octorok Grotto -> GV Grotto Ledge
{.entranceIndex = 0x01A9, .room = 0x06, .angle = 0x4000, .pos = {.x = 109.281f, .y = -20.0f, .z = -1601.42f}}, // Deku Theater -> LW Beyond Mido {.entranceIndex = ENTR_LOST_WOODS_1, .room = 0x06, .angle = 0x4000, .pos = {.x = 109.281f, .y = -20.0f, .z = -1601.42f}}, // Deku Theater -> LW Beyond Mido
}; };
static s16 grottoExitList[NUM_GROTTOS] = {0}; static s16 grottoExitList[NUM_GROTTOS] = {0};
@ -139,8 +139,8 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
// If Link hits a grotto exit, load the entrance index from the grotto exit list // If Link hits a grotto exit, load the entrance index from the grotto exit list
// based on the current grotto ID // based on the current grotto ID
if (nextEntranceIndex == 0x7FFF) { if (nextEntranceIndex == ENTR_RETURN_GROTTO) {
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId); Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId, false);
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId); EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId);
nextEntranceIndex = grottoExitList[grottoId]; nextEntranceIndex = grottoExitList[grottoId];
} }
@ -158,18 +158,18 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
// When the nextEntranceIndex is determined by a dynamic exit, // When the nextEntranceIndex is determined by a dynamic exit,
// or set by Entrance_OverrideBlueWarp to mark a blue warp entrance, // or set by Entrance_OverrideBlueWarp to mark a blue warp entrance,
// we have to set the respawn information and nextEntranceIndex manually // we have to set the respawn information and nextEntranceIndex manually
if (gPlayState != NULL && gPlayState->nextEntranceIndex != -1) { if (gPlayState != NULL && gPlayState->nextEntranceIndex != ENTR_LOAD_OPENING) {
gSaveContext.respawnFlag = 2; gSaveContext.respawnFlag = 2;
nextEntranceIndex = grotto.entranceIndex; nextEntranceIndex = grotto.entranceIndex;
gPlayState->fadeTransition = 3; gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
gSaveContext.nextTransitionType = 3; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE;
} else if (gPlayState == NULL) { // Handle spawn position when loading from a save file } else if (gPlayState == NULL) { // Handle spawn position when loading from a save file
gSaveContext.respawnFlag = 2; gSaveContext.respawnFlag = 2;
nextEntranceIndex = grotto.entranceIndex; nextEntranceIndex = grotto.entranceIndex;
gSaveContext.nextTransitionType = 3; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE;
// Otherwise return 0x7FFF and let the game handle it // Otherwise return 0x7FFF (ENTR_RETURN_GROTTO) and let the game handle it
} else { } else {
nextEntranceIndex = 0x7FFF; nextEntranceIndex = ENTR_RETURN_GROTTO;
} }
lastEntranceType = GROTTO_RETURN; lastEntranceType = GROTTO_RETURN;
@ -211,7 +211,7 @@ void Grotto_OverrideActorEntrance(Actor* thisx) {
if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) { if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) {
// Find the override for the matching index from the grotto Load List // Find the override for the matching index from the grotto Load List
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_LOAD_START + index); Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_LOAD_START + index, false);
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_LOAD_START + index); EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_LOAD_START + index);
index = grottoLoadList[index]; index = grottoLoadList[index];

View file

@ -3,7 +3,7 @@
#include "z64math.h" #include "z64math.h"
#define NUM_GROTTOS 33 #define NUM_GROTTOS GROTTO_OFFSET_MAX
#define NOT_GROTTO 0 #define NOT_GROTTO 0
#define GROTTO_LOAD 1 #define GROTTO_LOAD 1
#define GROTTO_RETURN 2 #define GROTTO_RETURN 2

View file

@ -159,6 +159,7 @@ typedef enum {
RAND_INF_CHILD_FISHING, RAND_INF_CHILD_FISHING,
RAND_INF_ADULT_FISHING, RAND_INF_ADULT_FISHING,
RAND_INF_10_BIG_POES, RAND_INF_10_BIG_POES,
RAND_INF_GRANT_GANONS_BOSSKEY,
// If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be ceil(RAND_INF_MAX / 16) // If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be ceil(RAND_INF_MAX / 16)

View file

@ -219,7 +219,7 @@ std::unordered_map<RandomizerTrickArea, std::string> rtAreaNames = {
{ RTAREA_LAKE_HYLIA, "Lake Hylia"}, { RTAREA_LAKE_HYLIA, "Lake Hylia"},
{ RTAREA_GERUDO_VALLEY, "Gerudo Valley"}, { RTAREA_GERUDO_VALLEY, "Gerudo Valley"},
{ RTAREA_GERUDO_FORTRESS, "Gerudo Fortress"}, { RTAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
{ RTAREA_WASTELAND, "Desert Wasteland"}, { RTAREA_WASTELAND, "Haunted Wasteland"},
{ RTAREA_DESERT_COLOSSUS, "Desert Colossus"}, { RTAREA_DESERT_COLOSSUS, "Desert Colossus"},
{ RTAREA_MARKET, "Hyrule Market"}, { RTAREA_MARKET, "Hyrule Market"},
{ RTAREA_HYRULE_CASTLE, "Hyrule Castle"}, { RTAREA_HYRULE_CASTLE, "Hyrule Castle"},

View file

@ -305,7 +305,7 @@ extern "C" void Randomizer_InitSaveFile() {
switch (startingAge) { switch (startingAge) {
case RO_AGE_ADULT: // Adult case RO_AGE_ADULT: // Adult
gSaveContext.linkAge = LINK_AGE_ADULT; gSaveContext.linkAge = LINK_AGE_ADULT;
gSaveContext.entranceIndex = 0x5F4; gSaveContext.entranceIndex = ENTR_TEMPLE_OF_TIME_7;
gSaveContext.savedSceneNum = SCENE_LON_LON_RANCH; // Set scene num manually to ToT gSaveContext.savedSceneNum = SCENE_LON_LON_RANCH; // Set scene num manually to ToT
break; break;
case RO_AGE_CHILD: // Child case RO_AGE_CHILD: // Child

View file

@ -0,0 +1,491 @@
#include "ResolutionEditor.h"
#include <ImGui/imgui.h>
#include <libultraship/libultraship.h>
#include <soh/UIWidgets.hpp>
#include <graphic/Fast3D/gfx_pc.h>
/* Console Variables are grouped under gAdvancedResolution. (e.g. "gAdvancedResolution.Enabled")
The following cvars are used in Libultraship and can be edited here:
- Enabled - Turns Advanced Resolution Mode on.
- AspectRatioX, AspectRatioY - Aspect ratio controls. To toggle off, set either to zero.
- VerticalPixelCount, VerticalResolutionToggle - Resolution controls.
- PixelPerfectMode, IntegerScale.Factor - Pixel Perfect Mode a.k.a. integer scaling controls.
- IntegerScale.FitAutomatically - Automatic resizing for Pixel Perfect Mode.
- IntegerScale.NeverExceedBounds - Prevents manual resizing from exceeding screen bounds.
The following cvars are also implemented in LUS for niche use cases:
- IgnoreAspectCorrection - Stretch framebuffer to fill screen.
This is something of a power-user setting for niche setups that most people won't need or care about,
but may be useful if playing the Switch/Wii U ports on a 4:3 television.
- IntegerScale.ExceedBoundsBy - Offset the max screen bounds, usually by +1.
This isn't that useful at the moment, so it's unused here.
*/
namespace AdvancedResolutionSettings {
enum setting { UPDATE_aspectRatioX, UPDATE_aspectRatioY, UPDATE_verticalPixelCount };
const char* aspectRatioPresetLabels[] = {
"Off", "Custom", "Original (4:3)", "Widescreen (16:9)", "Nintendo 3DS (5:3)", "16:10 (8:5)", "Ultrawide (21:9)"
};
const float aspectRatioPresetsX[] = { 0.0f, 16.0f, 4.0f, 16.0f, 5.0f, 16.0f, 21.0f };
const float aspectRatioPresetsY[] = { 0.0f, 9.0f, 3.0f, 9.0f, 3.0f, 10.0f, 9.0f };
const int default_aspectRatio = 1; // Default combo list option
const char* pixelCountPresetLabels[] = { "Custom", "Native N64 (240p)", "2x (480p)", "3x (720p)", "4x (960p)",
"5x (1200p)", "6x (1440p)", "Full HD (1080p)", "4K (2160p)" };
const int pixelCountPresets[] = { 480, 240, 480, 720, 960, 1200, 1440, 1080, 2160 };
const int default_pixelCount = 0; // Default combo list option
// Resolution clamp values as hardcoded in LUS::Gui::ApplyResolutionChanges()
const uint32_t minVerticalPixelCount = SCREEN_HEIGHT;
const uint32_t maxVerticalPixelCount = 4320; // 18x native, or 8K TV resolution
const unsigned short default_maxIntegerScaleFactor = 6; // Default size of Integer scale factor slider.
enum messageType { MESSAGE_ERROR, MESSAGE_WARNING, MESSAGE_QUESTION, MESSAGE_INFO, MESSAGE_GRAY_75 };
const ImVec4 messageColor[]{
{ 0.85f, 0.0f, 0.0f, 1.0f }, // MESSAGE_ERROR
{ 0.85f, 0.85f, 0.0f, 1.0f }, // MESSAGE_WARNING
{ 0.0f, 0.85f, 0.85f, 1.0f }, // MESSAGE_QUESTION
{ 0.0f, 0.85f, 0.55f, 1.0f }, // MESSAGE_INFO
{ 0.75f, 0.75f, 0.75f, 1.0f } // MESSAGE_GRAY_75
};
const float enhancementSpacerHeight = 19.0f;
void AdvancedResolutionSettingsWindow::InitElement() {
}
void AdvancedResolutionSettingsWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(497, 599), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Advanced Resolution Settings", &mIsVisible)) {
// Initialise update flags.
bool update[3];
for (uint8_t i = 0; i < sizeof(update); i++)
update[i] = false;
// Initialise integer scale bounds.
short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get
// overridden depending on viewport res
short integerScale_maximumBounds = 1; // can change when window is resized
// This is mostly just for UX purposes, as Fit Automatically logic is part of LUS.
if (((float)gfx_current_game_window_viewport.width / gfx_current_game_window_viewport.height) >
((float)gfx_current_dimensions.width / gfx_current_dimensions.height)) {
// Scale to window height
integerScale_maximumBounds = gfx_current_game_window_viewport.height / gfx_current_dimensions.height;
} else {
// Scale to window width
integerScale_maximumBounds = gfx_current_game_window_viewport.width / gfx_current_dimensions.width;
}
// Lower-clamping maximum bounds value to 1 is no-longer necessary as that's accounted for in LUS.
// Letting it go below 1 in this Editor will even allow for checking if screen bounds are being exceeded.
if (default_maxIntegerScaleFactor < integerScale_maximumBounds) {
max_integerScaleFactor =
integerScale_maximumBounds + CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
}
// Combo List defaults
static int item_aspectRatio = CVarGetInteger("gAdvancedResolution.UIComboItem.AspectRatio", 3);
static int item_pixelCount = CVarGetInteger("gAdvancedResolution.UIComboItem.PixelCount", default_pixelCount);
// Stored Values for non-UIWidgets elements
static float aspectRatioX =
CVarGetFloat("gAdvancedResolution.AspectRatioX", aspectRatioPresetsX[item_aspectRatio]);
static float aspectRatioY =
CVarGetFloat("gAdvancedResolution.AspectRatioY", aspectRatioPresetsY[item_aspectRatio]);
static int verticalPixelCount =
CVarGetInteger("gAdvancedResolution.VerticalPixelCount", pixelCountPresets[item_pixelCount]);
// Additional settings
static bool showHorizontalResField = false;
static int horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
// Disabling flags
const bool disabled_everything = !CVarGetInteger("gAdvancedResolution.Enabled", 0);
const bool disabled_pixelCount = !CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0);
#ifdef __APPLE__
// Display HiDPI warning. (Remove this once we can definitively say it's fixed.)
ImGui::TextColored(messageColor[MESSAGE_INFO],
ICON_FA_INFO_CIRCLE " These settings may behave incorrectly on Retina displays.");
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
#endif
if (ImGui::CollapsingHeader("Original Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
// The original resolution slider (for convenience)
const bool disabled_resolutionSlider = (CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0) &&
CVarGetInteger("gAdvancedResolution.Enabled", 0)) ||
CVarGetInteger("gLowResMode", 0);
if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f,
2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) {
LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(
CVarGetFloat("gInternalResolution", 1));
}
UIWidgets::Tooltip("Multiplies your output resolution by the value entered.");
// The original MSAA slider (also for convenience)
#ifndef __WIIU__
if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true,
false)) {
LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1));
};
UIWidgets::Tooltip(
"Activates multi-sample anti-aliasing when above 1x, up to 8x for 8 samples for every pixel.\n\n"
" " ICON_FA_INFO_CIRCLE
" (Higher MSAA with low resolution can approximate an authentic \"real N64\" look!)");
#endif
// N64 Mode toggle (again for convenience)
// UIWidgets::PaddedEnhancementCheckbox("(Enhancements>Graphics) N64 Mode", "gLowResMode", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, false);
}
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
// Activator
UIWidgets::PaddedEnhancementCheckbox("Enable advanced settings.", "gAdvancedResolution.Enabled", false, false,
false, "", UIWidgets::CheckboxGraphics::Cross, false);
// Error/Warning display
if (!CVarGetInteger("gLowResMode", 0)) {
if (IsDroppingFrames()) { // Significant frame drop warning
ImGui::TextColored(messageColor[MESSAGE_WARNING],
ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring.");
UIWidgets::Spacer(2);
} else { // No warnings
UIWidgets::Spacer(enhancementSpacerHeight);
}
} else { // N64 Mode warning
ImGui::TextColored(messageColor[MESSAGE_QUESTION],
ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings.");
ImGui::SameLine();
if (ImGui::Button("Click to disable")) {
CVarSetInteger("gLowResMode", 0);
CVarSave();
}
}
// Resolution visualiser
ImGui::Text("Viewport dimensions: %d x %d", gfx_current_game_window_viewport.width,
gfx_current_game_window_viewport.height);
ImGui::Text("Internal resolution: %d x %d", gfx_current_dimensions.width, gfx_current_dimensions.height);
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
if (disabled_everything) { // Hide aspect ratio controls.
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
}
// Aspect Ratio
ImGui::Text("Force aspect ratio:");
ImGui::SameLine();
ImGui::TextColored(messageColor[MESSAGE_GRAY_75], "(Select \"Off\" to disable.)");
// Presets
if (ImGui::Combo(" ", &item_aspectRatio, aspectRatioPresetLabels,
IM_ARRAYSIZE(aspectRatioPresetLabels)) &&
item_aspectRatio != default_aspectRatio) { // don't change anything if "Custom" is selected.
aspectRatioX = aspectRatioPresetsX[item_aspectRatio];
aspectRatioY = aspectRatioPresetsY[item_aspectRatio];
if (showHorizontalResField) {
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
}
CVarSetFloat("gAdvancedResolution.AspectRatioX", aspectRatioX);
CVarSetFloat("gAdvancedResolution.AspectRatioY", aspectRatioY);
CVarSetInteger("gAdvancedResolution.UIComboItem.AspectRatio", item_aspectRatio);
CVarSave();
}
// Hide aspect ratio input fields if using one of the presets.
if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) {
// Declare input interaction bools outside of IF statement to prevent Y field from disappearing.
const bool input_X = ImGui::InputFloat("X", &aspectRatioX, 0.1f, 1.0f, "%.3f");
const bool input_Y = ImGui::InputFloat("Y", &aspectRatioY, 0.1f, 1.0f, "%.3f");
if (input_X || input_Y) {
item_aspectRatio = default_aspectRatio;
update[UPDATE_aspectRatioX] = true;
update[UPDATE_aspectRatioY] = true;
}
} else if (showHorizontalResField) { // Show calculated aspect ratio
if (item_aspectRatio) {
UIWidgets::Spacer(2);
const float resolvedAspectRatio = (float)gfx_current_dimensions.width / gfx_current_dimensions.height;
ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio);
} else {
UIWidgets::Spacer(enhancementSpacerHeight);
}
}
if (disabled_everything) { // Hide aspect ratio controls.
UIWidgets::ReEnableComponent("disabledTooltipText");
}
UIWidgets::Spacer(0);
// Vertical Resolution
UIWidgets::PaddedEnhancementCheckbox("Set fixed vertical resolution (disables Resolution slider)",
"gAdvancedResolution.VerticalResolutionToggle", true, false,
disabled_everything, "", UIWidgets::CheckboxGraphics::Cross, false);
UIWidgets::Tooltip(
"Override the resolution scale slider and use the settings below, irrespective of window size.");
if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls.
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
}
if (ImGui::Combo("Pixel Count Presets", &item_pixelCount, pixelCountPresetLabels,
IM_ARRAYSIZE(pixelCountPresetLabels)) &&
item_pixelCount != default_pixelCount) { // don't change anything if "Custom" is selected.
verticalPixelCount = pixelCountPresets[item_pixelCount];
if (showHorizontalResField) {
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
}
CVarSetInteger("gAdvancedResolution.VerticalPixelCount", verticalPixelCount);
CVarSetInteger("gAdvancedResolution.UIComboItem.PixelCount", item_pixelCount);
CVarSave();
}
// Horizontal Resolution, if visibility is enabled for it.
if (showHorizontalResField) {
// Only show the field if Aspect Ratio is being enforced.
if ((aspectRatioX > 0.0f) && (aspectRatioY > 0.0f)) {
// So basically we're "faking" this one by setting aspectRatioX instead.
if (ImGui::InputInt("Horiz. Pixel Count", &horizontalPixelCount, 8, 320)) {
item_aspectRatio = default_aspectRatio;
if (horizontalPixelCount < SCREEN_WIDTH) {
horizontalPixelCount = SCREEN_WIDTH;
}
aspectRatioX = horizontalPixelCount;
aspectRatioY = verticalPixelCount;
update[UPDATE_aspectRatioX] = true;
update[UPDATE_aspectRatioY] = true;
}
} else { // Display a notice instead.
ImGui::TextColored(messageColor[MESSAGE_QUESTION],
ICON_FA_QUESTION_CIRCLE " \"Force aspect ratio\" required.");
// ImGui::Text(" ");
ImGui::SameLine();
if (ImGui::Button("Click to resolve")) {
item_aspectRatio = default_aspectRatio; // Set it to Custom
aspectRatioX = aspectRatioPresetsX[2]; // but use the 4:3 defaults
aspectRatioY = aspectRatioPresetsY[2];
update[UPDATE_aspectRatioX] = true;
update[UPDATE_aspectRatioY] = true;
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
}
}
}
// Vertical Resolution part 2
if (ImGui::InputInt("Vertical Pixel Count", &verticalPixelCount, 8, 240)) {
item_pixelCount = default_pixelCount;
update[UPDATE_verticalPixelCount] = true;
// Account for the natural instinct to enter horizontal first.
// Ignore vertical resolutions that are below the lower clamp constant.
if (showHorizontalResField && !(verticalPixelCount < minVerticalPixelCount)) {
item_aspectRatio = default_aspectRatio;
aspectRatioX = horizontalPixelCount;
aspectRatioY = verticalPixelCount;
update[UPDATE_aspectRatioX] = true;
update[UPDATE_aspectRatioY] = true;
}
}
if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls.
UIWidgets::ReEnableComponent("disabledTooltipText");
}
UIWidgets::Spacer(0);
// Integer scaling settings group (Pixel-perfect Mode)
static const ImGuiTreeNodeFlags IntegerScalingResolvedImGuiFlag =
CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ? ImGuiTreeNodeFlags_DefaultOpen
: ImGuiTreeNodeFlags_None;
if (ImGui::CollapsingHeader("Integer Scaling Settings", IntegerScalingResolvedImGuiFlag)) {
const bool disabled_pixelPerfectMode =
!CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) || disabled_everything;
// Pixel-perfect Mode
UIWidgets::PaddedEnhancementCheckbox("Pixel-perfect Mode", "gAdvancedResolution.PixelPerfectMode", true,
true, disabled_pixelCount || disabled_everything, "",
UIWidgets::CheckboxGraphics::Cross, false);
UIWidgets::Tooltip("Don't scale image to fill window.");
if (disabled_pixelCount && CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0)) {
CVarSetInteger("gAdvancedResolution.PixelPerfectMode", 0);
CVarSave();
}
// Integer Scaling
UIWidgets::EnhancementSliderInt(
"Integer scale factor: %d", "##ARSIntScale", "gAdvancedResolution.IntegerScale.Factor", 1,
max_integerScaleFactor, "%d", 1, true,
disabled_pixelPerfectMode || CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0));
UIWidgets::Tooltip("Integer scales the image. Only available in pixel-perfect mode.");
// Display warning if size is being clamped or if framebuffer is larger than viewport.
if (!disabled_pixelPerfectMode &&
(CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) &&
CVarGetInteger("gAdvancedResolution.IntegerScale.Factor", 1) > integerScale_maximumBounds)) {
ImGui::SameLine();
ImGui::TextColored(messageColor[MESSAGE_WARNING], ICON_FA_EXCLAMATION_TRIANGLE " Window exceeded.");
}
UIWidgets::PaddedEnhancementCheckbox(
"Automatically scale image to fit viewport", "gAdvancedResolution.IntegerScale.FitAutomatically", true,
true, disabled_pixelPerfectMode, "", UIWidgets::CheckboxGraphics::Cross, false);
UIWidgets::Tooltip("Automatically sets scale factor to fit window. Only available in pixel-perfect mode.");
if (CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0)) {
// This is just here to update the value shown on the slider.
// The function in LUS to handle this setting will ignore IntegerScaleFactor while active.
CVarSetInteger("gAdvancedResolution.IntegerScale.Factor", integerScale_maximumBounds);
// CVarSave();
}
} // End of integer scaling settings
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
// Collapsible panel for additional settings
if (ImGui::CollapsingHeader("Additional Settings")) {
UIWidgets::Spacer(0);
#if defined(__SWITCH__) || defined(__WIIU__)
// Disable aspect correction, stretching the framebuffer to fill the viewport.
// This option is only really needed on systems limited to 16:9 TV resolutions, such as consoles.
// The associated cvar is still functional on PC platforms if you want to use it though.
UIWidgets::PaddedEnhancementCheckbox("Disable aspect correction and stretch the output image.\n"
"(Might be useful for 4:3 televisions!)\n"
"Not available in Pixel Perfect Mode.",
"gAdvancedResolution.IgnoreAspectCorrection", false, true,
CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ||
disabled_everything,
"", UIWidgets::CheckboxGraphics::Cross, false);
#else
if (CVarGetInteger("gAdvancedResolution.IgnoreAspectCorrection", 0)) {
// This setting is intentionally not exposed on PC platforms,
// but may be accidentally activated for varying reasons.
// Having this button should hopefully prevent support headaches.
ImGui::TextColored(messageColor[MESSAGE_QUESTION], ICON_FA_QUESTION_CIRCLE
" If the image is stretched and you don't know why, click this.");
if (ImGui::Button("Click to reenable aspect correction.")) {
CVarSetInteger("gAdvancedResolution.IgnoreAspectCorrection", 0);
CVarSave();
}
UIWidgets::Spacer(2);
}
#endif
// A requested addition; an alternative way of displaying the resolution field.
if (ImGui::Checkbox("Show a horizontal resolution field, instead of aspect ratio.", &showHorizontalResField)) {
if (!showHorizontalResField && (aspectRatioX > 0.0f)) { // when turning this setting off
// Refresh relevant values
aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
} else { // when turning this setting on
item_aspectRatio = default_aspectRatio;
if (aspectRatioX > 0.0f) {
// Refresh relevant values in the opposite order
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
}
}
update[UPDATE_aspectRatioX] = true;
}
// Beginning of Integer Scaling additional settings.
{
// UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
// Integer Scaling - Never Exceed Bounds.
const bool disabled_neverExceedBounds =
!CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ||
CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0) || disabled_everything;
const bool checkbox_neverExceedBounds =
UIWidgets::PaddedEnhancementCheckbox("Prevent integer scaling from exceeding screen bounds.\n"
"(Makes screen bounds take priority over specified factor.)",
"gAdvancedResolution.IntegerScale.NeverExceedBounds",
true, false, disabled_neverExceedBounds, "",
UIWidgets::CheckboxGraphics::Cross, true);
UIWidgets::Tooltip(
"Prevents integer scaling factor from exceeding screen bounds.\n\n"
"Enabled: Will clamp the scaling factor and display a gentle warning in the resolution editor.\n"
"Disabled: Will allow scaling to exceed screen bounds, for users who want to crop overscan.\n\n"
" " ICON_FA_INFO_CIRCLE
" Please note that exceeding screen bounds may show a scroll bar on-screen.");
// Initialise the (currently unused) "Exceed Bounds By" cvar if it's been changed.
if (checkbox_neverExceedBounds &&
CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
CVarSetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
CVarSave();
}
// Integer Scaling - Exceed Bounds By 1x/Offset.
// A popular feature in some retro frontends/upscalers, sometimes called "crop overscan" or "1080p 5x".
/*
UIWidgets::PaddedEnhancementCheckbox("Allow integer scale factor to go +1 above maximum screen bounds.", "gAdvancedResolution.IntegerScale.ExceedBoundsBy", false, false, !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) || disabled_everything, "", UIWidgets::CheckboxGraphics::Cross, false);
*/
// It does actually function as expected, but exceeding the bottom of the screen shows a scroll bar.
// I've ended up commenting this one out because of the scroll bar, and for simplicity.
// Display an info message about the scroll bar.
if (!CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) ||
CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
if (disabled_neverExceedBounds) { // Dim this help text accordingly
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
}
ImGui::TextColored(messageColor[MESSAGE_INFO],
" " ICON_FA_INFO_CIRCLE
" A scroll bar may become visible if screen bounds are exceeded.");
if (disabled_neverExceedBounds) { // Dim this help text accordingly
UIWidgets::ReEnableComponent("disabledTooltipText");
}
// Another support helper button, to disable the unused "Exceed Bounds By" cvar.
// (Remove this button if uncommenting the checkbox.)
if (CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
if (ImGui::Button("Click to reset a console variable that may be causing this.")) {
CVarSetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
CVarSave();
}
}
} else {
ImGui::Text(" ");
}
// UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
} // End of Integer Scaling additional settings.
} // End of additional settings
// Clamp and update the cvars that don't use UIWidgets
if (update[UPDATE_aspectRatioX] || update[UPDATE_aspectRatioY] || update[UPDATE_verticalPixelCount]) {
if (update[UPDATE_aspectRatioX]) {
if (aspectRatioX < 0.0f) {
aspectRatioX = 0.0f;
}
CVarSetFloat("gAdvancedResolution.AspectRatioX", aspectRatioX);
}
if (update[UPDATE_aspectRatioY]) {
if (aspectRatioY < 0.0f) {
aspectRatioY = 0.0f;
}
CVarSetFloat("gAdvancedResolution.AspectRatioY", aspectRatioY);
}
if (update[UPDATE_verticalPixelCount]) {
// There's a upper and lower clamp on the Libultraship side too,
// so clamping it here is entirely visual, so the vertical resolution field reflects it.
if (verticalPixelCount < minVerticalPixelCount) {
verticalPixelCount = minVerticalPixelCount;
}
if (verticalPixelCount > maxVerticalPixelCount) {
verticalPixelCount = maxVerticalPixelCount;
}
CVarSetInteger("gAdvancedResolution.VerticalPixelCount", verticalPixelCount);
}
CVarSetInteger("gAdvancedResolution.UIComboItem.AspectRatio", item_aspectRatio);
CVarSetInteger("gAdvancedResolution.UIComboItem.PixelCount", item_pixelCount);
CVarSave();
}
}
ImGui::End();
}
void AdvancedResolutionSettingsWindow::UpdateElement() {
}
bool AdvancedResolutionSettingsWindow::IsDroppingFrames() {
// a rather imprecise way of checking for frame drops.
// but it's mostly there to inform the player of large drops.
const short targetFPS = CVarGetInteger("gInterpolationFPS", 20);
const float threshold = targetFPS / 20.0f + 4.1f;
return ImGui::GetIO().Framerate < targetFPS - threshold;
}
} // namespace AdvancedResolutionSettings

View file

@ -0,0 +1,16 @@
#pragma once
#include <libultraship/libultraship.h>
namespace AdvancedResolutionSettings {
class AdvancedResolutionSettingsWindow : public LUS::GuiWindow {
private:
bool IsDroppingFrames();
public:
using LUS::GuiWindow::GuiWindow;
void InitElement() override;
void DrawElement() override;
void UpdateElement() override;
};
} // namespace AdvancedResolutionSettings

View file

@ -64,7 +64,7 @@ typedef struct SaveStateInfo {
int16_t blueWarpTimerCopy; /* From door_warp_1 */ int16_t blueWarpTimerCopy; /* From door_warp_1 */
SeqScriptState seqScriptStateCopy[4];// Unrelocated SeqScriptState seqScriptStateCopy[4];// Unrelocated
unk_D_8016E750 unk_D_8016E750Copy[4]; ActiveSequence gActiveSeqsCopy[4];
ActiveSound gActiveSoundsCopy[7][MAX_CHANNELS_PER_BANK]; ActiveSound gActiveSoundsCopy[7][MAX_CHANNELS_PER_BANK];
uint8_t gSoundBankMutedCopy[7]; uint8_t gSoundBankMutedCopy[7];
@ -905,7 +905,7 @@ void SaveState::Save(void) {
memcpy(&info->audioHeapCopy, gAudioHeap, AUDIO_HEAP_SIZE /* sizeof(gAudioContext) */); memcpy(&info->audioHeapCopy, gAudioHeap, AUDIO_HEAP_SIZE /* sizeof(gAudioContext) */);
memcpy(&info->audioContextCopy, &gAudioContext, sizeof(AudioContext)); memcpy(&info->audioContextCopy, &gAudioContext, sizeof(AudioContext));
memcpy(&info->unk_D_8016E750Copy, D_8016E750, sizeof(info->unk_D_8016E750Copy)); memcpy(&info->gActiveSeqsCopy, gActiveSeqs, sizeof(info->gActiveSeqsCopy));
BackupSeqScriptState(); BackupSeqScriptState();
memcpy(info->gActiveSoundsCopy, gActiveSounds, sizeof(gActiveSounds)); memcpy(info->gActiveSoundsCopy, gActiveSounds, sizeof(gActiveSounds));
@ -944,7 +944,7 @@ void SaveState::Load(void) {
memcpy(gAudioHeap, &info->audioHeapCopy, AUDIO_HEAP_SIZE); memcpy(gAudioHeap, &info->audioHeapCopy, AUDIO_HEAP_SIZE);
memcpy(&gAudioContext, &info->audioContextCopy, sizeof(AudioContext)); memcpy(&gAudioContext, &info->audioContextCopy, sizeof(AudioContext));
memcpy(D_8016E750, &info->unk_D_8016E750Copy, sizeof(info->unk_D_8016E750Copy)); memcpy(gActiveSeqs, &info->gActiveSeqsCopy, sizeof(info->gActiveSeqsCopy));
LoadSeqScriptState(); LoadSeqScriptState();
memcpy(&gSaveContext, &info->saveContextCopy, sizeof(gSaveContext)); memcpy(&gSaveContext, &info->saveContextCopy, sizeof(gSaveContext));

View file

@ -30,6 +30,7 @@
#include <AudioPlayer.h> #include <AudioPlayer.h>
#include "Enhancements/speechsynthesizer/SpeechSynthesizer.h" #include "Enhancements/speechsynthesizer/SpeechSynthesizer.h"
#include "Enhancements/controls/GameControlEditor.h" #include "Enhancements/controls/GameControlEditor.h"
#include "Enhancements/controls/SohInputEditorWindow.h"
#include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/cosmetics/CosmeticsEditor.h"
#include "Enhancements/audio/AudioCollection.h" #include "Enhancements/audio/AudioCollection.h"
#include "Enhancements/audio/AudioEditor.h" #include "Enhancements/audio/AudioEditor.h"
@ -49,6 +50,9 @@
#include "Fonts.h" #include "Fonts.h"
#include <Utils/StringHelper.h> #include <Utils/StringHelper.h>
#include "Enhancements/custom-message/CustomMessageManager.h" #include "Enhancements/custom-message/CustomMessageManager.h"
#include "Enhancements/presets.h"
#include "util.h"
#include <boost_custom/container_hash/hash_32.hpp>
#if not defined (__SWITCH__) && not defined(__WIIU__) #if not defined (__SWITCH__) && not defined(__WIIU__)
#include "Extractor/Extract.h" #include "Extractor/Extract.h"
@ -77,9 +81,11 @@
#include "SohGui.hpp" #include "SohGui.hpp"
#include "ActorDB.h" #include "ActorDB.h"
#ifdef ENABLE_CROWD_CONTROL #ifdef ENABLE_REMOTE_CONTROL
#include "Enhancements/crowd-control/CrowdControl.h" #include "Enhancements/crowd-control/CrowdControl.h"
#include "Enhancements/game-interactor/GameInteractor_Sail.h"
CrowdControl* CrowdControl::Instance; CrowdControl* CrowdControl::Instance;
GameInteractorSail* GameInteractorSail::Instance;
#endif #endif
#include "Enhancements/mods.h" #include "Enhancements/mods.h"
@ -116,6 +122,10 @@ CrowdControl* CrowdControl::Instance;
#include "soh/config/ConfigUpdaters.h" #include "soh/config/ConfigUpdaters.h"
#include "soh/Enhancements/accessible-actors/ActorAccessibility.h" #include "soh/Enhancements/accessible-actors/ActorAccessibility.h"
#include "Enhancements//accessible-actors/ActorAccessibility.h" #include "Enhancements//accessible-actors/ActorAccessibility.h"
void SoH_ProcessDroppedFiles(std::string filePath);
OTRGlobals* OTRGlobals::Instance; OTRGlobals* OTRGlobals::Instance;
SaveManager* SaveManager::Instance; SaveManager* SaveManager::Instance;
CustomMessageManager* CustomMessageManager::Instance; CustomMessageManager* CustomMessageManager::Instance;
@ -254,20 +264,32 @@ OTRGlobals::OTRGlobals() {
OTRFiles.push_back(sohOtrPath); OTRFiles.push_back(sohOtrPath);
} }
std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName); std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName);
std::vector<std::string> patchOTRs = {};
if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) { if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) {
if (std::filesystem::is_directory(patchesPath)) { if (std::filesystem::is_directory(patchesPath)) {
for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) { for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath, std::filesystem::directory_options::follow_directory_symlink)) {
if (StringHelper::IEquals(p.path().extension().string(), ".otr")) { if (StringHelper::IEquals(p.path().extension().string(), ".otr")) {
OTRFiles.push_back(p.path().generic_string()); patchOTRs.push_back(p.path().generic_string());
} }
} }
} }
} }
std::string sohAccessibilityPath = LUS::Context::GetPathRelativeToAppDirectory("accessibility.otr"); std::string sohAccessibilityPath = LUS::Context::GetPathRelativeToAppDirectory("accessibility.otr");
if (std::filesystem::exists(sohAccessibilityPath)) { if (std::filesystem::exists(sohAccessibilityPath)) {
OTRFiles.push_back(sohAccessibilityPath); OTRFiles.push_back(sohAccessibilityPath);
} }
std::sort(patchOTRs.begin(), patchOTRs.end(), [](const std::string& a, const std::string& b) {
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
[](char c1, char c2) {
return std::tolower(c1) < std::tolower(c2);
}
);
});
OTRFiles.insert(OTRFiles.end(), patchOTRs.begin(), patchOTRs.end());
std::unordered_set<uint32_t> ValidHashes = { std::unordered_set<uint32_t> ValidHashes = {
OOT_PAL_MQ, OOT_PAL_MQ,
OOT_NTSC_JP_MQ, OOT_NTSC_JP_MQ,
@ -285,9 +307,26 @@ OTRGlobals::OTRGlobals() {
OOT_PAL_GC_DBG1, OOT_PAL_GC_DBG1,
OOT_PAL_GC_DBG2 OOT_PAL_GC_DBG2
}; };
// tell LUS to reserve 3 SoH specific threads (Game, Audio, Save)
context = LUS::Context::CreateInstance("Ship of Harkinian", appShortName, "shipofharkinian.json", OTRFiles, {}, 3);
context = LUS::Context::CreateUninitializedInstance("Ship of Harkinian", appShortName, "shipofharkinian.json");
context->InitLogging();
context->InitConfiguration();
context->InitConsoleVariables();
// tell LUS to reserve 3 SoH specific threads (Game, Audio, Save)
context->InitResourceManager(OTRFiles, {}, 3);
context->InitControlDeck({BTN_MODIFIER1, BTN_MODIFIER2});
context->GetControlDeck()->SetSinglePlayerMappingMode(true);
context->InitCrashHandler();
context->InitConsole();
auto sohInputEditorWindow = std::make_shared<SohInputEditorWindow>("gControllerConfigurationEnabled", "Input Editor");
context->InitWindow(sohInputEditorWindow);
context->InitAudio();
context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Animation, "Animation", std::make_shared<LUS::AnimationFactory>()); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Animation, "Animation", std::make_shared<LUS::AnimationFactory>());
context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared<LUS::PlayerAnimationFactory>()); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared<LUS::PlayerAnimationFactory>());
context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Room, "Room", std::make_shared<LUS::SceneFactory>()); // Is room scene? maybe? context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Room, "Room", std::make_shared<LUS::SceneFactory>()); // Is room scene? maybe?
@ -643,7 +682,7 @@ extern "C" void VanillaItemTable_Init() {
} }
} }
std::unordered_map<uint32_t, uint32_t> ItemIDtoGetItemID{ std::unordered_map<ItemID, GetItemID> ItemIDtoGetItemIDMap {
{ ITEM_ARROWS_LARGE, GI_ARROWS_LARGE }, { ITEM_ARROWS_LARGE, GI_ARROWS_LARGE },
{ ITEM_ARROWS_MEDIUM, GI_ARROWS_MEDIUM }, { ITEM_ARROWS_MEDIUM, GI_ARROWS_MEDIUM },
{ ITEM_ARROWS_SMALL, GI_ARROWS_SMALL }, { ITEM_ARROWS_SMALL, GI_ARROWS_SMALL },
@ -673,7 +712,8 @@ std::unordered_map<uint32_t, uint32_t> ItemIDtoGetItemID{
{ ITEM_BUG, GI_BUGS }, { ITEM_BUG, GI_BUGS },
{ ITEM_BULLET_BAG_30, GI_BULLET_BAG_30 }, { ITEM_BULLET_BAG_30, GI_BULLET_BAG_30 },
{ ITEM_BULLET_BAG_40, GI_BULLET_BAG_40 }, { ITEM_BULLET_BAG_40, GI_BULLET_BAG_40 },
{ ITEM_BULLET_BAG_50, GI_BULLET_BAG_50 }, { ITEM_CHICKEN, GI_CHICKEN }, { ITEM_BULLET_BAG_50, GI_BULLET_BAG_50 },
{ ITEM_CHICKEN, GI_CHICKEN },
{ ITEM_CLAIM_CHECK, GI_CLAIM_CHECK }, { ITEM_CLAIM_CHECK, GI_CLAIM_CHECK },
{ ITEM_COJIRO, GI_COJIRO }, { ITEM_COJIRO, GI_COJIRO },
{ ITEM_COMPASS, GI_COMPASS }, { ITEM_COMPASS, GI_COMPASS },
@ -769,11 +809,42 @@ std::unordered_map<uint32_t, uint32_t> ItemIDtoGetItemID{
{ ITEM_WEIRD_EGG, GI_WEIRD_EGG } { ITEM_WEIRD_EGG, GI_WEIRD_EGG }
}; };
extern "C" int32_t GetGIID(uint32_t itemID) { extern "C" GetItemID RetrieveGetItemIDFromItemID(ItemID itemID) {
if (ItemIDtoGetItemID.contains(itemID)) { if (ItemIDtoGetItemIDMap.contains(itemID)) {
return ItemIDtoGetItemID.at(itemID); return ItemIDtoGetItemIDMap.at(itemID);
} }
return -1; return GI_MAX;
}
std::unordered_map<ItemID, RandomizerGet> ItemIDtoRandomizerGetMap {
{ ITEM_SONG_MINUET, RG_MINUET_OF_FOREST },
{ ITEM_SONG_BOLERO, RG_BOLERO_OF_FIRE },
{ ITEM_SONG_SERENADE, RG_SERENADE_OF_WATER },
{ ITEM_SONG_REQUIEM, RG_REQUIEM_OF_SPIRIT },
{ ITEM_SONG_NOCTURNE, RG_NOCTURNE_OF_SHADOW },
{ ITEM_SONG_PRELUDE, RG_PRELUDE_OF_LIGHT },
{ ITEM_SONG_LULLABY, RG_ZELDAS_LULLABY },
{ ITEM_SONG_EPONA, RG_EPONAS_SONG },
{ ITEM_SONG_SARIA, RG_SARIAS_SONG },
{ ITEM_SONG_SUN, RG_SUNS_SONG },
{ ITEM_SONG_TIME, RG_SONG_OF_TIME },
{ ITEM_SONG_STORMS, RG_SONG_OF_STORMS },
{ ITEM_MEDALLION_FOREST, RG_FOREST_MEDALLION },
{ ITEM_MEDALLION_FIRE, RG_FIRE_MEDALLION },
{ ITEM_MEDALLION_WATER, RG_WATER_MEDALLION },
{ ITEM_MEDALLION_SPIRIT, RG_SPIRIT_MEDALLION },
{ ITEM_MEDALLION_SHADOW, RG_SHADOW_MEDALLION },
{ ITEM_MEDALLION_LIGHT, RG_LIGHT_MEDALLION },
{ ITEM_KOKIRI_EMERALD, RG_KOKIRI_EMERALD },
{ ITEM_GORON_RUBY, RG_GORON_RUBY },
{ ITEM_ZORA_SAPPHIRE, RG_ZORA_SAPPHIRE },
};
extern "C" RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID) {
if (ItemIDtoRandomizerGetMap.contains(itemID)) {
return ItemIDtoRandomizerGetMap.at(itemID);
}
return RG_MAX;
} }
extern "C" void OTRExtScanner() { extern "C" void OTRExtScanner() {
@ -1031,9 +1102,9 @@ extern "C" void InitOTR() {
OTRGlobals::Instance = new OTRGlobals(); OTRGlobals::Instance = new OTRGlobals();
CustomMessageManager::Instance = new CustomMessageManager(); CustomMessageManager::Instance = new CustomMessageManager();
ItemTableManager::Instance = new ItemTableManager(); ItemTableManager::Instance = new ItemTableManager();
GameInteractor::Instance = new GameInteractor();
SaveManager::Instance = new SaveManager(); SaveManager::Instance = new SaveManager();
SohGui::SetupGuiElements(); SohGui::SetupGuiElements();
GameInteractor::Instance = new GameInteractor();
AudioCollection::Instance = new AudioCollection(); AudioCollection::Instance = new AudioCollection();
ActorDB::Instance = new ActorDB(); ActorDB::Instance = new ActorDB();
#ifdef __APPLE__ #ifdef __APPLE__
@ -1043,7 +1114,12 @@ extern "C" void InitOTR() {
SpeechSynthesizer::Instance = new SAPISpeechSynthesizer(); SpeechSynthesizer::Instance = new SAPISpeechSynthesizer();
SpeechSynthesizer::Instance->Init(); SpeechSynthesizer::Instance->Init();
#endif #endif
#ifdef ENABLE_REMOTE_CONTROL
CrowdControl::Instance = new CrowdControl();
GameInteractorSail::Instance = new GameInteractorSail();
#endif
clearMtx = (uintptr_t)&gMtxClear; clearMtx = (uintptr_t)&gMtxClear;
OTRMessage_Init(); OTRMessage_Init();
ActorAccessibility_Init(); ActorAccessibility_Init();
@ -1054,6 +1130,11 @@ extern "C" void InitOTR() {
InitMods(); InitMods();
ActorDB::AddBuiltInCustomActors(); ActorDB::AddBuiltInCustomActors();
// #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer
CVarClear("gRandomizerNewFileDropped");
CVarClear("gRandomizerDroppedFile");
// #endregion
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFileDropped>(SoH_ProcessDroppedFiles);
time_t now = time(NULL); time_t now = time(NULL);
tm *tm_now = localtime(&now); tm *tm_now = localtime(&now);
@ -1064,13 +1145,17 @@ extern "C" void InitOTR() {
} }
srand(now); srand(now);
#ifdef ENABLE_CROWD_CONTROL #ifdef ENABLE_REMOTE_CONTROL
CrowdControl::Instance = new CrowdControl(); SDLNet_Init();
CrowdControl::Instance->Init(); if (CVarGetInteger("gRemote.Enabled", 0)) {
if (CVarGetInteger("gCrowdControl", 0)) { switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
CrowdControl::Instance->Enable(); case GI_SCHEME_SAIL:
} else { GameInteractorSail::Instance->Enable();
CrowdControl::Instance->Disable(); break;
case GI_SCHEME_CROWD_CONTROL:
CrowdControl::Instance->Enable();
break;
}
} }
#endif #endif
@ -1087,9 +1172,18 @@ extern "C" void SaveManager_ThreadPoolWait() {
extern "C" void DeinitOTR() { extern "C" void DeinitOTR() {
SaveManager_ThreadPoolWait(); SaveManager_ThreadPoolWait();
OTRAudio_Exit(); OTRAudio_Exit();
#ifdef ENABLE_CROWD_CONTROL #ifdef ENABLE_REMOTE_CONTROL
CrowdControl::Instance->Disable(); if (CVarGetInteger("gRemote.Enabled", 0)) {
CrowdControl::Instance->Shutdown(); switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
case GI_SCHEME_SAIL:
GameInteractorSail::Instance->Disable();
break;
case GI_SCHEME_CROWD_CONTROL:
CrowdControl::Instance->Disable();
break;
}
}
SDLNet_Quit();
#endif #endif
ActorAccessibility_Shutdown(); ActorAccessibility_Shutdown();
// Destroying gui here because we have shared ptrs to LUS objects which output to SPDLOG which is destroyed before these shared ptrs. // Destroying gui here because we have shared ptrs to LUS objects which output to SPDLOG which is destroyed before these shared ptrs.
@ -1142,7 +1236,7 @@ extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) {
OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter); OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter);
} }
extern bool ShouldClearTextureCacheAtEndOfFrame; extern bool ToggleAltAssetsAtEndOfFrame;
extern "C" void Graph_StartFrame() { extern "C" void Graph_StartFrame() {
#ifndef __WIIU__ #ifndef __WIIU__
@ -1225,13 +1319,21 @@ extern "C" void Graph_StartFrame() {
} }
#endif #endif
case KbScancode::LUS_KB_TAB: { case KbScancode::LUS_KB_TAB: {
// Toggle HD Assets ToggleAltAssetsAtEndOfFrame = true;
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
ShouldClearTextureCacheAtEndOfFrame = true;
break; break;
} }
} }
#endif #endif
if (CVarGetInteger("gNewFileDropped", 0)) {
std::string filePath = SohUtils::Sanitize(CVarGetString("gDroppedFile", ""));
if (!filePath.empty()) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnFileDropped>(filePath);
}
CVarClear("gNewFileDropped");
CVarClear("gDroppedFile");
}
OTRGlobals::Instance->context->GetWindow()->StartFrame(); OTRGlobals::Instance->context->GetWindow()->StartFrame();
} }
@ -1297,10 +1399,14 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
} }
} }
if (ShouldClearTextureCacheAtEndOfFrame) { if (ToggleAltAssetsAtEndOfFrame) {
ToggleAltAssetsAtEndOfFrame = false;
// Actually update the CVar now before runing the alt asset update listeners
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
gfx_texture_cache_clear(); gfx_texture_cache_clear();
LUS::SkeletonPatcher::UpdateSkeletons(); LUS::SkeletonPatcher::UpdateSkeletons();
ShouldClearTextureCacheAtEndOfFrame = false; GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
} }
// OTRTODO: FIGURE OUT END FRAME POINT // OTRTODO: FIGURE OUT END FRAME POINT
@ -1426,6 +1532,14 @@ extern "C" void ResourceMgr_DirtyDirectory(const char* resName) {
LUS::Context::GetInstance()->GetResourceManager()->DirtyDirectory(resName); LUS::Context::GetInstance()->GetResourceManager()->DirtyDirectory(resName);
} }
extern "C" void ResourceMgr_UnloadResource(const char* resName) {
std::string path = resName;
if (path.substr(0, 7) == "__OTR__") {
path = path.substr(7);
}
auto res = LUS::Context::GetInstance()->GetResourceManager()->UnloadResource(path);
}
// OTRTODO: There is probably a more elegant way to go about this... // OTRTODO: There is probably a more elegant way to go about this...
// Kenix: This is definitely leaking memory when it's called. // Kenix: This is definitely leaking memory when it's called.
extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) { extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) {
@ -1452,6 +1566,27 @@ extern "C" uint8_t ResourceMgr_FileExists(const char* filePath) {
return ExtensionCache.contains(path); return ExtensionCache.contains(path);
} }
extern "C" uint8_t ResourceMgr_FileAltExists(const char* filePath) {
std::string path = filePath;
if (path.substr(0, 7) == "__OTR__") {
path = path.substr(7);
}
if (path.substr(0, 4) != "alt/") {
path = "alt/" + path;
}
return ExtensionCache.contains(path);
}
// Unloads a resource if an alternate version exists when alt assets are enabled
// The resource is only removed from the internal cache to prevent it from used in the next resource lookup
extern "C" void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName) {
if (CVarGetInteger("gAltAssets", 0) && ResourceMgr_FileAltExists((char*) resName)) {
ResourceMgr_UnloadResource((char*) resName);
}
}
extern "C" void ResourceMgr_LoadFile(const char* resName) { extern "C" void ResourceMgr_LoadFile(const char* resName) {
LUS::Context::GetInstance()->GetResourceManager()->LoadResource(resName); LUS::Context::GetInstance()->GetResourceManager()->LoadResource(resName);
} }
@ -1491,6 +1626,11 @@ extern "C" char* ResourceMgr_LoadFileFromDisk(const char* filePath) {
return data; return data;
} }
extern "C" uint8_t ResourceMgr_TexIsRaw(const char* texPath) {
auto res = std::static_pointer_cast<LUS::Texture>(GetResourceByNameHandlingMQ(texPath));
return res->Flags & TEX_FLAG_LOAD_AS_RAW;
}
extern "C" uint8_t ResourceMgr_ResourceIsBackground(char* texPath) { extern "C" uint8_t ResourceMgr_ResourceIsBackground(char* texPath) {
auto res = GetResourceByNameHandlingMQ(texPath); auto res = GetResourceByNameHandlingMQ(texPath);
return res->GetInitData()->Type == LUS::ResourceType::SOH_Background; return res->GetInitData()->Type == LUS::ResourceType::SOH_Background;
@ -1576,10 +1716,20 @@ extern "C" void ResourceMgr_PushCurrentDirectory(char* path)
extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path) extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path)
{ {
// When an alt resource exists for the DL, we need to unload the original asset
// to clear the cache so the alt asset will be loaded instead
// OTRTODO: If Alt loading over original cache is fixed, this line can most likely be removed
ResourceMgr_UnloadOriginalWhenAltExists(path);
auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path)); auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path));
return (Gfx*)&res->Instructions[0]; return (Gfx*)&res->Instructions[0];
} }
extern "C" uint8_t ResourceMgr_FileIsCustomByName(const char* path) {
auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path));
return res->GetInitData()->IsCustom;
}
typedef struct { typedef struct {
int index; int index;
Gfx instruction; Gfx instruction;
@ -1611,6 +1761,11 @@ extern "C" void ResourceMgr_PatchGfxByName(const char* path, const char* patchNa
// index /= 2; // index /= 2;
// } // }
// Do not patch custom assets as they most likely do not have the same instructions as authentic assets
if (res->GetInitData()->IsCustom) {
return;
}
Gfx* gfx = (Gfx*)&res->Instructions[index]; Gfx* gfx = (Gfx*)&res->Instructions[index];
if (!originalGfx.contains(path) || !originalGfx[path].contains(patchName)) { if (!originalGfx.contains(path) || !originalGfx[path].contains(patchName)) {
@ -1627,6 +1782,11 @@ extern "C" void ResourceMgr_PatchGfxCopyCommandByName(const char* path, const ch
auto res = std::static_pointer_cast<LUS::DisplayList>( auto res = std::static_pointer_cast<LUS::DisplayList>(
LUS::Context::GetInstance()->GetResourceManager()->LoadResource(path)); LUS::Context::GetInstance()->GetResourceManager()->LoadResource(path));
// Do not patch custom assets as they most likely do not have the same instructions as authentic assets
if (res->GetInitData()->IsCustom) {
return;
}
Gfx* destinationGfx = (Gfx*)&res->Instructions[destinationIndex]; Gfx* destinationGfx = (Gfx*)&res->Instructions[destinationIndex];
Gfx sourceGfx = res->Instructions[sourceIndex]; Gfx sourceGfx = res->Instructions[sourceIndex];
@ -2041,10 +2201,10 @@ Color_RGB8 GetColorForControllerLED() {
if (source == LED_SOURCE_CUSTOM) { if (source == LED_SOURCE_CUSTOM) {
color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 }); color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
} }
if (criticalOverride || source == LED_SOURCE_HEALTH) { if (gPlayState && (criticalOverride || source == LED_SOURCE_HEALTH)) {
if (HealthMeter_IsCritical()) { if (HealthMeter_IsCritical()) {
color = { 0xFF, 0, 0 }; color = { 0xFF, 0, 0 };
} else if (source == LED_SOURCE_HEALTH) { } else if (gSaveContext.healthCapacity != 0 && source == LED_SOURCE_HEALTH) {
if (gSaveContext.health / gSaveContext.healthCapacity <= 0.4f) { if (gSaveContext.health / gSaveContext.healthCapacity <= 0.4f) {
color = { 0xFF, 0xFF, 0 }; color = { 0xFF, 0xFF, 0 };
} else { } else {
@ -2061,15 +2221,22 @@ Color_RGB8 GetColorForControllerLED() {
} }
extern "C" void OTRControllerCallback(uint8_t rumble) { extern "C" void OTRControllerCallback(uint8_t rumble) {
auto physicalDevice = LUS::Context::GetInstance()->GetControlDeck()->GetDeviceFromPortIndex(0); // We call this every tick, SDL accounts for this use and prevents driver spam
// https://github.com/libsdl-org/SDL/blob/f17058b562c8a1090c0c996b42982721ace90903/src/joystick/SDL_joystick.c#L1114-L1144
LUS::Context::GetInstance()->GetControlDeck()->GetControllerByPort(0)->GetLED()->SetLEDColor(GetColorForControllerLED());
if (physicalDevice->CanSetLed()) { static std::shared_ptr<SohInputEditorWindow> controllerConfigWindow = nullptr;
// We call this every tick, SDL accounts for this use and prevents driver spam if (controllerConfigWindow == nullptr) {
// https://github.com/libsdl-org/SDL/blob/f17058b562c8a1090c0c996b42982721ace90903/src/joystick/SDL_joystick.c#L1114-L1144 controllerConfigWindow = std::dynamic_pointer_cast<SohInputEditorWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Input Editor"));
physicalDevice->SetLedColor(0, GetColorForControllerLED()); } else if (controllerConfigWindow->TestingRumble()) {
return;
} }
physicalDevice->SetRumble(0, rumble); if (rumble) {
LUS::Context::GetInstance()->GetControlDeck()->GetControllerByPort(0)->GetRumble()->StartRumble();
} else {
LUS::Context::GetInstance()->GetControlDeck()->GetControllerByPort(0)->GetRumble()->StopRumble();
}
} }
extern "C" float OTRGetAspectRatio() { extern "C" float OTRGetAspectRatio() {
@ -2108,12 +2275,12 @@ extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len) {
} }
extern "C" int Controller_ShouldRumble(size_t slot) { extern "C" int Controller_ShouldRumble(size_t slot) {
auto controlDeck = LUS::Context::GetInstance()->GetControlDeck(); for (auto [id, mapping] : LUS::Context::GetInstance()
->GetControlDeck()
if (slot < controlDeck->GetNumConnectedPorts()) { ->GetControllerByPort(static_cast<uint8_t>(slot))
auto physicalDevice = controlDeck->GetDeviceFromPortIndex(slot); ->GetRumble()
->GetAllRumbleMappings()) {
if (physicalDevice->GetProfile(slot)->UseRumble && physicalDevice->CanRumble()) { if (mapping->PhysicalDeviceIsConnected()) {
return 1; return 1;
} }
} }
@ -2338,7 +2505,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
actorParams = stone->params; actorParams = stone->params;
// if we're in a generic grotto // if we're in a generic grotto
if (play->sceneNum == 62 && actorParams == 14360) { if (play->sceneNum == SCENE_GROTTOS && actorParams == 14360) {
// look for the chest in the actorlist to determine // look for the chest in the actorlist to determine
// which grotto we're in // which grotto we're in
int numOfActorLists = int numOfActorLists =
@ -2430,6 +2597,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetWarpSongMessage(textId, Randomizer_GetSettingValue(RSK_WARP_SONG_HINTS) == RO_GENERIC_OFF); messageEntry = OTRGlobals::Instance->gRandomizer->GetWarpSongMessage(textId, Randomizer_GetSettingValue(RSK_WARP_SONG_HINTS) == RO_GENERIC_OFF);
} else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) { } else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, textId); messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, textId);
} else if (textId == TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW);
} else if (textId == 0x3052 || (textId >= 0x3069 && textId <= 0x3070)) { //Fire Temple gorons } else if (textId == 0x3052 || (textId >= 0x3069 && textId <= 0x3070)) { //Fire Temple gorons
u16 choice = Random(0, NUM_GORON_MESSAGES); u16 choice = Random(0, NUM_GORON_MESSAGES);
messageEntry = OTRGlobals::Instance->gRandomizer->GetGoronMessage(choice); messageEntry = OTRGlobals::Instance->gRandomizer->GetGoronMessage(choice);
@ -2554,6 +2723,77 @@ void OTRAudio_SfxCaptureThread() {
{ {
return std::unique_lock<std::mutex>(audio.mutex); return std::unique_lock<std::mutex>(audio.mutex);
} }
extern "C" void CheckTracker_OnMessageClose() { //extern "C" void CheckTracker_OnMessageClose() {
CheckTracker::CheckTrackerDialogClosed(); // CheckTracker::CheckTrackerDialogClosed();
//=======
void SoH_ProcessDroppedFiles(std::string filePath) {
try {
std::ifstream configStream(filePath);
if (!configStream) {
return;
}
nlohmann::json configJson;
configStream >> configJson;
// #region SOH [Randomizer] TODO: Refactor spoiler file handling for randomizer
if (configJson.contains("version") && configJson.contains("finalSeed")) {
CVarSetString("gRandomizerDroppedFile", filePath.c_str());
CVarSetInteger("gRandomizerNewFileDropped", 1);
return;
}
// #endregion
if (!configJson.contains("CVars")) {
return;
}
clearCvars(enhancementsCvars);
clearCvars(cheatCvars);
clearCvars(randomizerCvars);
// Flatten everything under CVars into a single array
auto cvars = configJson["CVars"].flatten();
for (auto& [key, value] : cvars.items()) {
// Replace slashes with dots in key, and remove leading dot
std::string path = key;
std::replace(path.begin(), path.end(), '/', '.');
if (path[0] == '.') {
path.erase(0, 1);
}
if (value.is_string()) {
CVarSetString(path.c_str(), value.get<std::string>().c_str());
} else if (value.is_number_integer()) {
CVarSetInteger(path.c_str(), value.get<int>());
} else if (value.is_number_float()) {
CVarSetFloat(path.c_str(), value.get<float>());
}
}
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGuiWindow("Console")->Hide();
gui->GetGuiWindow("Actor Viewer")->Hide();
gui->GetGuiWindow("Collision Viewer")->Hide();
gui->GetGuiWindow("Save Editor")->Hide();
gui->GetGuiWindow("Display List Viewer")->Hide();
gui->GetGuiWindow("Stats")->Hide();
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();
gui->SaveConsoleVariablesOnNextTick();
uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
} catch (std::exception& e) {
SPDLOG_ERROR("Failed to load config file: {}", e.what());
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
} catch (...) {
SPDLOG_ERROR("Failed to load config file");
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
}
} }
// #endregion

View file

@ -12,6 +12,9 @@
#define GAME_PLATFORM_N64 0 #define GAME_PLATFORM_N64 0
#define GAME_PLATFORM_GC 1 #define GAME_PLATFORM_GC 1
#define BTN_MODIFIER1 0x00040
#define BTN_MODIFIER2 0x00080
#ifdef __cplusplus #ifdef __cplusplus
#include <Context.h> #include <Context.h>
#include "Enhancements/savestates.h" #include "Enhancements/savestates.h"
@ -86,11 +89,15 @@ uint32_t ResourceMgr_GetGameVersion(int index);
uint32_t ResourceMgr_GetGamePlatform(int index); uint32_t ResourceMgr_GetGamePlatform(int index);
uint32_t ResourceMgr_GetGameRegion(int index); uint32_t ResourceMgr_GetGameRegion(int index);
void ResourceMgr_LoadDirectory(const char* resName); void ResourceMgr_LoadDirectory(const char* resName);
void ResourceMgr_UnloadResource(const char* resName);
char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize); char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
uint8_t ResourceMgr_FileExists(const char* resName); uint8_t ResourceMgr_FileExists(const char* resName);
uint8_t ResourceMgr_FileAltExists(const char* resName);
void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName);
char* GetResourceDataByNameHandlingMQ(const char* path); char* GetResourceDataByNameHandlingMQ(const char* path);
void ResourceMgr_LoadFile(const char* resName); void ResourceMgr_LoadFile(const char* resName);
char* ResourceMgr_LoadFileFromDisk(const char* filePath); char* ResourceMgr_LoadFileFromDisk(const char* filePath);
uint8_t ResourceMgr_TexIsRaw(const char* texPath);
uint8_t ResourceMgr_ResourceIsBackground(char* texPath); uint8_t ResourceMgr_ResourceIsBackground(char* texPath);
char* ResourceMgr_LoadJPEG(char* data, size_t dataSize); char* ResourceMgr_LoadJPEG(char* data, size_t dataSize);
uint16_t ResourceMgr_LoadTexWidthByName(char* texPath); uint16_t ResourceMgr_LoadTexWidthByName(char* texPath);
@ -101,6 +108,7 @@ AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path);
char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc); char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc);
Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc); Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc);
Gfx* ResourceMgr_LoadGfxByName(const char* path); Gfx* ResourceMgr_LoadGfxByName(const char* path);
uint8_t ResourceMgr_FileIsCustomByName(const char* path);
void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction); void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction);
void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName); void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path); char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path);
@ -171,8 +179,10 @@ void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void SaveManager_ThreadPoolWait(); void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose(); void CheckTracker_OnMessageClose();
int32_t GetGIID(uint32_t itemID);
int32_t GetGIID(uint32_t itemID);
GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -47,6 +47,11 @@ std::filesystem::path SaveManager::GetFileName(int fileNum) {
return sSavePath / ("file" + std::to_string(fileNum + 1) + ".sav"); return sSavePath / ("file" + std::to_string(fileNum + 1) + ".sav");
} }
std::filesystem::path SaveManager::GetFileTempName(int fileNum) {
const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
return sSavePath / ("file" + std::to_string(fileNum + 1) + ".temp");
}
SaveManager::SaveManager() { SaveManager::SaveManager() {
coreSectionIDsByName["base"] = SECTION_ID_BASE; coreSectionIDsByName["base"] = SECTION_ID_BASE;
coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER; coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER;
@ -65,6 +70,10 @@ SaveManager::SaveManager() {
AddInitFunction(InitFileImpl); AddInitFunction(InitFileImpl);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([this](uint32_t fileNum) { ThreadPoolWait(); });
smThreadPool = std::make_shared<BS::thread_pool>(1);
for (SaveFileMetaInfo& info : fileMetaInfo) { for (SaveFileMetaInfo& info : fileMetaInfo) {
info.valid = false; info.valid = false;
info.deaths = 0; info.deaths = 0;
@ -357,12 +366,14 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
}); });
} }
// Init() here is an extension of InitSram, and thus not truly an initializer for SaveManager itself. don't put any class initialization stuff here
void SaveManager::Init() { void SaveManager::Init() {
// Wait on saves that snuck through the Wait in OnExitGame
ThreadPoolWait();
const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save")); const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
const std::filesystem::path sGlobalPath = sSavePath / std::string("global.sav"); const std::filesystem::path sGlobalPath = sSavePath / std::string("global.sav");
auto sOldSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.sav"); auto sOldSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.sav");
auto sOldBackupSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.bak"); auto sOldBackupSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.bak");
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([this](uint32_t fileNum) { ThreadPoolWait(); });
// If the save directory does not exist, create it // If the save directory does not exist, create it
if (!std::filesystem::exists(sSavePath)) { if (!std::filesystem::exists(sSavePath)) {
@ -403,7 +414,6 @@ void SaveManager::Init() {
} else { } else {
CreateDefaultGlobal(); CreateDefaultGlobal();
} }
smThreadPool = std::make_shared<BS::thread_pool>(1);
// Load files to initialize metadata // Load files to initialize metadata
for (int fileNum = 0; fileNum < MaxFiles; fileNum++) { for (int fileNum = 0; fileNum < MaxFiles; fileNum++) {
@ -722,7 +732,7 @@ void SaveManager::InitFileDebug() {
} }
} }
gSaveContext.entranceIndex = 0xCD; gSaveContext.entranceIndex = ENTR_HYRULE_FIELD_0;
gSaveContext.magicLevel = 0; gSaveContext.magicLevel = 0;
gSaveContext.sceneFlags[5].swch = 0x40000000; gSaveContext.sceneFlags[5].swch = 0x40000000;
} }
@ -865,10 +875,36 @@ void SaveManager::InitFileMaxed() {
} }
} }
gSaveContext.entranceIndex = 0xCD; gSaveContext.entranceIndex = ENTR_HYRULE_FIELD_0;
gSaveContext.sceneFlags[5].swch = 0x40000000; gSaveContext.sceneFlags[5].swch = 0x40000000;
} }
#if defined(__WIIU__) || defined(__SWITCH__)
// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm
int copy_file(const char* src, const char* dst) {
alignas(0x40) uint8_t buf[4096];
FILE* r = fopen(src, "r");
if (!r) {
return -1;
}
FILE* w = fopen(dst, "w");
if (!w) {
return -2;
}
size_t res;
while ((res = fread(buf, 1, sizeof(buf), r)) > 0) {
if (fwrite(buf, 1, res, w) != res) {
break;
}
}
fclose(r);
fclose(w);
return res >= 0 ? 0 : res;
}
#endif
// Threaded SaveFile takes copy of gSaveContext for local unmodified storage // Threaded SaveFile takes copy of gSaveContext for local unmodified storage
void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) { void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) {
@ -910,19 +946,42 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se
svi.func(saveContext, sectionID, false); svi.func(saveContext, sectionID, false);
} }
std::filesystem::path fileName = GetFileName(fileNum);
std::filesystem::path tempFile = GetFileTempName(fileNum);
if (std::filesystem::exists(tempFile)) {
std::filesystem::remove(tempFile);
}
#if defined(__SWITCH__) || defined(__WIIU__) #if defined(__SWITCH__) || defined(__WIIU__)
FILE* w = fopen(GetFileName(fileNum).c_str(), "w"); FILE* w = fopen(tempFile.c_str(), "w");
std::string json_string = saveBlock.dump(4); std::string json_string = saveBlock.dump(4);
fwrite(json_string.c_str(), sizeof(char), json_string.length(), w); fwrite(json_string.c_str(), sizeof(char), json_string.length(), w);
fclose(w); fclose(w);
#else #else
std::ofstream output(GetFileName(fileNum)); std::ofstream output(tempFile);
output << std::setw(4) << saveBlock << std::endl; output << std::setw(4) << saveBlock << std::endl;
output.close();
#endif #endif
if (std::filesystem::exists(fileName)) {
std::filesystem::remove(fileName);
}
#if defined(__SWITCH__) || defined(__WIIU__)
copy_file(tempFile.c_str(), fileName.c_str());
#else
std::filesystem::copy_file(tempFile, fileName);
#endif
if (std::filesystem::exists(tempFile)) {
std::filesystem::remove(tempFile);
}
delete saveContext; delete saveContext;
InitMeta(fileNum); InitMeta(fileNum);
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum);
SPDLOG_INFO("Save File Finish - fileNum: {}", fileNum);
} }
// SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded // SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded
@ -2105,32 +2164,6 @@ void SaveManager::LoadStruct(const std::string& name, LoadStructFunc func) {
} }
} }
#if defined(__WIIU__) || defined(__SWITCH__)
// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm
int copy_file(const char* src, const char* dst) {
alignas(0x40) uint8_t buf[4096];
FILE* r = fopen(src, "r");
if (!r) {
return -1;
}
FILE* w = fopen(dst, "w");
if (!w) {
return -2;
}
size_t res;
while ((res = fread(buf, 1, sizeof(buf), r)) > 0) {
if (fwrite(buf, 1, res, w) != res) {
break;
}
}
fclose(r);
fclose(w);
return res >= 0 ? 0 : res;
}
#endif
void SaveManager::CopyZeldaFile(int from, int to) { void SaveManager::CopyZeldaFile(int from, int to) {
assert(std::filesystem::exists(GetFileName(from))); assert(std::filesystem::exists(GetFileName(from)));
DeleteZeldaFile(to); DeleteZeldaFile(to);

View file

@ -142,6 +142,7 @@ class SaveManager {
private: private:
std::filesystem::path GetFileName(int fileNum); std::filesystem::path GetFileName(int fileNum);
std::filesystem::path GetFileTempName(int fileNum);
nlohmann::json saveBlock; nlohmann::json saveBlock;
void ConvertFromUnversioned(); void ConvertFromUnversioned();

View file

@ -8,8 +8,10 @@
#include "SohGui.hpp" #include "SohGui.hpp"
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <ImGui/imgui.h> #ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <ImGui/imgui.h>
#include <ImGui/imgui_internal.h> #include <ImGui/imgui_internal.h>
#include <libultraship/libultraship.h> #include <libultraship/libultraship.h>
#include <Fast3D/gfx_pc.h> #include <Fast3D/gfx_pc.h>
@ -31,14 +33,16 @@
#include "soh/resource/type/Skeleton.h" #include "soh/resource/type/Skeleton.h"
#include "libultraship/libultraship.h" #include "libultraship/libultraship.h"
#ifdef ENABLE_CROWD_CONTROL #ifdef ENABLE_REMOTE_CONTROL
#include "Enhancements/crowd-control/CrowdControl.h" #include "Enhancements/crowd-control/CrowdControl.h"
#include "Enhancements/game-interactor/GameInteractor_Sail.h"
#endif #endif
#include "Enhancements/game-interactor/GameInteractor.h" #include "Enhancements/game-interactor/GameInteractor.h"
#include "Enhancements/cosmetics/authenticGfxPatches.h" #include "Enhancements/cosmetics/authenticGfxPatches.h"
#include "Enhancements/resolution-editor/ResolutionEditor.h"
bool ShouldClearTextureCacheAtEndOfFrame = false; bool ToggleAltAssetsAtEndOfFrame = false;
bool isBetaQuestEnabled = false; bool isBetaQuestEnabled = false;
extern "C" { extern "C" {
@ -118,6 +122,7 @@ namespace SohGui {
std::shared_ptr<ColViewerWindow> mColViewerWindow; std::shared_ptr<ColViewerWindow> mColViewerWindow;
std::shared_ptr<SaveEditorWindow> mSaveEditorWindow; std::shared_ptr<SaveEditorWindow> mSaveEditorWindow;
std::shared_ptr<DLViewerWindow> mDLViewerWindow; std::shared_ptr<DLViewerWindow> mDLViewerWindow;
std::shared_ptr<ValueViewerWindow> mValueViewerWindow;
std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow; std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow;
std::shared_ptr<CheckTracker::CheckTrackerSettingsWindow> mCheckTrackerSettingsWindow; std::shared_ptr<CheckTracker::CheckTrackerSettingsWindow> mCheckTrackerSettingsWindow;
std::shared_ptr<CheckTracker::CheckTrackerWindow> mCheckTrackerWindow; std::shared_ptr<CheckTracker::CheckTrackerWindow> mCheckTrackerWindow;
@ -126,6 +131,8 @@ namespace SohGui {
std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow; std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow;
std::shared_ptr<RandomizerSettingsWindow> mRandomizerSettingsWindow; std::shared_ptr<RandomizerSettingsWindow> mRandomizerSettingsWindow;
std::shared_ptr<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow> mAdvancedResolutionSettingsWindow;
void SetupGuiElements() { void SetupGuiElements() {
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
@ -169,6 +176,8 @@ namespace SohGui {
gui->AddGuiWindow(mSaveEditorWindow); gui->AddGuiWindow(mSaveEditorWindow);
mDLViewerWindow = std::make_shared<DLViewerWindow>("gDLViewerEnabled", "Display List Viewer"); mDLViewerWindow = std::make_shared<DLViewerWindow>("gDLViewerEnabled", "Display List Viewer");
gui->AddGuiWindow(mDLViewerWindow); gui->AddGuiWindow(mDLViewerWindow);
mValueViewerWindow = std::make_shared<ValueViewerWindow>("gValueViewer.WindowOpen", "Value Viewer");
gui->AddGuiWindow(mValueViewerWindow);
mGameplayStatsWindow = std::make_shared<GameplayStatsWindow>("gGameplayStatsEnabled", "Gameplay Stats"); mGameplayStatsWindow = std::make_shared<GameplayStatsWindow>("gGameplayStatsEnabled", "Gameplay Stats");
gui->AddGuiWindow(mGameplayStatsWindow); gui->AddGuiWindow(mGameplayStatsWindow);
mCheckTrackerWindow = std::make_shared<CheckTracker::CheckTrackerWindow>("gCheckTrackerEnabled", "Check Tracker"); mCheckTrackerWindow = std::make_shared<CheckTracker::CheckTrackerWindow>("gCheckTrackerEnabled", "Check Tracker");
@ -183,9 +192,12 @@ namespace SohGui {
gui->AddGuiWindow(mItemTrackerSettingsWindow); gui->AddGuiWindow(mItemTrackerSettingsWindow);
mRandomizerSettingsWindow = std::make_shared<RandomizerSettingsWindow>("gRandomizerSettingsEnabled", "Randomizer Settings"); mRandomizerSettingsWindow = std::make_shared<RandomizerSettingsWindow>("gRandomizerSettingsEnabled", "Randomizer Settings");
gui->AddGuiWindow(mRandomizerSettingsWindow); gui->AddGuiWindow(mRandomizerSettingsWindow);
mAdvancedResolutionSettingsWindow = std::make_shared<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow>("gAdvancedResolutionEditorEnabled", "Advanced Resolution Settings");
gui->AddGuiWindow(mAdvancedResolutionSettingsWindow);
} }
void Destroy() { void Destroy() {
mAdvancedResolutionSettingsWindow = nullptr;
mRandomizerSettingsWindow = nullptr; mRandomizerSettingsWindow = nullptr;
mItemTrackerWindow = nullptr; mItemTrackerWindow = nullptr;
mItemTrackerSettingsWindow = nullptr; mItemTrackerSettingsWindow = nullptr;
@ -194,6 +206,7 @@ namespace SohGui {
mCheckTrackerSettingsWindow = nullptr; mCheckTrackerSettingsWindow = nullptr;
mGameplayStatsWindow = nullptr; mGameplayStatsWindow = nullptr;
mDLViewerWindow = nullptr; mDLViewerWindow = nullptr;
mValueViewerWindow = nullptr;
mSaveEditorWindow = nullptr; mSaveEditorWindow = nullptr;
mColViewerWindow = nullptr; mColViewerWindow = nullptr;
mActorViewerWindow = nullptr; mActorViewerWindow = nullptr;

View file

@ -17,6 +17,7 @@
#include "Enhancements/debugger/colViewer.h" #include "Enhancements/debugger/colViewer.h"
#include "Enhancements/debugger/debugSaveEditor.h" #include "Enhancements/debugger/debugSaveEditor.h"
#include "Enhancements/debugger/dlViewer.h" #include "Enhancements/debugger/dlViewer.h"
#include "Enhancements/debugger/valueViewer.h"
#include "Enhancements/gameplaystatswindow.h" #include "Enhancements/gameplaystatswindow.h"
#include "Enhancements/randomizer/randomizer_check_tracker.h" #include "Enhancements/randomizer/randomizer_check_tracker.h"
#include "Enhancements/randomizer/randomizer_entrance_tracker.h" #include "Enhancements/randomizer/randomizer_entrance_tracker.h"

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