Compare commits

...

53 Commits

Author SHA1 Message Date
Garrett Cox 612da023f0 Bump version to MacReady Foxtrot 8.0.5 (#3982) 2024-02-28 22:46:55 -05:00
Malkierian 368a9015ac Add Unix timestamp to renamed corrupted file to prevent trying to copy over existing file. (#3984) 2024-02-28 22:46:32 -05:00
Malkierian ed9cb1dfd2 Fix CVar evaluation for scummed checks being hidden. (#3985) 2024-02-28 22:45:16 -05:00
Archez fb6ea42560 prevent remember save location in dungeons/boss rooms (#3983) 2024-02-28 22:33:51 -05:00
Malkierian b26f2b21da [UX Improvement] Catch save loading errors and notify user (#3979)
* Add `SohModalWindow` and `SohModal`. Runs as window, always "visible", but not drawing if no popups are registered.

Adds error catching for save file corruption (malformed json) that renames the file in question to prevent future loading issues and uses `SohModalWindow` to inform the user of the error.

* Apply suggestions from code review

---------

Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>
2024-02-28 21:12:23 -06:00
Archez 358dd47da7 remove zapd extraction from mac launch script (#3981) 2024-02-28 21:03:08 -06:00
Malkierian ea1ffdd041 Adds messageboxes to no_ui handling so they don't show if you have it on. (#3977) 2024-02-28 20:04:44 -06:00
Archez ef9fc0a9ec fix endianess issue with camera setting data (#3950) 2024-02-20 08:38:10 -06:00
Archez 19af4481c0 fix object unload using wrong index (#3949) 2024-02-20 08:31:24 -06:00
Malkierian 30a063b75d [Tweak] Move Personal Notes to Save File (#3909)
* Moves personal notes to the save file under a new itemTracker save section.

* Update soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp

---------

Co-authored-by: Archez <Archez@users.noreply.github.com>
2024-02-15 20:44:40 -06:00
Pepper0ni cf6101f4da Logic bug: child cannot climb forest temple to the straight hallway either (#3934) 2024-02-15 20:03:58 -06:00
Archez 3d3b8bfc5b Allow IsSaveLoaded to consider debug saves (#3929) 2024-02-15 19:54:35 -06:00
Malkierian 0cb4cd158a Adds reset function to z_en_si (GS Token) to set getItemId to vanilla after resetting SoH. (#3925) 2024-02-15 19:53:18 -06:00
briaguya 43fed2d77e ci: pin switch docker image to known working version (#3917) 2024-02-15 19:51:31 -06:00
Malkierian 11a0a00633 Add CVarClear to appropriate sections of randomizer Locations and Tricks tabs to get around issue with saving blank CVar strings. (#3916) 2024-02-15 19:50:31 -06:00
inspectredc 107a365b71 Add safety measure to Scene_CommandObjectList to prevent crash (#3904)
* dont let k overflow

* Update soh/soh/z_scene_otr.cpp

Co-authored-by: Archez <Archez@users.noreply.github.com>

---------

Co-authored-by: Archez <Archez@users.noreply.github.com>
2024-02-15 19:38:54 -06:00
Rozelette bb1078e99c Account for removed object dependency in Deku Scrub Leader (#3878)
* Account for removed object dependency in Deku Scrub Leader

* Update soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c

---------

Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
2024-02-01 19:58:22 -06:00
inspectredc 2529dc59bd Increase Door Cull range (#3888)
* cullzone

* better cvar name

* Update soh/src/code/z_actor.c

* Update soh/src/code/z_actor.c

---------

Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
2024-02-01 19:58:08 -06:00
Malkierian 61cf2bd323 No Magic Numbers for Preset Location Exclusions (#3801)
* Adds `FormatLocations` and `PRESET_ENTRY_TYPE_CPP_STRING` to allow for feeding `RandomizerCheck` values directly in presets instead of a string with magic numbers.

* Switch to concatenation with `std::to_string`.

* Forgot to remove <format> include XD
2024-02-01 19:29:24 -06:00
Malkierian 16ee20c2a8 Renames Freecam to Free Look. (#3771)
Adds helper info for invert, distance, and transition speed options.
2024-02-01 19:26:36 -06:00
Archez 7ef6a434f9 fix timestamp truncation (#3874) 2024-02-01 19:25:57 -06:00
Malkierian e3825ec263 Unify defaults for reward count sliders with Greg As Reward so they need to register changes. (#3875) 2024-02-01 19:23:06 -06:00
Pepper0ni cb82e77e40 fix bombchu logic bugs (#3720) 2024-02-01 19:15:13 -06:00
Archez 63cf3610e5 Fix: Move Ruto earring fix to graphic patch and fix Ganon fight rubble DL reference (#3810)
* move ruto earing fix to real patch

* use stringpath for ganon rubble
2024-01-15 10:31:33 -06:00
inspectredc dd5d8088f6 Revert actor uncullZone related checks back to match decomp (#3828) 2024-01-15 10:31:06 -06:00
Archez 1da1b1a2bb Tweak: Improve pause menu dungeon map performance (#3773)
* add map palettes per pulse to leverage shader caching

* use unregister blended with kaleido maps

* use Gfx_TextureCacheDelete for KD lava

* bump lus

* add miss tex clears for KD
2024-01-15 09:39:54 -06:00
inspectredc db02870a05 Restore Original Scene Command Object List Behaviour (MacReady) (#3827)
* Restore Original Scene_CommandObjectList Behaviour

* remove some vrom stuff

* add some comments
2024-01-15 09:30:20 -06:00
Malkierian 861bd09848 Adds a log statement to show the SoH version at startup, in case crashes don't produce a stack trace from which to glean that information. (#3786) 2024-01-15 09:26:19 -06:00
Archez 8426cc93e5 Fix: Tektite texture not loading for death animation (#3808)
* fix tektite death texture loading

* add string header
2024-01-15 09:25:57 -06:00
Amaro Martínez 21796367a0 Use temporary mirror for Boost download URL (#3822) 2024-01-08 12:39:49 -06:00
inspectredc 321c258d69 Fix Fire Temple Boss Door Logic (#3774)
* Fix Fire Temple Boss Door Logic

* Update soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp

Co-authored-by: Adam Bird <Archez@users.noreply.github.com>

---------

Co-authored-by: Adam Bird <Archez@users.noreply.github.com>
2024-01-05 20:55:09 -05:00
Malkierian 02938cfba2 Fix Starting Triforce Piece Count (#3797)
* Move zeroing of triforcePiecesCollected to the beginning of `Randomizer_InitSaveFile()` to fix starting TFP count issues.
2024-01-05 20:53:18 -05:00
Garrett Cox 37b2fc0745 Make noclip only effect player (#3788) 2024-01-05 20:51:48 -05:00
Amaro Martínez 6cb3a830bd Restore previous Boost download URL (#3809)
This reverts commit 96abadd904.
2024-01-05 18:41:09 -05:00
Amaro Martínez 96abadd904 Update Boost download URL (#3776) 2023-12-31 16:06:47 -05:00
Malkierian fa8a0e2a76 [Rando] Fix Check Tracker Area Totals (#3758)
* Fix the calculation of `areaChecksGotten` to account for flags functionality setting invisible checks.
Change `areaChecksTotal` to dynamic calculation based on tracker visibility, now that all checks are being added to `checksByArea`.
Both are updated in realtime when either "Show all GS locations" or "Hide right side shop items" are toggled.
Reformat all remaining unencapsulated if statements.

* Changed helper variable change and call to `RecalculateAreaTotals()` to when the options are toggled instead of checking every frame.
Removed redundant if...else.
Clarified areaChecksGotten increment/decrement functionality based on current status and incoming status change.

* Removed unused code.
2023-12-31 13:58:31 -05:00
Garrett Cox 60faf3f750 Bump version to MacReady Echo 8.0.4 (#3537) 2023-12-17 22:47:39 -06:00
Garrett Cox ef910a02f7 Remove use of static variable in en_box (#3536) 2023-12-17 21:47:02 -06:00
inspectredc f607afc754 Add player state dead check to Player_UseTunicBoots (#3530)
* Add player state dead check to Player_UseTunicBoots

* Update soh/src/overlays/actors/ovl_player_actor/z_player.c

---------

Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
2023-12-17 21:46:35 -06:00
inspectredc 865bcc57a7 Fix Logical Error With Darunias Door Entrance (#3529) 2023-12-17 21:20:09 -06:00
Garrett Cox b5caf33a9b Initialize GameInteractor before SaveManager so it can correctly set up a hook (#3535) 2023-12-17 21:19:30 -06:00
Adam Bird f655ab592d Re-implement Pause menu Dungeon map texture effects (#3496)
* first pass implement dungeon maps

* wrap up map dungeon implementation

* add comments and enums, rename vars

* bump lus
2023-12-17 15:42:34 -05:00
briaguya e6fc34e4c2 bump lus to latest 1.x (#3528) 2023-12-17 13:36:38 -05:00
Adam Bird d370ca93fd [Tweak] Improve KD lava effect performance and stability (#3501)
* improve kd lava performance and stability

* enum typo

* account for rock tex size
2023-12-17 13:23:07 -05:00
Garrett Cox fcf2141266 Fix JSON parsing every frame on file select (#3513)
* Fix JSON parsing every frame on file select

* string (#73)

---------

Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>
2023-12-16 21:40:09 -05:00
Adam Bird e2f1cebfb5 fix condition causing enemies to always be small (#3512) 2023-12-16 20:56:36 -05:00
Adam Bird 907b770676 more swordless fixes when time traveling (#3510) 2023-12-16 20:56:14 -05:00
Adam Bird ea49196bae retain gameplay stats window size (#3508) 2023-12-16 20:52:36 -05:00
Adam Bird f65b711376 fix led crash when set to health (#3507) 2023-12-16 20:52:07 -05:00
Adam Bird 269e9faa46 change adult shooting gallery reward and add message (#3506) 2023-12-16 20:51:44 -05:00
Malkierian 35301556d9 Added "Always show gold skulltula" to check tracker options. Toggleable without restart. (#3505)
Made "Hide right-side shop checks" toggleable without restart.
2023-12-16 20:51:25 -05:00
Adam Bird fb0f7169d7 fix jabu mq minimap mark points (#3494) 2023-12-16 20:50:32 -05:00
Adam Bird fbc397a131 Tweak skeleton limbs to use OTR path strings for loading DList and improve Alt toggling (#3479)
* set otr string paths for DList on skeleton limbs

* delay toggling alt assets cvar to end of frame
2023-12-04 07:33:14 -06:00
60 changed files with 951 additions and 710 deletions
+1 -1
View File
@@ -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
View File
@@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
project(Ship VERSION 8.0.3 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Delta" 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
View File
@@ -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
+4 -1
View File
@@ -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 {
-234
View File
@@ -7,68 +7,6 @@ export RESPATH="${SNAME%/MacOS*}/Resources"
export LIBPATH="${SNAME%/MacOS*}/Frameworks"
export DYLD_FALLBACK_LIBRARY_PATH="$LIBPATH"
remap_hashes ()
{
# Remap v64 and n64 hashes to their z64 hash equivalent
# ZAPD will handle converting the data into z64 format
case "$ROMHASH" in
a9059b56e761c9034fbe02fe4c24985aaa835dac) # v64
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
;;
24708102dc504d3f375a37f4ae4e149c167dc515) # n64
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
;;
580dd0bd1b6d2c51cc20a764eece84dba558964c) # v64
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
;;
d6342c59007e57c1194661ec6880b2f078403f4e) # n64
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
;;
d0bdc2eb320668b4ba6893b9aefe4040a73123ff) # v64
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
;;
4946ab250f6ac9b32d76b21f309ebb8ebc8103d2) # n64
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
;;
663c34f1b2c05a09e5beffe4d0dcd440f7d49dc7) # v64
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
;;
24c73d378b0620a380ce5ef9f2b186c6c157a68b) # n64
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
;;
8ebf2e29313f44f2d49e5b4191971d09919e8e48) # v64
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
;;
4264bf7b875737b8fae77d52322a5099d051fc11) # n64
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
;;
973bc6fe56010a8d646166a1182a81b4f13b8cf9) # v64
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
;;
d327752c46edc70ff3668b9514083dbbee08927c) # v64
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
;;
ecdeb1747560834e079c22243febea7f6f26ba3b) # v64
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
;;
f19f8662ec7abee29484a272a6fda53e39efe0f1) # n64
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
;;
ab519ce04a33818ce2c39b3c514a751d807a494a) # v64
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
;;
c19a34f7646305e1755249fca2071e178bd7cd00) # n64
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
;;
25e8ae79ea0839ca5c984473f7460d8040c36f9c) # v64
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
;;
166c02770d67fcc3954c443eb400a6a3573d3fc0) # n64
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
;;
esac
}
if [ ! -e "$SHIP_HOME" ]; then mkdir "$SHIP_HOME"; fi
if [ ! -e "$SHIP_HOME"/mods ]; then
@@ -76,178 +14,6 @@ if [ ! -e "$SHIP_HOME"/mods ]; then
touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt
fi
# If either OTR doesn't exist kick off the OTR gen process
if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then
# If no ROMs exist kick off the file selection prompts
while [ ! -e "$SHIP_HOME"/*.*64 ] && [ ! -e "$SHIP_HOME"/oot*.otr ]; do
SHOULD_PROMPT_FOR_ROM=1
while [ $SHOULD_PROMPT_FOR_ROM -eq 1 ]; do
SHOULD_PROMPT_FOR_ROM=0
# Use osascript to prompt the user to chose a file
DROPROM=`osascript <<-EOF
set romFile to choose file of type {"b64","n64","v64","z64"} with prompt "Please select your ROM:"
return POSIX path of romFile
EOF`
# If no rom was selected, the user cancelled, so exit
if [[ -z $DROPROM ]] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
echo "No ROM selected. Exiting..."
exit 1
elif [[ -z $DROPROM ]]; then
break;
fi
# If an invalid rom was selected, let the user know and ask to try again
ROMHASH="$(shasum "$DROPROM" | awk '{ print $1 }')"
remap_hashes
case "$ROMHASH" in
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
ROM_TYPE=0;;
0227d7c0074f2d0ac935631990da8ec5914597b4)
ROM_TYPE=0;;
328a1f1beba30ce5e178f031662019eb32c5f3b5)
ROM_TYPE=0;;
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
ROM_TYPE=0;;
f46239439f59a2a594ef83cf68ef65043b1bffe2)
ROM_TYPE=1;;
50bebedad9e0f10746a52b07239e47fa6c284d03)
ROM_TYPE=1;;
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
ROM_TYPE=1;;
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
ROM_TYPE=1;;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
ROM_TYPE=1;;
*)
TRY_AGAIN_RESULT=`osascript <<-EOF
set alertText to "Incompatible ROM hash"
set alertMessage to "Incompatible ROM provided, would you like to try again?"
return display alert alertText \
message alertMessage \
as critical \
buttons {"Cancel", "Try Again"}
EOF`
if [[ "$TRY_AGAIN_RESULT" == "button returned:Try Again" ]]; then
SHOULD_PROMPT_FOR_ROM=1
continue;
else
echo "No ROM selected. Exiting..."
exit 1
fi
esac
cp "$DROPROM" "$SHIP_HOME"
# Ask user if they would also like to select the other variant (MQ/Vanilla)
if [ $ROM_TYPE -eq 0 ] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
set alertText to "Success"
set alertMessage to "Would you also like to provide a Master Quest ROM?"
return display alert alertText \
message alertMessage \
buttons {"No", "Yes"}
EOF`
elif [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
set alertText to "Success"
set alertMessage to "Would you also like to provide a Vanilla (Non Master Quest) ROM?"
return display alert alertText \
message alertMessage \
buttons {"No", "Yes"}
EOF`
fi
if [[ "$UPLOAD_ANOTHER_RESULT" == "button returned:Yes" ]]; then
UPLOAD_ANOTHER_RESULT="button returned:No"
SHOULD_PROMPT_FOR_ROM=1
continue;
fi
break
done
done
# At this point we should now have 1 or more valid roms in $SHIP_HOME directory
# Prepare tmp dir
for ROMPATH in "$SHIP_HOME"/*.*64
do
ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
export ASSETDIR
cp -r "$RESPATH/assets" "$ASSETDIR"
mkdir -p "$ASSETDIR"/tmp
cp "$ROMPATH" "$ASSETDIR"/tmp/rom.z64
cd "$ASSETDIR" || return
# If an invalid rom was detected, let the user know
ROMHASH="$(shasum "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')"
remap_hashes
case "$ROMHASH" in
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
ROM=GC_NMQ_D
OTRNAME="oot.otr";;
0227d7c0074f2d0ac935631990da8ec5914597b4)
ROM=GC_NMQ_PAL_F
OTRNAME="oot.otr";;
328a1f1beba30ce5e178f031662019eb32c5f3b5)
ROM=N64_PAL_10
OTRNAME="oot.otr";;
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
ROM=N64_PAL_11
OTRNAME="oot.otr";;
f46239439f59a2a594ef83cf68ef65043b1bffe2)
ROM=GC_MQ_PAL_F
OTRNAME="oot-mq.otr";;
50bebedad9e0f10746a52b07239e47fa6c284d03)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
*)
osascript -e 'display notification "One or more invalid ROM provided" with title "Ship Of Harkinian"'
rm -r "$ASSETDIR"
cd "$SNAME"
continue;
esac
# Only generate OTR if we don't have on of this type yet
if [ -e "$SHIP_HOME"/"$OTRNAME" ]; then
rm -r "$ASSETDIR"
cd "$SNAME"
continue;
fi
osascript -e 'display notification "Generating OTR..." with title "Ship Of Harkinian"'
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --portVer "@CMAKE_PROJECT_VERSION@"
if [ -e "$ASSETDIR"/oot.otr ]; then
osascript -e 'display notification "OTR successfully generated" with title "Ship Of Harkinian"'
cp "$ASSETDIR"/oot.otr "$SHIP_HOME"/"$OTRNAME"
rm -r "$ASSETDIR"
cd "$SNAME"
fi
done
if [ ! -e "$SHIP_HOME"/oot*.otr ]; then
osascript -e 'display notification "OTR failed to generate" with title "Ship Of Harkinian"'
exit 1;
fi
fi
cd "$SNAME"
arch_name="$(uname -m)"
launch_arch="arm64"
if [ "${arch_name}" = "x86_64" ] && [ "$(sysctl -in sysctl.proc_translated)" != "1" ]; then
@@ -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);
}
@@ -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
@@ -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;
@@ -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,10 +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);
+1 -1
View File
@@ -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;
+17 -14
View File
@@ -47,7 +47,7 @@ void ReloadSceneTogglingLinkAge() {
void RegisterInfiniteMoney() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
@@ -58,7 +58,7 @@ void RegisterInfiniteMoney() {
void RegisterInfiniteHealth() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
if (gSaveContext.health < gSaveContext.healthCapacity) {
gSaveContext.health = gSaveContext.healthCapacity;
@@ -69,7 +69,7 @@ void RegisterInfiniteHealth() {
void RegisterInfiniteAmmo() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
// Deku Sticks
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
@@ -106,7 +106,7 @@ void RegisterInfiniteAmmo() {
void RegisterInfiniteMagic() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
@@ -117,7 +117,7 @@ void RegisterInfiniteMagic() {
void RegisterInfiniteNayrusLove() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
gSaveContext.nayrusLoveTimer = 0x44B;
}
@@ -126,7 +126,7 @@ void RegisterInfiniteNayrusLove() {
void RegisterMoonJumpOnL() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@@ -141,7 +141,7 @@ void RegisterMoonJumpOnL() {
void RegisterInfiniteISG() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gEzISG", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@@ -153,7 +153,7 @@ void RegisterInfiniteISG() {
//Permanent quick put away (QPA) glitched damage value
void RegisterEzQPA() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gEzQPA", 0) != 0) {
Player* player = GET_PLAYER(gPlayState);
@@ -165,7 +165,7 @@ void RegisterEzQPA() {
void RegisterUnrestrictedItems() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) return;
if (!GameInteractor::IsSaveLoaded(true)) return;
if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
@@ -193,11 +193,14 @@ void RegisterFreezeTime() {
/// Switches Link's age and respawns him at the last entrance he entered.
void RegisterSwitchAge() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!GameInteractor::IsSaveLoaded()) {
static bool warped = false;
if (!GameInteractor::IsSaveLoaded(true)) {
CVarClear("gSwitchAge");
warped = false;
return;
}
static bool warped = false;
static Vec3f playerPos;
static int16_t playerYaw;
static RoomContext* roomCtx;
@@ -231,7 +234,7 @@ void RegisterSwitchAge() {
void RegisterOcarinaTimeTravel() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
if (!GameInteractor::IsSaveLoaded()) {
if (!GameInteractor::IsSaveLoaded(true)) {
CVarClear("gTimeTravel");
return;
}
@@ -1038,8 +1041,8 @@ void RegisterRandomizedEnemySizes() {
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_EN_DH;
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) {
+11
View File
@@ -12,6 +12,14 @@ void clearCvars(std::vector<const char*> cvarsToClear) {
}
}
std::string FormatLocations(std::vector<RandomizerCheck> locs) {
std::string locString = "";
for (auto loc: locs) {
locString += std::to_string(loc) + ",";
}
return locString;
}
void applyPreset(std::vector<PresetEntry> entries) {
for(auto& [cvar, type, value] : entries) {
switch (type) {
@@ -24,6 +32,9 @@ void applyPreset(std::vector<PresetEntry> entries) {
case PRESET_ENTRY_TYPE_STRING:
CVarSetString(cvar, std::get<const char*>(value));
break;
case PRESET_ENTRY_TYPE_CPP_STRING:
CVarSetString(cvar, std::get<std::string>(value).c_str());
break;
}
}
}
+12 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include <variant>
#include <cstdint>
@@ -11,6 +12,7 @@ enum PresetEntryType {
PRESET_ENTRY_TYPE_S32,
PRESET_ENTRY_TYPE_FLOAT,
PRESET_ENTRY_TYPE_STRING,
PRESET_ENTRY_TYPE_CPP_STRING,
};
enum PresetType {
@@ -36,15 +38,19 @@ enum RandomizerPreset {
typedef struct PresetEntry {
const char* cvar;
PresetEntryType type;
std::variant<int32_t, float, const char*> value;
std::variant<int32_t, float, const char*, std::string> value;
} PresetEntry;
std::string FormatLocations(std::vector<RandomizerCheck> locs);
#define PRESET_ENTRY_S32(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_S32, value }
#define PRESET_ENTRY_FLOAT(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_FLOAT, value }
#define PRESET_ENTRY_STRING(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_STRING, value }
#define PRESET_ENTRY_CPP_STRING(cvar, value) \
{ cvar, PRESET_ENTRY_TYPE_CPP_STRING, value }
void DrawPresetSelector(PresetType presetType);
void clearCvars(std::vector<const char*> cvarsToClear);
@@ -866,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),
@@ -958,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),
@@ -1011,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),
@@ -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));
+30 -30
View File
@@ -364,31 +364,16 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
#pragma GCC push_options
#pragma GCC optimize ("O0")
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
try {
if (strcmp(spoilerFileName, "") != 0) {
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return false;
}
json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;
if (!spoilerFileJson.contains("version") || !spoilerFileJson.contains("finalSeed")) {
return false;
}
if (strcmp(spoilerFileName, "") != 0) {
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return false;
} else {
return true;
}
return false;
} catch (std::exception& e) {
SPDLOG_ERROR("Error checking if spoiler file exists: {}", e.what());
return false;
} catch (...) {
SPDLOG_ERROR("Error checking if spoiler file exists");
return false;
}
return false;
}
#pragma GCC pop_options
#pragma optimize("", on)
@@ -494,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!",
@@ -4215,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",
@@ -4244,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",
@@ -4273,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",
@@ -4302,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",
@@ -4697,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();
@@ -4874,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();
@@ -5078,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();
}
@@ -5116,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);
@@ -78,6 +78,7 @@ bool initialized;
bool doAreaScroll;
bool previousShowHidden = false;
bool hideShopRightChecks = true;
bool alwaysShowGS = false;
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
@@ -106,8 +107,9 @@ 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 lastLocationChecked = RC_UNKNOWN_CHECK;
RandomizerCheckArea previousArea = RCAREA_INVALID;
@@ -224,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;
@@ -232,10 +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 (IsVisibleInCheckTracker(rcObj)) {
if (!gSaveContext.checkTrackerData[rc].skipped) {
areaChecksGotten[rcObj.rcArea]++;
} else {
gSaveContext.checkTrackerData[rc].skipped = false;
}
}
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
@@ -345,6 +369,7 @@ void ClearAreaChecksAndTotals() {
for (auto& [rcArea, vec] : checksByArea) {
vec.clear();
areaChecksGotten[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
}
}
@@ -424,9 +449,9 @@ void CheckTrackerLoadGame(int32_t fileNum) {
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))
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) {
@@ -434,11 +459,13 @@ void CheckTrackerLoadGame(int32_t fileNum) {
} else {
realRcObj = rcObj;
}
if (!IsVisibleInCheckTracker(realRcObj)) continue;
checksByArea.find(realRcObj.rcArea)->second.push_back(realRcObj);
if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
areaChecksGotten[realRcObj.rcArea]++;
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)) {
@@ -463,6 +490,7 @@ void CheckTrackerLoadGame(int32_t fileNum) {
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 ||
@@ -783,9 +811,13 @@ void Teardown() {
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;
@@ -898,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();
@@ -936,10 +967,11 @@ 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();
@@ -954,12 +986,14 @@ void CheckTrackerWindow::DrawElement() {
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("???");
@@ -973,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;
}
@@ -1084,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)) {
@@ -1148,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))
) &&
@@ -1197,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() {
@@ -1226,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;
}
@@ -1267,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) {
@@ -1382,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));
@@ -1409,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;
@@ -1517,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();
@@ -1572,6 +1631,9 @@ void CheckTrackerWindow::InitElement() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(CheckTrackerSceneFlagSet);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(CheckTrackerFlagSet);
hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1);
alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0);
LocationTable_Init();
}
@@ -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 {
@@ -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);
}
+3 -3
View File
@@ -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();
}
+1 -1
View File
@@ -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;
+112 -71
View File
@@ -118,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;
@@ -295,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>());
@@ -1035,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__
@@ -1057,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);
@@ -1136,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
@@ -1145,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__
@@ -1228,14 +1235,21 @@ extern "C" void Graph_StartFrame() {
}
#endif
case KbScancode::LUS_KB_TAB: {
// Toggle HD Assets
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
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();
}
@@ -1301,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
@@ -2099,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 {
@@ -2468,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;
@@ -2488,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);
@@ -2577,70 +2596,92 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
gfx_register_blended_texture(name, mask, replacement);
}
// #region SOH [TODO] Ideally this should move to being event based, it's currently run every frame on the file select screen
extern "C" void SoH_ProcessDroppedFiles() {
const char* droppedFile = CVarGetString("gDroppedFile", "");
if (CVarGetInteger("gNewFileDropped", 0) && strcmp(droppedFile, "") != 0) {
try {
std::ifstream configStream(SohUtils::Sanitize(droppedFile));
if (!configStream) {
return;
}
extern "C" void Gfx_UnregisterBlendedTexture(const char* name) {
gfx_unregister_blended_texture(name);
}
nlohmann::json configJson;
configStream >> configJson;
extern "C" void Gfx_TextureCacheDelete(const uint8_t* texAddr) {
char* imgName = (char*)texAddr;
if (!configJson.contains("CVars")) {
return;
}
if (texAddr == nullptr) {
return;
}
clearCvars(enhancementsCvars);
clearCvars(cheatCvars);
clearCvars(randomizerCvars);
if (ResourceMgr_OTRSigCheck(imgName)) {
texAddr = (const uint8_t*)GetResourceDataByNameHandlingMQ(imgName);
}
// Flatten everything under CVars into a single array
auto cvars = configJson["CVars"].flatten();
gfx_texture_cache_delete(texAddr);
}
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");
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
+2 -1
View File
@@ -173,9 +173,10 @@ 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();
void SoH_ProcessDroppedFiles();
int32_t GetGIID(uint32_t itemID);
#endif
+58 -41
View File
@@ -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>
@@ -1025,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() {
+10 -1
View File
@@ -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);
}
}
+2
View File
@@ -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 */
+6 -4
View File
@@ -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;
@@ -558,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");
@@ -908,8 +908,10 @@ void DrawEnhancementsMenu() {
{
if (ImGui::BeginMenu("Mods")) {
if (UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", "gAltAssets", false, false)) {
ShouldClearTextureCacheAtEndOfFrame = true;
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
// 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);
+54
View File
@@ -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 });
}
+15
View File
@@ -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);
};
@@ -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;
}
+19 -31
View File
@@ -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;
}
+15 -5
View File
@@ -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,
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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 ||
+5 -3
View File
@@ -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;
-1
View File
@@ -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);
+4 -1
View File
@@ -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);
+14
View File
@@ -1521,6 +1521,13 @@ 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 {
@@ -1589,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);
+5 -2
View File
@@ -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
View File
@@ -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);
@@ -10,10 +10,6 @@
#include <stdlib.h> // malloc
#include <string.h> // memcpy
// OTRTODO: Replace usage of this method when we can clear the cache
// for a single texture without the need of a DL opcode in the render code
void gfx_texture_cache_clear();
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
#define LAVA_TEX_WIDTH 32
@@ -69,7 +65,7 @@ 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;
@@ -112,6 +108,22 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
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);
@@ -121,7 +133,6 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
// When KD is dead, just immediately copy the rock texture
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
u32* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex);
memcpy(sLavaFloorModifiedTexRaw, rockTex, rockSize);
}
@@ -145,7 +156,16 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTex);
}
gfx_texture_cache_clear();
// 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) {
@@ -170,6 +190,11 @@ void func_808C12C4(u8* arg1, s16 arg2) {
// 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 width = ResourceGetTexWidthByName(arg0);
s32 size = ResourceGetTexHeightByName(arg0) * width;
@@ -203,9 +228,7 @@ void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
}
free(sp54);
// Need to clear the cache after updating sLavaWavyTexRaw
gfx_texture_cache_clear();
Gfx_TextureCacheDelete(sLavaWavyTexRaw);
}
// Modified to support CPU modified texture with the resource system
@@ -234,8 +257,7 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
}
}
// Need to clear the cache after updating sLavaWavyTex
gfx_texture_cache_clear();
Gfx_TextureCacheDelete(sLavaWavyTex);
}
void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) {
@@ -325,7 +347,7 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
// #region SOH [General]
// Init mask values for all blended textures
// Init mask values for all KD blended textures
for (int i = 0; i < ARRAY_COUNT(sMaskTex8x16); i++) {
sMaskTex8x16[i] = 0;
}
@@ -341,10 +363,6 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
for (int i = 0; i < ARRAY_COUNT(sMaskTex32x16); i++) {
sMaskTex32x16[i] = 0;
}
// Set all true for the lava as it will always replace the scene texture
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
sMaskTexLava[i] = 1;
}
// Register all blended textures
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015890, sMaskTex8x16, NULL);
@@ -358,6 +376,13 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL);
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL);
// 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
@@ -1174,15 +1199,24 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) {
for (i2 = 0; i2 < 20; i2++) {
s16 new_var = this->unk_1C2 & (LAVA_TEX_SIZE - 1);
// 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];
// 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;
@@ -1322,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};
@@ -472,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
@@ -628,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);
@@ -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;
@@ -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;
@@ -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!"
+7 -1
View File
@@ -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);
@@ -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;
@@ -10629,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;
@@ -1030,18 +1030,18 @@ void FileChoose_UpdateRandomizer() {
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);
@@ -1076,7 +1076,6 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
Input* input = &this->state.input[0];
bool dpad = CVarGetInteger("gDpadText", 0);
SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer();
if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
@@ -1267,7 +1266,6 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
s8 i = 0;
bool dpad = CVarGetInteger("gDpadText", 0);
SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer();
if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
@@ -3038,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();
}
}
@@ -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);
@@ -312,6 +312,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gQuestIconGoldSkulltulaTex, 24, 24, 8);
}
// Unique index for both pulse phases
uint8_t palettePulseIdx = (mapBgPulseStage ? 40 : 20) - mapBgPulseTimer;
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
stepR = (mapBgPulseR - mapBgPulseColors[mapBgPulseStage][0]) / mapBgPulseTimer;
stepG = (mapBgPulseG - mapBgPulseColors[mapBgPulseStage][1]) / mapBgPulseTimer;
@@ -324,6 +327,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
interfaceCtx->mapPalette[28] = (rgba16 & 0xFF00) >> 8;
interfaceCtx->mapPalette[29] = rgba16 & 0xFF;
interfaceCtx->mapPalettesPulse[palettePulseIdx][28] = (rgba16 & 0xFF00) >> 8;
interfaceCtx->mapPalettesPulse[palettePulseIdx][29] = rgba16 & 0xFF;
mapBgPulseTimer--;
if (mapBgPulseTimer == 0) {
mapBgPulseStage ^= 1;
@@ -335,7 +341,8 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_POINT);
gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha);
gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalette);
// Use a unique palette address per frame so the renderer/shader can cache all variations
gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalettesPulse[palettePulseIdx]);
gDPSetTextureLUT(POLY_KAL_DISP++, G_TT_RGBA16);
u8 mirroredWorld = CVarGetInteger("gMirroredWorld", 0);
@@ -343,18 +350,15 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
// Offset the U value of each vertex to be in the mirror boundary for the map textures
if (mirroredWorld) {
for (size_t i = 0; i < 8; i++) {
pauseCtx->mapPageVtx[60 + i].v.tc[0] += 48 << 5;
pauseCtx->mapPageVtx[60 + i].v.tc[0] += MAP_48x85_TEX_WIDTH << 5;
}
}
gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[60], 8, 0);
// The dungeon map textures are recreated each frame, so always invalidate them
gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[0]);
gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[1]);
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, 48, 85, 0, G_TX_WRAP | mirrorMode,
G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH,
MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK,
G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
// Swap vertices to render left half on the right and vice-versa
if (mirroredWorld) {
@@ -363,9 +367,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0);
}
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, 48, 85, 0,
G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOLOD);
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH,
MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK,
G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
if (mirroredWorld) {
gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0);
@@ -13,6 +13,10 @@ extern u8 gItemAgeReqs[];
extern u8 gAreaGsFlags[];
extern bool gSelectingMask;
#define MAP_48x85_TEX_WIDTH 48
#define MAP_48x85_TEX_HEIGHT 85
#define MAP_48x85_TEX_SIZE ((MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT) / 2) // 48x85 CI4 texture
#define AGE_REQ_ADULT LINK_AGE_ADULT
#define AGE_REQ_CHILD LINK_AGE_CHILD
#define AGE_REQ_NONE 9
@@ -3315,13 +3315,121 @@ void KaleidoScope_UpdateCursorSize(PauseContext* pauseCtx) {
pauseCtx->cursorVtx[14].v.ob[1] = pauseCtx->cursorVtx[15].v.ob[1] = pauseCtx->cursorVtx[12].v.ob[1] - 16;
}
// Modifed map texture buffers for registered blend effects and the room indicator color
static uint8_t mapLeftTexModified[MAP_48x85_TEX_SIZE];
static uint8_t mapRightTexModified[MAP_48x85_TEX_SIZE];
static uint8_t* mapLeftTexModifiedRaw = NULL;
static uint8_t* mapRightTexModifiedRaw = NULL;
static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT];
// Load dungeon maps into the interface context
// SoH [General] - Modified to account for our resource system and HD textures
void KaleidoScope_LoadDungeonMap(PlayState* play) {
InterfaceContext* interfaceCtx = &play->interfaceCtx;
// Free old textures
if (mapLeftTexModifiedRaw != NULL) {
free(mapLeftTexModifiedRaw);
mapLeftTexModifiedRaw = NULL;
}
if (mapRightTexModifiedRaw != NULL) {
free(mapRightTexModifiedRaw);
mapRightTexModifiedRaw = NULL;
}
// Unload original textures to bypass cache result for lookups
ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX]);
ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]);
interfaceCtx->mapSegmentName[0] = sDungeonMapTexs[R_MAP_TEX_INDEX];
interfaceCtx->mapSegmentName[1] = sDungeonMapTexs[R_MAP_TEX_INDEX + 1];
interfaceCtx->mapSegment[0] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX]);
interfaceCtx->mapSegment[1] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]);
// When the texture is HD (raw) we need to copy a dynamic amount of data
// Otherwise the original asset has a static size
if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) {
u32 width = ResourceGetTexWidthByName(interfaceCtx->mapSegmentName[0]);
u32 height = ResourceGetTexHeightByName(interfaceCtx->mapSegmentName[0]);
size_t size = (width * height) / 2; // account for CI4 size
// Resource size being larger than the calculated CI size means it is most likely not a CI4 texture
// Abort early and unregister the blended effect to avoid crashing
if (size < ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0])) {
interfaceCtx->mapSegment[0] = NULL;
interfaceCtx->mapSegment[1] = NULL;
Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[0]);
Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[1]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]);
return;
}
u8* map1TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]);
u8* map2TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]);
mapLeftTexModifiedRaw = malloc(size);
mapRightTexModifiedRaw = malloc(size);
memcpy(mapLeftTexModifiedRaw, map1TexRaw, size);
memcpy(mapRightTexModifiedRaw, map2TexRaw, size);
interfaceCtx->mapSegment[0] = mapLeftTexModifiedRaw;
interfaceCtx->mapSegment[1] = mapRightTexModifiedRaw;
} else {
u8* map1Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]);
u8* map2Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]);
memcpy(mapLeftTexModified, map1Tex, MAP_48x85_TEX_SIZE);
memcpy(mapRightTexModified, map2Tex, MAP_48x85_TEX_SIZE);
interfaceCtx->mapSegment[0] = mapLeftTexModified;
interfaceCtx->mapSegment[1] = mapRightTexModified;
}
// Mark and register the blend mask for the copied textures
if (mapBlendMask[0] != 1) {
for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) {
mapBlendMask[i] = 1;
}
}
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, interfaceCtx->mapSegment[0]);
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, interfaceCtx->mapSegment[1]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegment[0]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegment[1]);
}
static uint8_t registeredDungeonMapTextureHook = false;
void KaleidoScope_RegisterUpdatedDungeonMapTexture() {
if (gPlayState == NULL) {
return;
}
PauseContext* pauseCtx = &gPlayState->pauseCtx;
// Kaleido is not open in a dungeon so there is nothing to do
if (R_PAUSE_MENU_MODE < 3 || pauseCtx->state < 4 || pauseCtx->state > 7 || !sInDungeonScene) {
return;
}
KaleidoScope_UpdateDungeonMap(gPlayState);
// KaleidoScope_UpdateDungeonMap will update the palette index for the current floor if the cursor is on the floor
// If the player toggles alt assets while the cursor is not in the floor level, then we handle the palette index here
if (gPlayState->sceneNum >= SCENE_DEKU_TREE && gPlayState->sceneNum <= SCENE_TREASURE_BOX_SHOP &&
(VREG(30) + 3) == pauseCtx->dungeonMapSlot && (VREG(30) + 3) != pauseCtx->cursorPoint[PAUSE_MAP]) {
InterfaceContext* interfaceCtx = &gPlayState->interfaceCtx;
int32_t size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]);
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14);
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14);
}
}
void KaleidoScope_UpdateDungeonMap(PlayState* play) {
@@ -3333,19 +3441,34 @@ void KaleidoScope_UpdateDungeonMap(PlayState* play) {
KaleidoScope_LoadDungeonMap(play);
Map_SetFloorPalettesData(play, pauseCtx->dungeonMapSlot - 3);
// Copy the map palette values to all pulse palettes
for (uint8_t i = 0; i < ARRAY_COUNT(interfaceCtx->mapPalettesPulse); i++) {
memcpy(interfaceCtx->mapPalettesPulse[i], interfaceCtx->mapPalette, sizeof(interfaceCtx->mapPalette));
}
s32 size = MAP_48x85_TEX_SIZE;
if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) {
size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]);
}
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) {
// HDTODO: Handle Runtime Modified Textures (HD)
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], 2040, interfaceCtx->mapPaletteIndex, 14);
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14);
}
}
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) {
// HDTODO: Handle Runtime Modified Textures (HD)
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], 2040, interfaceCtx->mapPaletteIndex, 14);
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14);
}
}
// Register alt listener to update the blended dungeon map textures on alt toggle
if (!registeredDungeonMapTextureHook) {
registeredDungeonMapTextureHook = true;
GameInteractor_RegisterOnAssetAltChange(KaleidoScope_RegisterUpdatedDungeonMapTexture);
}
}
void KaleidoScope_Update(PlayState* play)
@@ -1762,6 +1762,13 @@ static MapMarkData sMapMarkJabuJabuBellyMq[] = {
} },
{ MAP_MARK_NONE, 0, { 0 } },
},
// Jabu-Jabu's Belly minimap 16
// SoH [General] - This entry corresponds to Big Octorok's room and is missing in the MQ game
// N64 hardware does an OoB read and lands on MQ Forest Temple room 0
// To avoid UB with OoB for SoH, the correct entry is now added below
{
{ MAP_MARK_NONE, 0, { 0 } },
},
};
static MapMarkData sMapMarkForestTempleMq[] = {