Compare commits

..

36 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
49 changed files with 596 additions and 575 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.4 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Echo" 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
@@ -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() {
@@ -198,7 +198,7 @@ public:
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);
+15 -12
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;
}
+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),
@@ -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));
+16 -8
View File
@@ -4207,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",
@@ -4236,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",
@@ -4265,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",
@@ -4294,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",
@@ -4689,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();
@@ -4866,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();
@@ -5070,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();
}
@@ -5108,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);
@@ -107,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;
@@ -225,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;
@@ -233,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);
@@ -346,6 +369,7 @@ void ClearAreaChecksAndTotals() {
for (auto& [rcArea, vec] : checksByArea) {
vec.clear();
areaChecksGotten[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
}
}
@@ -425,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) {
@@ -437,8 +461,11 @@ void CheckTrackerLoadGame(int32_t fileNum) {
}
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 ||
@@ -514,10 +542,6 @@ void CheckTrackerTransition(uint32_t sceneNum) {
}
void CheckTrackerFrame() {
if (IS_RANDO) {
hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1);
alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0);
}
if (!GameInteractor::IsSaveLoaded()) {
return;
}
@@ -787,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;
@@ -902,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();
@@ -940,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();
@@ -958,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("???");
@@ -977,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;
}
@@ -1200,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() {
@@ -1229,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;
}
@@ -1270,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) {
@@ -1385,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));
@@ -1412,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;
@@ -1520,9 +1568,15 @@ 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);
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.");
UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "");
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();
@@ -1577,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;
+21 -4
View File
@@ -297,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>());
@@ -1143,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
@@ -2486,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;
@@ -2597,6 +2596,24 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
gfx_register_blended_texture(name, mask, replacement);
}
extern "C" void Gfx_UnregisterBlendedTexture(const char* name) {
gfx_unregister_blended_texture(name);
}
extern "C" void Gfx_TextureCacheDelete(const uint8_t* texAddr) {
char* imgName = (char*)texAddr;
if (texAddr == nullptr) {
return;
}
if (ResourceMgr_OTRSigCheck(imgName)) {
texAddr = (const uint8_t*)GetResourceDataByNameHandlingMQ(imgName);
}
gfx_texture_cache_delete(texAddr);
}
void SoH_ProcessDroppedFiles(std::string filePath) {
try {
std::ifstream configStream(filePath);
+2
View File
@@ -173,6 +173,8 @@ void Entrance_InitEntranceTrackingData(void);
void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex);
void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void Gfx_UnregisterBlendedTexture(const char* name);
void Gfx_TextureCacheDelete(const uint8_t* addr);
void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose();
+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() {
+9
View File
@@ -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 */
+1 -1
View File
@@ -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");
+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);
};
+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);
+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
@@ -123,7 +119,9 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
sMaskTexLava[i] = maskVal;
}
}
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, NULL);
Gfx_TextureCacheDelete(sMaskTexLava);
return;
}
@@ -165,7 +163,9 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
}
}
gfx_texture_cache_clear();
Gfx_TextureCacheDelete(sMaskTexLava);
Gfx_TextureCacheDelete(sLavaWavyTex);
Gfx_TextureCacheDelete(sLavaFloorModifiedTex);
}
void func_808C12C4(u8* arg1, s16 arg2) {
@@ -228,6 +228,7 @@ void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
}
free(sp54);
Gfx_TextureCacheDelete(sLavaWavyTexRaw);
}
// Modified to support CPU modified texture with the resource system
@@ -255,6 +256,8 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
temp_s3[i + temp2] = sp54[i + i2];
}
}
Gfx_TextureCacheDelete(sLavaWavyTex);
}
void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) {
@@ -373,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
@@ -1206,6 +1216,7 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) {
}
} else {
sMaskTexLava[new_var] = 1;
Gfx_TextureCacheDelete(sMaskTexLava);
}
this->unk_1C2 += 37;
@@ -1345,18 +1356,6 @@ void BossDodongo_Draw(Actor* thisx, PlayState* play) {
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTex32x16);
}
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTexLava);
// Using WORK_DISP to invalidate these textures as they are used in drawing the scene textures which happens
// before actors are drawn. WORK_DISP comes before POLAY_OPA_DISP. It is probably not meant for this, but it
// at least works for now.
// Alternatively, having a way to invalidate just these pointers from the Update func should be sufficient.
if (sLavaFloorModifiedTexRaw != NULL) {
gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTexRaw);
} else {
gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTex);
}
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;
}
@@ -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);
@@ -3036,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();
}
}
@@ -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);
@@ -349,10 +356,6 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
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, 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);
@@ -1205,8 +1205,6 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, void** textures) {
return gfx;
}
static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT];
void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
static Color_RGB8 D_8082ACF4[12] = {
{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 255, 255, 0 }, { 0, 0, 0 },
@@ -1375,10 +1373,6 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
}
}
// Need to invalidate the blend mask every frame. Ideally this would be done in KaleidoScope_DrawDungeonMap
// but the reference is not shared between files
gSPInvalidateTexCache(POLY_KAL_DISP++, mapBlendMask);
if (pauseCtx->pageIndex) { // pageIndex != PAUSE_ITEM
gDPPipeSync(OVERLAY_DISP++);
gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA);
@@ -3326,6 +3320,7 @@ 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
@@ -3357,19 +3352,16 @@ void KaleidoScope_LoadDungeonMap(PlayState* play) {
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 end undo the blended effect by clearing the mask to avoid crashing
// Abort early and unregister the blended effect to avoid crashing
if (size < ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0])) {
if (mapBlendMask[0] != 0) {
for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) {
mapBlendMask[i] = 0;
}
}
interfaceCtx->mapSegment[0] = NULL;
interfaceCtx->mapSegment[1] = NULL;
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, NULL);
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, NULL);
Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[0]);
Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[1]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]);
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]);
return;
}
@@ -3404,6 +3396,11 @@ void KaleidoScope_LoadDungeonMap(PlayState* play) {
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;
@@ -3444,6 +3441,11 @@ 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])) {