mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-05-25 07:32:48 -04:00
Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 612da023f0 | |||
| 368a9015ac | |||
| ed9cb1dfd2 | |||
| fb6ea42560 | |||
| b26f2b21da | |||
| 358dd47da7 | |||
| ea1ffdd041 | |||
| ef9fc0a9ec | |||
| 19af4481c0 | |||
| 30a063b75d | |||
| cf6101f4da | |||
| 3d3b8bfc5b | |||
| 0cb4cd158a | |||
| 43fed2d77e | |||
| 11a0a00633 | |||
| 107a365b71 | |||
| bb1078e99c | |||
| 2529dc59bd | |||
| 61cf2bd323 | |||
| 16ee20c2a8 | |||
| 7ef6a434f9 | |||
| e3825ec263 | |||
| cb82e77e40 | |||
| 63cf3610e5 | |||
| dd5d8088f6 | |||
| 1da1b1a2bb | |||
| db02870a05 | |||
| 861bd09848 | |||
| 8426cc93e5 | |||
| 21796367a0 | |||
| 321c258d69 | |||
| 02938cfba2 | |||
| 37b2fc0745 | |||
| 6cb3a830bd | |||
| 96abadd904 | |||
| fa8a0e2a76 | |||
| 60faf3f750 | |||
| ef910a02f7 | |||
| f607afc754 | |||
| 865bcc57a7 | |||
| b5caf33a9b | |||
| f655ab592d | |||
| e6fc34e4c2 | |||
| d370ca93fd | |||
| fcf2141266 | |||
| e2f1cebfb5 | |||
| 907b770676 | |||
| ea49196bae | |||
| f65b711376 | |||
| 269e9faa46 | |||
| 35301556d9 | |||
| fb0f7169d7 | |||
| fbc397a131 | |||
| 818addfdda | |||
| f14c390364 | |||
| dd5e72a023 | |||
| d523b104d8 | |||
| fdcd9a7508 | |||
| c4f34624ba | |||
| 33fe8776bb | |||
| 02256fac66 | |||
| 621afc99f0 | |||
| 9d215b6dce | |||
| 420bdab328 | |||
| 717074ff86 | |||
| 3ab16b70c2 | |||
| 3cf9d655a7 | |||
| 95f27ace2e | |||
| d50ad4779d | |||
| f2df029efa | |||
| bdb201201e | |||
| f4e4545180 | |||
| 0ddb0711ad | |||
| 3234256b03 | |||
| d8a7a6c764 | |||
| 2dfbbc63e3 | |||
| 044d32a46f | |||
| afe032ea21 | |||
| fb45b66903 | |||
| ba987c49e2 | |||
| bd0672767a | |||
| e66eb8756d | |||
| bf31f2b330 | |||
| 4e9040d761 | |||
| 304016ddd2 | |||
| fe9c0fa4f7 | |||
| 384403edb5 | |||
| 60687aff0d | |||
| cf88b3d2bf | |||
| 78ffb41cd2 |
@@ -191,7 +191,7 @@ jobs:
|
||||
needs: generate-soh-otr
|
||||
runs-on: ${{ (vars.LINUX_RUNNER && fromJSON(vars.LINUX_RUNNER)) || 'ubuntu-latest' }}
|
||||
container:
|
||||
image: devkitpro/devkita64:latest
|
||||
image: devkitpro/devkita64:20240120
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
project(Ship VERSION 8.0.0 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Alfa" CACHE STRING "")
|
||||
project(Ship VERSION 8.0.5 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Foxtrot" CACHE STRING "")
|
||||
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
|
||||
|
||||
+1
-1
Submodule OTRExporter updated: 0d8f5570a8...04b85b95fa
+1
-1
Submodule libultraship updated: c75ff3653f...96c8a8929c
+1
-1
@@ -328,7 +328,7 @@ endif()
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
Boost
|
||||
URL https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.gz
|
||||
URL https://archives.boost.io/release/1.81.0/source/boost_1_81_0.tar.gz
|
||||
URL_HASH SHA256=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6
|
||||
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
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<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"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- 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_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x9238">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61E8"/>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x91E8">
|
||||
<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"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<Root>
|
||||
<File Name="nes_message_data_static">
|
||||
<Text Name="nes_message_data_static" CodeOffset="0xF6910"/>
|
||||
<Text Name="nes_message_data_static" CodeOffset="0xF68F0"/>
|
||||
</File>
|
||||
<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 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 Name="staff_message_data_static">
|
||||
<Text Name="staff_message_data_static" CodeOffset="0xFED58"/>
|
||||
<Text Name="staff_message_data_static" CodeOffset="0xFED38"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- 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_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<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"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- 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_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<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="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61E8" RangeEnd="0x91E8">
|
||||
<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"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- 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_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<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"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- 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_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<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"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
+4
-1
@@ -744,7 +744,6 @@ typedef struct {
|
||||
/* 0x0134 */ char** doActionSegment;
|
||||
/* 0x0138 */ u8* iconItemSegment;
|
||||
/* 0x013C */ char** mapSegment;
|
||||
char** mapSegmentName;
|
||||
/* 0x0140 */ u8 mapPalette[32];
|
||||
/* 0x0160 */ DmaRequest dmaRequest_160;
|
||||
/* 0x0180 */ DmaRequest dmaRequest_180;
|
||||
@@ -815,6 +814,10 @@ typedef struct {
|
||||
/* 0x026C */ u8 dinsNayrus; // "m_magic"; din's fire and nayru's love
|
||||
/* 0x026D */ u8 all; // "another"; enables all item restrictions
|
||||
} restrictions;
|
||||
// #region SOH [General]
|
||||
/* */ char* mapSegmentName[2]; // Tracks the map segment texture by OTR sig name
|
||||
/* */ u8 mapPalettesPulse[40][32]; // Used to have unique pointers per map pulse color for the shader backend. 40 for map pulse timer x2
|
||||
// #endregion
|
||||
} InterfaceContext; // size = 0x270
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -7,68 +7,6 @@ export RESPATH="${SNAME%/MacOS*}/Resources"
|
||||
export LIBPATH="${SNAME%/MacOS*}/Frameworks"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$LIBPATH"
|
||||
|
||||
remap_hashes ()
|
||||
{
|
||||
# Remap v64 and n64 hashes to their z64 hash equivalent
|
||||
# ZAPD will handle converting the data into z64 format
|
||||
case "$ROMHASH" in
|
||||
a9059b56e761c9034fbe02fe4c24985aaa835dac) # v64
|
||||
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
|
||||
;;
|
||||
24708102dc504d3f375a37f4ae4e149c167dc515) # n64
|
||||
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
|
||||
;;
|
||||
580dd0bd1b6d2c51cc20a764eece84dba558964c) # v64
|
||||
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
|
||||
;;
|
||||
d6342c59007e57c1194661ec6880b2f078403f4e) # n64
|
||||
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
|
||||
;;
|
||||
d0bdc2eb320668b4ba6893b9aefe4040a73123ff) # v64
|
||||
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
|
||||
;;
|
||||
4946ab250f6ac9b32d76b21f309ebb8ebc8103d2) # n64
|
||||
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
|
||||
;;
|
||||
663c34f1b2c05a09e5beffe4d0dcd440f7d49dc7) # v64
|
||||
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
|
||||
;;
|
||||
24c73d378b0620a380ce5ef9f2b186c6c157a68b) # n64
|
||||
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
|
||||
;;
|
||||
8ebf2e29313f44f2d49e5b4191971d09919e8e48) # v64
|
||||
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
|
||||
;;
|
||||
4264bf7b875737b8fae77d52322a5099d051fc11) # n64
|
||||
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
|
||||
;;
|
||||
973bc6fe56010a8d646166a1182a81b4f13b8cf9) # v64
|
||||
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
|
||||
;;
|
||||
d327752c46edc70ff3668b9514083dbbee08927c) # v64
|
||||
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
|
||||
;;
|
||||
ecdeb1747560834e079c22243febea7f6f26ba3b) # v64
|
||||
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
|
||||
;;
|
||||
f19f8662ec7abee29484a272a6fda53e39efe0f1) # n64
|
||||
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
|
||||
;;
|
||||
ab519ce04a33818ce2c39b3c514a751d807a494a) # v64
|
||||
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
|
||||
;;
|
||||
c19a34f7646305e1755249fca2071e178bd7cd00) # n64
|
||||
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
|
||||
;;
|
||||
25e8ae79ea0839ca5c984473f7460d8040c36f9c) # v64
|
||||
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
|
||||
;;
|
||||
166c02770d67fcc3954c443eb400a6a3573d3fc0) # n64
|
||||
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ ! -e "$SHIP_HOME" ]; then mkdir "$SHIP_HOME"; fi
|
||||
|
||||
if [ ! -e "$SHIP_HOME"/mods ]; then
|
||||
@@ -76,178 +14,6 @@ if [ ! -e "$SHIP_HOME"/mods ]; then
|
||||
touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt
|
||||
fi
|
||||
|
||||
# If either OTR doesn't exist kick off the OTR gen process
|
||||
if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then
|
||||
|
||||
# If no ROMs exist kick off the file selection prompts
|
||||
while [ ! -e "$SHIP_HOME"/*.*64 ] && [ ! -e "$SHIP_HOME"/oot*.otr ]; do
|
||||
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
while [ $SHOULD_PROMPT_FOR_ROM -eq 1 ]; do
|
||||
SHOULD_PROMPT_FOR_ROM=0
|
||||
# Use osascript to prompt the user to chose a file
|
||||
DROPROM=`osascript <<-EOF
|
||||
set romFile to choose file of type {"b64","n64","v64","z64"} with prompt "Please select your ROM:"
|
||||
return POSIX path of romFile
|
||||
EOF`
|
||||
|
||||
# If no rom was selected, the user cancelled, so exit
|
||||
if [[ -z $DROPROM ]] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
echo "No ROM selected. Exiting..."
|
||||
exit 1
|
||||
elif [[ -z $DROPROM ]]; then
|
||||
break;
|
||||
fi
|
||||
|
||||
# If an invalid rom was selected, let the user know and ask to try again
|
||||
ROMHASH="$(shasum "$DROPROM" | awk '{ print $1 }')"
|
||||
|
||||
remap_hashes
|
||||
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
ROM_TYPE=0;;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
ROM_TYPE=0;;
|
||||
328a1f1beba30ce5e178f031662019eb32c5f3b5)
|
||||
ROM_TYPE=0;;
|
||||
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
|
||||
ROM_TYPE=0;;
|
||||
f46239439f59a2a594ef83cf68ef65043b1bffe2)
|
||||
ROM_TYPE=1;;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
ROM_TYPE=1;;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
ROM_TYPE=1;;
|
||||
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
|
||||
ROM_TYPE=1;;
|
||||
517bd9714c73cb96c21e7c2ef640d7b55186102f)
|
||||
ROM_TYPE=1;;
|
||||
*)
|
||||
TRY_AGAIN_RESULT=`osascript <<-EOF
|
||||
set alertText to "Incompatible ROM hash"
|
||||
set alertMessage to "Incompatible ROM provided, would you like to try again?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
as critical \
|
||||
buttons {"Cancel", "Try Again"}
|
||||
EOF`
|
||||
if [[ "$TRY_AGAIN_RESULT" == "button returned:Try Again" ]]; then
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
continue;
|
||||
else
|
||||
echo "No ROM selected. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
esac
|
||||
|
||||
cp "$DROPROM" "$SHIP_HOME"
|
||||
|
||||
# Ask user if they would also like to select the other variant (MQ/Vanilla)
|
||||
if [ $ROM_TYPE -eq 0 ] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
|
||||
set alertText to "Success"
|
||||
set alertMessage to "Would you also like to provide a Master Quest ROM?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
buttons {"No", "Yes"}
|
||||
EOF`
|
||||
elif [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
|
||||
set alertText to "Success"
|
||||
set alertMessage to "Would you also like to provide a Vanilla (Non Master Quest) ROM?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
buttons {"No", "Yes"}
|
||||
EOF`
|
||||
fi
|
||||
|
||||
if [[ "$UPLOAD_ANOTHER_RESULT" == "button returned:Yes" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT="button returned:No"
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
continue;
|
||||
fi
|
||||
break
|
||||
done
|
||||
done
|
||||
|
||||
# At this point we should now have 1 or more valid roms in $SHIP_HOME directory
|
||||
|
||||
# Prepare tmp dir
|
||||
for ROMPATH in "$SHIP_HOME"/*.*64
|
||||
do
|
||||
ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
|
||||
export ASSETDIR
|
||||
cp -r "$RESPATH/assets" "$ASSETDIR"
|
||||
mkdir -p "$ASSETDIR"/tmp
|
||||
cp "$ROMPATH" "$ASSETDIR"/tmp/rom.z64
|
||||
cd "$ASSETDIR" || return
|
||||
|
||||
# If an invalid rom was detected, let the user know
|
||||
ROMHASH="$(shasum "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')"
|
||||
|
||||
remap_hashes
|
||||
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
ROM=GC_NMQ_D
|
||||
OTRNAME="oot.otr";;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
ROM=GC_NMQ_PAL_F
|
||||
OTRNAME="oot.otr";;
|
||||
328a1f1beba30ce5e178f031662019eb32c5f3b5)
|
||||
ROM=N64_PAL_10
|
||||
OTRNAME="oot.otr";;
|
||||
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
|
||||
ROM=N64_PAL_11
|
||||
OTRNAME="oot.otr";;
|
||||
f46239439f59a2a594ef83cf68ef65043b1bffe2)
|
||||
ROM=GC_MQ_PAL_F
|
||||
OTRNAME="oot-mq.otr";;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
517bd9714c73cb96c21e7c2ef640d7b55186102f)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
*)
|
||||
osascript -e 'display notification "One or more invalid ROM provided" with title "Ship Of Harkinian"'
|
||||
rm -r "$ASSETDIR"
|
||||
cd "$SNAME"
|
||||
continue;
|
||||
esac
|
||||
|
||||
# Only generate OTR if we don't have on of this type yet
|
||||
if [ -e "$SHIP_HOME"/"$OTRNAME" ]; then
|
||||
rm -r "$ASSETDIR"
|
||||
cd "$SNAME"
|
||||
continue;
|
||||
fi
|
||||
|
||||
osascript -e 'display notification "Generating OTR..." with title "Ship Of Harkinian"'
|
||||
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --portVer "@CMAKE_PROJECT_VERSION@"
|
||||
if [ -e "$ASSETDIR"/oot.otr ]; then
|
||||
osascript -e 'display notification "OTR successfully generated" with title "Ship Of Harkinian"'
|
||||
cp "$ASSETDIR"/oot.otr "$SHIP_HOME"/"$OTRNAME"
|
||||
rm -r "$ASSETDIR"
|
||||
cd "$SNAME"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -e "$SHIP_HOME"/oot*.otr ]; then
|
||||
osascript -e 'display notification "OTR failed to generate" with title "Ship Of Harkinian"'
|
||||
exit 1;
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$SNAME"
|
||||
|
||||
arch_name="$(uname -m)"
|
||||
launch_arch="arm64"
|
||||
if [ "${arch_name}" = "x86_64" ] && [ "$(sysctl -in sysctl.proc_translated)" != "1" ]; then
|
||||
|
||||
+1
-1
@@ -439,7 +439,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
|
||||
{ ACTOR_EN_DAIKU_KAKARIKO, "Carpenters (Kakariko)" },
|
||||
{ ACTOR_BG_BOWL_WALL, "Bombchu Bowling Alley Wall" },
|
||||
{ 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_DOOR_KILLER, "Fake Door" },
|
||||
{ ACTOR_BG_SPOT11_OASIS, "Oasis (Desert Colossus)" },
|
||||
|
||||
@@ -400,8 +400,9 @@ void AudioCollection::InitializeShufflePool() {
|
||||
if (shufflePoolInitialized) return;
|
||||
|
||||
for (auto& [seqId, seqInfo] : sequenceMap) {
|
||||
if (!seqInfo.canBeUsedAsReplacement) continue;
|
||||
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);
|
||||
} else {
|
||||
includedSequences.insert(&seqInfo);
|
||||
|
||||
@@ -258,24 +258,28 @@ namespace GameControlEditor {
|
||||
window->EndGroupPanelPublic(0);
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
window->BeginGroupPanelPublic("Third-Person Camera", ImGui::GetContentRegionAvail());
|
||||
window->BeginGroupPanelPublic("Free Look/Third-person Camera", ImGui::GetContentRegionAvail());
|
||||
|
||||
UIWidgets::PaddedEnhancementCheckbox("Free Camera", "gFreeCamera");
|
||||
DrawHelpIcon("Enables free camera control\nNote: You must remap C buttons off of the right stick in the "
|
||||
UIWidgets::PaddedEnhancementCheckbox("Enable Free Look", "gFreeCamera");
|
||||
DrawHelpIcon("Enables free look camera control\nNote: You must remap C buttons off of the right stick in the "
|
||||
"controller config menu, and map the camera stick to the right stick.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert Camera X Axis", "gInvertXAxis");
|
||||
DrawHelpIcon("Inverts the Camera X Axis in:\n-Free camera");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free camera");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert X Axis", "gInvertXAxis");
|
||||
DrawHelpIcon("Inverts the Camera X Axis in:\n-Free Look");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free Look");
|
||||
UIWidgets::Spacer(0);
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal",
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal",
|
||||
"gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical",
|
||||
DrawHelpIcon("Changes the sensitivity of the X axis control for Free Look");
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical",
|
||||
"gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
|
||||
DrawHelpIcon("Changes the sensitivity of the Y axis control for Free Look");
|
||||
UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist",
|
||||
"gFreeCameraDistMax", 100, 900, "", 185, true, false, true);
|
||||
UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed",
|
||||
DrawHelpIcon("How far the camera sits from Link while in Free Look mode");
|
||||
UIWidgets::PaddedEnhancementSliderInt("Transition Speed: %d", "##CamTranSpeed",
|
||||
"gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true);
|
||||
DrawHelpIcon("How quickly the camera changes to the distance specified above");
|
||||
window->EndGroupPanelPublic(0);
|
||||
}
|
||||
|
||||
@@ -312,6 +316,7 @@ namespace GameControlEditor {
|
||||
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
|
||||
"certain items.");
|
||||
UIWidgets::Spacer(0);
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false);
|
||||
DrawHelpIcon("Hold the assigned button to change the maximum walking speed\nTo change the assigned button, go into the Ports tabs above");
|
||||
if (CVarGetInteger("gEnableWalkModify", 0)) {
|
||||
@@ -323,6 +328,7 @@ namespace GameControlEditor {
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %d %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
|
||||
window->EndGroupPanelPublic(0);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
UIWidgets::Spacer(0);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
|
||||
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
|
||||
|
||||
@@ -1485,6 +1485,7 @@ void Draw_Placements(){
|
||||
}
|
||||
|
||||
void DrawSillyTab() {
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
if (CVarGetInteger("gLetItSnow", 0)) {
|
||||
if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) {
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
@@ -1569,6 +1570,7 @@ void DrawSillyTab() {
|
||||
CVarClear("gCosmetics.Kak_Windmill_Speed.Changed");
|
||||
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
|
||||
@@ -1767,6 +1769,10 @@ void CosmeticsEditorWindow::DrawElement() {
|
||||
ImGui::SameLine();
|
||||
UIWidgets::EnhancementCombobox("gCosmetics.DefaultColorScheme", colorSchemes, COLORSCHEME_N64);
|
||||
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 (ImGui::Button("Lock All Advanced", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) {
|
||||
for (auto& [id, cosmeticOption] : cosmeticOptions) {
|
||||
|
||||
@@ -8,6 +8,7 @@ extern "C" {
|
||||
#include "objects/object_gi_soldout/object_gi_soldout.h"
|
||||
#include "objects/object_ik/object_ik.h"
|
||||
#include "objects/object_link_child/object_link_child.h"
|
||||
#include "objects/object_ru2/object_ru2.h"
|
||||
|
||||
uint32_t ResourceMgr_GameHasMasterQuest();
|
||||
uint32_t ResourceMgr_GameHasOriginal();
|
||||
@@ -187,10 +188,25 @@ void PatchIronKnuckleTextureOverflow() {
|
||||
}
|
||||
}
|
||||
|
||||
void PatchPrincessRutoEaring() {
|
||||
// FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings.
|
||||
// Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so
|
||||
// it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the
|
||||
// earring texture loads into the same place in TMEM as the brick texture, so when it comes to rendering, TEXEL1
|
||||
// uses the earring texture with different clamp settings, and it displays without noticeable error. However, both
|
||||
// texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses
|
||||
// our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces
|
||||
// TEXEL1 with TEXEL0, which is most likely the original intention, and all is well.
|
||||
ResourceMgr_PatchGfxByName(gAdultRutoHeadDL, "RutoEaringTileFix", 162,
|
||||
gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED,
|
||||
TEXEL0, 0, PRIM_LOD_FRAC, COMBINED));
|
||||
}
|
||||
|
||||
void ApplyAuthenticGfxPatches() {
|
||||
PatchDekuStickTextureOverflow();
|
||||
PatchFreezardTextureOverflow();
|
||||
PatchIronKnuckleTextureOverflow();
|
||||
PatchPrincessRutoEaring();
|
||||
}
|
||||
|
||||
// Patches the Sold Out GI DL to render the texture in the mirror boundary
|
||||
|
||||
@@ -380,13 +380,13 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
effect->category = kEffectCatBoots;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->giEffect->parameters[0] = PLAYER_BOOTS_IRON;
|
||||
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_IRON;
|
||||
break;
|
||||
case kEffectForceHoverBoots:
|
||||
effect->category = kEffectCatBoots;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->giEffect->parameters[0] = PLAYER_BOOTS_HOVER;
|
||||
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_HOVER;
|
||||
break;
|
||||
case kEffectSlipperyFloor:
|
||||
effect->category = kEffectCatSlipperyFloor;
|
||||
|
||||
@@ -51,6 +51,7 @@ typedef enum {
|
||||
TEXT_WARP_NOCTURNE_OF_SHADOW = 0x891,
|
||||
TEXT_WARP_PRELUDE_OF_LIGHT = 0x892,
|
||||
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_NAVI = 0x1B3, // 0x1yy for Navi msg range
|
||||
} TextIDs;
|
||||
|
||||
@@ -207,6 +207,7 @@ static bool ResetHandler(std::shared_ptr<LUS::Console> Console, std::vector<std:
|
||||
return 1;
|
||||
}
|
||||
|
||||
gPlayState->gameplayFrames = 0;
|
||||
SET_NEXT_GAMESTATE(&gPlayState->state, TitleSetup_Init, GameState);
|
||||
gPlayState->state.running = false;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnExitGame>(gSaveContext.fileNum);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef _ENHANCEMENT_TYPES_H_
|
||||
#define _ENHANCEMENT_TYPES_H_
|
||||
|
||||
typedef enum {
|
||||
WARP_MODE_OVERRIDE_OFF,
|
||||
WARP_MODE_OVERRIDE_MQ_AS_VANILLA,
|
||||
@@ -74,3 +77,5 @@ typedef enum {
|
||||
DEKU_STICK_UNBREAKABLE,
|
||||
DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE,
|
||||
} DekuStickType;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,12 +37,19 @@ GameInteractionEffectQueryResult GameInteractor::RemoveEffect(GameInteractionEff
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
bool GameInteractor::IsSaveLoaded() {
|
||||
bool GameInteractor::IsSaveLoaded(bool allowDbgSave) {
|
||||
Player* player;
|
||||
if (gPlayState != NULL) {
|
||||
player = GET_PLAYER(gPlayState);
|
||||
}
|
||||
return (gPlayState == NULL || player == NULL || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) ? false : true;
|
||||
|
||||
// Checking for normal game mode prevents debug saves from reporting true on title screen
|
||||
if (gPlayState == NULL || player == NULL || gSaveContext.gameMode != GAMEMODE_NORMAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid save file or debug save
|
||||
return (gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2) || (allowDbgSave && gSaveContext.fileNum == 0xFF);
|
||||
}
|
||||
|
||||
bool GameInteractor::IsGameplayPaused() {
|
||||
|
||||
@@ -94,6 +94,7 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#define DEFINE_HOOK(name, type) \
|
||||
struct name { \
|
||||
@@ -193,8 +194,11 @@ public:
|
||||
|
||||
DEFINE_HOOK(OnSetGameLanguage, void());
|
||||
|
||||
DEFINE_HOOK(OnFileDropped, void(std::string filePath));
|
||||
DEFINE_HOOK(OnAssetAltChange, void());
|
||||
|
||||
// Helpers
|
||||
static bool IsSaveLoaded();
|
||||
static bool IsSaveLoaded(bool allowDbgSave = false);
|
||||
static bool IsGameplayPaused();
|
||||
static bool CanSpawnActor();
|
||||
static bool CanAddOrTakeAmmo(int16_t amount, int16_t item);
|
||||
|
||||
@@ -181,3 +181,9 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode) {
|
||||
void GameInteractor_ExecuteOnSetGameLanguage() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
|
||||
}
|
||||
|
||||
// MARK: - System
|
||||
|
||||
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnAssetAltChange>(fn);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode);
|
||||
|
||||
// MARK: - Game
|
||||
void GameInteractor_ExecuteOnSetGameLanguage();
|
||||
|
||||
// MARK: - System
|
||||
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -618,7 +618,7 @@ void DrawGameplayStatsOptionsTab() {
|
||||
}
|
||||
|
||||
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)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
|
||||
@@ -35,8 +35,6 @@ extern PlayState* gPlayState;
|
||||
extern void Overlay_DisplayText(float duration, const char* text);
|
||||
uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
|
||||
}
|
||||
bool performDelayedSave = false;
|
||||
bool performSave = false;
|
||||
|
||||
// TODO: When there's more uses of something like this, create a new GI::RawAction?
|
||||
void ReloadSceneTogglingLinkAge() {
|
||||
@@ -49,6 +47,7 @@ void ReloadSceneTogglingLinkAge() {
|
||||
|
||||
void RegisterInfiniteMoney() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
|
||||
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
|
||||
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
|
||||
@@ -59,6 +58,7 @@ void RegisterInfiniteMoney() {
|
||||
|
||||
void RegisterInfiniteHealth() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
|
||||
if (gSaveContext.health < gSaveContext.healthCapacity) {
|
||||
gSaveContext.health = gSaveContext.healthCapacity;
|
||||
@@ -69,6 +69,7 @@ void RegisterInfiniteHealth() {
|
||||
|
||||
void RegisterInfiniteAmmo() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
|
||||
// Deku Sticks
|
||||
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
|
||||
@@ -105,6 +106,7 @@ void RegisterInfiniteAmmo() {
|
||||
|
||||
void RegisterInfiniteMagic() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
|
||||
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
|
||||
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
|
||||
@@ -115,6 +117,7 @@ void RegisterInfiniteMagic() {
|
||||
|
||||
void RegisterInfiniteNayrusLove() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
|
||||
gSaveContext.nayrusLoveTimer = 0x44B;
|
||||
}
|
||||
@@ -123,7 +126,7 @@ void RegisterInfiniteNayrusLove() {
|
||||
|
||||
void RegisterMoonJumpOnL() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -138,7 +141,7 @@ void RegisterMoonJumpOnL() {
|
||||
|
||||
void RegisterInfiniteISG() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gEzISG", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -150,7 +153,7 @@ void RegisterInfiniteISG() {
|
||||
//Permanent quick put away (QPA) glitched damage value
|
||||
void RegisterEzQPA() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gEzQPA", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -162,7 +165,7 @@ void RegisterEzQPA() {
|
||||
|
||||
void RegisterUnrestrictedItems() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
|
||||
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
|
||||
@@ -191,13 +194,18 @@ void RegisterFreezeTime() {
|
||||
void RegisterSwitchAge() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
static bool warped = false;
|
||||
|
||||
if (!GameInteractor::IsSaveLoaded(true)) {
|
||||
CVarClear("gSwitchAge");
|
||||
warped = false;
|
||||
return;
|
||||
}
|
||||
|
||||
static Vec3f playerPos;
|
||||
static int16_t playerYaw;
|
||||
static RoomContext* roomCtx;
|
||||
static s32 roomNum;
|
||||
|
||||
if (!gPlayState) return;
|
||||
|
||||
if (CVarGetInteger("gSwitchAge", 0) && !warped) {
|
||||
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
|
||||
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
|
||||
@@ -217,7 +225,7 @@ void RegisterSwitchAge() {
|
||||
func_80097534(gPlayState, roomCtx); // load map for new room (unloading the previous room)
|
||||
}
|
||||
warped = false;
|
||||
CVarSetInteger("gSwitchAge", 0);
|
||||
CVarClear("gSwitchAge");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -226,7 +234,8 @@ void RegisterSwitchAge() {
|
||||
void RegisterOcarinaTimeTravel() {
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
|
||||
if (!gPlayState) {
|
||||
if (!GameInteractor::IsSaveLoaded(true)) {
|
||||
CVarClear("gTimeTravel");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -258,14 +267,12 @@ void RegisterOcarinaTimeTravel() {
|
||||
|
||||
void AutoSave(GetItemEntry itemEntry) {
|
||||
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 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 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) &&
|
||||
(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)) {
|
||||
// Autosave for all items
|
||||
performSave = true;
|
||||
@@ -326,25 +333,9 @@ void AutoSave(GetItemEntry itemEntry) {
|
||||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
|
||||
performSave = true;
|
||||
}
|
||||
if (gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN || gPlayState->sceneNum == SCENE_GROTTOS ||
|
||||
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) {
|
||||
if (performSave) {
|
||||
Play_PerformSave(gPlayState);
|
||||
performSave = false;
|
||||
performDelayedSave = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1045,8 +1036,16 @@ void RegisterRandomizedEnemySizes() {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
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.
|
||||
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || actor->id == ACTOR_EN_BROB) {
|
||||
// Exclude wobbly platforms in Jabu because they need to act like platforms.
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -1055,9 +1054,8 @@ void RegisterRandomizedEnemySizes() {
|
||||
|
||||
uint8_t bigActor = rand() % 2;
|
||||
|
||||
// Big actor. Dodongo and Volvagia are always smaller because they're impossible when bigger.
|
||||
if (bigActor && actor->id != ACTOR_BOSS_DODONGO && actor->id != ACTOR_BOSS_FD &&
|
||||
actor->id != ACTOR_BOSS_FD2) {
|
||||
// Big actor
|
||||
if (bigActor && !smallOnlyEnemy) {
|
||||
randomNumber = rand() % 200;
|
||||
// Between 100% and 300% size.
|
||||
randomScale = 1.0f + (randomNumber / 100);
|
||||
|
||||
@@ -12,6 +12,14 @@ void clearCvars(std::vector<const char*> cvarsToClear) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatLocations(std::vector<RandomizerCheck> locs) {
|
||||
std::string locString = "";
|
||||
for (auto loc: locs) {
|
||||
locString += std::to_string(loc) + ",";
|
||||
}
|
||||
return locString;
|
||||
}
|
||||
|
||||
void applyPreset(std::vector<PresetEntry> entries) {
|
||||
for(auto& [cvar, type, value] : entries) {
|
||||
switch (type) {
|
||||
@@ -24,6 +32,9 @@ void applyPreset(std::vector<PresetEntry> entries) {
|
||||
case PRESET_ENTRY_TYPE_STRING:
|
||||
CVarSetString(cvar, std::get<const char*>(value));
|
||||
break;
|
||||
case PRESET_ENTRY_TYPE_CPP_STRING:
|
||||
CVarSetString(cvar, std::get<std::string>(value).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <cstdint>
|
||||
@@ -11,6 +12,7 @@ enum PresetEntryType {
|
||||
PRESET_ENTRY_TYPE_S32,
|
||||
PRESET_ENTRY_TYPE_FLOAT,
|
||||
PRESET_ENTRY_TYPE_STRING,
|
||||
PRESET_ENTRY_TYPE_CPP_STRING,
|
||||
};
|
||||
|
||||
enum PresetType {
|
||||
@@ -36,15 +38,19 @@ enum RandomizerPreset {
|
||||
typedef struct PresetEntry {
|
||||
const char* cvar;
|
||||
PresetEntryType type;
|
||||
std::variant<int32_t, float, const char*> value;
|
||||
std::variant<int32_t, float, const char*, std::string> value;
|
||||
} PresetEntry;
|
||||
|
||||
std::string FormatLocations(std::vector<RandomizerCheck> locs);
|
||||
|
||||
#define PRESET_ENTRY_S32(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_S32, value }
|
||||
#define PRESET_ENTRY_FLOAT(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_FLOAT, value }
|
||||
#define PRESET_ENTRY_STRING(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_STRING, value }
|
||||
#define PRESET_ENTRY_CPP_STRING(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_CPP_STRING, value }
|
||||
|
||||
void DrawPresetSelector(PresetType presetType);
|
||||
void clearCvars(std::vector<const char*> cvarsToClear);
|
||||
@@ -217,6 +223,31 @@ const std::vector<const char*> enhancementsCvars = {
|
||||
"gFixTexturesOOB",
|
||||
"gIvanCoopModeEnabled",
|
||||
"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",
|
||||
};
|
||||
|
||||
const std::vector<const char*> cheatCvars = {
|
||||
@@ -269,7 +300,23 @@ const std::vector<const char*> cheatCvars = {
|
||||
"gNoRedeadFreeze",
|
||||
"gBombTimerMultiplier",
|
||||
"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 = {
|
||||
@@ -399,6 +446,15 @@ const std::vector<const char*> randomizerCvars = {
|
||||
"gRandomizeGregHint",
|
||||
"gRandoManualSeedEntry",
|
||||
"gRandomizerSettingsEnabled",
|
||||
"gRandomizeTriforceHuntTotalPieces",
|
||||
"gRandomizeTriforceHuntRequiredPieces",
|
||||
"gRandomizeTriforceHunt",
|
||||
"gRandomizeShuffleMasterSword",
|
||||
"gRandomizeSariaHint",
|
||||
"gRandomizeRupeeNames",
|
||||
"gRandomizeFrogsHint",
|
||||
"gRandoRelevantNavi",
|
||||
"gRandoQuestItemFanfares",
|
||||
};
|
||||
|
||||
const std::vector<PresetEntry> vanillaPlusPresetEntries = {
|
||||
@@ -816,7 +872,8 @@ const std::vector<PresetEntry> spockRacePresetEntries = {
|
||||
PRESET_ENTRY_S32("gRandomizeDampeHint", 1),
|
||||
PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN),
|
||||
PRESET_ENTRY_S32("gRandomizeEnableBombchuDrops", 1),
|
||||
PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "78,143,144,229,"),
|
||||
PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations(
|
||||
{ RC_MARKET_10_BIG_POES, RC_KAK_40_GOLD_SKULLTULA_REWARD, RC_KAK_50_GOLD_SKULLTULA_REWARD, RC_ZR_FROGS_OCARINA_GAME })),
|
||||
PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_OPEN),
|
||||
PRESET_ENTRY_S32("gRandomizeFullWallets", 1),
|
||||
PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP),
|
||||
@@ -908,7 +965,8 @@ const std::vector<PresetEntry> spockRaceNoLogicPresetEntries = {
|
||||
PRESET_ENTRY_S32("gRandomizeDampeHint", 1),
|
||||
PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN),
|
||||
PRESET_ENTRY_S32("gRandomizeEnableBombchuDrops", 1),
|
||||
PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "78,143,144,229,"),
|
||||
PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations(
|
||||
{ RC_MARKET_10_BIG_POES, RC_KAK_40_GOLD_SKULLTULA_REWARD, RC_KAK_50_GOLD_SKULLTULA_REWARD, RC_ZR_FROGS_OCARINA_GAME })),
|
||||
PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_OPEN),
|
||||
PRESET_ENTRY_S32("gRandomizeFullWallets", 1),
|
||||
PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP),
|
||||
@@ -961,7 +1019,7 @@ const std::vector<PresetEntry> s6PresetEntries = {
|
||||
PRESET_ENTRY_S32("gRandomizeBigPoeTargetCount", 1),
|
||||
PRESET_ENTRY_S32("gRandomizeCuccosToReturn", 4),
|
||||
PRESET_ENTRY_S32("gRandomizeDoorOfTime", RO_DOOROFTIME_OPEN),
|
||||
PRESET_ENTRY_STRING("gRandomizeExcludedLocations", "48,"),
|
||||
PRESET_ENTRY_CPP_STRING("gRandomizeExcludedLocations", FormatLocations({ RC_DEKU_THEATER_MASK_OF_TRUTH })),
|
||||
PRESET_ENTRY_S32("gRandomizeForest", RO_FOREST_CLOSED_DEKU),
|
||||
PRESET_ENTRY_S32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SKIP),
|
||||
PRESET_ENTRY_S32("gRandomizeGerudoFortress", RO_GF_FAST),
|
||||
|
||||
@@ -24,11 +24,11 @@ void LocationTable_Init() {
|
||||
|
||||
//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_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_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_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_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(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(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(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_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);
|
||||
@@ -53,7 +53,7 @@ void LocationTable_Init() {
|
||||
//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_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_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);
|
||||
@@ -73,7 +73,7 @@ void LocationTable_Init() {
|
||||
//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_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_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);
|
||||
@@ -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);
|
||||
|
||||
//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_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_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_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(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(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_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_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);
|
||||
@@ -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_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_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_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_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(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_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_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_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_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_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {}, 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(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(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_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_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_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
|
||||
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);
|
||||
|
||||
//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_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);
|
||||
|
||||
@@ -104,7 +104,7 @@ void AreaTable_Init_DeathMountain() {
|
||||
Entrance(DEATH_MOUNTAIN_TRAIL, {[]{return true;}}),
|
||||
Entrance(GC_WOODS_WARP, {[]{return GCWoodsWarpOpen;}}),
|
||||
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));}}),
|
||||
});
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ void AreaTable_Init_FireTemple() {
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(FIRE_TEMPLE_FIRST_ROOM, {[]{return true;}}),
|
||||
Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && LogicFireBossDoorJump) || CanUse(HOVER_BOOTS) || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}));}}),
|
||||
Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && (LogicFireBossDoorJump || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}))) || CanUse(HOVER_BOOTS));}}),
|
||||
});
|
||||
|
||||
areaTable[FIRE_TEMPLE_LOOP_ENEMIES] = Area("Fire Temple Loop Enemies", "Fire Temple", FIRE_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
|
||||
@@ -172,7 +172,7 @@ void AreaTable_Init_ForestTemple() {
|
||||
Entrance(FOREST_TEMPLE_WEST_CORRIDOR, {[]{return true;}}),
|
||||
Entrance(FOREST_TEMPLE_NW_OUTDOORS_UPPER, {[]{return CanUse(HOVER_BOOTS) || (LogicForestOutsideBackdoor && CanJumpslash && GoronBracelet);}}),
|
||||
Entrance(FOREST_TEMPLE_NW_CORRIDOR_TWISTED, {[]{return IsAdult && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
|
||||
Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
|
||||
Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return IsAdult && (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
|
||||
});
|
||||
|
||||
areaTable[FOREST_TEMPLE_NW_CORRIDOR_TWISTED] = Area("Forest Temple NW Corridor Twisted", "Forest Temple", FOREST_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
|
||||
@@ -536,7 +536,7 @@ namespace Logic {
|
||||
Fish = HasBottle && FishAccess;
|
||||
Fairy = HasBottle && FairyAccess;
|
||||
|
||||
FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20);
|
||||
FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20) && (BombBag || BombchusInLogic);
|
||||
CanPlayBowling = (BombchusInLogic && FoundBombchus) || (!BombchusInLogic && BombBag);
|
||||
HasBombchus = (BuyBombchus10 || BuyBombchus20 || (AmmoDrops.Is(AMMODROPS_BOMBCHU) && FoundBombchus));
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "playthrough.hpp"
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
#include <boost_custom/container_hash/hash_32.hpp>
|
||||
#include "custom_messages.hpp"
|
||||
#include "fill.hpp"
|
||||
@@ -8,6 +9,7 @@
|
||||
#include "random.hpp"
|
||||
#include "spoiler_log.hpp"
|
||||
#include "soh/Enhancements/randomizer/randomizerTypes.h"
|
||||
#include "variables.h"
|
||||
|
||||
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);
|
||||
Random_Init(finalHash);
|
||||
Settings::hash = std::to_string(finalHash);
|
||||
|
||||
@@ -360,29 +360,12 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
|
||||
{ "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 GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
|
||||
if (strcmp(spoilerFileName, "") != 0) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -496,6 +479,13 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
|
||||
"Zu {{location}}?\x1B&%gOK&No%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,
|
||||
CustomMessage("Water level control system.&Keep away!",
|
||||
"Wasserstand Kontrollsystem&Finger weg!",
|
||||
@@ -659,7 +649,7 @@ void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
return;
|
||||
|
||||
@@ -1293,7 +1283,7 @@ std::string FormatJsonHintText(std::string jsonHint) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
return;
|
||||
|
||||
@@ -1394,7 +1384,7 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
@@ -1415,7 +1405,7 @@ void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
@@ -1495,7 +1485,7 @@ int16_t Randomizer::GetVanillaMerchantPrice(RandomizerCheck check) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
return;
|
||||
|
||||
@@ -1558,7 +1548,7 @@ void Randomizer::ParseItemLocationsFile(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) {
|
||||
return;
|
||||
}
|
||||
@@ -2547,6 +2537,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
|
||||
{ RC_LH_CHILD_FISHING, RAND_INF_CHILD_FISHING },
|
||||
{ RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING },
|
||||
{ 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) {
|
||||
@@ -3143,7 +3134,9 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
DrawPresetSelector(PRESET_TYPE_RANDOMIZER);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, "");
|
||||
@@ -3166,13 +3159,17 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
}
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
ImGui::BeginDisabled(CVarGetInteger("gRandomizerDontGenerateSpoiler", 0) && gSaveContext.gameMode != GAMEMODE_FILE_SELECT);
|
||||
if (ImGui::Button("Generate Randomizer")) {
|
||||
GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : "");
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
|
||||
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
|
||||
if (!CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
|
||||
std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
|
||||
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
|
||||
}
|
||||
|
||||
// RANDOTODO settings presets
|
||||
// std::string presetfilepath = CVarGetString("gLoadedPreset", "");
|
||||
@@ -3180,6 +3177,8 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
|
||||
UIWidgets::PaddedSeparator();
|
||||
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
static ImVec2 cellPadding(8.0f, 8.0f);
|
||||
|
||||
@@ -4208,7 +4207,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount",
|
||||
"gRandomizeLacsStoneCount", 1, 4, "", 4, true, true, false);
|
||||
"gRandomizeLacsStoneCount", 1, 4, "", 3, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount",
|
||||
@@ -4237,7 +4236,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount",
|
||||
"gRandomizeLacsMedallionCount", 1, 7, "", 7, true, true, false);
|
||||
"gRandomizeLacsMedallionCount", 1, 7, "", 6, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount",
|
||||
@@ -4266,7 +4265,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount",
|
||||
"gRandomizeLacsRewardCount", 1, 10, "", 10, true, true, false);
|
||||
"gRandomizeLacsRewardCount", 1, 10, "", 9, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount",
|
||||
@@ -4295,7 +4294,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount",
|
||||
"gRandomizeLacsDungeonCount", 1, 9, "", 9, true, true, false);
|
||||
"gRandomizeLacsDungeonCount", 1, 9, "", 8, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount",
|
||||
@@ -4690,7 +4689,11 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
excludedLocationString += std::to_string(excludedLocationIt);
|
||||
excludedLocationString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
|
||||
if (excludedLocationString == "") {
|
||||
CVarClear("gRandomizeExcludedLocations");
|
||||
} else {
|
||||
CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
|
||||
}
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
@@ -4867,7 +4870,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
enabledTrickString += std::to_string(enabledTrickIt);
|
||||
enabledTrickString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
CVarClear("gRandomizeEnabledTricks");
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
@@ -5071,7 +5074,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
enabledTrickString += std::to_string(enabledTrickIt);
|
||||
enabledTrickString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
CVarClear("gRandomizeEnabledTricks");
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
|
||||
@@ -5109,7 +5112,11 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
enabledTrickString += std::to_string(enabledTrickIt);
|
||||
enabledTrickString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
if (enabledTrickString == "") {
|
||||
CVarClear("gRandomizeEnabledTricks");
|
||||
} else {
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
}
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
DrawTagChips(*rtObject.rtTags);
|
||||
@@ -5224,6 +5231,8 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (disableEditingRandoSettings) {
|
||||
UIWidgets::ReEnableComponent("");
|
||||
|
||||
@@ -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_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_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_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),
|
||||
@@ -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_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_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_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),
|
||||
@@ -783,7 +783,7 @@ std::map<RandomizerCheckArea, std::string> rcAreaNames = {
|
||||
{ RCAREA_LAKE_HYLIA, "Lake Hylia"},
|
||||
{ RCAREA_GERUDO_VALLEY, "Gerudo Valley"},
|
||||
{ RCAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
|
||||
{ RCAREA_WASTELAND, "Desert Wasteland"},
|
||||
{ RCAREA_WASTELAND, "Haunted Wasteland"},
|
||||
{ RCAREA_DESERT_COLOSSUS, "Desert Colossus"},
|
||||
{ RCAREA_MARKET, "Hyrule Market"},
|
||||
{ RCAREA_HYRULE_CASTLE, "Hyrule Castle"},
|
||||
|
||||
@@ -73,20 +73,12 @@ bool showLinksPocket;
|
||||
bool fortressFast;
|
||||
bool fortressNormal;
|
||||
|
||||
bool bypassRandoCheck = true;
|
||||
// persistent during gameplay
|
||||
bool initialized;
|
||||
bool doAreaScroll;
|
||||
bool previousShowHidden = false;
|
||||
bool hideShopRightChecks = true;
|
||||
|
||||
bool checkCollected = false;
|
||||
int checkLoops = 0;
|
||||
int checkCounter = 0;
|
||||
u16 savedFrames = 0;
|
||||
bool messageCloseCheck = false;
|
||||
bool pendingSaleCheck = false;
|
||||
bool transitionCheck = false;
|
||||
bool alwaysShowGS = false;
|
||||
|
||||
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
|
||||
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
|
||||
@@ -115,15 +107,13 @@ std::map<RandomizerCheckArea, std::vector<RandomizerCheckObject>> checksByArea;
|
||||
bool areasFullyChecked[RCAREA_INVALID];
|
||||
u32 areasSpoiled = 0;
|
||||
bool showVOrMQ;
|
||||
s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)"
|
||||
bool optCollapseAll; // A bool that will collapse all checks once
|
||||
s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)"
|
||||
s8 areaCheckTotals[RCAREA_INVALID];
|
||||
bool optCollapseAll; // A bool that will collapse all checks once
|
||||
bool optExpandAll; // A bool that will expand all checks once
|
||||
RandomizerCheck lastItemGetCheck = RC_UNKNOWN_CHECK;
|
||||
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
|
||||
RandomizerCheckArea previousArea = RCAREA_INVALID;
|
||||
RandomizerCheckArea currentArea = RCAREA_INVALID;
|
||||
std::vector<RandomizerCheckArea> checkAreas;
|
||||
std::vector<GetItemEntry> itemsReceived;
|
||||
OSContPad* trackerButtonsPressed;
|
||||
|
||||
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,
|
||||
BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
|
||||
|
||||
void SetLastItemGetRC(RandomizerCheck rc) {
|
||||
lastItemGetCheck = rc;
|
||||
}
|
||||
|
||||
void DefaultCheckData(RandomizerCheck rc) {
|
||||
gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED;
|
||||
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) {
|
||||
gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED;
|
||||
RandomizerCheckObject rcObj;
|
||||
@@ -248,13 +254,12 @@ void SetCheckCollected(RandomizerCheck rc) {
|
||||
} else {
|
||||
rcObj = RandomizerCheckObjects::GetAllRCObjects().find(rc)->second;
|
||||
}
|
||||
if (!gSaveContext.checkTrackerData[rc].skipped) {
|
||||
areaChecksGotten[rcObj.rcArea]++;
|
||||
} else {
|
||||
gSaveContext.checkTrackerData[rc].skipped = false;
|
||||
}
|
||||
if (!checkAreas.empty()) {
|
||||
checkAreas.erase(checkAreas.begin());
|
||||
if (IsVisibleInCheckTracker(rcObj)) {
|
||||
if (!gSaveContext.checkTrackerData[rc].skipped) {
|
||||
areaChecksGotten[rcObj.rcArea]++;
|
||||
} else {
|
||||
gSaveContext.checkTrackerData[rc].skipped = false;
|
||||
}
|
||||
}
|
||||
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};
|
||||
|
||||
bool EvaluateCheck(RandomizerCheckObject rco) {
|
||||
if (HasItemBeenCollected(rco.rc) && gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_COLLECTED &&
|
||||
gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_SAVED) {
|
||||
SetCheckCollected(rco.rc);
|
||||
return true;
|
||||
}
|
||||
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 ClearAreaChecksAndTotals() {
|
||||
for (auto& [rcArea, vec] : checksByArea) {
|
||||
vec.clear();
|
||||
areaChecksGotten[rcArea] = 0;
|
||||
areaCheckTotals[rcArea] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetShopSeen(uint32_t sceneNum, bool prices) {
|
||||
RandomizerCheck start = startingShopItem.find(sceneNum)->second;
|
||||
if (sceneNum == SCENE_POTION_SHOP_KAKARIKO && !LINK_IS_ADULT) {
|
||||
return;
|
||||
}
|
||||
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) {
|
||||
start = RC_KAK_BAZAAR_ITEM_1;
|
||||
}
|
||||
@@ -452,10 +444,64 @@ bool HasItemBeenCollected(RandomizerCheck rc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void CheckTrackerDialogClosed() {
|
||||
if (messageCloseCheck) {
|
||||
messageCloseCheck = false;
|
||||
void CheckTrackerLoadGame(int32_t fileNum) {
|
||||
LoadSettings();
|
||||
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) {
|
||||
@@ -479,10 +525,6 @@ void CheckTrackerTransition(uint32_t sceneNum) {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
return;
|
||||
}
|
||||
gSaveContext;
|
||||
if (transitionCheck) {
|
||||
transitionCheck = false;
|
||||
}
|
||||
doAreaScroll = true;
|
||||
previousArea = currentArea;
|
||||
currentArea = GetCheckArea();
|
||||
@@ -503,28 +545,12 @@ void CheckTrackerFrame() {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
return;
|
||||
}
|
||||
if (!checkAreas.empty() && !transitionCheck && !messageCloseCheck && !pendingSaleCheck) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (CheckByArea()) {
|
||||
checkCounter = 0;
|
||||
break;
|
||||
} else {
|
||||
checkCounter++;
|
||||
}
|
||||
// TODO: Move to OnAmmoChange hook once it gets added.
|
||||
if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED &&
|
||||
gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) {
|
||||
if (BEANS_BOUGHT >= 10) {
|
||||
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
|
||||
}
|
||||
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
|
||||
if (!IS_RANDO) {
|
||||
if (giEntry.itemId == ITEM_SHIELD_DEKU) {
|
||||
SetCheckCollected(RC_KF_SHOP_ITEM_3);
|
||||
SetCheckCollected(RC_KF_SHOP_ITEM_1);
|
||||
return;
|
||||
}else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) {
|
||||
SetCheckCollected(RC_QUEEN_GOHMA);
|
||||
@@ -565,16 +591,19 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) {
|
||||
SetCheckCollected(RC_GIFT_FROM_SAGES);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_LULLABY) {
|
||||
SetCheckCollected(RC_SONG_FROM_IMPA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_EPONA) {
|
||||
SetCheckCollected(RC_SONG_FROM_MALON);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_SARIA) {
|
||||
SetCheckCollected(RC_SONG_FROM_SARIA);
|
||||
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);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_TIME) {
|
||||
@@ -601,42 +630,127 @@ void CheckTrackerItemReceive(GetItemEntry giEntry) {
|
||||
} else if (giEntry.itemId == ITEM_SONG_PRELUDE) {
|
||||
SetCheckCollected(RC_SHEIK_AT_TEMPLE);
|
||||
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);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_WEIRD_EGG) {
|
||||
SetCheckCollected(RC_HC_MALON_EGG);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_BEAN) {
|
||||
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerSceneFlagSet(int16_t sceneNum, int16_t flagType, int32_t flag) {
|
||||
if (flagType != FLAG_SCENE_TREASURE && flagType != FLAG_SCENE_COLLECTIBLE) {
|
||||
return;
|
||||
}
|
||||
if (sceneNum == SCENE_GRAVEYARD && flag == 0x19 && flagType == FLAG_SCENE_COLLECTIBLE) { // Gravedigging tour special case
|
||||
SetCheckCollected(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR);
|
||||
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;
|
||||
}
|
||||
}
|
||||
auto checkArea = GetCheckArea();
|
||||
if (gSaveContext.pendingSale != ITEM_NONE) {
|
||||
pendingSaleCheck = true;
|
||||
checkAreas.push_back(checkArea);
|
||||
}
|
||||
|
||||
void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
|
||||
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;
|
||||
}
|
||||
if (scene == SCENE_DESERT_COLOSSUS && (gSaveContext.entranceIndex == 485 || gSaveContext.entranceIndex == 489)) {
|
||||
checkAreas.push_back(RCAREA_SPIRIT_TEMPLE);
|
||||
return;
|
||||
}
|
||||
if (GET_PLAYER(gPlayState) == nullptr) {
|
||||
transitionCheck = true;
|
||||
return;
|
||||
}
|
||||
if (gPlayState->msgCtx.msgMode != MSGMODE_NONE) {
|
||||
checkAreas.push_back(checkArea);
|
||||
messageCloseCheck = true;
|
||||
return;
|
||||
}
|
||||
if (IS_RANDO || (!IS_RANDO && giEntry.getItemCategory != ITEM_CATEGORY_JUNK)) {
|
||||
checkAreas.push_back(checkArea);
|
||||
checkCollected = true;
|
||||
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
if ((!IS_RANDO && ((rcObj.vOrMQ == RCVORMQ_MQ && !IS_MASTER_QUEST) ||
|
||||
(rcObj.vOrMQ == RCVORMQ_VANILLA && IS_MASTER_QUEST))) ||
|
||||
(IS_RANDO && ((OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
|
||||
rcObj.vOrMQ == RCVORMQ_VANILLA) ||
|
||||
!OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
|
||||
rcObj.vOrMQ == RCVORMQ_MQ))) {
|
||||
continue;
|
||||
}
|
||||
SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
|
||||
SpoilerCollectionCheckType scCheckType = scCheck.type;
|
||||
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF &&
|
||||
(scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT ||
|
||||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
|
||||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW ||
|
||||
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) {
|
||||
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
|
||||
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
|
||||
if (gameSave || savedFrames > 0) {
|
||||
if (gameSave) {
|
||||
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
|
||||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
@@ -673,15 +787,9 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
|
||||
|
||||
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
|
||||
SaveTrackerData(saveContext, sectionID, fullSave);
|
||||
if (fullSave) {
|
||||
savedFrames = 40;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadFile() {
|
||||
Teardown();
|
||||
LoadSettings();
|
||||
TrySetAreas();
|
||||
SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
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("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() {
|
||||
initialized = false;
|
||||
for (auto& [rcArea, vec] : checksByArea) {
|
||||
vec.clear();
|
||||
areaChecksGotten[rcArea] = 0;
|
||||
}
|
||||
ClearAreaChecksAndTotals();
|
||||
checksByArea.clear();
|
||||
areasSpoiled = 0;
|
||||
checkCollected = false;
|
||||
checkLoops = 0;
|
||||
|
||||
lastLocationChecked = RC_UNKNOWN_CHECK;
|
||||
}
|
||||
|
||||
void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) {
|
||||
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]++;
|
||||
} 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]--;
|
||||
}
|
||||
gSaveContext.checkTrackerData[check] = data;
|
||||
@@ -874,8 +930,7 @@ void CheckTrackerWindow::DrawElement() {
|
||||
for (auto& [rcArea, objs] : checksByArea) {
|
||||
RandomizerCheckArea thisArea = currentArea;
|
||||
|
||||
const int areaChecksTotal = static_cast<int>(objs.size());
|
||||
thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaChecksTotal);
|
||||
thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaCheckTotals[rcArea]);
|
||||
//Last Area needs to be cleaned up
|
||||
if (lastArea != RCAREA_INVALID && doDraw) {
|
||||
UIWidgets::PaddedSeparator();
|
||||
@@ -912,26 +967,33 @@ void CheckTrackerWindow::DrawElement() {
|
||||
stemp = RandomizerCheckObjects::GetRCAreaName(rcArea) + "##TreeNode";
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f,
|
||||
mainColor.b / 255.0f, mainColor.a / 255.0f));
|
||||
if (doingCollapseOrExpand)
|
||||
if (doingCollapseOrExpand) {
|
||||
ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always);
|
||||
else
|
||||
} else {
|
||||
ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once);
|
||||
}
|
||||
doDraw = ImGui::TreeNode(stemp.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 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 (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
|
||||
if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(DungeonSceneLookupByArea(rcArea)))
|
||||
ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaChecksTotal);
|
||||
else
|
||||
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaChecksTotal);
|
||||
if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(
|
||||
DungeonSceneLookupByArea(rcArea))) {
|
||||
ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
|
||||
} else {
|
||||
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaChecksTotal);
|
||||
ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("???");
|
||||
@@ -945,11 +1007,13 @@ void CheckTrackerWindow::DrawElement() {
|
||||
doAreaScroll = false;
|
||||
}
|
||||
for (auto rco : objs) {
|
||||
if (doDraw && isThisAreaSpoiled && IsVisibleInCheckTracker(rco))
|
||||
if (IsVisibleInCheckTracker(rco) && doDraw && isThisAreaSpoiled) {
|
||||
DrawLocation(rco);
|
||||
}
|
||||
}
|
||||
if (doDraw)
|
||||
if (doDraw) {
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
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
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING
|
||||
:false;
|
||||
hideShopRightChecks = IS_RANDO ? CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1) : false;
|
||||
|
||||
if (IS_RANDO) {
|
||||
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) {
|
||||
@@ -1120,7 +1183,7 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
|
||||
) &&
|
||||
(rcObj.rcType != RCTYPE_MERCHANT || showMerchants) &&
|
||||
(rcObj.rcType != RCTYPE_OCARINA || showOcarinas) &&
|
||||
(rcObj.rcType != RCTYPE_SKULL_TOKEN ||
|
||||
(rcObj.rcType != RCTYPE_SKULL_TOKEN || alwaysShowGS ||
|
||||
(showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) ||
|
||||
(showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea))
|
||||
) &&
|
||||
@@ -1148,8 +1211,8 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
|
||||
}
|
||||
else if (rcObj.vanillaCompletion) {
|
||||
return (rcObj.vOrMQ == RCVORMQ_BOTH ||
|
||||
rcObj.vOrMQ == RCVORMQ_MQ && OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) ||
|
||||
rcObj.vOrMQ == RCVORMQ_VANILLA && !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) ||
|
||||
(rcObj.vOrMQ == RCVORMQ_MQ && IS_MASTER_QUEST) ||
|
||||
(rcObj.vOrMQ == RCVORMQ_VANILLA && !IS_MASTER_QUEST) ||
|
||||
rcObj.rc == RC_GIFT_FROM_SAGES) && rcObj.rc != RC_LINKS_POCKET;
|
||||
}
|
||||
return false;
|
||||
@@ -1169,8 +1232,9 @@ void UpdateAreaFullyChecked(RandomizerCheckArea area) {
|
||||
|
||||
void UpdateAreas(RandomizerCheckArea area) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAllOrdering() {
|
||||
@@ -1198,30 +1262,36 @@ bool CompareChecks(RandomizerCheckObject i, RandomizerCheckObject j) {
|
||||
bool iSaved = iShow.status == RCSHOW_SAVED;
|
||||
bool jCollected = jShow.status == RCSHOW_COLLECTED || 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;
|
||||
else if (iSaved && !jSaved)
|
||||
} else if (iCollected && !jCollected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!iShow.skipped && jShow.skipped)
|
||||
if (!iSaved && jSaved) {
|
||||
return true;
|
||||
else if (iShow.skipped && !jShow.skipped)
|
||||
} else if (iSaved && !jSaved) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType))
|
||||
if (!iShow.skipped && jShow.skipped) {
|
||||
return true;
|
||||
else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType))
|
||||
} else if (iShow.skipped && !jShow.skipped) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i.rc < j.rc)
|
||||
if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType)) {
|
||||
return true;
|
||||
else if (i.rc > j.rc)
|
||||
} else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i.rc < j.rc) {
|
||||
return true;
|
||||
} else if (i.rc > j.rc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1239,47 +1309,54 @@ void DrawLocation(RandomizerCheckObject rcObj) {
|
||||
RandomizerCheckStatus status = checkData.status;
|
||||
bool skipped = checkData.skipped;
|
||||
if (status == RCSHOW_COLLECTED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
} else if (status == RCSHOW_SAVED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) {
|
||||
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);
|
||||
} else if (skipped) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) {
|
||||
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);
|
||||
} else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) {
|
||||
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);
|
||||
} else if (status == RCSHOW_SCUMMED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerKnownHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerScummedHide", 0)) {
|
||||
return;
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) :
|
||||
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);
|
||||
} else if (status == RCSHOW_UNCHECKED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) {
|
||||
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);
|
||||
}
|
||||
|
||||
//Main Text
|
||||
txt = rcObj.rcShortName;
|
||||
if (lastLocationChecked == rcObj.rc)
|
||||
if (lastLocationChecked == rcObj.rc) {
|
||||
txt = "* " + txt;
|
||||
}
|
||||
|
||||
// Draw button - for Skipped/Seen/Scummed/Unchecked only
|
||||
if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) {
|
||||
@@ -1354,8 +1431,9 @@ void DrawLocation(RandomizerCheckObject rcObj) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (txt == "" && skipped)
|
||||
txt = "Skipped"; //TODO language
|
||||
if (txt == "" && skipped) {
|
||||
txt = "Skipped"; // TODO language
|
||||
}
|
||||
|
||||
if (txt != "") {
|
||||
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() {
|
||||
float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat("gCosmetics.RainbowSpeed", 0.6f));
|
||||
for (auto& cvar : rainbowCVars) {
|
||||
if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0)
|
||||
if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Color_RGBA8 newColor;
|
||||
newColor.r = sin(freqHue + 0) * 127 + 128;
|
||||
@@ -1489,8 +1568,16 @@ void CheckTrackerSettingsWindow::DrawElement() {
|
||||
}
|
||||
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::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.");
|
||||
if (UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true)) {
|
||||
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();
|
||||
|
||||
@@ -1533,14 +1620,19 @@ void CheckTrackerWindow::InitElement() {
|
||||
SaveManager::Instance->AddInitFunction(InitTrackerData);
|
||||
sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1);
|
||||
SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>(CheckTrackerLoadGame);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) {
|
||||
Teardown();
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSaleEnd>(CheckTrackerSaleEnd);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition);
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -48,10 +48,7 @@ void Teardown();
|
||||
void UpdateAllOrdering();
|
||||
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
|
||||
void InitTrackerData(bool isDebug);
|
||||
void SetLastItemGetRC(RandomizerCheck rc);
|
||||
RandomizerCheckArea GetCheckArea();
|
||||
void CheckTrackerDialogClosed();
|
||||
void ToggleShopRightChecks();
|
||||
void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
|
||||
} // namespace CheckTracker
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
|
||||
static s8 hasCopiedEntranceTable = 0;
|
||||
static s8 hasModifiedEntranceTable = 0;
|
||||
|
||||
void Entrance_SetEntranceDiscovered(u16 entranceIndex);
|
||||
void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance);
|
||||
|
||||
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
|
||||
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
|
||||
@@ -276,13 +276,13 @@ s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) {
|
||||
return nextEntranceIndex;
|
||||
}
|
||||
|
||||
Entrance_SetEntranceDiscovered(nextEntranceIndex);
|
||||
Entrance_SetEntranceDiscovered(nextEntranceIndex, false);
|
||||
EntranceTracker_SetLastEntranceOverride(nextEntranceIndex);
|
||||
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex));
|
||||
}
|
||||
|
||||
s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) {
|
||||
Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex]);
|
||||
Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex], false);
|
||||
EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]);
|
||||
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex]));
|
||||
}
|
||||
@@ -398,6 +398,11 @@ void Entrance_SetSavewarpEntrance(void) {
|
||||
gSaveContext.entranceIndex = 0x0486; // Gerudo Fortress -> Thieve's Hideout spawn 0
|
||||
} else if (scene == SCENE_LINKS_HOUSE) {
|
||||
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE);
|
||||
} else if (CVarGetInteger("gRememberSaveLocation", 0) && scene != SCENE_FAIRYS_FOUNTAIN && scene != SCENE_GROTTOS &&
|
||||
// Use the saved entrance value with remember save location, except when in grottos/fairy fountains or if
|
||||
// the entrance index is -1 (new save)
|
||||
gSaveContext.entranceIndex != -1) {
|
||||
return;
|
||||
} else if (LINK_IS_CHILD) {
|
||||
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); // Child Overworld Spawn
|
||||
} else {
|
||||
@@ -784,7 +789,7 @@ u8 Entrance_GetIsEntranceDiscovered(u16 entranceIndex) {
|
||||
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
|
||||
// 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)) {
|
||||
@@ -796,14 +801,20 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
|
||||
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
|
||||
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
|
||||
gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit;
|
||||
// Set connected
|
||||
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
|
||||
if (entranceIndex == gSaveContext.entranceOverrides[i].index) {
|
||||
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination);
|
||||
break;
|
||||
|
||||
// Set reverse entrance when not decoupled
|
||||
if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {
|
||||
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ void Entrance_OverrideSpawnScene(int32_t sceneNum, int32_t spawn);
|
||||
int32_t Entrance_OverrideSpawnSceneRoom(int32_t sceneNum, int32_t spawn, int32_t room);
|
||||
void Entrance_EnableFW(void);
|
||||
uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex);
|
||||
void Entrance_SetEntranceDiscovered(uint16_t entranceIndex);
|
||||
void Entrance_SetEntranceDiscovered(uint16_t entranceIndex, uint8_t isReversedEntrance);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -140,7 +140,7 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
|
||||
// If Link hits a grotto exit, load the entrance index from the grotto exit list
|
||||
// based on the current grotto ID
|
||||
if (nextEntranceIndex == 0x7FFF) {
|
||||
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);
|
||||
nextEntranceIndex = grottoExitList[grottoId];
|
||||
}
|
||||
@@ -211,7 +211,7 @@ void Grotto_OverrideActorEntrance(Actor* thisx) {
|
||||
|
||||
if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) {
|
||||
// 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);
|
||||
index = grottoLoadList[index];
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ void DrawBottle(ItemTrackerItem item);
|
||||
void DrawQuest(ItemTrackerItem item);
|
||||
void DrawSong(ItemTrackerItem item);
|
||||
|
||||
int itemTrackerSectionId;
|
||||
|
||||
bool shouldUpdateVectors = true;
|
||||
|
||||
std::vector<ItemTrackerItem> mainWindowItems = {};
|
||||
@@ -282,11 +284,6 @@ void ItemTrackerOnFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
void SaveNotes(uint32_t fileNum) {
|
||||
CVarSetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), std::string(std::begin(itemTrackerNotes), std::end(itemTrackerNotes)).c_str());
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
|
||||
bool IsValidSaveFile() {
|
||||
bool validSave = gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2;
|
||||
return validSave;
|
||||
@@ -734,7 +731,7 @@ void DrawNotes(bool resizeable = false) {
|
||||
}
|
||||
if ((ImGui::IsItemDeactivatedAfterEdit() || (notesNeedSave && notesIdleFrames > notesMaxIdleFrames)) && IsValidSaveFile()) {
|
||||
notesNeedSave = false;
|
||||
SaveNotes(gSaveContext.fileNum);
|
||||
SaveManager::Instance->SaveSection(gSaveContext.fileNum, itemTrackerSectionId, true);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
@@ -959,6 +956,26 @@ void UpdateVectors() {
|
||||
shouldUpdateVectors = false;
|
||||
}
|
||||
|
||||
void ItemTrackerInitFile(bool isDebug) {
|
||||
itemTrackerNotes.clear();
|
||||
itemTrackerNotes.push_back(0);
|
||||
}
|
||||
|
||||
void ItemTrackerSaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
|
||||
SaveManager::Instance->SaveData("personalNotes", std::string(std::begin(itemTrackerNotes), std::end(itemTrackerNotes)).c_str());
|
||||
}
|
||||
|
||||
void ItemTrackerLoadFile() {
|
||||
std::string initialTrackerNotes = "";
|
||||
SaveManager::Instance->LoadData("personalNotes", initialTrackerNotes);
|
||||
itemTrackerNotes.resize(initialTrackerNotes.length() + 1);
|
||||
if (initialTrackerNotes != "") {
|
||||
SohUtils::CopyStringToCharArray(itemTrackerNotes.Data, initialTrackerNotes.c_str(), itemTrackerNotes.size());
|
||||
} else {
|
||||
itemTrackerNotes.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ItemTrackerWindow::DrawElement() {
|
||||
UpdateVectors();
|
||||
|
||||
@@ -1223,14 +1240,9 @@ void ItemTrackerWindow::InitElement() {
|
||||
itemTrackerNotes.push_back(0);
|
||||
}
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadFile>([](uint32_t fileNum) {
|
||||
const char* initialTrackerNotes = CVarGetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), "");
|
||||
itemTrackerNotes.resize(strlen(initialTrackerNotes) + 1);
|
||||
strcpy(itemTrackerNotes.Data, initialTrackerNotes);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnDeleteFile>([](uint32_t fileNum) {
|
||||
CVarSetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), "");
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
});
|
||||
SaveManager::Instance->AddInitFunction(ItemTrackerInitFile);
|
||||
itemTrackerSectionId = SaveManager::Instance->AddSaveFunction("itemTrackerData", 1, ItemTrackerSaveFile, true, -1);
|
||||
SaveManager::Instance->AddLoadFunction("itemTrackerData", 1, ItemTrackerLoadFile);
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(ItemTrackerOnFrame);
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ std::unordered_map<RandomizerTrickArea, std::string> rtAreaNames = {
|
||||
{ RTAREA_LAKE_HYLIA, "Lake Hylia"},
|
||||
{ RTAREA_GERUDO_VALLEY, "Gerudo Valley"},
|
||||
{ RTAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
|
||||
{ RTAREA_WASTELAND, "Desert Wasteland"},
|
||||
{ RTAREA_WASTELAND, "Haunted Wasteland"},
|
||||
{ RTAREA_DESERT_COLOSSUS, "Desert Colossus"},
|
||||
{ RTAREA_MARKET, "Hyrule Market"},
|
||||
{ RTAREA_HYRULE_CASTLE, "Hyrule Castle"},
|
||||
|
||||
@@ -207,6 +207,9 @@ extern "C" void Randomizer_InitSaveFile() {
|
||||
gSaveContext.randomizerInf[i] = 0;
|
||||
}
|
||||
|
||||
// Reset triforce pieces collected
|
||||
gSaveContext.triforcePiecesCollected = 0;
|
||||
|
||||
gSaveContext.cutsceneIndex = 0; // no intro cutscene
|
||||
// Starts pending ice traps out at 0 before potentially incrementing them down the line.
|
||||
gSaveContext.pendingIceTrapCount = 0;
|
||||
@@ -442,8 +445,5 @@ extern "C" void Randomizer_InitSaveFile() {
|
||||
gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth
|
||||
}
|
||||
|
||||
// Reset triforce pieces collected
|
||||
gSaveContext.triforcePiecesCollected = 0;
|
||||
|
||||
SetStartingItems();
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ void RegisterOnInterfaceUpdateHook() {
|
||||
|
||||
prevTimer = timer;
|
||||
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
static int16_t lostHealth = 0;
|
||||
static int16_t prevHealth = 0;
|
||||
|
||||
+191
-17
@@ -49,6 +49,9 @@
|
||||
#include "Fonts.h"
|
||||
#include <Utils/StringHelper.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__)
|
||||
#include "Extractor/Extract.h"
|
||||
@@ -115,6 +118,8 @@ CrowdControl* CrowdControl::Instance;
|
||||
|
||||
#include "soh/config/ConfigUpdaters.h"
|
||||
|
||||
void SoH_ProcessDroppedFiles(std::string filePath);
|
||||
|
||||
OTRGlobals* OTRGlobals::Instance;
|
||||
SaveManager* SaveManager::Instance;
|
||||
CustomMessageManager* CustomMessageManager::Instance;
|
||||
@@ -253,15 +258,26 @@ OTRGlobals::OTRGlobals() {
|
||||
OTRFiles.push_back(sohOtrPath);
|
||||
}
|
||||
std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName);
|
||||
std::vector<std::string> patchOTRs = {};
|
||||
if (patchesPath.length() > 0 && std::filesystem::exists(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")) {
|
||||
OTRFiles.push_back(p.path().generic_string());
|
||||
patchOTRs.push_back(p.path().generic_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 = {
|
||||
OOT_PAL_MQ,
|
||||
OOT_NTSC_JP_MQ,
|
||||
@@ -281,6 +297,7 @@ OTRGlobals::OTRGlobals() {
|
||||
};
|
||||
// tell LUS to reserve 3 SoH specific threads (Game, Audio, Save)
|
||||
context = LUS::Context::CreateInstance("Ship of Harkinian", appShortName, "shipofharkinian.json", OTRFiles, {}, 3);
|
||||
SPDLOG_INFO("Starting Ship of Harkinian version {}", (char*)gBuildVersion);
|
||||
|
||||
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>());
|
||||
@@ -1021,9 +1038,9 @@ extern "C" void InitOTR() {
|
||||
OTRGlobals::Instance = new OTRGlobals();
|
||||
CustomMessageManager::Instance = new CustomMessageManager();
|
||||
ItemTableManager::Instance = new ItemTableManager();
|
||||
GameInteractor::Instance = new GameInteractor();
|
||||
SaveManager::Instance = new SaveManager();
|
||||
SohGui::SetupGuiElements();
|
||||
GameInteractor::Instance = new GameInteractor();
|
||||
AudioCollection::Instance = new AudioCollection();
|
||||
ActorDB::Instance = new ActorDB();
|
||||
#ifdef __APPLE__
|
||||
@@ -1043,6 +1060,11 @@ extern "C" void InitOTR() {
|
||||
|
||||
InitMods();
|
||||
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);
|
||||
tm *tm_now = localtime(&now);
|
||||
@@ -1122,8 +1144,7 @@ extern "C" uint64_t GetUnixTimestamp() {
|
||||
auto time = std::chrono::system_clock::now();
|
||||
auto since_epoch = time.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch);
|
||||
long now = millis.count();
|
||||
return now;
|
||||
return (uint64_t)millis.count();
|
||||
}
|
||||
|
||||
// C->C++ Bridge
|
||||
@@ -1131,7 +1152,7 @@ extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) {
|
||||
OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter);
|
||||
}
|
||||
|
||||
extern bool ShouldClearTextureCacheAtEndOfFrame;
|
||||
extern bool ToggleAltAssetsAtEndOfFrame;
|
||||
|
||||
extern "C" void Graph_StartFrame() {
|
||||
#ifndef __WIIU__
|
||||
@@ -1214,13 +1235,21 @@ extern "C" void Graph_StartFrame() {
|
||||
}
|
||||
#endif
|
||||
case KbScancode::LUS_KB_TAB: {
|
||||
// Toggle HD Assets
|
||||
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
|
||||
ShouldClearTextureCacheAtEndOfFrame = true;
|
||||
ToggleAltAssetsAtEndOfFrame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#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();
|
||||
}
|
||||
|
||||
@@ -1286,10 +1315,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();
|
||||
LUS::SkeletonPatcher::UpdateSkeletons();
|
||||
ShouldClearTextureCacheAtEndOfFrame = false;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
|
||||
}
|
||||
|
||||
// OTRTODO: FIGURE OUT END FRAME POINT
|
||||
@@ -1415,6 +1448,14 @@ extern "C" void ResourceMgr_DirtyDirectory(const char* 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...
|
||||
// Kenix: This is definitely leaking memory when it's called.
|
||||
extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) {
|
||||
@@ -1441,6 +1482,27 @@ extern "C" uint8_t ResourceMgr_FileExists(const char* filePath) {
|
||||
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) {
|
||||
LUS::Context::GetInstance()->GetResourceManager()->LoadResource(resName);
|
||||
}
|
||||
@@ -1480,6 +1542,11 @@ extern "C" char* ResourceMgr_LoadFileFromDisk(const char* filePath) {
|
||||
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) {
|
||||
auto res = GetResourceByNameHandlingMQ(texPath);
|
||||
return res->GetInitData()->Type == LUS::ResourceType::SOH_Background;
|
||||
@@ -1565,10 +1632,20 @@ extern "C" void ResourceMgr_PushCurrentDirectory(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));
|
||||
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 {
|
||||
int index;
|
||||
Gfx instruction;
|
||||
@@ -1600,6 +1677,11 @@ extern "C" void ResourceMgr_PatchGfxByName(const char* path, const char* patchNa
|
||||
// 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];
|
||||
|
||||
if (!originalGfx.contains(path) || !originalGfx[path].contains(patchName)) {
|
||||
@@ -1616,6 +1698,11 @@ extern "C" void ResourceMgr_PatchGfxCopyCommandByName(const char* path, const ch
|
||||
auto res = std::static_pointer_cast<LUS::DisplayList>(
|
||||
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 sourceGfx = res->Instructions[sourceIndex];
|
||||
|
||||
@@ -2030,10 +2117,10 @@ Color_RGB8 GetColorForControllerLED() {
|
||||
if (source == LED_SOURCE_CUSTOM) {
|
||||
color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
|
||||
}
|
||||
if (criticalOverride || source == LED_SOURCE_HEALTH) {
|
||||
if (gPlayState && (criticalOverride || source == LED_SOURCE_HEALTH)) {
|
||||
if (HealthMeter_IsCritical()) {
|
||||
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) {
|
||||
color = { 0xFF, 0xFF, 0 };
|
||||
} else {
|
||||
@@ -2399,8 +2486,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
randoInf = RAND_INF_MERCHANTS_CARPET_SALESMAN;
|
||||
}
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(randoInf, textId, Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_ON_HINT);
|
||||
} else if (Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) &&
|
||||
(textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)) {
|
||||
} else if (textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId);
|
||||
} else if (textId == TEXT_CURSED_SKULLTULA_PEOPLE) {
|
||||
actorParams = GET_PLAYER(play)->targetActor->params;
|
||||
@@ -2419,6 +2505,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
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) {
|
||||
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
|
||||
u16 choice = Random(0, NUM_GORON_MESSAGES);
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetGoronMessage(choice);
|
||||
@@ -2508,6 +2596,92 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
|
||||
gfx_register_blended_texture(name, mask, replacement);
|
||||
}
|
||||
|
||||
extern "C" void CheckTracker_OnMessageClose() {
|
||||
CheckTracker::CheckTrackerDialogClosed();
|
||||
extern "C" void Gfx_UnregisterBlendedTexture(const char* name) {
|
||||
gfx_unregister_blended_texture(name);
|
||||
}
|
||||
|
||||
extern "C" void Gfx_TextureCacheDelete(const uint8_t* texAddr) {
|
||||
char* imgName = (char*)texAddr;
|
||||
|
||||
if (texAddr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ResourceMgr_OTRSigCheck(imgName)) {
|
||||
texAddr = (const uint8_t*)GetResourceDataByNameHandlingMQ(imgName);
|
||||
}
|
||||
|
||||
gfx_texture_cache_delete(texAddr);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -86,11 +86,15 @@ uint32_t ResourceMgr_GetGameVersion(int index);
|
||||
uint32_t ResourceMgr_GetGamePlatform(int index);
|
||||
uint32_t ResourceMgr_GetGameRegion(int index);
|
||||
void ResourceMgr_LoadDirectory(const char* resName);
|
||||
void ResourceMgr_UnloadResource(const char* resName);
|
||||
char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
|
||||
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);
|
||||
void ResourceMgr_LoadFile(const char* resName);
|
||||
char* ResourceMgr_LoadFileFromDisk(const char* filePath);
|
||||
uint8_t ResourceMgr_TexIsRaw(const char* texPath);
|
||||
uint8_t ResourceMgr_ResourceIsBackground(char* texPath);
|
||||
char* ResourceMgr_LoadJPEG(char* data, size_t dataSize);
|
||||
uint16_t ResourceMgr_LoadTexWidthByName(char* texPath);
|
||||
@@ -101,6 +105,7 @@ AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path);
|
||||
char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc);
|
||||
Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc);
|
||||
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_UnpatchGfxByName(const char* path, const char* patchName);
|
||||
char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path);
|
||||
@@ -168,6 +173,8 @@ void Entrance_InitEntranceTrackingData(void);
|
||||
void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex);
|
||||
void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
|
||||
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
|
||||
void Gfx_UnregisterBlendedTexture(const char* name);
|
||||
void Gfx_TextureCacheDelete(const uint8_t* addr);
|
||||
void SaveManager_ThreadPoolWait();
|
||||
void CheckTracker_OnMessageClose();
|
||||
|
||||
|
||||
+121
-71
@@ -9,6 +9,7 @@
|
||||
#include <variables.h>
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "SohGui.hpp"
|
||||
|
||||
#define NOGDI // avoid various windows defines that conflict with things in z64.h
|
||||
#include <spdlog/spdlog.h>
|
||||
@@ -47,6 +48,11 @@ std::filesystem::path SaveManager::GetFileName(int fileNum) {
|
||||
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() {
|
||||
coreSectionIDsByName["base"] = SECTION_ID_BASE;
|
||||
coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER;
|
||||
@@ -65,6 +71,10 @@ SaveManager::SaveManager() {
|
||||
|
||||
AddInitFunction(InitFileImpl);
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([this](uint32_t fileNum) { ThreadPoolWait(); });
|
||||
|
||||
smThreadPool = std::make_shared<BS::thread_pool>(1);
|
||||
|
||||
for (SaveFileMetaInfo& info : fileMetaInfo) {
|
||||
info.valid = false;
|
||||
info.deaths = 0;
|
||||
@@ -357,12 +367,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() {
|
||||
// Wait on saves that snuck through the Wait in OnExitGame
|
||||
ThreadPoolWait();
|
||||
const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
|
||||
const std::filesystem::path sGlobalPath = sSavePath / std::string("global.sav");
|
||||
auto sOldSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.sav");
|
||||
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 (!std::filesystem::exists(sSavePath)) {
|
||||
@@ -403,7 +415,6 @@ void SaveManager::Init() {
|
||||
} else {
|
||||
CreateDefaultGlobal();
|
||||
}
|
||||
smThreadPool = std::make_shared<BS::thread_pool>(1);
|
||||
|
||||
// Load files to initialize metadata
|
||||
for (int fileNum = 0; fileNum < MaxFiles; fileNum++) {
|
||||
@@ -869,6 +880,32 @@ void SaveManager::InitFileMaxed() {
|
||||
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
|
||||
|
||||
void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) {
|
||||
@@ -910,19 +947,42 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se
|
||||
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__)
|
||||
FILE* w = fopen(GetFileName(fileNum).c_str(), "w");
|
||||
FILE* w = fopen(tempFile.c_str(), "w");
|
||||
std::string json_string = saveBlock.dump(4);
|
||||
fwrite(json_string.c_str(), sizeof(char), json_string.length(), w);
|
||||
fclose(w);
|
||||
#else
|
||||
std::ofstream output(GetFileName(fileNum));
|
||||
std::ofstream output(tempFile);
|
||||
output << std::setw(4) << saveBlock << std::endl;
|
||||
output.close();
|
||||
#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;
|
||||
InitMeta(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
|
||||
@@ -966,51 +1026,67 @@ void SaveManager::SaveGlobal() {
|
||||
|
||||
void SaveManager::LoadFile(int fileNum) {
|
||||
SPDLOG_INFO("Load File - fileNum: {}", fileNum);
|
||||
assert(std::filesystem::exists(GetFileName(fileNum)));
|
||||
std::filesystem::path fileName = GetFileName(fileNum);
|
||||
assert(std::filesystem::exists(fileName));
|
||||
InitFile(false);
|
||||
|
||||
std::ifstream input(GetFileName(fileNum));
|
||||
|
||||
saveBlock = nlohmann::json::object();
|
||||
input >> saveBlock;
|
||||
if (!saveBlock.contains("version")) {
|
||||
SPDLOG_ERROR("Save at " + GetFileName(fileNum).string() + " contains no version");
|
||||
assert(false);
|
||||
}
|
||||
switch (saveBlock["version"].get<int>()) {
|
||||
case 1:
|
||||
for (auto& block : saveBlock["sections"].items()) {
|
||||
int sectionVersion = block.value()["version"];
|
||||
std::string sectionName = block.key();
|
||||
if (!sectionLoadHandlers.contains(sectionName)) {
|
||||
// Unloadable sections aren't necessarily errors, they are probably mods that were unloaded
|
||||
// TODO report in a more noticeable manner
|
||||
SPDLOG_WARN("Save " + GetFileName(fileNum).string() + " contains unloadable section " + sectionName);
|
||||
continue;
|
||||
}
|
||||
SectionLoadHandler& handler = sectionLoadHandlers[sectionName];
|
||||
if (!handler.contains(sectionVersion)) {
|
||||
// A section that has a loader without a handler for the specific version means that the user has a mod
|
||||
// at an earlier version than the save has. In this case, the user probably wants to load the save.
|
||||
// Report the error so that the user can rectify the error.
|
||||
// TODO report in a more noticeable manner
|
||||
SPDLOG_ERROR("Save " + GetFileName(fileNum).string() + " contains section " + sectionName +
|
||||
" with an unloadable version " + std::to_string(sectionVersion));
|
||||
assert(false);
|
||||
continue;
|
||||
}
|
||||
currentJsonContext = &block.value()["data"];
|
||||
handler[sectionVersion]();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SPDLOG_ERROR("Unrecognized save version " + std::to_string(saveBlock["version"].get<int>()) + " in " +
|
||||
GetFileName(fileNum).string());
|
||||
std::ifstream input(fileName);
|
||||
|
||||
try {
|
||||
saveBlock = nlohmann::json::object();
|
||||
input >> saveBlock;
|
||||
if (!saveBlock.contains("version")) {
|
||||
SPDLOG_ERROR("Save at " + fileName.string() + " contains no version");
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
switch (saveBlock["version"].get<int>()) {
|
||||
case 1:
|
||||
for (auto& block : saveBlock["sections"].items()) {
|
||||
int sectionVersion = block.value()["version"];
|
||||
std::string sectionName = block.key();
|
||||
if (!sectionLoadHandlers.contains(sectionName)) {
|
||||
// Unloadable sections aren't necessarily errors, they are probably mods that were unloaded
|
||||
// TODO report in a more noticeable manner
|
||||
SPDLOG_WARN("Save " + GetFileName(fileNum).string() + " contains unloadable section " +
|
||||
sectionName);
|
||||
continue;
|
||||
}
|
||||
SectionLoadHandler& handler = sectionLoadHandlers[sectionName];
|
||||
if (!handler.contains(sectionVersion)) {
|
||||
// A section that has a loader without a handler for the specific version means that the user
|
||||
// has a mod at an earlier version than the save has. In this case, the user probably wants to
|
||||
// load the save. Report the error so that the user can rectify the error.
|
||||
// TODO report in a more noticeable manner
|
||||
SPDLOG_ERROR("Save " + GetFileName(fileNum).string() + " contains section " + sectionName +
|
||||
" with an unloadable version " + std::to_string(sectionVersion));
|
||||
assert(false);
|
||||
continue;
|
||||
}
|
||||
currentJsonContext = &block.value()["data"];
|
||||
handler[sectionVersion]();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SPDLOG_ERROR("Unrecognized save version " + std::to_string(saveBlock["version"].get<int>()) + " in " +
|
||||
GetFileName(fileNum).string());
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
InitMeta(fileNum);
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnLoadFile>(fileNum);
|
||||
} catch (const std::exception& e) {
|
||||
input.close();
|
||||
std::filesystem::path newFile(LUS::Context::GetPathRelativeToAppDirectory("Save") + ("/file" + std::to_string(fileNum + 1) + "-" + std::to_string(GetUnixTimestamp()) + ".bak"));
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
copy_file(fileName.c_str(), newFile.c_str());
|
||||
#else
|
||||
std::filesystem::copy_file(fileName, newFile);
|
||||
#endif
|
||||
|
||||
std::filesystem::remove(fileName);
|
||||
SohGui::RegisterPopup("Error loading save file", "A problem occurred loading the save in slot " + std::to_string(fileNum + 1) + ".\nSave file corruption is suspected.\n" +
|
||||
"The file has been renamed to prevent further issues.");
|
||||
}
|
||||
InitMeta(fileNum);
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnLoadFile>(fileNum);
|
||||
}
|
||||
|
||||
void SaveManager::ThreadPoolWait() {
|
||||
@@ -2105,32 +2181,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) {
|
||||
assert(std::filesystem::exists(GetFileName(from)));
|
||||
DeleteZeldaFile(to);
|
||||
|
||||
@@ -142,6 +142,7 @@ class SaveManager {
|
||||
|
||||
private:
|
||||
std::filesystem::path GetFileName(int fileNum);
|
||||
std::filesystem::path GetFileTempName(int fileNum);
|
||||
nlohmann::json saveBlock;
|
||||
|
||||
void ConvertFromUnversioned();
|
||||
|
||||
+10
-1
@@ -38,7 +38,7 @@
|
||||
#include "Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "Enhancements/cosmetics/authenticGfxPatches.h"
|
||||
|
||||
bool ShouldClearTextureCacheAtEndOfFrame = false;
|
||||
bool ToggleAltAssetsAtEndOfFrame = false;
|
||||
bool isBetaQuestEnabled = false;
|
||||
|
||||
extern "C" {
|
||||
@@ -125,6 +125,7 @@ namespace SohGui {
|
||||
std::shared_ptr<ItemTrackerSettingsWindow> mItemTrackerSettingsWindow;
|
||||
std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow;
|
||||
std::shared_ptr<RandomizerSettingsWindow> mRandomizerSettingsWindow;
|
||||
std::shared_ptr<SohModalWindow> mModalWindow;
|
||||
|
||||
void SetupGuiElements() {
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
@@ -183,9 +184,13 @@ namespace SohGui {
|
||||
gui->AddGuiWindow(mItemTrackerSettingsWindow);
|
||||
mRandomizerSettingsWindow = std::make_shared<RandomizerSettingsWindow>("gRandomizerSettingsEnabled", "Randomizer Settings");
|
||||
gui->AddGuiWindow(mRandomizerSettingsWindow);
|
||||
mModalWindow = std::make_shared<SohModalWindow>("gOpenWindows.modalWindowEnabled", "Modal Window");
|
||||
gui->AddGuiWindow(mModalWindow);
|
||||
mModalWindow->Show();
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
mModalWindow = nullptr;
|
||||
mRandomizerSettingsWindow = nullptr;
|
||||
mItemTrackerWindow = nullptr;
|
||||
mItemTrackerSettingsWindow = nullptr;
|
||||
@@ -205,4 +210,8 @@ namespace SohGui {
|
||||
mConsoleWindow = nullptr;
|
||||
mSohMenuBar = nullptr;
|
||||
}
|
||||
|
||||
void RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function<void()> button1callback, std::function<void()> button2callback) {
|
||||
mModalWindow->RegisterPopup(title, message, button1, button2, button1callback, button2callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Enhancements/randomizer/randomizer_entrance_tracker.h"
|
||||
#include "Enhancements/randomizer/randomizer_item_tracker.h"
|
||||
#include "Enhancements/randomizer/randomizer_settings_window.h"
|
||||
#include "SohModals.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -37,6 +38,7 @@ namespace SohGui {
|
||||
void SetupGuiElements();
|
||||
void Draw();
|
||||
void Destroy();
|
||||
void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function<void()> button1callback = nullptr, std::function<void()> button2callback = nullptr);
|
||||
}
|
||||
|
||||
#endif /* SohGui_hpp */
|
||||
|
||||
+27
-5
@@ -28,7 +28,7 @@
|
||||
#include "Enhancements/randomizer/randomizer_item_tracker.h"
|
||||
#include "Enhancements/randomizer/randomizer_settings_window.h"
|
||||
|
||||
extern bool ShouldClearTextureCacheAtEndOfFrame;
|
||||
extern bool ToggleAltAssetsAtEndOfFrame;
|
||||
extern bool isBetaQuestEnabled;
|
||||
|
||||
extern "C" PlayState* gPlayState;
|
||||
@@ -492,6 +492,8 @@ extern std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow;
|
||||
void DrawEnhancementsMenu() {
|
||||
if (ImGui::BeginMenu("Enhancements"))
|
||||
{
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
DrawPresetSelector(PRESET_TYPE_ENHANCEMENTS);
|
||||
|
||||
UIWidgets::PaddedSeparator();
|
||||
@@ -532,6 +534,8 @@ void DrawEnhancementsMenu() {
|
||||
" - Small keys: Small silver chest\n"
|
||||
" - Boss keys: Vanilla size and texture\n"
|
||||
" - Skulltula Tokens: Small skulltula chest\n"
|
||||
"\n"
|
||||
"NOTE: Textures will not apply if you are using a mod pack with a custom chest model."
|
||||
);
|
||||
if (CVarGetInteger("gChestSizeAndTextureMatchesContents", CSMC_DISABLED) != CSMC_DISABLED) {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Chests of Agony", "gChestSizeDependsStoneOfAgony", true, false);
|
||||
@@ -554,7 +558,7 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Remember Save Location", "gRememberSaveLocation", true, false);
|
||||
UIWidgets::Tooltip("When loading a save, places Link at the last entrance he went through.\n"
|
||||
"This doesn't work if the save was made in a grotto.");
|
||||
"This doesn't work if the save was made in grottos/fairy fountains or dungeons.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Magic Arrow Equip Animation", "gSkipArrowAnimation", true, false);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip save confirmation", "gSkipSaveConfirmation", true, false);
|
||||
UIWidgets::Tooltip("Skip the \"Game saved.\" confirmation screen");
|
||||
@@ -603,7 +607,7 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Nuts explode bombs", "gNutsExplodeBombs", true, false);
|
||||
UIWidgets::Tooltip("Makes nuts explode bombs, similar to how they interact with bombchus. This does not affect bombflowers.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Equip Multiple Arrows at Once", "gSeparateArrows", true, false);
|
||||
UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots");
|
||||
UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots. (Note this will disable the behaviour of the 'Equip Dupe' glitch)");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Bow as Child/Slingshot as Adult", "gBowSlingShotAmmoFix", true, false);
|
||||
UIWidgets::Tooltip("Allows child to use bow with arrows.\nAllows adult to use slingshot with seeds.\n\nRequires glitches or 'Timeless Equipment' cheat to equip.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Better Farore's Wind", "gBetterFW", true, false);
|
||||
@@ -904,7 +908,10 @@ void DrawEnhancementsMenu() {
|
||||
{
|
||||
if (ImGui::BeginMenu("Mods")) {
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", "gAltAssets", false, false)) {
|
||||
ShouldClearTextureCacheAtEndOfFrame = true;
|
||||
// The checkbox will flip the alt asset CVar, but we instead want it to change at the end of the game frame
|
||||
// We toggle it back while setting the flag to update the CVar later
|
||||
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
|
||||
ToggleAltAssetsAtEndOfFrame = true;
|
||||
}
|
||||
UIWidgets::Tooltip("Toggle between standard assets and alternate assets. Usually mods will indicate if this setting has to be used or not.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Disable Bomb Billboarding", "gDisableBombBillboarding", true, false);
|
||||
@@ -1059,6 +1066,11 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::Tooltip("Fixes the bushes to drop items correctly rather than spawning undefined items.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Fix falling from vine edges", "gFixVineFall", true, false);
|
||||
UIWidgets::Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Fix Link's eyes open while sleeping", "gFixEyesOpenWhileSleeping", true, false);
|
||||
UIWidgets::Tooltip("Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Fix Darunia dancing too fast", "gEnhancements.FixDaruniaDanceSpeed",
|
||||
true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
UIWidgets::Tooltip("Fixes Darunia's dancing speed so he dances to the beat of Saria's Song, like in vanilla.");
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@@ -1194,6 +1206,8 @@ void DrawEnhancementsMenu() {
|
||||
|
||||
UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f);
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
@@ -1235,6 +1249,8 @@ void DrawEnhancementsMenu() {
|
||||
void DrawCheatsMenu() {
|
||||
if (ImGui::BeginMenu("Cheats"))
|
||||
{
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
if (ImGui::BeginMenu("Infinite...")) {
|
||||
UIWidgets::EnhancementCheckbox("Money", "gInfiniteMoney");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Health", "gInfiniteHealth", true, false);
|
||||
@@ -1389,6 +1405,8 @@ void DrawCheatsMenu() {
|
||||
}
|
||||
UIWidgets::Tooltip("Clears the cutscene pointer to a value safe for wrong warps.");
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
@@ -1402,6 +1420,8 @@ extern std::shared_ptr<DLViewerWindow> mDLViewerWindow;
|
||||
|
||||
void DrawDeveloperToolsMenu() {
|
||||
if (ImGui::BeginMenu("Developer Tools")) {
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
UIWidgets::EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled");
|
||||
UIWidgets::Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right, and open the debug menu with L on the pause screen");
|
||||
if (CVarGetInteger("gDebugEnabled", 0)) {
|
||||
@@ -1474,6 +1494,8 @@ void DrawDeveloperToolsMenu() {
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
@@ -1622,4 +1644,4 @@ void SohMenuBar::DrawElement() {
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
}
|
||||
} // namespace SohGui
|
||||
} // namespace SohGui
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "SohModals.h"
|
||||
#include "ImGui/imgui.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <libultraship/bridge.h>
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "UIWidgets.hpp"
|
||||
#include "OTRGlobals.h"
|
||||
#include "z64.h"
|
||||
|
||||
extern "C" PlayState* gPlayState;
|
||||
struct SohModal {
|
||||
std::string title_;
|
||||
std::string message_;
|
||||
std::string button1_;
|
||||
std::string button2_;
|
||||
std::function<void()> button1callback_;
|
||||
std::function<void()> button2callback_;
|
||||
};
|
||||
std::vector<SohModal> modals;
|
||||
|
||||
void SohModalWindow::DrawElement() {
|
||||
if (modals.size() > 0) {
|
||||
SohModal curModal = modals.at(0);
|
||||
if (!ImGui::IsPopupOpen(curModal.title_.c_str())) {
|
||||
ImGui::OpenPopup(curModal.title_.c_str());
|
||||
}
|
||||
if (ImGui::BeginPopupModal(curModal.title_.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
ImGui::Text(curModal.message_.c_str());
|
||||
if (ImGui::Button(curModal.button1_.c_str())) {
|
||||
if (curModal.button1callback_ != nullptr) {
|
||||
curModal.button1callback_();
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
modals.erase(modals.begin());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (curModal.button2_ != "") {
|
||||
if (ImGui::Button(curModal.button2_.c_str())) {
|
||||
if (curModal.button2callback_ != nullptr) {
|
||||
curModal.button2callback_();
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
modals.erase(modals.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void SohModalWindow::RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function<void()> button1callback, std::function<void()> button2callback) {
|
||||
modals.push_back({ title, message, button1, button2, button1callback, button2callback });
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "window/gui/GuiMenuBar.h"
|
||||
#include "window/gui/GuiElement.h"
|
||||
|
||||
class SohModalWindow : public LUS::GuiWindow {
|
||||
public:
|
||||
using LUS::GuiWindow::GuiWindow;
|
||||
|
||||
void InitElement() override {};
|
||||
void DrawElement() override;
|
||||
void UpdateElement() override {};
|
||||
void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function<void()> button1callback = nullptr, std::function<void()> button2callback = nullptr);
|
||||
};
|
||||
@@ -103,38 +103,42 @@ void SkeletonFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shared_p
|
||||
{
|
||||
std::shared_ptr<Skeleton> skel = std::static_pointer_cast<Skeleton>(resource);
|
||||
|
||||
std::string skeletonType = reader->Attribute("Type");
|
||||
// std::string skeletonLimbType = reader->Attribute("LimbType");
|
||||
int numLimbs = reader->IntAttribute("LimbCount");
|
||||
int numDLs = reader->IntAttribute("DisplayListCount");
|
||||
skel->type = SkeletonType::Flex; // Default to Flex for legacy reasons
|
||||
if (reader->FindAttribute("Type")) {
|
||||
std::string skeletonType = reader->Attribute("Type");
|
||||
|
||||
if (skeletonType == "Flex") {
|
||||
skel->type = SkeletonType::Flex;
|
||||
} else if (skeletonType == "Curve") {
|
||||
skel->type = SkeletonType::Curve;
|
||||
} else if (skeletonType == "Normal") {
|
||||
skel->type = SkeletonType::Normal;
|
||||
if (skeletonType == "Flex") {
|
||||
skel->type = SkeletonType::Flex;
|
||||
} else if (skeletonType == "Curve") {
|
||||
skel->type = SkeletonType::Curve;
|
||||
} else if (skeletonType == "Normal") {
|
||||
skel->type = SkeletonType::Normal;
|
||||
}
|
||||
}
|
||||
|
||||
skel->type = SkeletonType::Flex;
|
||||
skel->limbType = LimbType::LOD;
|
||||
skel->limbType = LimbType::LOD; // Default to LOD for legacy reasons
|
||||
if (reader->FindAttribute("LimbType")) {
|
||||
std::string skeletonLimbType = reader->Attribute("LimbType");
|
||||
|
||||
// if (skeletonLimbType == "Standard")
|
||||
// skel->limbType = LimbType::Standard;
|
||||
// else if (skeletonLimbType == "LOD")
|
||||
// skel->limbType = LimbType::LOD;
|
||||
// else if (skeletonLimbType == "Curve")
|
||||
// skel->limbType = LimbType::Curve;
|
||||
// else if (skeletonLimbType == "Skin")
|
||||
// skel->limbType = LimbType::Skin;
|
||||
// else if (skeletonLimbType == "Legacy")
|
||||
// Sskel->limbType = LimbType::Legacy;
|
||||
if (skeletonLimbType == "Standard") {
|
||||
skel->limbType = LimbType::Standard;
|
||||
} else if (skeletonLimbType == "LOD") {
|
||||
skel->limbType = LimbType::LOD;
|
||||
} else if (skeletonLimbType == "Curve") {
|
||||
skel->limbType = LimbType::Curve;
|
||||
} else if (skeletonLimbType == "Skin") {
|
||||
skel->limbType = LimbType::Skin;
|
||||
} else if (skeletonLimbType == "Legacy") {
|
||||
skel->limbType = LimbType::Legacy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
skel->limbCount = reader->IntAttribute("LimbCount");
|
||||
skel->dListCount = reader->IntAttribute("DisplayListCount");
|
||||
|
||||
auto child = reader->FirstChildElement();
|
||||
|
||||
skel->limbCount = numLimbs;
|
||||
skel->dListCount = numDLs;
|
||||
|
||||
while (child != nullptr) {
|
||||
std::string childName = child->Name();
|
||||
|
||||
|
||||
@@ -132,15 +132,15 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.lodLimb.sibling = skeletonLimb->siblingIndex;
|
||||
|
||||
if (skeletonLimb->dListPtr != "") {
|
||||
auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = nullptr;
|
||||
}
|
||||
|
||||
if (skeletonLimb->dList2Ptr != "") {
|
||||
auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dList2Ptr = "__OTR__" + skeletonLimb->dList2Ptr;
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = (Gfx*)skeletonLimb->dList2Ptr.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = nullptr;
|
||||
}
|
||||
@@ -153,8 +153,8 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.standardLimb.dList = nullptr;
|
||||
|
||||
if (!skeletonLimb->dListPtr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.standardLimb.dList = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.standardLimb.dList = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
}
|
||||
} else if (skeletonLimb->limbType == LUS::LimbType::Curve) {
|
||||
skeletonLimb->limbData.skelCurveLimb.firstChildIdx = skeletonLimb->childIndex;
|
||||
@@ -163,13 +163,13 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = nullptr;
|
||||
|
||||
if (!skeletonLimb->dListPtr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[0] = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
}
|
||||
|
||||
if (!skeletonLimb->dList2Ptr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dList2Ptr = "__OTR__" + skeletonLimb->dList2Ptr;
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = (Gfx*)skeletonLimb->dList2Ptr.c_str();
|
||||
}
|
||||
} else if (skeletonLimb->limbType == LUS::LimbType::Skin) {
|
||||
skeletonLimb->limbData.skinLimb.jointPos.x = skeletonLimb->transX;
|
||||
@@ -189,14 +189,23 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
}
|
||||
|
||||
if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_DList) {
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList.c_str());
|
||||
skeletonLimb->limbData.skinLimb.segment = res ? res->GetRawPointer() : nullptr;
|
||||
if (skeletonLimb->skinDList != "") {
|
||||
skeletonLimb->skinDList = "__OTR__" + skeletonLimb->skinDList;
|
||||
skeletonLimb->limbData.skinLimb.segment = (Gfx*)skeletonLimb->skinDList.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.skinLimb.segment = nullptr;
|
||||
}
|
||||
} else if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_4) {
|
||||
skeletonLimb->skinAnimLimbData.totalVtxCount = skeletonLimb->skinVtxCnt;
|
||||
skeletonLimb->skinAnimLimbData.limbModifCount = skeletonLimb->skinLimbModifCount;
|
||||
skeletonLimb->skinAnimLimbData.limbModifications = skeletonLimb->skinLimbModifArray.data();
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList2.c_str());
|
||||
skeletonLimb->skinAnimLimbData.dlist = (Gfx*)(res ? res->GetRawPointer() : nullptr);
|
||||
|
||||
if (skeletonLimb->skinDList2 != "") {
|
||||
skeletonLimb->skinDList2 = "__OTR__" + skeletonLimb->skinDList2;
|
||||
skeletonLimb->skinAnimLimbData.dlist = (Gfx*)skeletonLimb->skinDList2.c_str();
|
||||
} else {
|
||||
skeletonLimb->skinAnimLimbData.dlist = nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < skeletonLimb->skinLimbModifArray.size(); i++) {
|
||||
skeletonLimb->skinAnimLimbData.limbModifications[i].vtxCount = skeletonLimb->skinLimbModifVertexArrays[i].size();
|
||||
@@ -254,8 +263,8 @@ void SkeletonLimbFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shar
|
||||
limbData.lodLimb.jointPos.z = skelLimb->transZ;
|
||||
|
||||
if (skelLimb->dListPtr != "") {
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess((const char*)skelLimb->dListPtr.c_str());
|
||||
limbData.lodLimb.dLists[0] = (Gfx*)(res ? res->GetRawPointer() : nullptr);
|
||||
skelLimb->dListPtr = "__OTR__" + skelLimb->dListPtr;
|
||||
limbData.lodLimb.dLists[0] = (Gfx*)skelLimb->dListPtr.c_str();
|
||||
} else {
|
||||
limbData.lodLimb.dLists[0] = nullptr;
|
||||
}
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ void Audio_osWritebackDCache(void* mem, s32 size)
|
||||
|
||||
s32 osAiSetFrequency(u32 freq)
|
||||
{
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 osEPiStartDma(OSPiHandle* handle, OSIoMesg* mb, s32 direction)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
std::vector<std::string> sceneNames = {
|
||||
"Inside the Deku Tree",
|
||||
@@ -318,3 +319,20 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
|
||||
strncpy(destination, source.c_str(), size - 1);
|
||||
destination[size - 1] = '\0';
|
||||
}
|
||||
|
||||
std::string SohUtils::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;
|
||||
}
|
||||
|
||||
@@ -12,4 +12,6 @@ namespace SohUtils {
|
||||
// Copies a string and ensures the destination is null terminated if the source string is larger than size
|
||||
// Only up to size-1 characters are copied from the source string
|
||||
void CopyStringToCharArray(char* destination, std::string source, size_t size);
|
||||
|
||||
std::string Sanitize(std::string stringValue);
|
||||
} // namespace SohUtils
|
||||
|
||||
@@ -72,6 +72,9 @@ MessageTableEntry* OTRMessage_LoadTable(const char* filePath, bool isNES) {
|
||||
_message_0xFFFC_nes = (char*)file->messages[i].msg.c_str();
|
||||
}
|
||||
|
||||
// Assert that the first message starts at the first text ID
|
||||
assert(table[0].textId == 0x0001);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
@@ -104,6 +107,9 @@ extern "C" void OTRMessage_Init()
|
||||
sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str();
|
||||
sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size();
|
||||
}
|
||||
|
||||
// Assert staff credits start at the first credits ID
|
||||
assert(sStaffMessageEntryTablePtr[0].textId == 0x0500);
|
||||
}
|
||||
|
||||
CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID);
|
||||
|
||||
+19
-31
@@ -149,6 +149,13 @@ bool Scene_CommandMeshHeader(PlayState* play, LUS::ISceneCommand* cmd) {
|
||||
|
||||
extern "C" void* func_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId);
|
||||
|
||||
bool OTRfunc_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId) {
|
||||
|
||||
objectCtx->status[bankIndex].id = -objectId;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) {
|
||||
// LUS::SetObjectList* cmdObj = static_pointer_cast<LUS::SetObjectList>(cmd);
|
||||
LUS::SetObjectList* cmdObj = (LUS::SetObjectList*)cmd;
|
||||
@@ -164,49 +171,30 @@ bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) {
|
||||
void* nextPtr;
|
||||
|
||||
k = 0;
|
||||
// i = play->objectCtx.unk_09;
|
||||
i = 0;
|
||||
i = play->objectCtx.unk_09;
|
||||
firstStatus = &play->objectCtx.status[0];
|
||||
status = &play->objectCtx.status[i];
|
||||
|
||||
for (int i = 0; i < cmdObj->objects.size(); i++) {
|
||||
bool alreadyIncluded = false;
|
||||
|
||||
for (int j = 0; j < play->objectCtx.num; j++) {
|
||||
if (play->objectCtx.status[j].id == cmdObj->objects[i]) {
|
||||
alreadyIncluded = true;
|
||||
break;
|
||||
// Loop until a mismatch in the object lists
|
||||
// Then clear all object ids past that in the context object list and kill actors for those objects
|
||||
for (i = play->objectCtx.unk_09, k = 0; i < play->objectCtx.num; i++, k++) {
|
||||
if (k >= cmdObj->objects.size() || play->objectCtx.status[i].id != cmdObj->objects[k]) {
|
||||
for (j = i; j < play->objectCtx.num; j++) {
|
||||
play->objectCtx.status[j].id = OBJECT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyIncluded) {
|
||||
play->objectCtx.status[play->objectCtx.num++].id = cmdObj->objects[i];
|
||||
func_80031A28(play, &play->actorCtx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
while (i < play->objectCtx.num) {
|
||||
if (status->id != *objectEntry) {
|
||||
status2 = &play->objectCtx.status[i];
|
||||
for (j = i; j < play->objectCtx.num; j++) {
|
||||
status2->id = OBJECT_INVALID;
|
||||
status2++;
|
||||
}
|
||||
play->objectCtx.num = i;
|
||||
func_80031A28(play, &play->actorCtx);
|
||||
|
||||
continue;
|
||||
// Continuing from the last index, add the remaining object ids from the command object list
|
||||
for (; k < cmdObj->objects.size(); k++, i++) {
|
||||
if (i < OBJECT_EXCHANGE_BANK_MAX - 1) {
|
||||
OTRfunc_800982FC(&play->objectCtx, i, cmdObj->objects[k]);
|
||||
}
|
||||
|
||||
i++;
|
||||
k++;
|
||||
objectEntry++;
|
||||
status++;
|
||||
}
|
||||
|
||||
play->objectCtx.num = i;
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -204,6 +204,17 @@ u8 Inventory_DeleteEquipment(PlayState* play, s16 equipment) {
|
||||
|
||||
if (equipment == EQUIP_TYPE_TUNIC) {
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4);
|
||||
// non-vanilla: remove goron and zora tunics from item buttons if assignable tunics is on
|
||||
if (CVarGetInteger("gAssignableTunicsAndBoots", 0) && equipValue != EQUIP_VALUE_TUNIC_KOKIRI) {
|
||||
ItemID item = (equipValue == EQUIP_VALUE_TUNIC_GORON ? ITEM_TUNIC_GORON : ITEM_TUNIC_ZORA);
|
||||
for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
if (gSaveContext.equips.buttonItems[i] == item) {
|
||||
gSaveContext.equips.buttonItems[i] = ITEM_NONE;
|
||||
gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// end non-vanilla
|
||||
}
|
||||
|
||||
if (equipment == EQUIP_TYPE_SWORD) {
|
||||
|
||||
+15
-5
@@ -1219,8 +1219,7 @@ void Actor_Init(Actor* actor, PlayState* play) {
|
||||
CollisionCheck_InitInfo(&actor->colChkInfo);
|
||||
actor->floorBgId = BGCHECK_SCENE;
|
||||
ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f);
|
||||
//if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex))
|
||||
{
|
||||
if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
||||
//Actor_SetObjectDependency(play, actor);
|
||||
actor->init(actor, play);
|
||||
actor->init = NULL;
|
||||
@@ -2861,11 +2860,19 @@ s32 func_800314D4(PlayState* play, Actor* actor, Vec3f* arg2, f32 arg3) {
|
||||
if ((arg2->z > -actor->uncullZoneScale) && (arg2->z < (actor->uncullZoneForward + actor->uncullZoneScale))) {
|
||||
var = (arg3 < 1.0f) ? 1.0f : 1.0f / arg3;
|
||||
|
||||
if ((((fabsf(arg2->x) - actor->uncullZoneScale) * var) < 2.0f) &&
|
||||
(((arg2->y + actor->uncullZoneDownward) * var) > -2.0f) &&
|
||||
(((arg2->y - actor->uncullZoneScale) * var) < 2.0f)) {
|
||||
// #region SoH [Widescreen support]
|
||||
// Doors will cull quite noticeably on wider screens. For these actors the zone is increased
|
||||
f32 limit = 1.0f;
|
||||
if (((actor->id == ACTOR_EN_DOOR) || (actor->id == ACTOR_DOOR_SHUTTER)) && CVarGetInteger("gIncreaseDoorUncullZones", 1)) {
|
||||
limit = 2.0f;
|
||||
}
|
||||
|
||||
if ((((fabsf(arg2->x) - actor->uncullZoneScale) * var) < limit) &&
|
||||
(((arg2->y + actor->uncullZoneDownward) * var) > -limit) &&
|
||||
(((arg2->y - actor->uncullZoneScale) * var) < limit)) {
|
||||
return true;
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -3129,6 +3136,9 @@ void Actor_FreeOverlay(ActorDBEntry* dbEntry) {
|
||||
osSyncPrintf(VT_RST);
|
||||
}
|
||||
|
||||
// SoH: Flag to check if actors are being spawned from the actor entry list
|
||||
// This flag is checked against to allow actors which dont have an objectBankIndex in the objectCtx slot/status array to spawn
|
||||
// An example of what this fixes, is that it allows hookshot to be used as child
|
||||
int gMapLoading = 0;
|
||||
|
||||
Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 posX, f32 posY, f32 posZ,
|
||||
|
||||
@@ -1902,7 +1902,7 @@ s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResul
|
||||
s32 bgId2;
|
||||
f32 nx, ny, nz; // unit normal of polygon
|
||||
|
||||
if (CVarGetInteger("gNoClip", 0) != 0) {
|
||||
if (CVarGetInteger("gNoClip", 0) && actor != NULL && actor->id == ACTOR_PLAYER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7887,7 +7887,7 @@ s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear free camera if an action is performed that would move the camera (targeting, first person, talking)
|
||||
// Clear free look if an action is performed that would move the camera (targeting, first person, talking)
|
||||
if (CVarGetInteger("gFreeCamera", 0) && SetCameraManual(camera) == 1 &&
|
||||
((mode >= CAM_MODE_TARGET && mode <= CAM_MODE_BATTLE) ||
|
||||
(mode >= CAM_MODE_FIRSTPERSON && mode <= CAM_MODE_CLIMBZ) || mode == CAM_MODE_HANGZ ||
|
||||
|
||||
@@ -16,9 +16,11 @@ typedef struct {
|
||||
union {
|
||||
u32 unk_00;
|
||||
struct {
|
||||
u32 unk_bit0 : 1;
|
||||
u32 unk_bit1 : 1;
|
||||
u32 validModes : 30;
|
||||
// SoH [Port] These bitfield values are unused and led to shifting in validModes for little endian systems
|
||||
// Removing those so that validModes can be a complete 32 bit value
|
||||
// u32 unk_bit0 : 1;
|
||||
// u32 unk_bit1 : 1;
|
||||
u32 validModes;
|
||||
};
|
||||
};
|
||||
CameraMode* cameraModes;
|
||||
|
||||
@@ -524,7 +524,6 @@ void Map_Init(PlayState* play) {
|
||||
interfaceCtx->unk_25A = -1;
|
||||
|
||||
interfaceCtx->mapSegment = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*));
|
||||
interfaceCtx->mapSegmentName = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*));
|
||||
// "MAP texture initialization scene_data_ID=%d mapSegment=%x"
|
||||
osSyncPrintf("\n\n\nMAP テクスチャ初期化 scene_data_ID=%d\nmapSegment=%x\n\n", play->sceneNum,
|
||||
interfaceCtx->mapSegment, play);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "textures/message_static/message_static.h"
|
||||
#include "textures/message_texture_static/message_texture_static.h"
|
||||
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
@@ -3069,7 +3070,9 @@ void Message_Draw(PlayState* play) {
|
||||
POLY_OPA_DISP = plusOne;
|
||||
}
|
||||
plusOne = Graph_GfxPlusOne(polyOpaP = POLY_OPA_DISP);
|
||||
gSPDisplayList(OVERLAY_DISP++, plusOne);
|
||||
if (!GameInteractor_NoUIActive()) {
|
||||
gSPDisplayList(OVERLAY_DISP++, plusOne);
|
||||
}
|
||||
Message_DrawMain(play, &plusOne);
|
||||
gSPEndDisplayList(plusOne++);
|
||||
Graph_BranchDlist(polyOpaP, plusOne);
|
||||
@@ -3384,7 +3387,7 @@ void Message_Update(PlayState* play) {
|
||||
}
|
||||
sLastPlayedSong = 0xFF;
|
||||
osSyncPrintf("OCARINA_MODE=%d chk_ocarina_no=%d\n", play->msgCtx.ocarinaMode, msgCtx->unk_E3F2);
|
||||
CheckTracker_OnMessageClose();
|
||||
// TODO: OnMessageClose hook
|
||||
break;
|
||||
case MSGMODE_PAUSED:
|
||||
break;
|
||||
|
||||
@@ -1469,6 +1469,7 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER;
|
||||
} else {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
if (gSaveContext.inventory.items[SLOT_NUT] != ITEM_NONE) {
|
||||
@@ -1520,18 +1521,25 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// In Rando, when switching to adult for the second+ time, if a sword was not previously
|
||||
// equiped in MS shuffle, then we need to set the swordless flag again
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_NONE) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
gSaveContext.equips.equipment = gSaveContext.adultEquips.equipment;
|
||||
}
|
||||
} else {
|
||||
// When becoming child, set swordless flag if player doesn't have kokiri sword
|
||||
// Only in rando to keep swordless link bugs in vanilla
|
||||
if (IS_RANDO && (EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0) {
|
||||
if (IS_RANDO && CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
// When using enhancements, set swordless flag if player doesn't have kokiri sword or hasn't equipped a sword yet.
|
||||
// Then set the child equips button items to item none to ensure kokiri sword is not equipped
|
||||
if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) && ((EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0 || Flags_GetInfTable(INFTABLE_SWORDLESS))) {
|
||||
if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) && (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0 || Flags_GetInfTable(INFTABLE_SWORDLESS))) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
gSaveContext.childEquips.buttonItems[0] = ITEM_NONE;
|
||||
}
|
||||
@@ -1567,7 +1575,7 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
// Equips kokiri sword in the inventory screen only if kokiri sword exists in inventory and a sword has been equipped already
|
||||
if (!((EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0) && !Flags_GetInfTable(INFTABLE_SWORDLESS)) {
|
||||
if (!(CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) && !Flags_GetInfTable(INFTABLE_SWORDLESS)) {
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
|
||||
}
|
||||
} else if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) {
|
||||
@@ -1588,6 +1596,13 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// In Rando, when switching to child from a swordless adult, and child Link previously had a
|
||||
// sword equiped, then we need to unset the swordless flag to match
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
|
||||
gSaveContext.equips.buttonItems[0] != ITEM_NONE) {
|
||||
Flags_UnsetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
|
||||
@@ -1597,7 +1612,7 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
(only kokiri tunic/boots equipped, no sword, no C-button items, no D-Pad items).
|
||||
When becoming child, set swordless flag if player doesn't have kokiri sword
|
||||
Only in rando to keep swordless link bugs in vanilla*/
|
||||
if (EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment == 0) {
|
||||
if (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ u64 D_801614D0[0xA00];
|
||||
#endif
|
||||
|
||||
PlayState* gPlayState;
|
||||
s16 firstInit = 0;
|
||||
|
||||
s16 gEnPartnerId;
|
||||
|
||||
@@ -490,6 +491,12 @@ void Play_Init(GameState* thisx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Properly initialize the frame counter so it doesn't use garbage data
|
||||
if (!firstInit) {
|
||||
play->gameplayFrames = 0;
|
||||
firstInit = 1;
|
||||
}
|
||||
|
||||
// Invalid entrance, so immediately exit the game to opening title
|
||||
if (gSaveContext.entranceIndex == -1) {
|
||||
gSaveContext.entranceIndex = 0;
|
||||
@@ -2329,8 +2336,28 @@ void Play_PerformSave(PlayState* play) {
|
||||
if (play != NULL && gSaveContext.fileNum != 0xFF) {
|
||||
Play_SaveSceneFlags(play);
|
||||
gSaveContext.savedSceneNum = play->sceneNum;
|
||||
|
||||
// Track values from temp B
|
||||
uint8_t prevB = gSaveContext.equips.buttonItems[0];
|
||||
uint8_t prevStatus = gSaveContext.buttonStatus[0];
|
||||
|
||||
// Replicate the B button restore from minigames/epona that kaleido does
|
||||
if (gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT ||
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_BOW ||
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU ||
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE ||
|
||||
(gSaveContext.equips.buttonItems[0] == ITEM_NONE && !Flags_GetInfTable(INFTABLE_SWORDLESS))) {
|
||||
|
||||
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
|
||||
Interface_RandoRestoreSwordless();
|
||||
}
|
||||
|
||||
Save_SaveFile();
|
||||
|
||||
// Restore temp B values back
|
||||
gSaveContext.equips.buttonItems[0] = prevB;
|
||||
gSaveContext.buttonStatus[0] = prevStatus;
|
||||
|
||||
uint8_t triforceHuntCompleted =
|
||||
IS_RANDO &&
|
||||
gSaveContext.triforcePiecesCollected == Randomizer_GetSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) &&
|
||||
|
||||
@@ -277,6 +277,11 @@ void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 h
|
||||
bg->b.imagePal = 0;
|
||||
bg->b.imageFlip = CVarGetInteger("gMirroredWorld", 0) ? G_BG_FLAG_FLIPS : 0;
|
||||
|
||||
// When an alt resource exists for the background, 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((char*) source);
|
||||
|
||||
if (ResourceMgr_ResourceIsBackground((char*) source)) {
|
||||
char* blob = (char*) ResourceGetDataByName((char *) source);
|
||||
swapAndConvertJPEG(blob);
|
||||
|
||||
@@ -83,9 +83,10 @@ void Object_UpdateBank(ObjectContext* objectCtx) {
|
||||
RomFile* objectFile;
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
|
||||
for (i = 0; i < objectCtx->num; i++) {
|
||||
if (status->id < 0) {
|
||||
/*
|
||||
if (status->dmaRequest.vromAddr == 0) {
|
||||
osCreateMesgQueue(&status->loadQueue, &status->loadMsg, 1);
|
||||
objectFile = &gObjectTable[-status->id];
|
||||
@@ -96,10 +97,12 @@ void Object_UpdateBank(ObjectContext* objectCtx) {
|
||||
} else if (!osRecvMesg(&status->loadQueue, NULL, OS_MESG_NOBLOCK)) {
|
||||
status->id = -status->id;
|
||||
}
|
||||
*/
|
||||
status->id = -status->id;
|
||||
}
|
||||
status++;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
s32 Object_GetIndex(ObjectContext* objectCtx, s16 objectId) {
|
||||
|
||||
+60
-57
@@ -59,65 +59,68 @@ void Sram_OpenSave() {
|
||||
|
||||
Save_LoadFile();
|
||||
|
||||
if (!CVarGetInteger("gRememberSaveLocation", 0) || gSaveContext.savedSceneNum == SCENE_FAIRYS_FOUNTAIN ||
|
||||
gSaveContext.savedSceneNum == SCENE_GROTTOS) {
|
||||
switch (gSaveContext.savedSceneNum) {
|
||||
case SCENE_DEKU_TREE:
|
||||
case SCENE_DODONGOS_CAVERN:
|
||||
case SCENE_JABU_JABU:
|
||||
case SCENE_FOREST_TEMPLE:
|
||||
case SCENE_FIRE_TEMPLE:
|
||||
case SCENE_WATER_TEMPLE:
|
||||
case SCENE_SPIRIT_TEMPLE:
|
||||
case SCENE_SHADOW_TEMPLE:
|
||||
case SCENE_BOTTOM_OF_THE_WELL:
|
||||
case SCENE_ICE_CAVERN:
|
||||
case SCENE_GANONS_TOWER:
|
||||
case SCENE_GERUDO_TRAINING_GROUND:
|
||||
case SCENE_THIEVES_HIDEOUT:
|
||||
case SCENE_INSIDE_GANONS_CASTLE:
|
||||
gSaveContext.entranceIndex = dungeonEntrances[gSaveContext.savedSceneNum];
|
||||
break;
|
||||
case SCENE_DEKU_TREE_BOSS:
|
||||
gSaveContext.entranceIndex = 0;
|
||||
break;
|
||||
case SCENE_DODONGOS_CAVERN_BOSS:
|
||||
gSaveContext.entranceIndex = 4;
|
||||
break;
|
||||
case SCENE_JABU_JABU_BOSS:
|
||||
gSaveContext.entranceIndex = 0x28;
|
||||
break;
|
||||
case SCENE_FOREST_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x169;
|
||||
break;
|
||||
case SCENE_FIRE_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x165;
|
||||
break;
|
||||
case SCENE_WATER_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x10;
|
||||
break;
|
||||
case SCENE_SPIRIT_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x82;
|
||||
break;
|
||||
case SCENE_SHADOW_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x37;
|
||||
break;
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR:
|
||||
case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE:
|
||||
case SCENE_GANONDORF_BOSS:
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR:
|
||||
case SCENE_GANON_BOSS:
|
||||
gSaveContext.entranceIndex = 0x41B;
|
||||
break;
|
||||
switch (gSaveContext.savedSceneNum) {
|
||||
case SCENE_DEKU_TREE:
|
||||
case SCENE_DODONGOS_CAVERN:
|
||||
case SCENE_JABU_JABU:
|
||||
case SCENE_FOREST_TEMPLE:
|
||||
case SCENE_FIRE_TEMPLE:
|
||||
case SCENE_WATER_TEMPLE:
|
||||
case SCENE_SPIRIT_TEMPLE:
|
||||
case SCENE_SHADOW_TEMPLE:
|
||||
case SCENE_BOTTOM_OF_THE_WELL:
|
||||
case SCENE_ICE_CAVERN:
|
||||
case SCENE_GANONS_TOWER:
|
||||
case SCENE_GERUDO_TRAINING_GROUND:
|
||||
case SCENE_THIEVES_HIDEOUT:
|
||||
case SCENE_INSIDE_GANONS_CASTLE:
|
||||
gSaveContext.entranceIndex = dungeonEntrances[gSaveContext.savedSceneNum];
|
||||
break;
|
||||
case SCENE_DEKU_TREE_BOSS:
|
||||
gSaveContext.entranceIndex = 0;
|
||||
break;
|
||||
case SCENE_DODONGOS_CAVERN_BOSS:
|
||||
gSaveContext.entranceIndex = 4;
|
||||
break;
|
||||
case SCENE_JABU_JABU_BOSS:
|
||||
gSaveContext.entranceIndex = 0x28;
|
||||
break;
|
||||
case SCENE_FOREST_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x169;
|
||||
break;
|
||||
case SCENE_FIRE_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x165;
|
||||
break;
|
||||
case SCENE_WATER_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x10;
|
||||
break;
|
||||
case SCENE_SPIRIT_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x82;
|
||||
break;
|
||||
case SCENE_SHADOW_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x37;
|
||||
break;
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR:
|
||||
case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE:
|
||||
case SCENE_GANONDORF_BOSS:
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR:
|
||||
case SCENE_GANON_BOSS:
|
||||
gSaveContext.entranceIndex = 0x41B;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (gSaveContext.savedSceneNum != SCENE_LINKS_HOUSE) {
|
||||
gSaveContext.entranceIndex = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? 0xBB : 0x5F4;
|
||||
} else {
|
||||
gSaveContext.entranceIndex = 0xBB;
|
||||
}
|
||||
default:
|
||||
// Use the saved entrance value with remember save location, except when in grottos/fairy fountains
|
||||
if (CVarGetInteger("gRememberSaveLocation", 0) && gSaveContext.savedSceneNum != SCENE_FAIRYS_FOUNTAIN &&
|
||||
gSaveContext.savedSceneNum != SCENE_GROTTOS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gSaveContext.savedSceneNum != SCENE_LINKS_HOUSE) {
|
||||
gSaveContext.entranceIndex = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? 0xBB : 0x5F4;
|
||||
} else {
|
||||
gSaveContext.entranceIndex = 0xBB;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex);
|
||||
|
||||
@@ -417,7 +417,12 @@ void func_800AEFC8(SkyboxContext* skyboxCtx, s16 skyboxId) {
|
||||
s32 j;
|
||||
s32 phi_s3 = 0;
|
||||
|
||||
if (skyboxId == SKYBOX_BAZAAR || (skyboxId > SKYBOX_HOUSE_KAKARIKO && skyboxId <= SKYBOX_BOMBCHU_SHOP)) {
|
||||
//! @bug All shops only provide 2 faces for their sky box. Mask shop is missing from the condition
|
||||
// meaning that the Mask shop will calculate 4 faces
|
||||
// This effect is not noticed as the faces are behind the camera, but will cause a crash in SoH.
|
||||
// SOH General: We have added the Mask shop to this check so only the 2 expected faces are calculated.
|
||||
if (skyboxId == SKYBOX_BAZAAR || skyboxId == SKYBOX_HAPPY_MASK_SHOP ||
|
||||
(skyboxId >= SKYBOX_KOKIRI_SHOP && skyboxId <= SKYBOX_BOMBCHU_SHOP)) {
|
||||
for (j = 0, i = 0; i < 2; i++, j += 2) {
|
||||
phi_s3 = func_800ADBB0(skyboxCtx, skyboxCtx->roomVtx, phi_s3, D_8012AEBC[i].unk_0, D_8012AEBC[i].unk_4,
|
||||
D_8012AEBC[i].unk_8, D_8012AEBC[i].unk_C, D_8012AEBC[i].unk_10, i, j);
|
||||
|
||||
@@ -59,8 +59,14 @@ void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyb
|
||||
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[2]);
|
||||
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[3]);
|
||||
|
||||
if (skyboxId != SKYBOX_BAZAAR) {
|
||||
if (skyboxId <= SKYBOX_HOUSE_KAKARIKO || skyboxId > SKYBOX_BOMBCHU_SHOP) {
|
||||
//! @bug All shops only provide 2 faces for their sky box. Mask shop is missing from the condition
|
||||
// meaning that the Mask shop will render the previously loaded sky box values, or uninitialized data.
|
||||
// This effect is not noticed as the faces are behind the camera, but will cause a crash in SoH.
|
||||
// SOH General: We have added the Mask shop to this check so only the 2 expected faces are rendered.
|
||||
if (skyboxId != SKYBOX_BAZAAR && skyboxId != SKYBOX_HAPPY_MASK_SHOP) {
|
||||
if (skyboxId < SKYBOX_KOKIRI_SHOP || skyboxId > SKYBOX_BOMBCHU_SHOP) {
|
||||
// Skip remaining faces for most shop skyboxes
|
||||
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[2]);
|
||||
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[4]);
|
||||
|
||||
@@ -5,9 +5,17 @@
|
||||
#include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#include <stdlib.h> // malloc
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
#define LAVA_TEX_WIDTH 32
|
||||
#define LAVA_TEX_HEIGHT 64
|
||||
#define LAVA_TEX_SIZE 2048
|
||||
|
||||
void BossDodongo_Init(Actor* thisx, PlayState* play);
|
||||
void BossDodongo_Destroy(Actor* thisx, PlayState* play);
|
||||
void BossDodongo_Update(Actor* thisx, PlayState* play);
|
||||
@@ -57,7 +65,14 @@ static u8 sMaskTex16x32[16 * 32] = { { 0 } };
|
||||
static u8 sMaskTex32x16[32 * 16] = { { 0 } };
|
||||
static u8 sMaskTex8x8[8 * 8] = { { 0 } };
|
||||
static u8 sMaskTex8x32[8 * 32] = { { 0 } };
|
||||
static u8 sMaskTexLava[32 * 64] = { { 0 } };
|
||||
static u8 sMaskTexLava[LAVA_TEX_WIDTH * LAVA_TEX_HEIGHT] = { { 0 } };
|
||||
|
||||
static u32* sLavaFloorModifiedTexRaw = NULL;
|
||||
static u32* sLavaWavyTexRaw = NULL;
|
||||
static u16 sLavaFloorModifiedTex[LAVA_TEX_SIZE];
|
||||
static u16 sLavaWavyTex[LAVA_TEX_SIZE];
|
||||
|
||||
static u8 hasRegisteredBlendedHook = 0;
|
||||
|
||||
static InitChainEntry sInitChain[] = {
|
||||
ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE),
|
||||
@@ -66,6 +81,93 @@ static InitChainEntry sInitChain[] = {
|
||||
ICHAIN_F32(targetArrowOffset, 8200.0f, ICHAIN_STOP),
|
||||
};
|
||||
|
||||
void BossDodongo_RegisterBlendedLavaTextureUpdate() {
|
||||
// Not in scene so there is nothing to do
|
||||
if (gPlayState == NULL || gPlayState->sceneNum != SCENE_DODONGOS_CAVERN_BOSS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Free old textures
|
||||
if (sLavaFloorModifiedTexRaw != NULL) {
|
||||
free(sLavaFloorModifiedTexRaw);
|
||||
sLavaFloorModifiedTexRaw = NULL;
|
||||
}
|
||||
if (sLavaWavyTexRaw != NULL) {
|
||||
free(sLavaWavyTexRaw);
|
||||
sLavaWavyTexRaw = NULL;
|
||||
}
|
||||
|
||||
// Unload original textures to bypass cache result for lookups
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorLavaTex);
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorRockTex);
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(gDodongosCavernBossLavaFloorTex);
|
||||
|
||||
// When the texture is HD (raw) we need to work with u32 values for RGBA32
|
||||
// Otherwise the original asset is u16 for RGBA16
|
||||
if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) {
|
||||
u32* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
|
||||
size_t lavaSize = ResourceGetSizeByName(sLavaFloorLavaTex);
|
||||
size_t floorSize = ResourceGetSizeByName(gDodongosCavernBossLavaFloorTex);
|
||||
size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex);
|
||||
|
||||
// If the sizes don't match, then don't bother with the blended effect to avoid crashing
|
||||
if (floorSize != lavaSize || floorSize != rockSize) {
|
||||
uint8_t maskVal = !!Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num);
|
||||
|
||||
if (sMaskTexLava[0] != maskVal) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = maskVal;
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, NULL);
|
||||
Gfx_TextureCacheDelete(sMaskTexLava);
|
||||
return;
|
||||
}
|
||||
|
||||
sLavaFloorModifiedTexRaw = malloc(lavaSize);
|
||||
sLavaWavyTexRaw = malloc(floorSize);
|
||||
|
||||
memcpy(sLavaFloorModifiedTexRaw, lavaTex, lavaSize);
|
||||
|
||||
// When KD is dead, just immediately copy the rock texture
|
||||
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
|
||||
u32* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
memcpy(sLavaFloorModifiedTexRaw, rockTex, rockSize);
|
||||
}
|
||||
|
||||
memcpy(sLavaWavyTexRaw, sLavaFloorModifiedTexRaw, floorSize);
|
||||
|
||||
// Register the blended effect for the raw texture
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTexRaw);
|
||||
} else {
|
||||
// When KD is dead, just immediately copy the rock texture
|
||||
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
|
||||
u16* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
memcpy(sLavaFloorModifiedTex, rockTex, sizeof(sLavaFloorModifiedTex));
|
||||
} else {
|
||||
u16* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
|
||||
memcpy(sLavaFloorModifiedTex, lavaTex, sizeof(sLavaFloorModifiedTex));
|
||||
}
|
||||
|
||||
// Register the blended effect for the non-raw texture
|
||||
memcpy(sLavaWavyTex, sLavaFloorModifiedTex, sizeof(sLavaWavyTex));
|
||||
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTex);
|
||||
}
|
||||
|
||||
// Set all true for the lava as it will always replace the scene texture
|
||||
if (sMaskTexLava[0] == 0) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_TextureCacheDelete(sMaskTexLava);
|
||||
Gfx_TextureCacheDelete(sLavaWavyTex);
|
||||
Gfx_TextureCacheDelete(sLavaFloorModifiedTex);
|
||||
}
|
||||
|
||||
void func_808C12C4(u8* arg1, s16 arg2) {
|
||||
if (arg2[arg1] != 0) {
|
||||
sMaskTex8x16[arg2 / 2] = 1;
|
||||
@@ -86,12 +188,54 @@ void func_808C12C4(u8* arg1, s16 arg2) {
|
||||
}
|
||||
}
|
||||
|
||||
void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
arg0 = GetResourceDataByNameHandlingMQ(arg0);
|
||||
floorTex = ResourceGetDataByName(floorTex);
|
||||
// Same as func_808C1554 but works with u32 values for RGBA32 raw textures
|
||||
void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
// Raw lava not registered, so abort the wave modification
|
||||
if (sLavaWavyTexRaw == NULL || sLavaFloorModifiedTexRaw == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
u16* temp_s3 = SEGMENTED_TO_VIRTUAL(arg0);
|
||||
u16* temp_s1 = SEGMENTED_TO_VIRTUAL(floorTex);
|
||||
u16 width = ResourceGetTexWidthByName(arg0);
|
||||
s32 size = ResourceGetTexHeightByName(arg0) * width;
|
||||
|
||||
u32* temp_s3 = sLavaWavyTexRaw;
|
||||
u32* temp_s1 = sLavaFloorModifiedTexRaw;
|
||||
s32 i;
|
||||
s32 i2;
|
||||
u32* sp54 = malloc(size * sizeof(u32)); // Match the size for lava floor tex
|
||||
s32 temp;
|
||||
s32 temp2;
|
||||
|
||||
// Multiplier is used to try to scale the wavy effect to match the scale of the HD texture
|
||||
// Applying sqrt(multiplier) to arg3 is to control how many pixels move left/right for the selected row
|
||||
// Applying to arg2 and M_PI help to space out the wave effect
|
||||
// It's not perfect but close enough
|
||||
u16 multiplier = width / LAVA_TEX_WIDTH;
|
||||
|
||||
for (i = 0; i < size; i += width) {
|
||||
temp = sinf((((i / width) + (s32)(((arg2 * multiplier) * 50.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier));
|
||||
for (i2 = 0; i2 < width; i2++) {
|
||||
sp54[i + ((temp + i2) & (width - 1))] = temp_s1[i + i2];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < width; i++) {
|
||||
temp = sinf(((i + (s32)(((arg2 * multiplier) * 80.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier));
|
||||
temp *= width;
|
||||
for (i2 = 0; i2 < size; i2 += width) {
|
||||
temp2 = (temp + i2) & (size - 1);
|
||||
temp_s3[i + temp2] = sp54[i + i2];
|
||||
}
|
||||
}
|
||||
|
||||
free(sp54);
|
||||
Gfx_TextureCacheDelete(sLavaWavyTexRaw);
|
||||
}
|
||||
|
||||
// Modified to support CPU modified texture with the resource system
|
||||
// Used for the original non-raw asset working with u16 values
|
||||
void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
u16* temp_s3 = sLavaWavyTex;
|
||||
u16* temp_s1 = sLavaFloorModifiedTex;
|
||||
s16 i;
|
||||
s16 i2;
|
||||
u16 sp54[2048];
|
||||
@@ -112,6 +256,8 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
temp_s3[i + temp2] = sp54[i + i2];
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_TextureCacheDelete(sLavaWavyTex);
|
||||
}
|
||||
|
||||
void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) {
|
||||
@@ -187,27 +333,21 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items);
|
||||
|
||||
if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead
|
||||
u16* LavaFloorTex = ResourceGetDataByName(gDodongosCavernBossLavaFloorTex);
|
||||
u16* LavaFloorRockTex = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
temp_s1_3 = SEGMENTED_TO_VIRTUAL(LavaFloorTex);
|
||||
temp_s2 = SEGMENTED_TO_VIRTUAL(LavaFloorRockTex);
|
||||
// SOH [General]
|
||||
// Applying the "cooled off" lava rock CPU modified texture for re-visiting the scene
|
||||
// is now handled by BossDodongo_RegisterBlendedLavaTextureUpdate below
|
||||
|
||||
Actor_Kill(&this->actor);
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f,
|
||||
-3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, 0x6000, true);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0, true);
|
||||
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 1;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
|
||||
|
||||
// #region SOH [General]
|
||||
// Init mask values for all KD blended textures
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTex8x16); i++) {
|
||||
sMaskTex8x16[i] = 0;
|
||||
}
|
||||
@@ -223,6 +363,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTex32x16); i++) {
|
||||
sMaskTex32x16[i] = 0;
|
||||
}
|
||||
|
||||
// Register all blended textures
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015890, sMaskTex8x16, NULL);
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_017210, sMaskTex8x32, NULL);
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015D90, sMaskTex16x16, NULL);
|
||||
@@ -234,10 +376,21 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL);
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL);
|
||||
|
||||
// OTRTODO: This is causing OOB memory reads with HD assets
|
||||
// commenting this out means the lava will stay lava even after beating king d
|
||||
//
|
||||
// Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaFloorRockTex);
|
||||
// Clear cache for masks
|
||||
Gfx_TextureCacheDelete(sMaskTex8x16);
|
||||
Gfx_TextureCacheDelete(sMaskTex8x32);
|
||||
Gfx_TextureCacheDelete(sMaskTex16x16);
|
||||
Gfx_TextureCacheDelete(sMaskTex16x32);
|
||||
Gfx_TextureCacheDelete(sMaskTex32x16);
|
||||
|
||||
BossDodongo_RegisterBlendedLavaTextureUpdate();
|
||||
|
||||
// Register alt listener to update the blended lava for the replacement texture based on alt path
|
||||
if (!hasRegisteredBlendedHook) {
|
||||
GameInteractor_RegisterOnAssetAltChange(BossDodongo_RegisterBlendedLavaTextureUpdate);
|
||||
hasRegisteredBlendedHook = 1;
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
|
||||
void BossDodongo_Destroy(Actor* thisx, PlayState* play) {
|
||||
@@ -1014,21 +1167,73 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO The lave floor bubbles with an effect that modifies the texture. This needs to be recreated shader-side.
|
||||
//func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
|
||||
// The lava bubbles with a wavy effect as a CPU modified texture
|
||||
// This has been done by maintaining copied/modified texture values in the actor code
|
||||
// The "cooling off" effect for the lava is pre-applied to the lava texture before applying
|
||||
// the wavy effect. Since this is two effects and closely related to the actor, I've opted
|
||||
// to handle them here rather than as a shader effect.
|
||||
//
|
||||
// Apply the corresponding wavy effect based on the texture being raw or not
|
||||
if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) {
|
||||
func_808C1554_Raw(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
|
||||
} else {
|
||||
func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the "cooling off" effect for the lava
|
||||
if (this->unk_1C6 != 0) {
|
||||
u16* ptr1 = ResourceGetDataByName(sLavaFloorLavaTex);
|
||||
u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
s16 i2;
|
||||
// Similar to above, the cooling off effect is a CPU modified texture effect
|
||||
// Apply corresponding to the texture being raw or not
|
||||
if (ResourceMgr_TexIsRaw(sLavaFloorRockTex)) {
|
||||
u32* ptr1 = sLavaFloorModifiedTexRaw;
|
||||
u32* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
u16 width = ResourceGetTexWidthByName(sLavaFloorRockTex);
|
||||
u16 height = ResourceGetTexHeightByName(sLavaFloorRockTex);
|
||||
s16 i2;
|
||||
|
||||
for (i2 = 0; i2 < 20; i2++) {
|
||||
s16 new_var = this->unk_1C2 & 0x7FF;
|
||||
// Get the scale based on the original texture size
|
||||
u16 widthScale = width / LAVA_TEX_WIDTH;
|
||||
u16 heightScale = height / LAVA_TEX_HEIGHT;
|
||||
u32 size = width * height;
|
||||
|
||||
sMaskTexLava[new_var] = 1;
|
||||
this->unk_1C2 += 37;
|
||||
for (i2 = 0; i2 < 20; i2++) {
|
||||
s16 new_var = this->unk_1C2 & (LAVA_TEX_SIZE - 1);
|
||||
|
||||
// Raw lava must be registered, otherwise skip the effect for incompatible texture pack
|
||||
// and instead set the mask to simulate the lava disappearing by turning black
|
||||
if (sLavaFloorModifiedTexRaw != NULL) {
|
||||
// Compute the index to a scaled position (scaling pseudo x,y as a 1D value)
|
||||
s32 indexStart =
|
||||
((new_var % LAVA_TEX_WIDTH) * widthScale) + ((new_var / LAVA_TEX_WIDTH) * width * heightScale);
|
||||
|
||||
// From the starting index, apply extra pixels right/down based on the scale
|
||||
for (size_t j = 0; j < heightScale; j++) {
|
||||
for (size_t i3 = 0; i3 < widthScale; i3++) {
|
||||
s32 scaledIndex = (indexStart + i3 + (j * width)) & (size - 1);
|
||||
ptr1[scaledIndex] = ptr2[scaledIndex];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sMaskTexLava[new_var] = 1;
|
||||
Gfx_TextureCacheDelete(sMaskTexLava);
|
||||
}
|
||||
|
||||
this->unk_1C2 += 37;
|
||||
}
|
||||
} else {
|
||||
u16* ptr1 = sLavaFloorModifiedTex;
|
||||
u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
s16 i2;
|
||||
|
||||
for (i2 = 0; i2 < 20; i2++) {
|
||||
s16 new_var = this->unk_1C2 & 0x7FF;
|
||||
|
||||
ptr1[new_var] = ptr2[new_var];
|
||||
this->unk_1C2 += 37;
|
||||
}
|
||||
}
|
||||
|
||||
Math_SmoothStepToF(&this->unk_224, 0.0f, 1.0f, 0.01f, 0.0f);
|
||||
}
|
||||
|
||||
@@ -1151,10 +1356,6 @@ void BossDodongo_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTex32x16);
|
||||
}
|
||||
|
||||
if (this->unk_1C6 != 0) {
|
||||
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTexLava);
|
||||
}
|
||||
|
||||
if ((this->unk_1C0 >= 2) && (this->unk_1C0 & 1)) {
|
||||
POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 255, 255, 0, 900, 1099);
|
||||
} else {
|
||||
|
||||
@@ -192,11 +192,11 @@ void DemoGj_Explode(DemoGj* this, PlayState* play, Vec3f* initialPos, Vec3f* dir
|
||||
phi_s0 = 0x21;
|
||||
}
|
||||
|
||||
Gfx* gfx = ResourceMgr_LoadGfxByName(gGanonRubbleDL);
|
||||
|
||||
// SoH [Port] Changed from &gGanonsCastleRubbleAroundArenaDL[28] to gGanonRubbleDL as it seems this was an error in the original rom/decomp
|
||||
// Other calls to EffectSsKakera_Spawn with OBJECT_GEFF use gGanonRubbleDL, so this change is to match that
|
||||
EffectSsKakera_Spawn(play, &explosionPos, &velocity, initialPos, -200, phi_s0, 10, 10, 0,
|
||||
Rand_ZeroOne() * 20.0f + 20.0f, 20, 300, (s32)(Rand_ZeroOne() * 30.0f) + 30, -1,
|
||||
OBJECT_GEFF, gfx);
|
||||
OBJECT_GEFF, gGanonRubbleDL);
|
||||
|
||||
theta += 0x2AAA;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ static InitChainEntry sInitChain[] = {
|
||||
};
|
||||
|
||||
static UNK_TYPE sUnused;
|
||||
GetItemEntry sItem;
|
||||
|
||||
Gfx gSkullTreasureChestChestSideAndLidDL[116] = {0};
|
||||
Gfx gGoldTreasureChestChestSideAndLidDL[116] = {0};
|
||||
@@ -88,6 +87,7 @@ Gfx gKeyTreasureChestChestFrontDL[128] = {0};
|
||||
Gfx gChristmasRedTreasureChestChestFrontDL[128] = {0};
|
||||
Gfx gChristmasGreenTreasureChestChestFrontDL[128] = {0};
|
||||
u8 hasCreatedRandoChestTextures = 0;
|
||||
u8 hasCustomChestDLs = 0;
|
||||
u8 hasChristmasChestTexturesAvailable = 0;
|
||||
|
||||
void EnBox_SetupAction(EnBox* this, EnBoxActionFunc actionFunc) {
|
||||
@@ -471,7 +471,7 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) {
|
||||
func_8002DBD0(&this->dyna.actor, &sp4C, &player->actor.world.pos);
|
||||
if (sp4C.z > -50.0f && sp4C.z < 0.0f && fabsf(sp4C.y) < 10.0f && fabsf(sp4C.x) < 20.0f &&
|
||||
Player_IsFacingActor(&this->dyna.actor, 0x3000, play)) {
|
||||
sItem = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F);
|
||||
GetItemEntry sItem = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F);
|
||||
GetItemEntry blueRupee = ItemTable_RetrieveEntry(MOD_NONE, GI_RUPEE_BLUE);
|
||||
|
||||
// RANDOTODO treasure chest game rando
|
||||
@@ -627,7 +627,7 @@ void EnBox_Update(Actor* thisx, PlayState* play) {
|
||||
}
|
||||
|
||||
if (((!IS_RANDO && ((this->dyna.actor.params >> 5 & 0x7F) == 0x7C)) ||
|
||||
(IS_RANDO && ABS(sItem.getItemId) == RG_ICE_TRAP)) &&
|
||||
(IS_RANDO && this->getItemEntry.getItemId == RG_ICE_TRAP)) &&
|
||||
this->actionFunc == EnBox_Open && this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100) {
|
||||
if (!CVarGetInteger("gAddTraps.enabled", 0)) {
|
||||
EnBox_SpawnIceSmoke(this, play);
|
||||
@@ -690,7 +690,7 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) {
|
||||
}
|
||||
|
||||
// Change texture
|
||||
if (!isVanilla && (csmc == CSMC_BOTH || csmc == CSMC_TEXTURE)) {
|
||||
if (!isVanilla && hasCreatedRandoChestTextures && !hasCustomChestDLs && (csmc == CSMC_BOTH || csmc == CSMC_TEXTURE)) {
|
||||
switch (getItemCategory) {
|
||||
case ITEM_CATEGORY_MAJOR:
|
||||
this->boxBodyDL = gGoldTreasureChestChestFrontDL;
|
||||
@@ -725,7 +725,7 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
if (CVarGetInteger("gLetItSnow", 0) && hasChristmasChestTexturesAvailable) {
|
||||
if (CVarGetInteger("gLetItSnow", 0) && hasChristmasChestTexturesAvailable && hasCreatedRandoChestTextures && !hasCustomChestDLs) {
|
||||
if (this->dyna.actor.scale.x == 0.01f) {
|
||||
this->boxBodyDL = gChristmasRedTreasureChestChestFrontDL;
|
||||
this->boxLidDL = gChristmasRedTreasureChestChestSideAndLidDL;
|
||||
@@ -767,7 +767,18 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) {
|
||||
}
|
||||
|
||||
void EnBox_CreateExtraChestTextures() {
|
||||
// Don't patch textures for custom chest models, as they do not import textures the exact same way as vanilla chests
|
||||
// OTRTODO: Make it so model packs can provide a unique DL per chest type, instead of us copying the brown chest and attempting to patch
|
||||
if (ResourceMgr_FileIsCustomByName(gTreasureChestChestFrontDL) ||
|
||||
ResourceMgr_FileIsCustomByName(gTreasureChestChestSideAndLidDL)) {
|
||||
hasCustomChestDLs = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hasCustomChestDLs = 0;
|
||||
|
||||
if (hasCreatedRandoChestTextures) return;
|
||||
|
||||
Gfx gTreasureChestChestTextures[] = {
|
||||
gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, gSkullTreasureChestFrontTex),
|
||||
gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, gSkullTreasureChestSideAndTopTex),
|
||||
|
||||
@@ -101,6 +101,9 @@ void EnDntJiji_Destroy(Actor* thisx, PlayState* play) {
|
||||
}
|
||||
|
||||
void EnDntJiji_SetFlower(EnDntJiji* this, PlayState* play) {
|
||||
// SOH: Due to removed object dependencies, parent was still NULL when Init was called. In order to properly set
|
||||
// stage, redo it here now that we are a frame later.
|
||||
this->stage = (EnDntDemo*)this->actor.parent;
|
||||
if (this->actor.bgCheckFlags & 1) {
|
||||
this->flowerPos = this->actor.world.pos;
|
||||
this->actionFunc = EnDntJiji_SetupWait;
|
||||
|
||||
@@ -95,6 +95,26 @@ static AnimationInfo sAnimationInfo[] = {
|
||||
{ &gDaruniaDancingEndAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -6.0f },
|
||||
};
|
||||
|
||||
// #region SOH [Enhancement] Only animations too fast need to be slowed down, otherwise not touched
|
||||
static AnimationInfo sAnimationInfoFix[] = {
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ &gDaruniaDancingLoop1Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, -10.0f }, //
|
||||
{ &gDaruniaDancingLoop1Anim, 0.77f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // hop
|
||||
{ &gDaruniaDancingLoop2Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // from hop to spin
|
||||
{ &gDaruniaDancingLoop3Anim, 0.77f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // spin
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ &gDaruniaDancingLoop4Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // from spin to hop
|
||||
{ NULL },
|
||||
};
|
||||
// #endregion
|
||||
|
||||
void EnDu_SetupAction(EnDu* this, EnDuActionFunc actionFunc) {
|
||||
this->actionFunc = actionFunc;
|
||||
}
|
||||
@@ -255,7 +275,13 @@ void func_809FE040(EnDu* this) {
|
||||
if (this->unk_1E6 >= 8) {
|
||||
this->unk_1E6 = 0;
|
||||
}
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
// #region SOH[Enhancement]
|
||||
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1)) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, animationIndices[this->unk_1E6]);
|
||||
// #endregion
|
||||
} else {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +297,13 @@ void func_809FE104(EnDu* this) {
|
||||
if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) {
|
||||
this->unk_1E6++;
|
||||
if (this->unk_1E6 < 4) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
// #region SOH[Enhancement]
|
||||
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1) && this->unk_1E6 <= 1) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, animationIndices[this->unk_1E6]);
|
||||
// #endregion
|
||||
} else {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,7 +497,13 @@ void func_809FE890(EnDu* this, PlayState* play) {
|
||||
}
|
||||
if (csAction->action == 7 || csAction->action == 8) {
|
||||
this->unk_1E6 = 0;
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_7);
|
||||
// #region SOH[Enhancement]
|
||||
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1)) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, ENDU_ANIM_7);
|
||||
// #endregion
|
||||
} else {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_7);
|
||||
}
|
||||
}
|
||||
this->unk_1EA = csAction->action;
|
||||
if (this->unk_1EA == 7) {
|
||||
|
||||
@@ -1027,8 +1027,8 @@ void EnGirlA_BuyEvent_ObtainBombchuPack(PlayState* play, EnGirlA* this) {
|
||||
Rupees_ChangeBy(-this->basePrice);
|
||||
|
||||
// Normally, buying a bombchu pack sets a flag indicating the pack is now sold out
|
||||
// If they're in logic for rando, skip setting that flag so they can be purchased repeatedly
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC)) {
|
||||
// If we're in rando, skip setting that flag so they can be purchased repeatedly
|
||||
if (IS_RANDO) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1255,8 +1255,7 @@ void EnGirlA_InitializeItemAction(EnGirlA* this, PlayState* play) {
|
||||
this->itemGiveFunc = itemEntry->itemGiveFunc;
|
||||
this->buyEventFunc = itemEntry->buyEventFunc;
|
||||
// If chus are in logic, make the 10 pack affordable without a wallet upgrade
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) &&
|
||||
this->getItemId == GI_BOMBCHUS_10) {
|
||||
if (IS_RANDO && this->getItemId == GI_BOMBCHUS_10) {
|
||||
this->basePrice = 99;
|
||||
} else {
|
||||
this->basePrice = itemEntry->price;
|
||||
|
||||
@@ -211,6 +211,13 @@ void EnInsect_Init(Actor* thisx, PlayState* play2) {
|
||||
|
||||
func_80A7D39C(this);
|
||||
|
||||
// For bugs that aren't linked to a soil patch, we remove the "short lived" flag to prevent them from despawning
|
||||
// And exit early to not increment the "bugs dropped count"
|
||||
if (CVarGetInteger("gNoBugsDespawn", 0) && this->soilActor == NULL) {
|
||||
this->unk_314 &= ~4;
|
||||
return;
|
||||
}
|
||||
|
||||
D_80A7DEB8++;
|
||||
} else {
|
||||
rand = Rand_ZeroOne();
|
||||
@@ -394,9 +401,6 @@ void func_80A7CAD0(EnInsect* this, PlayState* play) {
|
||||
}
|
||||
|
||||
void func_80A7CBC8(EnInsect* this) {
|
||||
if (CVarGetInteger("gNoBugsDespawn", 0) != 0) {
|
||||
return;
|
||||
}
|
||||
this->unk_31A = 60;
|
||||
func_80A7BF58(this);
|
||||
this->skelAnime.playSpeed = 1.9f;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "objects/object_tite/object_tite.h"
|
||||
#include "objects/object_ik/object_ik.h"
|
||||
|
||||
#include <string.h> // strcmp
|
||||
|
||||
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
|
||||
|
||||
void EnPart_Init(Actor* thisx, PlayState* play);
|
||||
@@ -297,11 +299,11 @@ void EnPart_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, func_80ACEAC0(play->state.gfxCtx, 255, 255, 255, 180, 180, 180));
|
||||
gSPSegment(POLY_OPA_DISP++, 0x09, func_80ACEAC0(play->state.gfxCtx, 225, 205, 115, 25, 20, 0));
|
||||
gSPSegment(POLY_OPA_DISP++, 0x0A, func_80ACEAC0(play->state.gfxCtx, 225, 205, 115, 25, 20, 0));
|
||||
} else if ((thisx->params == 9) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) {
|
||||
} else if ((thisx->params == 9) && (strcmp((const char*)this->displayList, object_tite_DL_002FF0) == 0)) {
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, object_tite_Tex_001300);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x09, object_tite_Tex_001700);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x0A, object_tite_Tex_001900);
|
||||
} else if ((thisx->params == 10) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) {
|
||||
} else if ((thisx->params == 10) && (strcmp((const char*)this->displayList, object_tite_DL_002FF0) == 0)) {
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, object_tite_Tex_001B00);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x09, object_tite_Tex_001F00);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x0A, object_tite_Tex_002100);
|
||||
|
||||
@@ -821,19 +821,6 @@ void func_80AF3F20(EnRu2* this, PlayState* play) {
|
||||
void EnRu2_Draw(Actor* thisx, PlayState* play) {
|
||||
EnRu2* this = (EnRu2*)thisx;
|
||||
|
||||
// FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings.
|
||||
// Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so
|
||||
// it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the
|
||||
// earring texture loads into the same place in tmem as the brick texture, so when it comes to rendering, TEXEL1
|
||||
// uses the earring texture with diffrent clamp settings, and it displays without noticeable error. However, both
|
||||
// texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses
|
||||
// our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces
|
||||
// TEXEL1 with TEXEL0, which is most likely the original intention, and all is well.
|
||||
Gfx* gfx = ResourceMgr_LoadGfxByName(gAdultRutoHeadDL);
|
||||
Gfx patch = gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED, TEXEL0, 0,
|
||||
PRIM_LOD_FRAC, COMBINED);
|
||||
gfx[0xA2] = patch;
|
||||
|
||||
if ((this->drawConfig < 0) || (this->drawConfig >= ARRAY_COUNT(sDrawFuncs)) ||
|
||||
(sDrawFuncs[this->drawConfig] == 0)) {
|
||||
// "Draw Mode is improper!"
|
||||
|
||||
@@ -16,6 +16,7 @@ void EnSi_Init(Actor* thisx, PlayState* play);
|
||||
void EnSi_Destroy(Actor* thisx, PlayState* play);
|
||||
void EnSi_Update(Actor* thisx, PlayState* play);
|
||||
void EnSi_Draw(Actor* thisx, PlayState* play);
|
||||
void EnSi_Reset();
|
||||
|
||||
s32 func_80AFB748(EnSi* this, PlayState* play);
|
||||
void func_80AFB768(EnSi* this, PlayState* play);
|
||||
@@ -61,7 +62,7 @@ const ActorInit En_Si_InitVars = {
|
||||
(ActorFunc)EnSi_Destroy,
|
||||
(ActorFunc)EnSi_Update,
|
||||
(ActorFunc)EnSi_Draw,
|
||||
NULL,
|
||||
(ActorResetFunc)EnSi_Reset,
|
||||
};
|
||||
|
||||
void EnSi_Init(Actor* thisx, PlayState* play) {
|
||||
@@ -224,6 +225,11 @@ void EnSi_Draw(Actor* thisx, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
void EnSi_Reset() {
|
||||
textId = 0xB4;
|
||||
giveItemId = ITEM_SKULL_TOKEN;
|
||||
}
|
||||
|
||||
void Randomizer_UpdateSkullReward(EnSi* this, PlayState* play) {
|
||||
Player* player = GET_PLAYER(play);
|
||||
|
||||
|
||||
@@ -313,7 +313,9 @@ void EnSth_GiveReward(EnSth* this, PlayState* play) {
|
||||
this->actor.parent = NULL;
|
||||
EnSth_SetupAction(this, EnSth_RewardObtainedTalk);
|
||||
gSaveContext.eventChkInf[EVENTCHKINF_SKULLTULA_REWARD_INDEX] |= this->eventFlag;
|
||||
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SKULLTULA_REWARD_INDEX << 4) + sEventFlagsShift[this->actor.params]);
|
||||
if (this->eventFlag != 0) {
|
||||
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SKULLTULA_REWARD_INDEX << 4) + sEventFlagsShift[this->actor.params]);
|
||||
}
|
||||
} else {
|
||||
EnSth_GivePlayerItem(this, play);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h"
|
||||
#include "objects/object_ossan/object_ossan.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_NO_LOCKON)
|
||||
|
||||
@@ -371,7 +372,8 @@ void EnSyatekiMan_EndGame(EnSyatekiMan* this, PlayState* play) {
|
||||
this->getItemId = GI_RUPEE_PURPLE;
|
||||
}
|
||||
} else {
|
||||
if(IS_RANDO && !Flags_GetTreasure(play, 0x1F)) {
|
||||
// Only give the adult rando reward when the player has a quiver
|
||||
if (IS_RANDO && !Flags_GetTreasure(play, 0x1F) && CUR_UPG_VALUE(UPG_QUIVER) > 0) {
|
||||
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_SHOOTING_GALLERY_REWARD, GI_QUIVER_50);
|
||||
this->getItemId = this->getItemEntry.getItemId;
|
||||
Flags_SetTreasure(play, 0x1F);
|
||||
@@ -448,6 +450,9 @@ void EnSyatekiMan_FinishPrize(EnSyatekiMan* this, PlayState* play) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_0D);
|
||||
} else if ((this->getItemId == GI_QUIVER_40) || (this->getItemId == GI_QUIVER_50)) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_0E);
|
||||
} else if (IS_RANDO && LINK_IS_ADULT && CUR_UPG_VALUE(UPG_QUIVER) == 0) {
|
||||
// In Rando without a quiver, display a message reminding the player to come back with a bow
|
||||
Message_StartTextbox(play, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW, NULL);
|
||||
}
|
||||
this->gameResult = SYATEKI_RESULT_NONE;
|
||||
this->actor.parent = this->tempGallery;
|
||||
|
||||
@@ -408,7 +408,7 @@ s32 EnTk_ChooseReward(EnTk* this) {
|
||||
f32 luck;
|
||||
s32 reward;
|
||||
|
||||
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, 0x1F) && this->heartPieceSpawned == 0) {
|
||||
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -624,10 +624,8 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
|
||||
|
||||
this->currentReward = EnTk_ChooseReward(this);
|
||||
|
||||
// merging in dampe tour fix seems messy, so i'm just wrapping this whole thing
|
||||
// in an n64dd check for now
|
||||
if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
|
||||
if (this->currentReward == 3) {
|
||||
if (this->currentReward == 3) {
|
||||
if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
|
||||
/*
|
||||
* Upgrade the purple rupee reward to the heart piece if this
|
||||
* is the first grand prize dig.
|
||||
@@ -635,37 +633,31 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
|
||||
if (!Flags_GetItemGetInf(ITEMGETINF_1C) && !(IS_RANDO || CVarGetInteger("gDampeWin", 0))) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_1C);
|
||||
this->currentReward = 4;
|
||||
} else if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, 0x1F) && this->heartPieceSpawned == 0) {
|
||||
} else if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
|
||||
this->currentReward = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && this->currentReward == 4) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0,
|
||||
0, 0, 0x1F06, true);
|
||||
this->heartPieceSpawned = 1;
|
||||
} else {
|
||||
Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
|
||||
/*
|
||||
* Upgrade the purple rupee reward to the heart piece if this
|
||||
* is the first grand prize dig.
|
||||
*/
|
||||
// If vanilla itemGetInf flag is not set, it's impossible for the new flag to be set, so return true.
|
||||
// Otherwise if the gGravediggingTourFix is enabled and the new flag hasn't been set, return true.
|
||||
// If true, spawn the heart piece and set the vanilla itemGetInf flag and new temp clear flag.
|
||||
if (!heartPieceSpawned &&
|
||||
(!(gSaveContext.itemGetInf[1] & ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE) ||
|
||||
CVarGetInteger("gGravediggingTourFix", 0) &&
|
||||
!Flags_GetCollectible(play, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE))) {
|
||||
this->currentReward = 4;
|
||||
gSaveContext.itemGetInf[1] |= ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE;
|
||||
heartPieceSpawned = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_RANDO && this->currentReward == 4) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0, 0, 0, 0x1906, true);
|
||||
this->heartPieceSpawned = 1;
|
||||
} else {
|
||||
if (this->currentReward == 3) {
|
||||
/*
|
||||
* Upgrade the purple rupee reward to the heart piece if this
|
||||
* is the first grand prize dig.
|
||||
*/
|
||||
// If vanilla itemGetInf flag is not set, it's impossible for the new flag to be set, so return true.
|
||||
// Otherwise if the gGravediggingTourFix is enabled and the new flag hasn't been set, return true.
|
||||
// If true, spawn the heart piece and set the vanilla itemGetInf flag and new temp clear flag.
|
||||
if (!heartPieceSpawned &&
|
||||
(!(gSaveContext.itemGetInf[1] & ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE) ||
|
||||
CVarGetInteger("gGravediggingTourFix", 0) &&
|
||||
!Flags_GetCollectible(play, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE))) {
|
||||
this->currentReward = 4;
|
||||
gSaveContext.itemGetInf[1] |= ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE;
|
||||
heartPieceSpawned = true;
|
||||
}
|
||||
}
|
||||
|
||||
EnItem00* reward = Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
|
||||
if (this->currentReward == 4) {
|
||||
reward->collectibleFlag = COLLECTFLAG_GRAVEDIGGING_HEART_PIECE;
|
||||
|
||||
@@ -10101,7 +10101,8 @@ void func_80847BA0(PlayState* play, Player* this) {
|
||||
|
||||
D_808535F0 = func_80041DB8(&play->colCtx, this->actor.wallPoly, this->actor.wallBgId);
|
||||
|
||||
if (CVarGetInteger("gFixVineFall", 0)) {
|
||||
// conflicts arise from these two being enabled at once, and with ClimbEverything on, FixVineFall is redundant anyway
|
||||
if (CVarGetInteger("gFixVineFall", 0) && !CVarGetInteger("gClimbEverything", 0)) {
|
||||
/* This fixes the "started climbing a wall and then immediately fell off" bug.
|
||||
* The main idea is if a climbing wall is detected, double-check that it will
|
||||
* still be valid once climbing begins by doing a second raycast with a small
|
||||
@@ -10628,7 +10629,14 @@ void Player_UseTunicBoots(Player* this, PlayState* play) {
|
||||
s32 i;
|
||||
s32 item;
|
||||
s32 actionParam;
|
||||
if (!(this->stateFlags1 & PLAYER_STATE1_INPUT_DISABLED || this->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || this->stateFlags1 & PLAYER_STATE1_IN_CUTSCENE || this->stateFlags1 & PLAYER_STATE1_TEXT_ON_SCREEN || this->stateFlags2 & PLAYER_STATE2_OCARINA_PLAYING)) {
|
||||
if (!(
|
||||
this->stateFlags1 & PLAYER_STATE1_INPUT_DISABLED ||
|
||||
this->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS ||
|
||||
this->stateFlags1 & PLAYER_STATE1_IN_CUTSCENE ||
|
||||
this->stateFlags1 & PLAYER_STATE1_TEXT_ON_SCREEN ||
|
||||
this->stateFlags1 & PLAYER_STATE1_DEAD ||
|
||||
this->stateFlags2 & PLAYER_STATE2_OCARINA_PLAYING
|
||||
)) {
|
||||
for (i = 0; i < ARRAY_COUNT(D_80854388); i++) {
|
||||
if (CHECK_BTN_ALL(sControlInput->press.button, D_80854388[i])) {
|
||||
break;
|
||||
@@ -15169,6 +15177,10 @@ void func_80852C50(PlayState* play, Player* this, CsCmdActorAction* arg2) {
|
||||
|
||||
sp24 = D_808547C4[this->unk_446];
|
||||
func_80852B4C(play, this, linkCsAction, &D_80854E50[ABS(sp24)]);
|
||||
|
||||
if (CVarGetInteger("gFixEyesOpenWhileSleeping", 0) && (play->csCtx.linkAction->action == 28 || play->csCtx.linkAction->action == 29)) {
|
||||
this->skelAnime.jointTable[22].x = 8;
|
||||
}
|
||||
}
|
||||
|
||||
void func_80852E14(Player* this, PlayState* play) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "file_choose.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "textures/title_static/title_static.h"
|
||||
#include "textures/parameter_static/parameter_static.h"
|
||||
@@ -1024,23 +1025,23 @@ void FileChoose_UpdateRandomizer() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SpoilerFileExists(CVarGetString("gSpoilerLog", ""))) {
|
||||
if (!SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && !CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
|
||||
CVarSetString("gSpoilerLog", "");
|
||||
fileSelectSpoilerFileLoaded = false;
|
||||
}
|
||||
|
||||
if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) ||
|
||||
if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) ||
|
||||
(!fileSelectSpoilerFileLoaded && SpoilerFileExists(CVarGetString("gSpoilerLog", "")))) {
|
||||
if (CVarGetInteger("gNewFileDropped", 0) != 0) {
|
||||
CVarSetString("gSpoilerLog", CVarGetString("gDroppedFile", "None"));
|
||||
if (CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) {
|
||||
CVarSetString("gSpoilerLog", CVarGetString("gRandomizerDroppedFile", "None"));
|
||||
}
|
||||
bool silent = true;
|
||||
if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) {
|
||||
if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) {
|
||||
silent = false;
|
||||
}
|
||||
CVarSetInteger("gNewSeedGenerated", 0);
|
||||
CVarSetInteger("gNewFileDropped", 0);
|
||||
CVarSetString("gDroppedFile", "");
|
||||
CVarSetInteger("gRandomizerNewFileDropped", 0);
|
||||
CVarSetString("gRandomizerDroppedFile", "");
|
||||
fileSelectSpoilerFileLoaded = false;
|
||||
const char* fileLoc = CVarGetString("gSpoilerLog", "");
|
||||
Randomizer_LoadSettings(fileLoc);
|
||||
@@ -1051,6 +1052,10 @@ void FileChoose_UpdateRandomizer() {
|
||||
Randomizer_LoadMasterQuestDungeons(fileLoc);
|
||||
Randomizer_LoadEntranceOverrides(fileLoc, silent);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
|
||||
if (SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
|
||||
remove(fileLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3031,11 +3036,7 @@ void FileChoose_LoadGame(GameState* thisx) {
|
||||
Entrance_Init();
|
||||
|
||||
// Handle randomized spawn positions after the save context has been setup from load
|
||||
// When remeber save location is on, set save warp if the save was in an a grotto, or
|
||||
// the entrance index is -1 from shuffle overwarld spawn
|
||||
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES) && ((!CVarGetInteger("gRememberSaveLocation", 0) ||
|
||||
gSaveContext.savedSceneNum == SCENE_FAIRYS_FOUNTAIN || gSaveContext.savedSceneNum == SCENE_GROTTOS) ||
|
||||
(CVarGetInteger("gRememberSaveLocation", 0) && Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_SPAWNS) && gSaveContext.entranceIndex == -1))) {
|
||||
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
|
||||
Entrance_SetSavewarpEntrance();
|
||||
}
|
||||
}
|
||||
@@ -3686,4 +3687,7 @@ void FileChoose_Init(GameState* thisx) {
|
||||
Font_LoadOrderedFont(&this->font);
|
||||
Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA);
|
||||
func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1);
|
||||
|
||||
// Originally this was only set when transitioning from the title screen, but gSkipLogoTitle skips that process so we're ensuring it's set here
|
||||
gSaveContext.gameMode = GAMEMODE_FILE_SELECT;
|
||||
}
|
||||
|
||||
@@ -456,7 +456,6 @@ void FileChoose_DrawNameEntry(GameState* thisx) {
|
||||
this->prevConfigMode = CM_MAIN_MENU;
|
||||
this->configMode = CM_NAME_ENTRY_TO_MAIN;
|
||||
CVarSetInteger("gOnFileSelectNameEntry", 0);
|
||||
CVarSetInteger("gNewFileDropped", 0);
|
||||
this->nameBoxAlpha[this->buttonIndex] = this->nameAlpha[this->buttonIndex] = 200;
|
||||
this->connectorAlpha[this->buttonIndex] = 255;
|
||||
func_800AA000(300.0f, 0xB4, 0x14, 0x64);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user