mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-05-24 23:31:34 -04:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bc6ca08e0 | |||
| 16aaf2f939 | |||
| 3971e3696e | |||
| f11f97e84e | |||
| 7a00658be9 | |||
| 7bc2259c82 | |||
| 3d73faa9a0 | |||
| 134aba4aa0 | |||
| 79a29a62ef | |||
| 3f67fed073 | |||
| cbeec006ec | |||
| fd9cd9c5eb | |||
| 4a576f45ee | |||
| 32bf8cc53c | |||
| 02f7310c16 | |||
| bfe13906e9 | |||
| 32288be744 | |||
| 19be6e9b99 | |||
| 0b8cc71d0d | |||
| ced34ab68a | |||
| e163d5bc3d | |||
| 612da023f0 | |||
| 368a9015ac | |||
| ed9cb1dfd2 | |||
| fb6ea42560 | |||
| b26f2b21da | |||
| 358dd47da7 | |||
| ea1ffdd041 | |||
| ef9fc0a9ec | |||
| 19af4481c0 | |||
| 30a063b75d | |||
| cf6101f4da | |||
| 3d3b8bfc5b | |||
| 0cb4cd158a | |||
| 43fed2d77e | |||
| 11a0a00633 | |||
| 107a365b71 | |||
| bb1078e99c | |||
| 2529dc59bd | |||
| 61cf2bd323 | |||
| 16ee20c2a8 | |||
| 7ef6a434f9 | |||
| e3825ec263 | |||
| cb82e77e40 | |||
| 63cf3610e5 | |||
| dd5d8088f6 | |||
| 1da1b1a2bb | |||
| db02870a05 | |||
| 861bd09848 | |||
| 8426cc93e5 | |||
| 21796367a0 | |||
| 321c258d69 | |||
| 02938cfba2 | |||
| 37b2fc0745 | |||
| 6cb3a830bd | |||
| 96abadd904 | |||
| fa8a0e2a76 | |||
| 60faf3f750 | |||
| ef910a02f7 | |||
| f607afc754 | |||
| 865bcc57a7 | |||
| b5caf33a9b | |||
| f655ab592d | |||
| e6fc34e4c2 | |||
| d370ca93fd | |||
| fcf2141266 | |||
| e2f1cebfb5 | |||
| 907b770676 | |||
| ea49196bae | |||
| f65b711376 | |||
| 269e9faa46 | |||
| 35301556d9 | |||
| fb0f7169d7 | |||
| fbc397a131 |
@@ -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: |
|
||||
@@ -235,7 +235,6 @@ jobs:
|
||||
- name: Install dependencies
|
||||
if: ${{ !vars.LINUX_RUNNER }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
project(Ship VERSION 8.0.3 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Delta" CACHE STRING "")
|
||||
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
|
||||
project(Ship VERSION 8.0.6 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Golf" CACHE STRING "" FORCE)
|
||||
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "" FORCE)
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
|
||||
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
|
||||
|
||||
+1
-1
Submodule libultraship updated: 59427a67bf...1ca7d0fa78
+1
-1
@@ -328,7 +328,7 @@ endif()
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
Boost
|
||||
URL https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.gz
|
||||
URL https://archives.boost.io/release/1.81.0/source/boost_1_81_0.tar.gz
|
||||
URL_HASH SHA256=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6
|
||||
SOURCE_SUBDIR "null" # Set to a nonexistent directory so boost is not built (we don't need to build it)
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP false # supress timestamp warning, not needed since the url wont change
|
||||
|
||||
@@ -28,4 +28,10 @@
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- legacy -->
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to pm if pmv2 is not available -->
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
||||
|
||||
@@ -46,10 +46,18 @@ template<> struct is_char_type<std::byte>: public boost::true_type {};
|
||||
|
||||
#endif // #if !BOOST_VERSION_HAS_HASH_RANGE
|
||||
|
||||
#if BOOST_USE_STD_TYPES
|
||||
#define BOOST_ENABLE_IF std::enable_if
|
||||
#define BOOST_IS_SAME std::is_same
|
||||
#else
|
||||
#define BOOST_ENABLE_IF boost::enable_if_
|
||||
#define BOOST_IS_SAME is_same
|
||||
#endif
|
||||
|
||||
template<class It>
|
||||
inline typename boost::enable_if_<
|
||||
inline typename BOOST_ENABLE_IF<
|
||||
is_char_type<typename std::iterator_traits<It>::value_type>::value &&
|
||||
is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value,
|
||||
BOOST_IS_SAME<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value,
|
||||
std::size_t>::type
|
||||
hash_range_32( uint32_t seed, It first, It last )
|
||||
{
|
||||
@@ -114,4 +122,7 @@ std::size_t>::type
|
||||
} // namespace hash_detail
|
||||
} // namespace boost
|
||||
|
||||
#undef BOOST_ENABLE_IF
|
||||
#undef BOOST_IS_SAME
|
||||
|
||||
#endif // #ifndef BOOST_HASH_DETAIL_HASH_RANGE_32_HPP
|
||||
|
||||
@@ -23,6 +23,18 @@
|
||||
|
||||
#endif // #if !BOOST_VERSION_HAS_HASH_RANGE
|
||||
|
||||
#if BOOST_USE_STD_TYPES
|
||||
#define BOOST_ENABLE_IF std::enable_if
|
||||
#define BOOST_IS_INTEGRAL hash_detail::is_integral
|
||||
#define BOOST_IS_UNSIGNED is_unsigned
|
||||
#define BOOST_MAKE_UNSIGNED make_unsigned
|
||||
#else
|
||||
#define BOOST_ENABLE_IF boost::enable_if_
|
||||
#define BOOST_IS_INTEGRAL boost::is_integral
|
||||
#define BOOST_IS_UNSIGNED boost::is_unsigned
|
||||
#define BOOST_MAKE_UNSIGNED boost::make_unsigned
|
||||
#endif
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
@@ -36,7 +48,7 @@ namespace boost
|
||||
{
|
||||
template<class T,
|
||||
bool bigger_than_size_t = (sizeof(T) > sizeof(uint32_t)),
|
||||
bool is_unsigned = boost::is_unsigned<T>::value,
|
||||
bool is_unsigned = BOOST_IS_UNSIGNED<T>::value,
|
||||
std::size_t size_t_bits = sizeof(uint32_t) * CHAR_BIT,
|
||||
std::size_t type_bits = sizeof(T) * CHAR_BIT>
|
||||
struct hash_integral_impl_32;
|
||||
@@ -53,7 +65,7 @@ namespace boost
|
||||
{
|
||||
static uint32_t fn( T v )
|
||||
{
|
||||
typedef typename boost::make_unsigned<T>::type U;
|
||||
typedef typename BOOST_MAKE_UNSIGNED<T>::type U;
|
||||
|
||||
if( v >= 0 )
|
||||
{
|
||||
@@ -97,7 +109,7 @@ namespace boost
|
||||
} // namespace hash_detail
|
||||
|
||||
template <typename T>
|
||||
typename boost::enable_if_<boost::is_integral<T>::value, uint32_t>::type
|
||||
typename BOOST_ENABLE_IF<BOOST_IS_INTEGRAL<T>::value, uint32_t>::type
|
||||
hash_value_32( T v )
|
||||
{
|
||||
return hash_detail::hash_integral_impl_32<T>::fn( v );
|
||||
@@ -106,7 +118,7 @@ namespace boost
|
||||
// contiguous ranges (string, vector, array)
|
||||
#if BOOST_VERSION_HAS_HASH_RANGE
|
||||
template <typename T>
|
||||
typename boost::enable_if_<container_hash::is_contiguous_range<T>::value, uint32_t>::type
|
||||
typename BOOST_ENABLE_IF<container_hash::is_contiguous_range<T>::value, uint32_t>::type
|
||||
hash_value_32( T const& v )
|
||||
{
|
||||
return boost::hash_range_32( v.data(), v.data() + v.size() );
|
||||
@@ -168,5 +180,9 @@ namespace boost
|
||||
} // namespace boost
|
||||
|
||||
#undef BOOST_HASH_CHAR_TRAITS
|
||||
#undef BOOST_ENABLE_IF
|
||||
#undef BOOST_IS_INTEGRAL
|
||||
#undef BOOST_IS_UNSIGNED
|
||||
#undef BOOST_MAKE_UNSIGNED
|
||||
|
||||
#endif // #ifndef BOOST_FUNCTIONAL_HASH_HASH_32_HPP
|
||||
|
||||
@@ -6,4 +6,6 @@
|
||||
|
||||
#define BOOST_VERSION_HAS_HASH_RANGE ((BOOST_VERSION / 100 % 1000) >= 81)
|
||||
|
||||
#define BOOST_USE_STD_TYPES ((BOOST_VERSION / 100 % 1000) >= 84)
|
||||
|
||||
#endif // #ifndef BOOST_CONTAINER_HASH_VERSION_HPP
|
||||
|
||||
@@ -1259,6 +1259,8 @@ void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable,
|
||||
s32 dListIndex);
|
||||
void SkelAnime_DrawSkeletonOpa(PlayState* play, SkelAnime* skelAnime, OverrideLimbDrawOpa overrideLimbDraw,
|
||||
PostLimbDrawOpa postLimbDraw, void* arg);
|
||||
Gfx* SkelAnime_DrawSkeleton2(PlayState* play, SkelAnime* skelAnime, OverrideLimbDrawOpa overrideLimbDraw,
|
||||
PostLimbDrawOpa postLimbDraw, void* arg, Gfx* gfx);
|
||||
void SkelAnime_DrawOpa(PlayState* play, void** skeleton, Vec3s* jointTable,
|
||||
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg);
|
||||
void SkelAnime_DrawFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
|
||||
@@ -2458,6 +2460,8 @@ void Message_DrawText(PlayState* play, Gfx** gfxP);
|
||||
|
||||
void Interface_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH);
|
||||
void Interface_RandoRestoreSwordless(void);
|
||||
s32 Ship_CalcShouldDrawAndUpdate(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW, bool* shouldDraw,
|
||||
bool* shouldUpdate);
|
||||
|
||||
// #endregion
|
||||
|
||||
|
||||
+6
-1
@@ -744,7 +744,6 @@ typedef struct {
|
||||
/* 0x0134 */ char** doActionSegment;
|
||||
/* 0x0138 */ u8* iconItemSegment;
|
||||
/* 0x013C */ char** mapSegment;
|
||||
char** mapSegmentName;
|
||||
/* 0x0140 */ u8 mapPalette[32];
|
||||
/* 0x0160 */ DmaRequest dmaRequest_160;
|
||||
/* 0x0180 */ DmaRequest dmaRequest_180;
|
||||
@@ -815,6 +814,10 @@ typedef struct {
|
||||
/* 0x026C */ u8 dinsNayrus; // "m_magic"; din's fire and nayru's love
|
||||
/* 0x026D */ u8 all; // "another"; enables all item restrictions
|
||||
} restrictions;
|
||||
// #region SOH [General]
|
||||
/* */ char* mapSegmentName[2]; // Tracks the map segment texture by OTR sig name
|
||||
/* */ u8 mapPalettesPulse[40][32]; // Used to have unique pointers per map pulse color for the shader backend. 40 for map pulse timer x2
|
||||
// #endregion
|
||||
} InterfaceContext; // size = 0x270
|
||||
|
||||
typedef struct {
|
||||
@@ -1402,6 +1405,8 @@ typedef struct PlayState {
|
||||
/* 0x1242B */ u8 unk_1242B;
|
||||
/* 0x1242C */ SceneTableEntry* loadedScene;
|
||||
/* 0x12430 */ char unk_12430[0xE8];
|
||||
// SOH [Custom Models] MTX tracker for flex based skeletons
|
||||
Mtx** flexLimbOverrideMTX;
|
||||
} PlayState; // size = 0x12518
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -7,68 +7,6 @@ export RESPATH="${SNAME%/MacOS*}/Resources"
|
||||
export LIBPATH="${SNAME%/MacOS*}/Frameworks"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$LIBPATH"
|
||||
|
||||
remap_hashes ()
|
||||
{
|
||||
# Remap v64 and n64 hashes to their z64 hash equivalent
|
||||
# ZAPD will handle converting the data into z64 format
|
||||
case "$ROMHASH" in
|
||||
a9059b56e761c9034fbe02fe4c24985aaa835dac) # v64
|
||||
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
|
||||
;;
|
||||
24708102dc504d3f375a37f4ae4e149c167dc515) # n64
|
||||
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
|
||||
;;
|
||||
580dd0bd1b6d2c51cc20a764eece84dba558964c) # v64
|
||||
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
|
||||
;;
|
||||
d6342c59007e57c1194661ec6880b2f078403f4e) # n64
|
||||
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
|
||||
;;
|
||||
d0bdc2eb320668b4ba6893b9aefe4040a73123ff) # v64
|
||||
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
|
||||
;;
|
||||
4946ab250f6ac9b32d76b21f309ebb8ebc8103d2) # n64
|
||||
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
|
||||
;;
|
||||
663c34f1b2c05a09e5beffe4d0dcd440f7d49dc7) # v64
|
||||
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
|
||||
;;
|
||||
24c73d378b0620a380ce5ef9f2b186c6c157a68b) # n64
|
||||
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
|
||||
;;
|
||||
8ebf2e29313f44f2d49e5b4191971d09919e8e48) # v64
|
||||
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
|
||||
;;
|
||||
4264bf7b875737b8fae77d52322a5099d051fc11) # n64
|
||||
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
|
||||
;;
|
||||
973bc6fe56010a8d646166a1182a81b4f13b8cf9) # v64
|
||||
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
|
||||
;;
|
||||
d327752c46edc70ff3668b9514083dbbee08927c) # v64
|
||||
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
|
||||
;;
|
||||
ecdeb1747560834e079c22243febea7f6f26ba3b) # v64
|
||||
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
|
||||
;;
|
||||
f19f8662ec7abee29484a272a6fda53e39efe0f1) # n64
|
||||
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
|
||||
;;
|
||||
ab519ce04a33818ce2c39b3c514a751d807a494a) # v64
|
||||
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
|
||||
;;
|
||||
c19a34f7646305e1755249fca2071e178bd7cd00) # n64
|
||||
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
|
||||
;;
|
||||
25e8ae79ea0839ca5c984473f7460d8040c36f9c) # v64
|
||||
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
|
||||
;;
|
||||
166c02770d67fcc3954c443eb400a6a3573d3fc0) # n64
|
||||
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ ! -e "$SHIP_HOME" ]; then mkdir "$SHIP_HOME"; fi
|
||||
|
||||
if [ ! -e "$SHIP_HOME"/mods ]; then
|
||||
@@ -76,178 +14,6 @@ if [ ! -e "$SHIP_HOME"/mods ]; then
|
||||
touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt
|
||||
fi
|
||||
|
||||
# If either OTR doesn't exist kick off the OTR gen process
|
||||
if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then
|
||||
|
||||
# If no ROMs exist kick off the file selection prompts
|
||||
while [ ! -e "$SHIP_HOME"/*.*64 ] && [ ! -e "$SHIP_HOME"/oot*.otr ]; do
|
||||
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
while [ $SHOULD_PROMPT_FOR_ROM -eq 1 ]; do
|
||||
SHOULD_PROMPT_FOR_ROM=0
|
||||
# Use osascript to prompt the user to chose a file
|
||||
DROPROM=`osascript <<-EOF
|
||||
set romFile to choose file of type {"b64","n64","v64","z64"} with prompt "Please select your ROM:"
|
||||
return POSIX path of romFile
|
||||
EOF`
|
||||
|
||||
# If no rom was selected, the user cancelled, so exit
|
||||
if [[ -z $DROPROM ]] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
echo "No ROM selected. Exiting..."
|
||||
exit 1
|
||||
elif [[ -z $DROPROM ]]; then
|
||||
break;
|
||||
fi
|
||||
|
||||
# If an invalid rom was selected, let the user know and ask to try again
|
||||
ROMHASH="$(shasum "$DROPROM" | awk '{ print $1 }')"
|
||||
|
||||
remap_hashes
|
||||
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
ROM_TYPE=0;;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
ROM_TYPE=0;;
|
||||
328a1f1beba30ce5e178f031662019eb32c5f3b5)
|
||||
ROM_TYPE=0;;
|
||||
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
|
||||
ROM_TYPE=0;;
|
||||
f46239439f59a2a594ef83cf68ef65043b1bffe2)
|
||||
ROM_TYPE=1;;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
ROM_TYPE=1;;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
ROM_TYPE=1;;
|
||||
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
|
||||
ROM_TYPE=1;;
|
||||
517bd9714c73cb96c21e7c2ef640d7b55186102f)
|
||||
ROM_TYPE=1;;
|
||||
*)
|
||||
TRY_AGAIN_RESULT=`osascript <<-EOF
|
||||
set alertText to "Incompatible ROM hash"
|
||||
set alertMessage to "Incompatible ROM provided, would you like to try again?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
as critical \
|
||||
buttons {"Cancel", "Try Again"}
|
||||
EOF`
|
||||
if [[ "$TRY_AGAIN_RESULT" == "button returned:Try Again" ]]; then
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
continue;
|
||||
else
|
||||
echo "No ROM selected. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
esac
|
||||
|
||||
cp "$DROPROM" "$SHIP_HOME"
|
||||
|
||||
# Ask user if they would also like to select the other variant (MQ/Vanilla)
|
||||
if [ $ROM_TYPE -eq 0 ] && [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
|
||||
set alertText to "Success"
|
||||
set alertMessage to "Would you also like to provide a Master Quest ROM?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
buttons {"No", "Yes"}
|
||||
EOF`
|
||||
elif [[ -z "$UPLOAD_ANOTHER_RESULT" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT=`osascript <<-EOF
|
||||
set alertText to "Success"
|
||||
set alertMessage to "Would you also like to provide a Vanilla (Non Master Quest) ROM?"
|
||||
return display alert alertText \
|
||||
message alertMessage \
|
||||
buttons {"No", "Yes"}
|
||||
EOF`
|
||||
fi
|
||||
|
||||
if [[ "$UPLOAD_ANOTHER_RESULT" == "button returned:Yes" ]]; then
|
||||
UPLOAD_ANOTHER_RESULT="button returned:No"
|
||||
SHOULD_PROMPT_FOR_ROM=1
|
||||
continue;
|
||||
fi
|
||||
break
|
||||
done
|
||||
done
|
||||
|
||||
# At this point we should now have 1 or more valid roms in $SHIP_HOME directory
|
||||
|
||||
# Prepare tmp dir
|
||||
for ROMPATH in "$SHIP_HOME"/*.*64
|
||||
do
|
||||
ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
|
||||
export ASSETDIR
|
||||
cp -r "$RESPATH/assets" "$ASSETDIR"
|
||||
mkdir -p "$ASSETDIR"/tmp
|
||||
cp "$ROMPATH" "$ASSETDIR"/tmp/rom.z64
|
||||
cd "$ASSETDIR" || return
|
||||
|
||||
# If an invalid rom was detected, let the user know
|
||||
ROMHASH="$(shasum "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')"
|
||||
|
||||
remap_hashes
|
||||
|
||||
case "$ROMHASH" in
|
||||
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
|
||||
ROM=GC_NMQ_D
|
||||
OTRNAME="oot.otr";;
|
||||
0227d7c0074f2d0ac935631990da8ec5914597b4)
|
||||
ROM=GC_NMQ_PAL_F
|
||||
OTRNAME="oot.otr";;
|
||||
328a1f1beba30ce5e178f031662019eb32c5f3b5)
|
||||
ROM=N64_PAL_10
|
||||
OTRNAME="oot.otr";;
|
||||
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
|
||||
ROM=N64_PAL_11
|
||||
OTRNAME="oot.otr";;
|
||||
f46239439f59a2a594ef83cf68ef65043b1bffe2)
|
||||
ROM=GC_MQ_PAL_F
|
||||
OTRNAME="oot-mq.otr";;
|
||||
50bebedad9e0f10746a52b07239e47fa6c284d03)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
517bd9714c73cb96c21e7c2ef640d7b55186102f)
|
||||
ROM=GC_MQ_D
|
||||
OTRNAME="oot-mq.otr";;
|
||||
*)
|
||||
osascript -e 'display notification "One or more invalid ROM provided" with title "Ship Of Harkinian"'
|
||||
rm -r "$ASSETDIR"
|
||||
cd "$SNAME"
|
||||
continue;
|
||||
esac
|
||||
|
||||
# Only generate OTR if we don't have on of this type yet
|
||||
if [ -e "$SHIP_HOME"/"$OTRNAME" ]; then
|
||||
rm -r "$ASSETDIR"
|
||||
cd "$SNAME"
|
||||
continue;
|
||||
fi
|
||||
|
||||
osascript -e 'display notification "Generating OTR..." with title "Ship Of Harkinian"'
|
||||
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --portVer "@CMAKE_PROJECT_VERSION@"
|
||||
if [ -e "$ASSETDIR"/oot.otr ]; then
|
||||
osascript -e 'display notification "OTR successfully generated" with title "Ship Of Harkinian"'
|
||||
cp "$ASSETDIR"/oot.otr "$SHIP_HOME"/"$OTRNAME"
|
||||
rm -r "$ASSETDIR"
|
||||
cd "$SNAME"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -e "$SHIP_HOME"/oot*.otr ]; then
|
||||
osascript -e 'display notification "OTR failed to generate" with title "Ship Of Harkinian"'
|
||||
exit 1;
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$SNAME"
|
||||
|
||||
arch_name="$(uname -m)"
|
||||
launch_arch="arm64"
|
||||
if [ "${arch_name}" = "x86_64" ] && [ "$(sysctl -in sysctl.proc_translated)" != "1" ]; then
|
||||
|
||||
@@ -258,24 +258,28 @@ namespace GameControlEditor {
|
||||
window->EndGroupPanelPublic(0);
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
window->BeginGroupPanelPublic("Third-Person Camera", ImGui::GetContentRegionAvail());
|
||||
window->BeginGroupPanelPublic("Free Look/Third-person Camera", ImGui::GetContentRegionAvail());
|
||||
|
||||
UIWidgets::PaddedEnhancementCheckbox("Free Camera", "gFreeCamera");
|
||||
DrawHelpIcon("Enables free camera control\nNote: You must remap C buttons off of the right stick in the "
|
||||
UIWidgets::PaddedEnhancementCheckbox("Enable Free Look", "gFreeCamera");
|
||||
DrawHelpIcon("Enables free look camera control\nNote: You must remap C buttons off of the right stick in the "
|
||||
"controller config menu, and map the camera stick to the right stick.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert Camera X Axis", "gInvertXAxis");
|
||||
DrawHelpIcon("Inverts the Camera X Axis in:\n-Free camera");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free camera");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert X Axis", "gInvertXAxis");
|
||||
DrawHelpIcon("Inverts the Camera X Axis in:\n-Free Look");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Invert Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free Look");
|
||||
UIWidgets::Spacer(0);
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal",
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal",
|
||||
"gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical",
|
||||
DrawHelpIcon("Changes the sensitivity of the X axis control for Free Look");
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical",
|
||||
"gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true);
|
||||
DrawHelpIcon("Changes the sensitivity of the Y axis control for Free Look");
|
||||
UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist",
|
||||
"gFreeCameraDistMax", 100, 900, "", 185, true, false, true);
|
||||
UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed",
|
||||
DrawHelpIcon("How far the camera sits from Link while in Free Look mode");
|
||||
UIWidgets::PaddedEnhancementSliderInt("Transition Speed: %d", "##CamTranSpeed",
|
||||
"gFreeCameraTransitionSpeed", 0, 900, "", 25, true, false, true);
|
||||
DrawHelpIcon("How quickly the camera changes to the distance specified above");
|
||||
window->EndGroupPanelPublic(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ extern "C" {
|
||||
#include "objects/object_gi_soldout/object_gi_soldout.h"
|
||||
#include "objects/object_ik/object_ik.h"
|
||||
#include "objects/object_link_child/object_link_child.h"
|
||||
#include "objects/object_ru2/object_ru2.h"
|
||||
|
||||
uint32_t ResourceMgr_GameHasMasterQuest();
|
||||
uint32_t ResourceMgr_GameHasOriginal();
|
||||
@@ -187,10 +188,25 @@ void PatchIronKnuckleTextureOverflow() {
|
||||
}
|
||||
}
|
||||
|
||||
void PatchPrincessRutoEaring() {
|
||||
// FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings.
|
||||
// Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so
|
||||
// it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the
|
||||
// earring texture loads into the same place in TMEM as the brick texture, so when it comes to rendering, TEXEL1
|
||||
// uses the earring texture with different clamp settings, and it displays without noticeable error. However, both
|
||||
// texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses
|
||||
// our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces
|
||||
// TEXEL1 with TEXEL0, which is most likely the original intention, and all is well.
|
||||
ResourceMgr_PatchGfxByName(gAdultRutoHeadDL, "RutoEaringTileFix", 162,
|
||||
gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED,
|
||||
TEXEL0, 0, PRIM_LOD_FRAC, COMBINED));
|
||||
}
|
||||
|
||||
void ApplyAuthenticGfxPatches() {
|
||||
PatchDekuStickTextureOverflow();
|
||||
PatchFreezardTextureOverflow();
|
||||
PatchIronKnuckleTextureOverflow();
|
||||
PatchPrincessRutoEaring();
|
||||
}
|
||||
|
||||
// Patches the Sold Out GI DL to render the texture in the mirror boundary
|
||||
|
||||
@@ -51,6 +51,7 @@ typedef enum {
|
||||
TEXT_WARP_NOCTURNE_OF_SHADOW = 0x891,
|
||||
TEXT_WARP_PRELUDE_OF_LIGHT = 0x892,
|
||||
TEXT_WARP_RANDOM_REPLACED_TEXT = 0x9200,
|
||||
TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW = 0x9210,
|
||||
TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range
|
||||
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range
|
||||
} TextIDs;
|
||||
|
||||
@@ -782,7 +782,7 @@ void DrawFlagTableArray16(const FlagTable& flagTable, uint16_t row, uint16_t& fl
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemHovered() && hasDescription) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", UIWidgets::WrappedText(flagTable.flagDescriptions.at(row * 16 + flagIndex), 60));
|
||||
ImGui::Text("%s", UIWidgets::WrappedText(flagTable.flagDescriptions.at(row * 16 + flagIndex), 60).c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
@@ -37,12 +37,19 @@ GameInteractionEffectQueryResult GameInteractor::RemoveEffect(GameInteractionEff
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
bool GameInteractor::IsSaveLoaded() {
|
||||
bool GameInteractor::IsSaveLoaded(bool allowDbgSave) {
|
||||
Player* player;
|
||||
if (gPlayState != NULL) {
|
||||
player = GET_PLAYER(gPlayState);
|
||||
}
|
||||
return (gPlayState == NULL || player == NULL || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) ? false : true;
|
||||
|
||||
// Checking for normal game mode prevents debug saves from reporting true on title screen
|
||||
if (gPlayState == NULL || player == NULL || gSaveContext.gameMode != GAMEMODE_NORMAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid save file or debug save
|
||||
return (gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2) || (allowDbgSave && gSaveContext.fileNum == 0xFF);
|
||||
}
|
||||
|
||||
bool GameInteractor::IsGameplayPaused() {
|
||||
|
||||
@@ -94,6 +94,7 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#define DEFINE_HOOK(name, type) \
|
||||
struct name { \
|
||||
@@ -193,10 +194,11 @@ public:
|
||||
|
||||
DEFINE_HOOK(OnSetGameLanguage, void());
|
||||
|
||||
DEFINE_HOOK(OnFileDropped, void(std::string filePath));
|
||||
DEFINE_HOOK(OnAssetAltChange, void());
|
||||
|
||||
// Helpers
|
||||
static bool IsSaveLoaded();
|
||||
static bool IsSaveLoaded(bool allowDbgSave = false);
|
||||
static bool IsGameplayPaused();
|
||||
static bool CanSpawnActor();
|
||||
static bool CanAddOrTakeAmmo(int16_t amount, int16_t item);
|
||||
|
||||
@@ -618,7 +618,7 @@ void DrawGameplayStatsOptionsTab() {
|
||||
}
|
||||
|
||||
void GameplayStatsWindow::DrawElement() {
|
||||
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("Gameplay Stats", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
|
||||
@@ -47,7 +47,7 @@ void ReloadSceneTogglingLinkAge() {
|
||||
|
||||
void RegisterInfiniteMoney() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
|
||||
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
|
||||
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
|
||||
@@ -58,7 +58,7 @@ void RegisterInfiniteMoney() {
|
||||
|
||||
void RegisterInfiniteHealth() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
|
||||
if (gSaveContext.health < gSaveContext.healthCapacity) {
|
||||
gSaveContext.health = gSaveContext.healthCapacity;
|
||||
@@ -69,7 +69,7 @@ void RegisterInfiniteHealth() {
|
||||
|
||||
void RegisterInfiniteAmmo() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
|
||||
// Deku Sticks
|
||||
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
|
||||
@@ -106,7 +106,7 @@ void RegisterInfiniteAmmo() {
|
||||
|
||||
void RegisterInfiniteMagic() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
|
||||
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
|
||||
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
|
||||
@@ -117,7 +117,7 @@ void RegisterInfiniteMagic() {
|
||||
|
||||
void RegisterInfiniteNayrusLove() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
|
||||
gSaveContext.nayrusLoveTimer = 0x44B;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ void RegisterInfiniteNayrusLove() {
|
||||
|
||||
void RegisterMoonJumpOnL() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -141,7 +141,7 @@ void RegisterMoonJumpOnL() {
|
||||
|
||||
void RegisterInfiniteISG() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gEzISG", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -153,7 +153,7 @@ void RegisterInfiniteISG() {
|
||||
//Permanent quick put away (QPA) glitched damage value
|
||||
void RegisterEzQPA() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gEzQPA", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -165,7 +165,7 @@ void RegisterEzQPA() {
|
||||
|
||||
void RegisterUnrestrictedItems() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
|
||||
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
|
||||
@@ -193,11 +193,14 @@ void RegisterFreezeTime() {
|
||||
/// Switches Link's age and respawns him at the last entrance he entered.
|
||||
void RegisterSwitchAge() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
static bool warped = false;
|
||||
|
||||
if (!GameInteractor::IsSaveLoaded(true)) {
|
||||
CVarClear("gSwitchAge");
|
||||
warped = false;
|
||||
return;
|
||||
}
|
||||
static bool warped = false;
|
||||
|
||||
static Vec3f playerPos;
|
||||
static int16_t playerYaw;
|
||||
static RoomContext* roomCtx;
|
||||
@@ -231,7 +234,7 @@ void RegisterSwitchAge() {
|
||||
void RegisterOcarinaTimeTravel() {
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
if (!GameInteractor::IsSaveLoaded(true)) {
|
||||
CVarClear("gTimeTravel");
|
||||
return;
|
||||
}
|
||||
@@ -1038,8 +1041,8 @@ void RegisterRandomizedEnemySizes() {
|
||||
uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || (actor->id == ACTOR_BOSS_SST && actor->params == -1);
|
||||
|
||||
// Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger.
|
||||
uint8_t smallOnlyEnemy =
|
||||
actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || ACTOR_EN_DH;
|
||||
uint8_t smallOnlyEnemy = actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD ||
|
||||
actor->id == ACTOR_BOSS_FD2 || actor->id == ACTOR_EN_DH;
|
||||
|
||||
// Only apply to enemies and bosses.
|
||||
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || excludedEnemy) {
|
||||
|
||||
@@ -12,6 +12,14 @@ void clearCvars(std::vector<const char*> cvarsToClear) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatLocations(std::vector<RandomizerCheck> locs) {
|
||||
std::string locString = "";
|
||||
for (auto loc: locs) {
|
||||
locString += std::to_string(loc) + ",";
|
||||
}
|
||||
return locString;
|
||||
}
|
||||
|
||||
void applyPreset(std::vector<PresetEntry> entries) {
|
||||
for(auto& [cvar, type, value] : entries) {
|
||||
switch (type) {
|
||||
@@ -24,6 +32,9 @@ void applyPreset(std::vector<PresetEntry> entries) {
|
||||
case PRESET_ENTRY_TYPE_STRING:
|
||||
CVarSetString(cvar, std::get<const char*>(value));
|
||||
break;
|
||||
case PRESET_ENTRY_TYPE_CPP_STRING:
|
||||
CVarSetString(cvar, std::get<std::string>(value).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <cstdint>
|
||||
@@ -11,6 +12,7 @@ enum PresetEntryType {
|
||||
PRESET_ENTRY_TYPE_S32,
|
||||
PRESET_ENTRY_TYPE_FLOAT,
|
||||
PRESET_ENTRY_TYPE_STRING,
|
||||
PRESET_ENTRY_TYPE_CPP_STRING,
|
||||
};
|
||||
|
||||
enum PresetType {
|
||||
@@ -36,15 +38,19 @@ enum RandomizerPreset {
|
||||
typedef struct PresetEntry {
|
||||
const char* cvar;
|
||||
PresetEntryType type;
|
||||
std::variant<int32_t, float, const char*> value;
|
||||
std::variant<int32_t, float, const char*, std::string> value;
|
||||
} PresetEntry;
|
||||
|
||||
std::string FormatLocations(std::vector<RandomizerCheck> locs);
|
||||
|
||||
#define PRESET_ENTRY_S32(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_S32, value }
|
||||
#define PRESET_ENTRY_FLOAT(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_FLOAT, value }
|
||||
#define PRESET_ENTRY_STRING(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_STRING, value }
|
||||
#define PRESET_ENTRY_CPP_STRING(cvar, value) \
|
||||
{ cvar, PRESET_ENTRY_TYPE_CPP_STRING, value }
|
||||
|
||||
void DrawPresetSelector(PresetType presetType);
|
||||
void clearCvars(std::vector<const char*> cvarsToClear);
|
||||
@@ -205,6 +211,8 @@ const std::vector<const char*> enhancementsCvars = {
|
||||
"gDisableLOD",
|
||||
"gDisableDrawDistance",
|
||||
"gDisableKokiriDrawDistance",
|
||||
"gEnhancements.WidescreenActorCulling",
|
||||
"gEnhancements.ExtendedCullingExcludeGlitchActors",
|
||||
"gLowResMode",
|
||||
"gDrawLineupTick",
|
||||
"gQuickBongoKill",
|
||||
@@ -866,7 +874,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 +967,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 +1021,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),
|
||||
|
||||
@@ -846,10 +846,10 @@ static void RandomizeDungeonItems() {
|
||||
}
|
||||
|
||||
if (GerudoKeys.Is(GERUDOKEYS_ANY_DUNGEON)) {
|
||||
auto gerudoKeys = FilterAndEraseFromPool(ItemPool, [](const auto i) { return i == GERUDO_FORTRESS_SMALL_KEY; });
|
||||
auto gerudoKeys = FilterAndEraseFromPool(ItemPool, [](const auto i) { return i == GERUDO_FORTRESS_SMALL_KEY || i == GERUDO_FORTRESS_KEY_RING; });
|
||||
AddElementsToPool(anyDungeonItems, gerudoKeys);
|
||||
} else if (GerudoKeys.Is(GERUDOKEYS_OVERWORLD)) {
|
||||
auto gerudoKeys = FilterAndEraseFromPool(ItemPool, [](const auto i) { return i == GERUDO_FORTRESS_SMALL_KEY; });
|
||||
auto gerudoKeys = FilterAndEraseFromPool(ItemPool, [](const auto i) { return i == GERUDO_FORTRESS_SMALL_KEY || i == GERUDO_FORTRESS_KEY_RING; });
|
||||
AddElementsToPool(overworldItems, gerudoKeys);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ void AreaTable_Init_DeathMountain() {
|
||||
Entrance(DEATH_MOUNTAIN_TRAIL, {[]{return true;}}),
|
||||
Entrance(GC_WOODS_WARP, {[]{return GCWoodsWarpOpen;}}),
|
||||
Entrance(GC_SHOP, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && (CanBlastOrSmash || GoronBracelet || GoronCityChildFire || CanUse(BOW)));}}),
|
||||
Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || GCDaruniasDoorOpenChild;}}),
|
||||
Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && GCDaruniasDoorOpenChild);}}),
|
||||
Entrance(GC_GROTTO_PLATFORM, {[]{return IsAdult && ((CanPlay(SongOfTime) && ((EffectiveHealth > 2) || CanUse(GORON_TUNIC) || CanUse(LONGSHOT) || CanUse(NAYRUS_LOVE))) || (EffectiveHealth > 1 && CanUse(GORON_TUNIC) && CanUse(HOOKSHOT)) || (CanUse(NAYRUS_LOVE) && CanUse(HOOKSHOT)) || (EffectiveHealth > 2 && CanUse(HOOKSHOT) && LogicGoronCityGrotto));}}),
|
||||
});
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ void AreaTable_Init_FireTemple() {
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(FIRE_TEMPLE_FIRST_ROOM, {[]{return true;}}),
|
||||
Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && LogicFireBossDoorJump) || CanUse(HOVER_BOOTS) || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}));}}),
|
||||
Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && (LogicFireBossDoorJump || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}))) || CanUse(HOVER_BOOTS));}}),
|
||||
});
|
||||
|
||||
areaTable[FIRE_TEMPLE_LOOP_ENEMIES] = Area("Fire Temple Loop Enemies", "Fire Temple", FIRE_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
|
||||
@@ -172,7 +172,7 @@ void AreaTable_Init_ForestTemple() {
|
||||
Entrance(FOREST_TEMPLE_WEST_CORRIDOR, {[]{return true;}}),
|
||||
Entrance(FOREST_TEMPLE_NW_OUTDOORS_UPPER, {[]{return CanUse(HOVER_BOOTS) || (LogicForestOutsideBackdoor && CanJumpslash && GoronBracelet);}}),
|
||||
Entrance(FOREST_TEMPLE_NW_CORRIDOR_TWISTED, {[]{return IsAdult && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
|
||||
Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
|
||||
Entrance(FOREST_TEMPLE_NW_CORRIDOR_STRAIGHTENED, {[]{return IsAdult && (CanUse(BOW) || CanUse(SLINGSHOT)) && GoronBracelet && SmallKeys(FOREST_TEMPLE, 2);}}),
|
||||
});
|
||||
|
||||
areaTable[FOREST_TEMPLE_NW_CORRIDOR_TWISTED] = Area("Forest Temple NW Corridor Twisted", "Forest Temple", FOREST_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
|
||||
@@ -536,7 +536,7 @@ namespace Logic {
|
||||
Fish = HasBottle && FishAccess;
|
||||
Fairy = HasBottle && FairyAccess;
|
||||
|
||||
FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20);
|
||||
FoundBombchus = (BombchuDrop || Bombchus || Bombchus5 || Bombchus10 || Bombchus20) && (BombBag || BombchusInLogic);
|
||||
CanPlayBowling = (BombchusInLogic && FoundBombchus) || (!BombchusInLogic && BombBag);
|
||||
HasBombchus = (BuyBombchus10 || BuyBombchus20 || (AmmoDrops.Is(AMMODROPS_BOMBCHU) && FoundBombchus));
|
||||
|
||||
|
||||
@@ -364,31 +364,16 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
|
||||
try {
|
||||
if (strcmp(spoilerFileName, "") != 0) {
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
json spoilerFileJson;
|
||||
spoilerFileStream >> spoilerFileJson;
|
||||
|
||||
if (!spoilerFileJson.contains("version") || !spoilerFileJson.contains("finalSeed")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(spoilerFileName, "") != 0) {
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (std::exception& e) {
|
||||
SPDLOG_ERROR("Error checking if spoiler file exists: {}", e.what());
|
||||
return false;
|
||||
} catch (...) {
|
||||
SPDLOG_ERROR("Error checking if spoiler file exists");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
#pragma optimize("", on)
|
||||
@@ -494,6 +479,13 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
|
||||
"Zu {{location}}?\x1B&%gOK&No%w\x02",
|
||||
"Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02"));
|
||||
|
||||
// Bow Shooting Gallery reminder
|
||||
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW,
|
||||
CustomMessage("Come back when you have your own&bow and you'll get a %rdifferent prize%w!",
|
||||
"Komm wieder sobald du deinen eigenen&Bogen hast, um einen %rspeziellen Preis%w zu&erhalten!",
|
||||
"J'aurai %rune autre récompense%w pour toi&lorsque tu auras ton propre arc."));
|
||||
|
||||
// Lake Hylia water level system
|
||||
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN,
|
||||
CustomMessage("Water level control system.&Keep away!",
|
||||
"Wasserstand Kontrollsystem&Finger weg!",
|
||||
@@ -4215,7 +4207,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount",
|
||||
"gRandomizeLacsStoneCount", 1, 4, "", 4, true, true, false);
|
||||
"gRandomizeLacsStoneCount", 1, 4, "", 3, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount",
|
||||
@@ -4244,7 +4236,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount",
|
||||
"gRandomizeLacsMedallionCount", 1, 7, "", 7, true, true, false);
|
||||
"gRandomizeLacsMedallionCount", 1, 7, "", 6, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount",
|
||||
@@ -4273,7 +4265,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount",
|
||||
"gRandomizeLacsRewardCount", 1, 10, "", 10, true, true, false);
|
||||
"gRandomizeLacsRewardCount", 1, 10, "", 9, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount",
|
||||
@@ -4302,7 +4294,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
break;
|
||||
case RO_LACS_GREG_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount",
|
||||
"gRandomizeLacsDungeonCount", 1, 9, "", 9, true, true, false);
|
||||
"gRandomizeLacsDungeonCount", 1, 9, "", 8, true, true, false);
|
||||
break;
|
||||
case RO_LACS_WILDCARD_REWARD:
|
||||
UIWidgets::PaddedEnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount",
|
||||
@@ -4697,7 +4689,11 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
excludedLocationString += std::to_string(excludedLocationIt);
|
||||
excludedLocationString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
|
||||
if (excludedLocationString == "") {
|
||||
CVarClear("gRandomizeExcludedLocations");
|
||||
} else {
|
||||
CVarSetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
|
||||
}
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
@@ -4874,7 +4870,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
enabledTrickString += std::to_string(enabledTrickIt);
|
||||
enabledTrickString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
CVarClear("gRandomizeEnabledTricks");
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
@@ -5078,7 +5074,7 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
enabledTrickString += std::to_string(enabledTrickIt);
|
||||
enabledTrickString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
CVarClear("gRandomizeEnabledTricks");
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
|
||||
@@ -5116,7 +5112,11 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
enabledTrickString += std::to_string(enabledTrickIt);
|
||||
enabledTrickString += ",";
|
||||
}
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
if (enabledTrickString == "") {
|
||||
CVarClear("gRandomizeEnabledTricks");
|
||||
} else {
|
||||
CVarSetString("gRandomizeEnabledTricks", enabledTrickString.c_str());
|
||||
}
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
DrawTagChips(*rtObject.rtTags);
|
||||
|
||||
@@ -78,6 +78,7 @@ bool initialized;
|
||||
bool doAreaScroll;
|
||||
bool previousShowHidden = false;
|
||||
bool hideShopRightChecks = true;
|
||||
bool alwaysShowGS = false;
|
||||
|
||||
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
|
||||
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
|
||||
@@ -102,12 +103,30 @@ std::map<SceneID, RandomizerCheckArea> DungeonRCAreasBySceneID = {
|
||||
{SCENE_INSIDE_GANONS_CASTLE, RCAREA_GANONS_CASTLE},
|
||||
};
|
||||
|
||||
// Dungeon entrances with obvious visual differences between MQ and vanilla qualifying as spoiling on sight
|
||||
std::vector<uint32_t> spoilingEntrances = {
|
||||
0x0000, // ENTR_DEKU_TREE_0
|
||||
0x0467, // ENTR_DODONGOS_CAVERN_1
|
||||
0x0028, // ENTR_JABU_JABU_0
|
||||
0x0407, // ENTR_JABU_JABU_1
|
||||
0x0169, // ENTR_FOREST_TEMPLE_0
|
||||
0x0165, // ENTR_FIRE_TEMPLE_0
|
||||
0x0175, // ENTR_FIRE_TEMPLE_1
|
||||
0x0423, // ENTR_WATER_TEMPLE_1
|
||||
0x0082, // ENTR_SPIRIT_TEMPLE_0
|
||||
0x02B2, // ENTR_SHADOW_TEMPLE_1
|
||||
0x0088, // ENTR_ICE_CAVERN_0
|
||||
0x0008, // ENTR_GERUDO_TRAINING_GROUNDS_0
|
||||
0x0467 // ENTR_INSIDE_GANONS_CASTLE_0
|
||||
};
|
||||
|
||||
std::map<RandomizerCheckArea, std::vector<RandomizerCheckObject>> checksByArea;
|
||||
bool areasFullyChecked[RCAREA_INVALID];
|
||||
u32 areasSpoiled = 0;
|
||||
bool showVOrMQ;
|
||||
s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)"
|
||||
bool optCollapseAll; // A bool that will collapse all checks once
|
||||
s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)"
|
||||
s8 areaCheckTotals[RCAREA_INVALID];
|
||||
bool optCollapseAll; // A bool that will collapse all checks once
|
||||
bool optExpandAll; // A bool that will expand all checks once
|
||||
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
|
||||
RandomizerCheckArea previousArea = RCAREA_INVALID;
|
||||
@@ -224,6 +243,26 @@ void TrySetAreas() {
|
||||
}
|
||||
}
|
||||
|
||||
void RecalculateAreaTotals() {
|
||||
for (auto [rcArea, rcObjects] : checksByArea) {
|
||||
if (rcArea == RCAREA_INVALID) {
|
||||
return;
|
||||
}
|
||||
areaChecksGotten[rcArea] = 0;
|
||||
areaCheckTotals[rcArea] = 0;
|
||||
for (auto rcObj : rcObjects) {
|
||||
if (!IsVisibleInCheckTracker(rcObj)) {
|
||||
continue;
|
||||
}
|
||||
areaCheckTotals[rcArea]++;
|
||||
if (gSaveContext.checkTrackerData[rcObj.rc].skipped || gSaveContext.checkTrackerData[rcObj.rc].status == RCSHOW_COLLECTED
|
||||
|| gSaveContext.checkTrackerData[rcObj.rc].status == RCSHOW_SAVED) {
|
||||
areaChecksGotten[rcArea]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetCheckCollected(RandomizerCheck rc) {
|
||||
gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED;
|
||||
RandomizerCheckObject rcObj;
|
||||
@@ -232,13 +271,19 @@ 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);
|
||||
|
||||
if (!IsAreaSpoiled(rcObj.rcArea)) {
|
||||
SetAreaSpoiled(rcObj.rcArea);
|
||||
}
|
||||
|
||||
doAreaScroll = true;
|
||||
UpdateOrdering(rcObj.rcArea);
|
||||
UpdateInventoryChecks();
|
||||
@@ -345,6 +390,7 @@ void ClearAreaChecksAndTotals() {
|
||||
for (auto& [rcArea, vec] : checksByArea) {
|
||||
vec.clear();
|
||||
areaChecksGotten[rcArea] = 0;
|
||||
areaCheckTotals[rcArea] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,9 +470,9 @@ void CheckTrackerLoadGame(int32_t fileNum) {
|
||||
TrySetAreas();
|
||||
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc];
|
||||
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET ||
|
||||
!RandomizerCheckObjects::GetAllRCObjects().contains(rc))
|
||||
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET || !RandomizerCheckObjects::GetAllRCObjects().contains(rc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RandomizerCheckObject realRcObj;
|
||||
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
|
||||
@@ -434,15 +480,22 @@ void CheckTrackerLoadGame(int32_t fileNum) {
|
||||
} else {
|
||||
realRcObj = rcObj;
|
||||
}
|
||||
if (!IsVisibleInCheckTracker(realRcObj)) continue;
|
||||
|
||||
checksByArea.find(realRcObj.rcArea)->second.push_back(realRcObj);
|
||||
if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
|
||||
areaChecksGotten[realRcObj.rcArea]++;
|
||||
if (IsVisibleInCheckTracker(realRcObj)) {
|
||||
areaCheckTotals[realRcObj.rcArea]++;
|
||||
if (rcTrackerData.status == RCSHOW_COLLECTED || rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
|
||||
areaChecksGotten[realRcObj.rcArea]++;
|
||||
}
|
||||
}
|
||||
|
||||
if (areaChecksGotten[realRcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(realRcObj.rcArea)) {
|
||||
areasSpoiled |= (1 << realRcObj.rcArea);
|
||||
}
|
||||
for (int i = RCAREA_KOKIRI_FOREST; i < RCAREA_INVALID; i++) {
|
||||
if (!IsAreaSpoiled(static_cast<RandomizerCheckArea>(i)) && (RandomizerCheckObjects::AreaIsOverworld(static_cast<RandomizerCheckArea>(i)) || !IS_RANDO ||
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_NONE ||
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SELECTION ||
|
||||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12))) {
|
||||
SetAreaSpoiled(static_cast<RandomizerCheckArea>(i));
|
||||
}
|
||||
}
|
||||
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) {
|
||||
@@ -463,6 +516,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 ||
|
||||
@@ -511,6 +565,9 @@ void CheckTrackerTransition(uint32_t sceneNum) {
|
||||
SetShopSeen(sceneNum, false);
|
||||
break;
|
||||
}
|
||||
if (!IsAreaSpoiled(currentArea) && (RandomizerCheckObjects::AreaIsOverworld(currentArea) || std::find(spoilingEntrances.begin(), spoilingEntrances.end(), gPlayState->nextEntranceIndex) != spoilingEntrances.end())) {
|
||||
SetAreaSpoiled(currentArea);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerFrame() {
|
||||
@@ -728,6 +785,7 @@ void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
|
||||
|
||||
void InitTrackerData(bool isDebug) {
|
||||
TrySetAreas();
|
||||
areasSpoiled = 0;
|
||||
for (auto& [rc, rco] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
if (rc != RC_UNKNOWN_CHECK && rc != RC_MAX) {
|
||||
DefaultCheckData(rc);
|
||||
@@ -755,6 +813,7 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
|
||||
SaveManager::Instance->SaveData("hintItem", saveContext->checkTrackerData[i].hintItem);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->SaveData("areasSpoiled", areasSpoiled);
|
||||
}
|
||||
|
||||
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
|
||||
@@ -770,6 +829,7 @@ void LoadFile() {
|
||||
SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled);
|
||||
}
|
||||
|
||||
void Teardown() {
|
||||
@@ -781,11 +841,24 @@ void Teardown() {
|
||||
lastLocationChecked = RC_UNKNOWN_CHECK;
|
||||
}
|
||||
|
||||
bool IsAreaSpoiled(RandomizerCheckArea rcArea) {
|
||||
return areasSpoiled & (1 << rcArea);
|
||||
}
|
||||
|
||||
void SetAreaSpoiled(RandomizerCheckArea rcArea) {
|
||||
areasSpoiled |= (1 << rcArea);
|
||||
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -795,10 +868,6 @@ void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) {
|
||||
void CheckTrackerWindow::DrawElement() {
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 540), ImGuiCond_FirstUseEver);
|
||||
|
||||
if (!initialized && (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) {
|
||||
if (CVarGetInteger("gCheckTrackerShowOnlyPaused", 0) && (gPlayState == nullptr || gPlayState->pauseCtx.state == 0)) {
|
||||
return;
|
||||
@@ -819,7 +888,7 @@ void CheckTrackerWindow::DrawElement() {
|
||||
|
||||
BeginFloatWindows("Check Tracker", mIsVisible, ImGuiWindowFlags_NoScrollbar);
|
||||
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
if (!GameInteractor::IsSaveLoaded() || !initialized) {
|
||||
ImGui::Text("Waiting for file load..."); //TODO Language
|
||||
EndFloatWindows();
|
||||
return;
|
||||
@@ -830,8 +899,6 @@ void CheckTrackerWindow::DrawElement() {
|
||||
sceneId = (SceneID)gPlayState->sceneNum;
|
||||
}
|
||||
|
||||
areasSpoiled |= (1 << currentArea);
|
||||
|
||||
//Quick Options
|
||||
#ifdef __WIIU__
|
||||
float headerHeight = 40.0f;
|
||||
@@ -893,13 +960,11 @@ void CheckTrackerWindow::DrawElement() {
|
||||
Color_RGBA8 mainColor;
|
||||
Color_RGBA8 extraColor;
|
||||
std::string stemp;
|
||||
s32 areaMask = 1;
|
||||
|
||||
for (auto& [rcArea, objs] : checksByArea) {
|
||||
RandomizerCheckArea thisArea = currentArea;
|
||||
|
||||
const int areaChecksTotal = static_cast<int>(objs.size());
|
||||
thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaChecksTotal);
|
||||
thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaCheckTotals[rcArea]);
|
||||
//Last Area needs to be cleaned up
|
||||
if (lastArea != RCAREA_INVALID && doDraw) {
|
||||
UIWidgets::PaddedSeparator();
|
||||
@@ -936,30 +1001,29 @@ void CheckTrackerWindow::DrawElement() {
|
||||
stemp = RandomizerCheckObjects::GetRCAreaName(rcArea) + "##TreeNode";
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f,
|
||||
mainColor.b / 255.0f, mainColor.a / 255.0f));
|
||||
if (doingCollapseOrExpand)
|
||||
if (doingCollapseOrExpand) {
|
||||
ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always);
|
||||
else
|
||||
} else {
|
||||
ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once);
|
||||
}
|
||||
doDraw = ImGui::TreeNode(stemp.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f,
|
||||
extraColor.b / 255.0f, extraColor.a / 255.0f));
|
||||
|
||||
isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0) || !IS_RANDO ||
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_NONE ||
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SELECTION ||
|
||||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12);
|
||||
isThisAreaSpoiled = IsAreaSpoiled(rcArea) || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0);
|
||||
|
||||
if (isThisAreaSpoiled) {
|
||||
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
|
||||
if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(DungeonSceneLookupByArea(rcArea)))
|
||||
ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaChecksTotal);
|
||||
else
|
||||
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaChecksTotal);
|
||||
if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(
|
||||
DungeonSceneLookupByArea(rcArea))) {
|
||||
ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
|
||||
} else {
|
||||
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaChecksTotal);
|
||||
ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("???");
|
||||
@@ -973,13 +1037,14 @@ 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;
|
||||
}
|
||||
|
||||
ImGui::EndTable(); //Checks Lead-out
|
||||
@@ -1084,7 +1149,6 @@ void LoadSettings() {
|
||||
showLinksPocket = IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING
|
||||
:false;
|
||||
hideShopRightChecks = IS_RANDO ? CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1) : false;
|
||||
|
||||
if (IS_RANDO) {
|
||||
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) {
|
||||
@@ -1148,7 +1212,7 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
|
||||
) &&
|
||||
(rcObj.rcType != RCTYPE_MERCHANT || showMerchants) &&
|
||||
(rcObj.rcType != RCTYPE_OCARINA || showOcarinas) &&
|
||||
(rcObj.rcType != RCTYPE_SKULL_TOKEN ||
|
||||
(rcObj.rcType != RCTYPE_SKULL_TOKEN || alwaysShowGS ||
|
||||
(showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) ||
|
||||
(showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea))
|
||||
) &&
|
||||
@@ -1184,10 +1248,10 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
|
||||
}
|
||||
|
||||
void UpdateInventoryChecks() {
|
||||
//For all the areas with compasses, if you have one, spoil the area
|
||||
//For all the areas with maps, if you have one, spoil the area
|
||||
for (auto [scene, area] : DungeonRCAreasBySceneID) {
|
||||
if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, scene)) {
|
||||
areasSpoiled |= (1 << area);
|
||||
SetAreaSpoiled(area);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1197,8 +1261,6 @@ void UpdateAreaFullyChecked(RandomizerCheckArea area) {
|
||||
|
||||
void UpdateAreas(RandomizerCheckArea area) {
|
||||
areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size();
|
||||
if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area))
|
||||
areasSpoiled |= (1 << area);
|
||||
}
|
||||
|
||||
void UpdateAllOrdering() {
|
||||
@@ -1226,30 +1288,36 @@ bool CompareChecks(RandomizerCheckObject i, RandomizerCheckObject j) {
|
||||
bool iSaved = iShow.status == RCSHOW_SAVED;
|
||||
bool jCollected = jShow.status == RCSHOW_COLLECTED || jShow.status == RCSHOW_SAVED;
|
||||
bool jSaved = jShow.status == RCSHOW_SAVED;
|
||||
if (!iCollected && jCollected)
|
||||
return true;
|
||||
else if (iCollected && !jCollected)
|
||||
return false;
|
||||
|
||||
if (!iSaved && jSaved)
|
||||
if (!iCollected && jCollected) {
|
||||
return true;
|
||||
else if (iSaved && !jSaved)
|
||||
} else if (iCollected && !jCollected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!iShow.skipped && jShow.skipped)
|
||||
if (!iSaved && jSaved) {
|
||||
return true;
|
||||
else if (iShow.skipped && !jShow.skipped)
|
||||
} else if (iSaved && !jSaved) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType))
|
||||
if (!iShow.skipped && jShow.skipped) {
|
||||
return true;
|
||||
else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType))
|
||||
} else if (iShow.skipped && !jShow.skipped) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i.rc < j.rc)
|
||||
if (!IsEoDCheck(i.rcType) && IsEoDCheck(j.rcType)) {
|
||||
return true;
|
||||
else if (i.rc > j.rc)
|
||||
} else if (IsEoDCheck(i.rcType) && !IsEoDCheck(j.rcType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i.rc < j.rc) {
|
||||
return true;
|
||||
} else if (i.rc > j.rc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1267,47 +1335,54 @@ void DrawLocation(RandomizerCheckObject rcObj) {
|
||||
RandomizerCheckStatus status = checkData.status;
|
||||
bool skipped = checkData.skipped;
|
||||
if (status == RCSHOW_COLLECTED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) {
|
||||
return;
|
||||
}
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default);
|
||||
CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default);
|
||||
extraColor = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default);
|
||||
} else if (status == RCSHOW_SAVED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) {
|
||||
return;
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default);
|
||||
}
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default);
|
||||
extraColor = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default);
|
||||
} else if (skipped) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) {
|
||||
return;
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default);
|
||||
}
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default);
|
||||
extraColor = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default);
|
||||
} else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) {
|
||||
return;
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default);
|
||||
}
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default);
|
||||
extraColor = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default);
|
||||
} else if (status == RCSHOW_SCUMMED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerKnownHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerScummedHide", 0)) {
|
||||
return;
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default);
|
||||
}
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default);
|
||||
extraColor = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default);
|
||||
} else if (status == RCSHOW_UNCHECKED) {
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0))
|
||||
if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) {
|
||||
return;
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default);
|
||||
}
|
||||
mainColor = !IsHeartPiece(rcObj.ogItemId) && !IS_RANDO ? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default) :
|
||||
CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default);
|
||||
extraColor = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default);
|
||||
}
|
||||
|
||||
//Main Text
|
||||
txt = rcObj.rcShortName;
|
||||
if (lastLocationChecked == rcObj.rc)
|
||||
if (lastLocationChecked == rcObj.rc) {
|
||||
txt = "* " + txt;
|
||||
}
|
||||
|
||||
// Draw button - for Skipped/Seen/Scummed/Unchecked only
|
||||
if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) {
|
||||
@@ -1382,8 +1457,9 @@ void DrawLocation(RandomizerCheckObject rcObj) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (txt == "" && skipped)
|
||||
txt = "Skipped"; //TODO language
|
||||
if (txt == "" && skipped) {
|
||||
txt = "Skipped"; // TODO language
|
||||
}
|
||||
|
||||
if (txt != "") {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f));
|
||||
@@ -1409,8 +1485,9 @@ int hue = 0;
|
||||
void RainbowTick() {
|
||||
float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat("gCosmetics.RainbowSpeed", 0.6f));
|
||||
for (auto& cvar : rainbowCVars) {
|
||||
if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0)
|
||||
if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Color_RGBA8 newColor;
|
||||
newColor.r = sin(freqHue + 0) * 127 + 128;
|
||||
@@ -1517,8 +1594,16 @@ void CheckTrackerSettingsWindow::DrawElement() {
|
||||
}
|
||||
UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers");
|
||||
UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.");
|
||||
UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops. Requires save reload.");
|
||||
if (UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true)) {
|
||||
hideShopRightChecks = !hideShopRightChecks;
|
||||
RecalculateAreaTotals();
|
||||
}
|
||||
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops.");
|
||||
if (UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "")) {
|
||||
alwaysShowGS = !alwaysShowGS;
|
||||
RecalculateAreaTotals();
|
||||
}
|
||||
UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings.");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
@@ -1572,6 +1657,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
|
||||
void InitTrackerData(bool isDebug);
|
||||
RandomizerCheckArea GetCheckArea();
|
||||
void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
|
||||
bool IsAreaSpoiled(RandomizerCheckArea rcArea);
|
||||
void SetAreaSpoiled(RandomizerCheckArea rcArea);
|
||||
} // namespace CheckTracker
|
||||
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "randomizer_check_tracker.h"
|
||||
#include <algorithm>
|
||||
|
||||
extern "C" {
|
||||
@@ -30,6 +31,8 @@ void DrawBottle(ItemTrackerItem item);
|
||||
void DrawQuest(ItemTrackerItem item);
|
||||
void DrawSong(ItemTrackerItem item);
|
||||
|
||||
int itemTrackerSectionId;
|
||||
|
||||
bool shouldUpdateVectors = true;
|
||||
|
||||
std::vector<ItemTrackerItem> mainWindowItems = {};
|
||||
@@ -282,26 +285,21 @@ 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;
|
||||
}
|
||||
|
||||
bool HasSong(ItemTrackerItem item) {
|
||||
return (1 << item.id) & gSaveContext.inventory.questItems;
|
||||
return GameInteractor::IsSaveLoaded() ? ((1 << item.id) & gSaveContext.inventory.questItems) : false;
|
||||
}
|
||||
|
||||
bool HasQuestItem(ItemTrackerItem item) {
|
||||
return (item.data & gSaveContext.inventory.questItems) != 0;
|
||||
return GameInteractor::IsSaveLoaded() ? (item.data & gSaveContext.inventory.questItems) : false;
|
||||
}
|
||||
|
||||
bool HasEquipment(ItemTrackerItem item) {
|
||||
return (item.data & gSaveContext.inventory.equipment) != 0;
|
||||
return GameInteractor::IsSaveLoaded() ? (item.data & gSaveContext.inventory.equipment) : false;
|
||||
}
|
||||
|
||||
ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
|
||||
@@ -409,8 +407,12 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
|
||||
#define IM_COL_GREEN IM_COL32(0, 255, 0, 255)
|
||||
#define IM_COL_GRAY IM_COL32(155, 155, 155, 255)
|
||||
#define IM_COL_PURPLE IM_COL32(180, 90, 200, 255)
|
||||
#define IM_COL_LIGHT_YELLOW IM_COL32(255, 255, 130, 255)
|
||||
|
||||
void DrawItemCount(ItemTrackerItem item) {
|
||||
void DrawItemCount(ItemTrackerItem item, bool hideMax) {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
return;
|
||||
}
|
||||
int iconSize = CVarGetInteger("gItemTrackerIconSize", 36);
|
||||
ItemTrackerNumbers currentAndMax = GetItemCurrentAndMax(item);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
@@ -419,7 +421,7 @@ void DrawItemCount(ItemTrackerItem item) {
|
||||
|
||||
if (item.id == ITEM_KEY_SMALL && IsValidSaveFile()) {
|
||||
std::string currentString = "";
|
||||
std::string maxString = std::to_string(currentAndMax.maxCapacity);
|
||||
std::string maxString = hideMax ? "???" : std::to_string(currentAndMax.maxCapacity);
|
||||
ImU32 currentColor = IM_COL_WHITE;
|
||||
ImU32 maxColor = IM_COL_GREEN;
|
||||
// "Collected / Max", "Current / Collected / Max", "Current / Max"
|
||||
@@ -551,7 +553,7 @@ void DrawQuest(ItemTrackerItem item) {
|
||||
ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1));
|
||||
|
||||
if (item.id == QUEST_SKULL_TOKEN) {
|
||||
DrawItemCount(item);
|
||||
DrawItemCount(item, false);
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
@@ -561,7 +563,7 @@ void DrawQuest(ItemTrackerItem item) {
|
||||
|
||||
void DrawItem(ItemTrackerItem item) {
|
||||
|
||||
uint32_t actualItemId = INV_CONTENT(item.id);
|
||||
uint32_t actualItemId = GameInteractor::IsSaveLoaded() ? INV_CONTENT(item.id) : ITEM_NONE;
|
||||
int iconSize = CVarGetInteger("gItemTrackerIconSize", 36);
|
||||
bool hasItem = actualItemId != ITEM_NONE;
|
||||
std::string itemName = "";
|
||||
@@ -620,7 +622,7 @@ void DrawItem(ItemTrackerItem item) {
|
||||
ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasItem && IsValidSaveFile() ? item.name : item.nameFaded),
|
||||
ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1));
|
||||
|
||||
DrawItemCount(item);
|
||||
DrawItemCount(item, false);
|
||||
ImGui::EndGroup();
|
||||
|
||||
if (itemName == "") {
|
||||
@@ -631,7 +633,7 @@ void DrawItem(ItemTrackerItem item) {
|
||||
}
|
||||
|
||||
void DrawBottle(ItemTrackerItem item) {
|
||||
uint32_t actualItemId = gSaveContext.inventory.items[SLOT(item.id) + item.data];
|
||||
uint32_t actualItemId = GameInteractor::IsSaveLoaded() ? (gSaveContext.inventory.items[SLOT(item.id) + item.data]) : false;
|
||||
bool hasItem = actualItemId != ITEM_NONE;
|
||||
|
||||
if (GameInteractor::IsSaveLoaded() && (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end())) {
|
||||
@@ -650,8 +652,8 @@ void DrawDungeonItem(ItemTrackerItem item) {
|
||||
ImU32 dungeonColor = IM_COL_WHITE;
|
||||
uint32_t bitMask = 1 << (item.id - ITEM_KEY_BOSS); // Bitset starts at ITEM_KEY_BOSS == 0. the rest are sequential
|
||||
int iconSize = CVarGetInteger("gItemTrackerIconSize", 36);
|
||||
bool hasItem = (bitMask & gSaveContext.inventory.dungeonItems[item.data]) != 0;
|
||||
bool hasSmallKey = (gSaveContext.inventory.dungeonKeys[item.data]) >= 0;
|
||||
bool hasItem = GameInteractor::IsSaveLoaded() ? (bitMask & gSaveContext.inventory.dungeonItems[item.data]) : false;
|
||||
bool hasSmallKey = GameInteractor::IsSaveLoaded() ? ((gSaveContext.inventory.dungeonKeys[item.data]) >= 0) : false;
|
||||
ImGui::BeginGroup();
|
||||
if (itemId == ITEM_KEY_SMALL) {
|
||||
ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasSmallKey && IsValidSaveFile() ? item.name : item.nameFaded),
|
||||
@@ -662,16 +664,18 @@ void DrawDungeonItem(ItemTrackerItem item) {
|
||||
ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1));
|
||||
}
|
||||
|
||||
if (ResourceMgr_IsSceneMasterQuest(item.data) && (CHECK_DUNGEON_ITEM(DUNGEON_MAP, item.data) || item.data == SCENE_GERUDO_TRAINING_GROUND || item.data == SCENE_INSIDE_GANONS_CASTLE)) {
|
||||
dungeonColor = IM_COL_PURPLE;
|
||||
if (CheckTracker::IsAreaSpoiled(RandomizerCheckObjects::GetRCAreaBySceneID(static_cast<SceneID>(item.data))) && GameInteractor::IsSaveLoaded()) {
|
||||
dungeonColor = (ResourceMgr_IsSceneMasterQuest(item.data) ? IM_COL_PURPLE : IM_COL_LIGHT_YELLOW);
|
||||
}
|
||||
|
||||
if (itemId == ITEM_KEY_SMALL) {
|
||||
DrawItemCount(item);
|
||||
DrawItemCount(item, !CheckTracker::IsAreaSpoiled(RandomizerCheckObjects::GetRCAreaBySceneID(static_cast<SceneID>(item.data))));
|
||||
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
// offset puts the text at the correct level. for some reason, if the save is loaded, the margin is 3 pixels higher only for small keys, so we use 16 then. Otherwise, 13 is where everything else is
|
||||
int offset = GameInteractor::IsSaveLoaded() ? 16 : 13;
|
||||
std::string dungeonName = itemTrackerDungeonShortNames[item.data];
|
||||
ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + 16)));
|
||||
ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + offset)));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, dungeonColor);
|
||||
ImGui::Text("%s", dungeonName.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
@@ -728,13 +732,15 @@ void DrawNotes(bool resizeable = false) {
|
||||
}
|
||||
};
|
||||
ImVec2 size = resizeable ? ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y) : ImVec2(((iconSize + iconSpacing) * 6) - 8, 200);
|
||||
if (ItemTrackerNotes::TrackerNotesInputTextMultiline("##ItemTrackerNotes", &itemTrackerNotes, size, ImGuiInputTextFlags_AllowTabInput)) {
|
||||
notesNeedSave = true;
|
||||
notesIdleFrames = 0;
|
||||
}
|
||||
if ((ImGui::IsItemDeactivatedAfterEdit() || (notesNeedSave && notesIdleFrames > notesMaxIdleFrames)) && IsValidSaveFile()) {
|
||||
notesNeedSave = false;
|
||||
SaveNotes(gSaveContext.fileNum);
|
||||
if (GameInteractor::IsSaveLoaded()) {
|
||||
if (ItemTrackerNotes::TrackerNotesInputTextMultiline("##ItemTrackerNotes", &itemTrackerNotes, size, ImGuiInputTextFlags_AllowTabInput)) {
|
||||
notesNeedSave = true;
|
||||
notesIdleFrames = 0;
|
||||
}
|
||||
if ((ImGui::IsItemDeactivatedAfterEdit() || (notesNeedSave && notesIdleFrames > notesMaxIdleFrames)) && IsValidSaveFile()) {
|
||||
notesNeedSave = false;
|
||||
SaveManager::Instance->SaveSection(gSaveContext.fileNum, itemTrackerSectionId, true);
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
@@ -959,6 +965,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 +1249,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);
|
||||
}
|
||||
|
||||
@@ -207,6 +207,9 @@ extern "C" void Randomizer_InitSaveFile() {
|
||||
gSaveContext.randomizerInf[i] = 0;
|
||||
}
|
||||
|
||||
// Reset triforce pieces collected
|
||||
gSaveContext.triforcePiecesCollected = 0;
|
||||
|
||||
gSaveContext.cutsceneIndex = 0; // no intro cutscene
|
||||
// Starts pending ice traps out at 0 before potentially incrementing them down the line.
|
||||
gSaveContext.pendingIceTrapCount = 0;
|
||||
@@ -442,8 +445,5 @@ extern "C" void Randomizer_InitSaveFile() {
|
||||
gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth
|
||||
}
|
||||
|
||||
// Reset triforce pieces collected
|
||||
gSaveContext.triforcePiecesCollected = 0;
|
||||
|
||||
SetStartingItems();
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ void RegisterOnInterfaceUpdateHook() {
|
||||
|
||||
prevTimer = timer;
|
||||
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (!GameInteractor::IsSaveLoaded(true)) return;
|
||||
|
||||
static int16_t lostHealth = 0;
|
||||
static int16_t prevHealth = 0;
|
||||
|
||||
+121
-72
@@ -118,6 +118,8 @@ CrowdControl* CrowdControl::Instance;
|
||||
|
||||
#include "soh/config/ConfigUpdaters.h"
|
||||
|
||||
void SoH_ProcessDroppedFiles(std::string filePath);
|
||||
|
||||
OTRGlobals* OTRGlobals::Instance;
|
||||
SaveManager* SaveManager::Instance;
|
||||
CustomMessageManager* CustomMessageManager::Instance;
|
||||
@@ -135,6 +137,8 @@ Color_RGB8 zoraColor = { 0x00, 0xEC, 0x64 };
|
||||
|
||||
float previousImGuiScale;
|
||||
|
||||
bool prevAltAssets = false;
|
||||
|
||||
// Same as NaviColor type from OoT src (z_actor.c), but modified to be sans alpha channel for Controller LED.
|
||||
typedef struct {
|
||||
Color_RGB8 inner;
|
||||
@@ -295,6 +299,9 @@ 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);
|
||||
prevAltAssets = CVarGetInteger("gAltAssets", 0);
|
||||
context->GetResourceManager()->SetAltAssetsEnabled(prevAltAssets);
|
||||
SPDLOG_INFO("Starting Ship of Harkinian version {}", (char*)gBuildVersion);
|
||||
|
||||
context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Animation, "Animation", std::make_shared<LUS::AnimationFactory>());
|
||||
context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared<LUS::PlayerAnimationFactory>());
|
||||
@@ -1035,9 +1042,9 @@ extern "C" void InitOTR() {
|
||||
OTRGlobals::Instance = new OTRGlobals();
|
||||
CustomMessageManager::Instance = new CustomMessageManager();
|
||||
ItemTableManager::Instance = new ItemTableManager();
|
||||
GameInteractor::Instance = new GameInteractor();
|
||||
SaveManager::Instance = new SaveManager();
|
||||
SohGui::SetupGuiElements();
|
||||
GameInteractor::Instance = new GameInteractor();
|
||||
AudioCollection::Instance = new AudioCollection();
|
||||
ActorDB::Instance = new ActorDB();
|
||||
#ifdef __APPLE__
|
||||
@@ -1057,6 +1064,11 @@ extern "C" void InitOTR() {
|
||||
|
||||
InitMods();
|
||||
ActorDB::AddBuiltInCustomActors();
|
||||
// #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer
|
||||
CVarClear("gRandomizerNewFileDropped");
|
||||
CVarClear("gRandomizerDroppedFile");
|
||||
// #endregion
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFileDropped>(SoH_ProcessDroppedFiles);
|
||||
|
||||
time_t now = time(NULL);
|
||||
tm *tm_now = localtime(&now);
|
||||
@@ -1136,8 +1148,7 @@ extern "C" uint64_t GetUnixTimestamp() {
|
||||
auto time = std::chrono::system_clock::now();
|
||||
auto since_epoch = time.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch);
|
||||
long now = millis.count();
|
||||
return now;
|
||||
return (uint64_t)millis.count();
|
||||
}
|
||||
|
||||
// C->C++ Bridge
|
||||
@@ -1145,7 +1156,7 @@ extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) {
|
||||
OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter);
|
||||
}
|
||||
|
||||
extern bool ShouldClearTextureCacheAtEndOfFrame;
|
||||
extern bool ToggleAltAssetsAtEndOfFrame;
|
||||
|
||||
extern "C" void Graph_StartFrame() {
|
||||
#ifndef __WIIU__
|
||||
@@ -1228,14 +1239,21 @@ extern "C" void Graph_StartFrame() {
|
||||
}
|
||||
#endif
|
||||
case KbScancode::LUS_KB_TAB: {
|
||||
// Toggle HD Assets
|
||||
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
|
||||
ShouldClearTextureCacheAtEndOfFrame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (CVarGetInteger("gNewFileDropped", 0)) {
|
||||
std::string filePath = SohUtils::Sanitize(CVarGetString("gDroppedFile", ""));
|
||||
if (!filePath.empty()) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnFileDropped>(filePath);
|
||||
}
|
||||
CVarClear("gNewFileDropped");
|
||||
CVarClear("gDroppedFile");
|
||||
}
|
||||
|
||||
OTRGlobals::Instance->context->GetWindow()->StartFrame();
|
||||
}
|
||||
|
||||
@@ -1301,10 +1319,13 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldClearTextureCacheAtEndOfFrame) {
|
||||
bool curAltAssets = CVarGetInteger("gAltAssets", 0);
|
||||
if (prevAltAssets != curAltAssets) {
|
||||
prevAltAssets = curAltAssets;
|
||||
LUS::Context::GetInstance()->GetResourceManager()->SetAltAssetsEnabled(curAltAssets);
|
||||
gfx_texture_cache_clear();
|
||||
LUS::SkeletonPatcher::UpdateSkeletons();
|
||||
ShouldClearTextureCacheAtEndOfFrame = false;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
|
||||
}
|
||||
|
||||
// OTRTODO: FIGURE OUT END FRAME POINT
|
||||
@@ -1477,10 +1498,14 @@ extern "C" uint8_t ResourceMgr_FileAltExists(const char* filePath) {
|
||||
return ExtensionCache.contains(path);
|
||||
}
|
||||
|
||||
extern "C" bool ResourceMgr_IsAltAssetsEnabled() {
|
||||
return LUS::Context::GetInstance()->GetResourceManager()->IsAltAssetsEnabled();
|
||||
}
|
||||
|
||||
// Unloads a resource if an alternate version exists when alt assets are enabled
|
||||
// The resource is only removed from the internal cache to prevent it from used in the next resource lookup
|
||||
extern "C" void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName) {
|
||||
if (CVarGetInteger("gAltAssets", 0) && ResourceMgr_FileAltExists((char*) resName)) {
|
||||
if (ResourceMgr_IsAltAssetsEnabled() && ResourceMgr_FileAltExists((char*) resName)) {
|
||||
ResourceMgr_UnloadResource((char*) resName);
|
||||
}
|
||||
}
|
||||
@@ -1717,6 +1742,7 @@ extern "C" char* ResourceMgr_LoadArrayByName(const char* path)
|
||||
return (char*)res->Scalars.data();
|
||||
}
|
||||
|
||||
// Return of LoadArrayByNameAsVec3s must be freed by the caller
|
||||
extern "C" char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path) {
|
||||
auto res = std::static_pointer_cast<LUS::Array>(GetResourceByNameHandlingMQ(path));
|
||||
|
||||
@@ -1849,7 +1875,7 @@ extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, Skel
|
||||
pathStr = pathStr.substr(sOtr.length());
|
||||
}
|
||||
|
||||
bool isAlt = CVarGetInteger("gAltAssets", 0);
|
||||
bool isAlt = ResourceMgr_IsAltAssetsEnabled();
|
||||
|
||||
if (isAlt) {
|
||||
pathStr = LUS::IResource::gAltAssetPrefix + pathStr;
|
||||
@@ -2099,10 +2125,10 @@ Color_RGB8 GetColorForControllerLED() {
|
||||
if (source == LED_SOURCE_CUSTOM) {
|
||||
color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
|
||||
}
|
||||
if (criticalOverride || source == LED_SOURCE_HEALTH) {
|
||||
if (gPlayState && (criticalOverride || source == LED_SOURCE_HEALTH)) {
|
||||
if (HealthMeter_IsCritical()) {
|
||||
color = { 0xFF, 0, 0 };
|
||||
} else if (source == LED_SOURCE_HEALTH) {
|
||||
} else if (gSaveContext.healthCapacity != 0 && source == LED_SOURCE_HEALTH) {
|
||||
if (gSaveContext.health / gSaveContext.healthCapacity <= 0.4f) {
|
||||
color = { 0xFF, 0xFF, 0 };
|
||||
} else {
|
||||
@@ -2468,8 +2494,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
randoInf = RAND_INF_MERCHANTS_CARPET_SALESMAN;
|
||||
}
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(randoInf, textId, Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_ON_HINT);
|
||||
} else if (Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) &&
|
||||
(textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)) {
|
||||
} else if (textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId);
|
||||
} else if (textId == TEXT_CURSED_SKULLTULA_PEOPLE) {
|
||||
actorParams = GET_PLAYER(play)->targetActor->params;
|
||||
@@ -2488,6 +2513,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetWarpSongMessage(textId, Randomizer_GetSettingValue(RSK_WARP_SONG_HINTS) == RO_GENERIC_OFF);
|
||||
} else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, textId);
|
||||
} else if (textId == TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW);
|
||||
} else if (textId == 0x3052 || (textId >= 0x3069 && textId <= 0x3070)) { //Fire Temple gorons
|
||||
u16 choice = Random(0, NUM_GORON_MESSAGES);
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetGoronMessage(choice);
|
||||
@@ -2577,70 +2604,92 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
|
||||
gfx_register_blended_texture(name, mask, replacement);
|
||||
}
|
||||
|
||||
// #region SOH [TODO] Ideally this should move to being event based, it's currently run every frame on the file select screen
|
||||
extern "C" void SoH_ProcessDroppedFiles() {
|
||||
const char* droppedFile = CVarGetString("gDroppedFile", "");
|
||||
if (CVarGetInteger("gNewFileDropped", 0) && strcmp(droppedFile, "") != 0) {
|
||||
try {
|
||||
std::ifstream configStream(SohUtils::Sanitize(droppedFile));
|
||||
if (!configStream) {
|
||||
return;
|
||||
}
|
||||
extern "C" void Gfx_UnregisterBlendedTexture(const char* name) {
|
||||
gfx_unregister_blended_texture(name);
|
||||
}
|
||||
|
||||
nlohmann::json configJson;
|
||||
configStream >> configJson;
|
||||
extern "C" void Gfx_TextureCacheDelete(const uint8_t* texAddr) {
|
||||
char* imgName = (char*)texAddr;
|
||||
|
||||
if (!configJson.contains("CVars")) {
|
||||
return;
|
||||
}
|
||||
if (texAddr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearCvars(enhancementsCvars);
|
||||
clearCvars(cheatCvars);
|
||||
clearCvars(randomizerCvars);
|
||||
if (ResourceMgr_OTRSigCheck(imgName)) {
|
||||
texAddr = (const uint8_t*)GetResourceDataByNameHandlingMQ(imgName);
|
||||
}
|
||||
|
||||
// Flatten everything under CVars into a single array
|
||||
auto cvars = configJson["CVars"].flatten();
|
||||
gfx_texture_cache_delete(texAddr);
|
||||
}
|
||||
|
||||
for (auto& [key, value] : cvars.items()) {
|
||||
// Replace slashes with dots in key, and remove leading dot
|
||||
std::string path = key;
|
||||
std::replace(path.begin(), path.end(), '/', '.');
|
||||
if (path[0] == '.') {
|
||||
path.erase(0, 1);
|
||||
}
|
||||
if (value.is_string()) {
|
||||
CVarSetString(path.c_str(), value.get<std::string>().c_str());
|
||||
} else if (value.is_number_integer()) {
|
||||
CVarSetInteger(path.c_str(), value.get<int>());
|
||||
} else if (value.is_number_float()) {
|
||||
CVarSetFloat(path.c_str(), value.get<float>());
|
||||
}
|
||||
}
|
||||
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGuiWindow("Console")->Hide();
|
||||
gui->GetGuiWindow("Actor Viewer")->Hide();
|
||||
gui->GetGuiWindow("Collision Viewer")->Hide();
|
||||
gui->GetGuiWindow("Save Editor")->Hide();
|
||||
gui->GetGuiWindow("Display List Viewer")->Hide();
|
||||
gui->GetGuiWindow("Stats")->Hide();
|
||||
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();
|
||||
|
||||
gui->SaveConsoleVariablesOnNextTick();
|
||||
|
||||
uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
|
||||
} catch (std::exception& e) {
|
||||
SPDLOG_ERROR("Failed to load config file: {}", e.what());
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
|
||||
return;
|
||||
} catch (...) {
|
||||
SPDLOG_ERROR("Failed to load config file");
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
|
||||
void SoH_ProcessDroppedFiles(std::string filePath) {
|
||||
try {
|
||||
std::ifstream configStream(filePath);
|
||||
if (!configStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json configJson;
|
||||
configStream >> configJson;
|
||||
|
||||
// #region SOH [Randomizer] TODO: Refactor spoiler file handling for randomizer
|
||||
if (configJson.contains("version") && configJson.contains("finalSeed")) {
|
||||
CVarSetString("gRandomizerDroppedFile", filePath.c_str());
|
||||
CVarSetInteger("gRandomizerNewFileDropped", 1);
|
||||
return;
|
||||
}
|
||||
// #endregion
|
||||
|
||||
if (!configJson.contains("CVars")) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearCvars(enhancementsCvars);
|
||||
clearCvars(cheatCvars);
|
||||
clearCvars(randomizerCvars);
|
||||
|
||||
// Flatten everything under CVars into a single array
|
||||
auto cvars = configJson["CVars"].flatten();
|
||||
|
||||
for (auto& [key, value] : cvars.items()) {
|
||||
// Replace slashes with dots in key, and remove leading dot
|
||||
std::string path = key;
|
||||
std::replace(path.begin(), path.end(), '/', '.');
|
||||
if (path[0] == '.') {
|
||||
path.erase(0, 1);
|
||||
}
|
||||
if (value.is_string()) {
|
||||
CVarSetString(path.c_str(), value.get<std::string>().c_str());
|
||||
} else if (value.is_number_integer()) {
|
||||
CVarSetInteger(path.c_str(), value.get<int>());
|
||||
} else if (value.is_number_float()) {
|
||||
CVarSetFloat(path.c_str(), value.get<float>());
|
||||
}
|
||||
}
|
||||
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGuiWindow("Console")->Hide();
|
||||
gui->GetGuiWindow("Actor Viewer")->Hide();
|
||||
gui->GetGuiWindow("Collision Viewer")->Hide();
|
||||
gui->GetGuiWindow("Save Editor")->Hide();
|
||||
gui->GetGuiWindow("Display List Viewer")->Hide();
|
||||
gui->GetGuiWindow("Stats")->Hide();
|
||||
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();
|
||||
|
||||
gui->SaveConsoleVariablesOnNextTick();
|
||||
|
||||
uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
|
||||
} catch (std::exception& e) {
|
||||
SPDLOG_ERROR("Failed to load config file: {}", e.what());
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
|
||||
return;
|
||||
} catch (...) {
|
||||
SPDLOG_ERROR("Failed to load config file");
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
@@ -120,6 +120,7 @@ void Ctx_ReadSaveFile(uintptr_t addr, void* dramAddr, size_t size);
|
||||
void Ctx_WriteSaveFile(uintptr_t addr, void* dramAddr, size_t size);
|
||||
|
||||
uint64_t GetPerfCounter();
|
||||
bool ResourceMgr_IsAltAssetsEnabled();
|
||||
struct SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, SkelAnime* skelAnime);
|
||||
void ResourceMgr_UnregisterSkeleton(SkelAnime* skelAnime);
|
||||
void ResourceMgr_ClearSkeletons();
|
||||
@@ -173,9 +174,10 @@ void Entrance_InitEntranceTrackingData(void);
|
||||
void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex);
|
||||
void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
|
||||
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
|
||||
void Gfx_UnregisterBlendedTexture(const char* name);
|
||||
void Gfx_TextureCacheDelete(const uint8_t* addr);
|
||||
void SaveManager_ThreadPoolWait();
|
||||
void CheckTracker_OnMessageClose();
|
||||
void SoH_ProcessDroppedFiles();
|
||||
|
||||
int32_t GetGIID(uint32_t itemID);
|
||||
#endif
|
||||
|
||||
+58
-41
@@ -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
-1
@@ -38,7 +38,6 @@
|
||||
#include "Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "Enhancements/cosmetics/authenticGfxPatches.h"
|
||||
|
||||
bool ShouldClearTextureCacheAtEndOfFrame = false;
|
||||
bool isBetaQuestEnabled = false;
|
||||
|
||||
extern "C" {
|
||||
@@ -125,6 +124,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 +183,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 +209,8 @@ namespace SohGui {
|
||||
mConsoleWindow = nullptr;
|
||||
mSohMenuBar = nullptr;
|
||||
}
|
||||
|
||||
void RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function<void()> button1callback, std::function<void()> button2callback) {
|
||||
mModalWindow->RegisterPopup(title, message, button1, button2, button1callback, button2callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Enhancements/randomizer/randomizer_entrance_tracker.h"
|
||||
#include "Enhancements/randomizer/randomizer_item_tracker.h"
|
||||
#include "Enhancements/randomizer/randomizer_settings_window.h"
|
||||
#include "SohModals.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -37,6 +38,7 @@ namespace SohGui {
|
||||
void SetupGuiElements();
|
||||
void Draw();
|
||||
void Destroy();
|
||||
void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function<void()> button1callback = nullptr, std::function<void()> button2callback = nullptr);
|
||||
}
|
||||
|
||||
#endif /* SohGui_hpp */
|
||||
|
||||
+67
-35
@@ -28,7 +28,6 @@
|
||||
#include "Enhancements/randomizer/randomizer_item_tracker.h"
|
||||
#include "Enhancements/randomizer/randomizer_settings_window.h"
|
||||
|
||||
extern bool ShouldClearTextureCacheAtEndOfFrame;
|
||||
extern bool isBetaQuestEnabled;
|
||||
|
||||
extern "C" PlayState* gPlayState;
|
||||
@@ -50,6 +49,13 @@ std::string GetWindowButtonText(const char* text, bool menuOpen) {
|
||||
if (!menuOpen) { strcat(buttonText, " "); }
|
||||
return buttonText;
|
||||
}
|
||||
|
||||
static std::unordered_map<LUS::WindowBackend, const char*> windowBackendNames = {
|
||||
{ LUS::WindowBackend::DX11, "DirectX" },
|
||||
{ LUS::WindowBackend::SDL_OPENGL, "OpenGL"},
|
||||
{ LUS::WindowBackend::SDL_METAL, "Metal" },
|
||||
{ LUS::WindowBackend::GX2, "GX2"}
|
||||
};
|
||||
|
||||
static const char* imguiScaleOptions[4] = { "Small", "Normal", "Large", "X-Large" };
|
||||
|
||||
@@ -102,6 +108,24 @@ extern "C" SaveContext gSaveContext;
|
||||
|
||||
namespace SohGui {
|
||||
|
||||
std::unordered_map<LUS::WindowBackend, const char*> availableWindowBackendsMap;
|
||||
LUS::WindowBackend configWindowBackend;
|
||||
|
||||
void UpdateWindowBackendObjects() {
|
||||
LUS::WindowBackend runningWindowBackend = LUS::Context::GetInstance()->GetWindow()->GetWindowBackend();
|
||||
int32_t configWindowBackendId = LUS::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1);
|
||||
if (configWindowBackendId != -1 && configWindowBackendId < static_cast<int>(LUS::WindowBackend::BACKEND_COUNT)) {
|
||||
configWindowBackend = static_cast<LUS::WindowBackend>(configWindowBackendId);
|
||||
} else {
|
||||
configWindowBackend = runningWindowBackend;
|
||||
}
|
||||
|
||||
auto availableWindowBackends = LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends();
|
||||
for (auto& backend : *availableWindowBackends) {
|
||||
availableWindowBackendsMap[backend] = windowBackendNames[backend];
|
||||
}
|
||||
}
|
||||
|
||||
void DrawMenuBarIcon() {
|
||||
static bool gameIconLoaded = false;
|
||||
if (!gameIconLoaded) {
|
||||
@@ -391,40 +415,24 @@ void DrawSettingsMenu() {
|
||||
UIWidgets::Tooltip("Changes the scaling of the ImGui menu elements.");
|
||||
|
||||
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
||||
|
||||
static std::unordered_map<LUS::WindowBackend, const char*> windowBackendNames = {
|
||||
{ LUS::WindowBackend::DX11, "DirectX" },
|
||||
{ LUS::WindowBackend::SDL_OPENGL, "OpenGL"},
|
||||
{ LUS::WindowBackend::SDL_METAL, "Metal" },
|
||||
{ LUS::WindowBackend::GX2, "GX2"}
|
||||
};
|
||||
|
||||
ImGui::Text("Renderer API (Needs reload)");
|
||||
LUS::WindowBackend runningWindowBackend = LUS::Context::GetInstance()->GetWindow()->GetWindowBackend();
|
||||
LUS::WindowBackend configWindowBackend;
|
||||
int configWindowBackendId = LUS::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1);
|
||||
if (configWindowBackendId != -1 && configWindowBackendId < static_cast<int>(LUS::WindowBackend::BACKEND_COUNT)) {
|
||||
configWindowBackend = static_cast<LUS::WindowBackend>(configWindowBackendId);
|
||||
} else {
|
||||
configWindowBackend = runningWindowBackend;
|
||||
}
|
||||
|
||||
if (LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
|
||||
if (availableWindowBackendsMap.size() <= 1) {
|
||||
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||
}
|
||||
if (ImGui::BeginCombo("##RApi", windowBackendNames[configWindowBackend])) {
|
||||
for (size_t i = 0; i < LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size(); i++) {
|
||||
auto backend = LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->data()[i];
|
||||
if (ImGui::Selectable(windowBackendNames[backend], backend == configWindowBackend)) {
|
||||
LUS::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", static_cast<int>(backend));
|
||||
LUS::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name",
|
||||
windowBackendNames[backend]);
|
||||
if (ImGui::BeginCombo("##RApi", availableWindowBackendsMap[configWindowBackend])) {
|
||||
for (auto backend : availableWindowBackendsMap) {
|
||||
if (ImGui::Selectable(backend.second, backend.first == configWindowBackend)) {
|
||||
LUS::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", static_cast<int>(backend.first));
|
||||
LUS::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name", backend.second);
|
||||
LUS::Context::GetInstance()->GetConfig()->Save();
|
||||
UpdateWindowBackendObjects();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
|
||||
if (availableWindowBackendsMap.size() <= 1) {
|
||||
UIWidgets::ReEnableComponent("");
|
||||
}
|
||||
|
||||
@@ -558,7 +566,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");
|
||||
@@ -907,10 +915,7 @@ void DrawEnhancementsMenu() {
|
||||
if (ImGui::BeginMenu("Graphics"))
|
||||
{
|
||||
if (ImGui::BeginMenu("Mods")) {
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", "gAltAssets", false, false)) {
|
||||
ShouldClearTextureCacheAtEndOfFrame = true;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
|
||||
}
|
||||
UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", "gAltAssets", false, false);
|
||||
UIWidgets::Tooltip("Toggle between standard assets and alternate assets. Usually mods will indicate if this setting has to be used or not.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Disable Bomb Billboarding", "gDisableBombBillboarding", true, false);
|
||||
UIWidgets::Tooltip("Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to replace bombs with 3D objects.");
|
||||
@@ -923,16 +928,39 @@ void DrawEnhancementsMenu() {
|
||||
}
|
||||
UIWidgets::PaddedEnhancementCheckbox("Disable LOD", "gDisableLOD", true, false);
|
||||
UIWidgets::Tooltip("Turns off the Level of Detail setting, making models use their higher-poly variants at any distance");
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Disable Draw Distance", "gDisableDrawDistance", true, false)) {
|
||||
if (CVarGetInteger("gDisableDrawDistance", 0) == 0) {
|
||||
if (UIWidgets::EnhancementSliderInt("Increase Actor Draw Distance: %dx", "##IncreaseActorDrawDistance",
|
||||
"gDisableDrawDistance", 1, 5, "", 1, true, false)) {
|
||||
if (CVarGetInteger("gDisableDrawDistance", 1) <= 1) {
|
||||
CVarSetInteger("gDisableKokiriDrawDistance", 0);
|
||||
}
|
||||
}
|
||||
UIWidgets::Tooltip("Turns off the objects draw distance, making objects being visible from a longer range");
|
||||
if (CVarGetInteger("gDisableDrawDistance", 0) == 1) {
|
||||
UIWidgets::Tooltip("Increases the range in which actors/objects are drawn");
|
||||
if (CVarGetInteger("gDisableDrawDistance", 1) > 1) {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Kokiri Draw Distance", "gDisableKokiriDrawDistance", true, false);
|
||||
UIWidgets::Tooltip("The Kokiri are mystical beings that fade into view when approached\nEnabling this will remove their draw distance");
|
||||
UIWidgets::Tooltip("The Kokiri are mystical beings that fade into view when approached\nEnabling this "
|
||||
"will remove their draw distance");
|
||||
}
|
||||
UIWidgets::PaddedEnhancementCheckbox("Widescreen Actor Culling", "gEnhancements.WidescreenActorCulling",
|
||||
true, false);
|
||||
UIWidgets::Tooltip("Adjusts the horizontal culling plane to account for widescreen resolutions");
|
||||
UIWidgets::PaddedEnhancementCheckbox(
|
||||
"Cull Glitch Useful Actors", "gEnhancements.ExtendedCullingExcludeGlitchActors", true, false,
|
||||
!CVarGetInteger("gEnhancements.WidescreenActorCulling", 0) &&
|
||||
CVarGetInteger("gDisableDrawDistance", 1) <= 1,
|
||||
"Requires Actor Draw Distance to be increased or Widescreen Actor Culling enabled");
|
||||
UIWidgets::Tooltip(
|
||||
"Exclude actors that are useful for glitches from the extended culling ranges.\n"
|
||||
"Some actors may still draw in the extended ranges, but will not \"update\" so that certain "
|
||||
"glitches that leverage the original culling requirements will still work.\n"
|
||||
"\n"
|
||||
"The following actors are excluded:\n"
|
||||
"- White clothed Gerudos\n"
|
||||
"- King Zora\n"
|
||||
"- Gossip Stones\n"
|
||||
"- Boulders\n"
|
||||
"- Blue Warps\n"
|
||||
"- Darunia\n"
|
||||
"- Gold Skulltulas");
|
||||
UIWidgets::PaddedEnhancementCheckbox("N64 Mode", "gLowResMode", true, false);
|
||||
UIWidgets::Tooltip("Sets aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Glitch line-up tick", "gDrawLineupTick", true, false);
|
||||
@@ -1608,6 +1636,10 @@ void DrawRandomizerMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
void SohMenuBar::InitElement() {
|
||||
UpdateWindowBackendObjects();
|
||||
}
|
||||
|
||||
void SohMenuBar::DrawElement() {
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
DrawMenuBarIcon();
|
||||
|
||||
@@ -10,7 +10,7 @@ class SohMenuBar : public LUS::GuiMenuBar {
|
||||
using LUS::GuiMenuBar::GuiMenuBar;
|
||||
protected:
|
||||
void DrawElement() override;
|
||||
void InitElement() override {};
|
||||
void InitElement() override;
|
||||
void UpdateElement() override {};
|
||||
};
|
||||
} // namespace SohGui
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "SohModals.h"
|
||||
#include "ImGui/imgui.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <libultraship/bridge.h>
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "UIWidgets.hpp"
|
||||
#include "OTRGlobals.h"
|
||||
#include "z64.h"
|
||||
|
||||
extern "C" PlayState* gPlayState;
|
||||
struct SohModal {
|
||||
std::string title_;
|
||||
std::string message_;
|
||||
std::string button1_;
|
||||
std::string button2_;
|
||||
std::function<void()> button1callback_;
|
||||
std::function<void()> button2callback_;
|
||||
};
|
||||
std::vector<SohModal> modals;
|
||||
|
||||
void SohModalWindow::DrawElement() {
|
||||
if (modals.size() > 0) {
|
||||
SohModal curModal = modals.at(0);
|
||||
if (!ImGui::IsPopupOpen(curModal.title_.c_str())) {
|
||||
ImGui::OpenPopup(curModal.title_.c_str());
|
||||
}
|
||||
if (ImGui::BeginPopupModal(curModal.title_.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
ImGui::Text(curModal.message_.c_str());
|
||||
if (ImGui::Button(curModal.button1_.c_str())) {
|
||||
if (curModal.button1callback_ != nullptr) {
|
||||
curModal.button1callback_();
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
modals.erase(modals.begin());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (curModal.button2_ != "") {
|
||||
if (ImGui::Button(curModal.button2_.c_str())) {
|
||||
if (curModal.button2callback_ != nullptr) {
|
||||
curModal.button2callback_();
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
modals.erase(modals.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void SohModalWindow::RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function<void()> button1callback, std::function<void()> button2callback) {
|
||||
modals.push_back({ title, message, button1, button2, button1callback, button2callback });
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "window/gui/GuiMenuBar.h"
|
||||
#include "window/gui/GuiElement.h"
|
||||
|
||||
class SohModalWindow : public LUS::GuiWindow {
|
||||
public:
|
||||
using LUS::GuiWindow::GuiWindow;
|
||||
|
||||
void InitElement() override {};
|
||||
void DrawElement() override;
|
||||
void UpdateElement() override {};
|
||||
void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function<void()> button1callback = nullptr, std::function<void()> button2callback = nullptr);
|
||||
};
|
||||
@@ -21,7 +21,7 @@ namespace UIWidgets {
|
||||
// Automatically adds newlines to break up text longer than a specified number of characters
|
||||
// Manually included newlines will still be respected and reset the line length
|
||||
// If line is midword when it hits the limit, text should break at the last encountered space
|
||||
char* WrappedText(const char* text, unsigned int charactersPerLine) {
|
||||
std::string WrappedText(const char* text, unsigned int charactersPerLine) {
|
||||
std::string newText(text);
|
||||
const size_t tipLength = newText.length();
|
||||
int lastSpace = -1;
|
||||
@@ -43,17 +43,17 @@ namespace UIWidgets {
|
||||
currentLineLength++;
|
||||
}
|
||||
|
||||
return strdup(newText.c_str());
|
||||
return newText;
|
||||
}
|
||||
|
||||
char* WrappedText(const std::string& text, unsigned int charactersPerLine) {
|
||||
std::string WrappedText(const std::string& text, unsigned int charactersPerLine) {
|
||||
return WrappedText(text.c_str(), charactersPerLine);
|
||||
}
|
||||
|
||||
void SetLastItemHoverText(const std::string& text) {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", WrappedText(text, 60));
|
||||
ImGui::Text("%s", WrappedText(text, 60).c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ namespace UIWidgets {
|
||||
void SetLastItemHoverText(const char* text) {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", WrappedText(text, 60));
|
||||
ImGui::Text("%s", WrappedText(text, 60).c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ namespace UIWidgets {
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", WrappedText(text, 60));
|
||||
ImGui::Text("%s", WrappedText(text, 60).c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ namespace UIWidgets {
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", WrappedText(text, 60));
|
||||
ImGui::Text("%s", WrappedText(text, 60).c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace UIWidgets {
|
||||
|
||||
void Tooltip(const char* text) {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", WrappedText(text));
|
||||
ImGui::SetTooltip("%s", WrappedText(text).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ namespace UIWidgets {
|
||||
constexpr float sliderButtonWidth = 30.0f;
|
||||
#endif
|
||||
|
||||
char* WrappedText(const char* text, unsigned int charactersPerLine = 60);
|
||||
char* WrappedText(const std::string& text, unsigned int charactersPerLine);
|
||||
std::string WrappedText(const char* text, unsigned int charactersPerLine = 60);
|
||||
std::string WrappedText(const std::string& text, unsigned int charactersPerLine);
|
||||
|
||||
void SetLastItemHoverText(const std::string& text);
|
||||
void SetLastItemHoverText(const char* text);
|
||||
|
||||
+7
-1
@@ -99,8 +99,14 @@ void aClearBufferImpl(uint16_t addr, int nbytes) {
|
||||
memset(BUF_U8(addr), 0, nbytes);
|
||||
}
|
||||
|
||||
void aLoadBufferImpl(const void *source_addr, uint16_t dest_addr, uint16_t nbytes) {
|
||||
void aLoadBufferImpl(const void* source_addr, uint16_t dest_addr, uint16_t nbytes) {
|
||||
#if __SANITIZE_ADDRESS__
|
||||
for (size_t i = 0; i < ROUND_DOWN_16(nbytes); i++) {
|
||||
BUF_U8(dest_addr)[i] = ((const unsigned char*)source_addr)[i];
|
||||
}
|
||||
#else
|
||||
memcpy(BUF_U8(dest_addr), source_addr, ROUND_DOWN_16(nbytes));
|
||||
#endif
|
||||
}
|
||||
|
||||
void aSaveBufferImpl(uint16_t source_addr, int16_t *dest_addr, uint16_t nbytes) {
|
||||
|
||||
@@ -132,15 +132,15 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.lodLimb.sibling = skeletonLimb->siblingIndex;
|
||||
|
||||
if (skeletonLimb->dListPtr != "") {
|
||||
auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = nullptr;
|
||||
}
|
||||
|
||||
if (skeletonLimb->dList2Ptr != "") {
|
||||
auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dList2Ptr = "__OTR__" + skeletonLimb->dList2Ptr;
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = (Gfx*)skeletonLimb->dList2Ptr.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = nullptr;
|
||||
}
|
||||
@@ -153,8 +153,8 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.standardLimb.dList = nullptr;
|
||||
|
||||
if (!skeletonLimb->dListPtr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.standardLimb.dList = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.standardLimb.dList = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
}
|
||||
} else if (skeletonLimb->limbType == LUS::LimbType::Curve) {
|
||||
skeletonLimb->limbData.skelCurveLimb.firstChildIdx = skeletonLimb->childIndex;
|
||||
@@ -163,13 +163,13 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = nullptr;
|
||||
|
||||
if (!skeletonLimb->dListPtr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[0] = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
}
|
||||
|
||||
if (!skeletonLimb->dList2Ptr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dList2Ptr = "__OTR__" + skeletonLimb->dList2Ptr;
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = (Gfx*)skeletonLimb->dList2Ptr.c_str();
|
||||
}
|
||||
} else if (skeletonLimb->limbType == LUS::LimbType::Skin) {
|
||||
skeletonLimb->limbData.skinLimb.jointPos.x = skeletonLimb->transX;
|
||||
@@ -189,14 +189,23 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
}
|
||||
|
||||
if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_DList) {
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList.c_str());
|
||||
skeletonLimb->limbData.skinLimb.segment = res ? res->GetRawPointer() : nullptr;
|
||||
if (skeletonLimb->skinDList != "") {
|
||||
skeletonLimb->skinDList = "__OTR__" + skeletonLimb->skinDList;
|
||||
skeletonLimb->limbData.skinLimb.segment = (Gfx*)skeletonLimb->skinDList.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.skinLimb.segment = nullptr;
|
||||
}
|
||||
} else if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_4) {
|
||||
skeletonLimb->skinAnimLimbData.totalVtxCount = skeletonLimb->skinVtxCnt;
|
||||
skeletonLimb->skinAnimLimbData.limbModifCount = skeletonLimb->skinLimbModifCount;
|
||||
skeletonLimb->skinAnimLimbData.limbModifications = skeletonLimb->skinLimbModifArray.data();
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList2.c_str());
|
||||
skeletonLimb->skinAnimLimbData.dlist = (Gfx*)(res ? res->GetRawPointer() : nullptr);
|
||||
|
||||
if (skeletonLimb->skinDList2 != "") {
|
||||
skeletonLimb->skinDList2 = "__OTR__" + skeletonLimb->skinDList2;
|
||||
skeletonLimb->skinAnimLimbData.dlist = (Gfx*)skeletonLimb->skinDList2.c_str();
|
||||
} else {
|
||||
skeletonLimb->skinAnimLimbData.dlist = nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < skeletonLimb->skinLimbModifArray.size(); i++) {
|
||||
skeletonLimb->skinAnimLimbData.limbModifications[i].vtxCount = skeletonLimb->skinLimbModifVertexArrays[i].size();
|
||||
@@ -254,8 +263,8 @@ void SkeletonLimbFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shar
|
||||
limbData.lodLimb.jointPos.z = skelLimb->transZ;
|
||||
|
||||
if (skelLimb->dListPtr != "") {
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess((const char*)skelLimb->dListPtr.c_str());
|
||||
limbData.lodLimb.dLists[0] = (Gfx*)(res ? res->GetRawPointer() : nullptr);
|
||||
skelLimb->dListPtr = "__OTR__" + skelLimb->dListPtr;
|
||||
limbData.lodLimb.dLists[0] = (Gfx*)skelLimb->dListPtr.c_str();
|
||||
} else {
|
||||
limbData.lodLimb.dLists[0] = nullptr;
|
||||
}
|
||||
|
||||
@@ -65,12 +65,11 @@ void SkeletonPatcher::ClearSkeletons()
|
||||
}
|
||||
|
||||
void SkeletonPatcher::UpdateSkeletons() {
|
||||
bool isHD = CVarGetInteger("gAltAssets", 0);
|
||||
auto resourceMgr = LUS::Context::GetInstance()->GetResourceManager();
|
||||
bool isHD = resourceMgr->IsAltAssetsEnabled();
|
||||
for (auto skel : skeletons) {
|
||||
Skeleton* newSkel =
|
||||
(Skeleton*)LUS::Context::GetInstance()->GetResourceManager()
|
||||
->LoadResource((isHD ? LUS::IResource::gAltAssetPrefix : "") + skel.vanillaSkeletonPath, true)
|
||||
.get();
|
||||
(Skeleton*)resourceMgr->LoadResource((isHD ? LUS::IResource::gAltAssetPrefix : "") + skel.vanillaSkeletonPath, true).get();
|
||||
|
||||
if (newSkel != nullptr) {
|
||||
skel.skelAnime->skeleton = newSkel->skeletonData.skeletonHeader.segment;
|
||||
|
||||
+19
-31
@@ -149,6 +149,13 @@ bool Scene_CommandMeshHeader(PlayState* play, LUS::ISceneCommand* cmd) {
|
||||
|
||||
extern "C" void* func_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId);
|
||||
|
||||
bool OTRfunc_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId) {
|
||||
|
||||
objectCtx->status[bankIndex].id = -objectId;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) {
|
||||
// LUS::SetObjectList* cmdObj = static_pointer_cast<LUS::SetObjectList>(cmd);
|
||||
LUS::SetObjectList* cmdObj = (LUS::SetObjectList*)cmd;
|
||||
@@ -164,49 +171,30 @@ bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) {
|
||||
void* nextPtr;
|
||||
|
||||
k = 0;
|
||||
// i = play->objectCtx.unk_09;
|
||||
i = 0;
|
||||
i = play->objectCtx.unk_09;
|
||||
firstStatus = &play->objectCtx.status[0];
|
||||
status = &play->objectCtx.status[i];
|
||||
|
||||
for (int i = 0; i < cmdObj->objects.size(); i++) {
|
||||
bool alreadyIncluded = false;
|
||||
|
||||
for (int j = 0; j < play->objectCtx.num; j++) {
|
||||
if (play->objectCtx.status[j].id == cmdObj->objects[i]) {
|
||||
alreadyIncluded = true;
|
||||
break;
|
||||
// Loop until a mismatch in the object lists
|
||||
// Then clear all object ids past that in the context object list and kill actors for those objects
|
||||
for (i = play->objectCtx.unk_09, k = 0; i < play->objectCtx.num; i++, k++) {
|
||||
if (k >= cmdObj->objects.size() || play->objectCtx.status[i].id != cmdObj->objects[k]) {
|
||||
for (j = i; j < play->objectCtx.num; j++) {
|
||||
play->objectCtx.status[j].id = OBJECT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyIncluded) {
|
||||
play->objectCtx.status[play->objectCtx.num++].id = cmdObj->objects[i];
|
||||
func_80031A28(play, &play->actorCtx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
while (i < play->objectCtx.num) {
|
||||
if (status->id != *objectEntry) {
|
||||
status2 = &play->objectCtx.status[i];
|
||||
for (j = i; j < play->objectCtx.num; j++) {
|
||||
status2->id = OBJECT_INVALID;
|
||||
status2++;
|
||||
}
|
||||
play->objectCtx.num = i;
|
||||
func_80031A28(play, &play->actorCtx);
|
||||
|
||||
continue;
|
||||
// Continuing from the last index, add the remaining object ids from the command object list
|
||||
for (; k < cmdObj->objects.size(); k++, i++) {
|
||||
if (i < OBJECT_EXCHANGE_BANK_MAX - 1) {
|
||||
OTRfunc_800982FC(&play->objectCtx, i, cmdObj->objects[k]);
|
||||
}
|
||||
|
||||
i++;
|
||||
k++;
|
||||
objectEntry++;
|
||||
status++;
|
||||
}
|
||||
|
||||
play->objectCtx.num = i;
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-1
@@ -467,7 +467,7 @@ void GameState_Destroy(GameState* gameState) {
|
||||
// Performing clear skeletons before unload resources fixes an actor heap corruption crash due to the skeleton patching system.
|
||||
ResourceMgr_ClearSkeletons();
|
||||
|
||||
if (CVarGetInteger("gAltAssets", 0)) {
|
||||
if (ResourceMgr_IsAltAssetsEnabled()) {
|
||||
ResourceUnloadDirectory("alt/*");
|
||||
gfx_texture_cache_clear();
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ static const ALIGN_ASSET(2) char rGfxPrintFontDataAlt[] = drGfxPrintFontDataAlt;
|
||||
// https://github.com/HarbourMasters/Shipwright/issues/2762
|
||||
typedef enum {hardcoded, otrDefault, otrAlt} font_texture_t;
|
||||
font_texture_t GfxPrint_TextureToUse() {
|
||||
if (CVarGetInteger("gAltAssets", 0) && ResourceMgr_FileExists(rGfxPrintFontDataAlt)) {
|
||||
if (ResourceMgr_IsAltAssetsEnabled() && ResourceMgr_FileExists(rGfxPrintFontDataAlt)) {
|
||||
// If we have alt assets enabled, and we have alt prefixed font texture, use that
|
||||
return otrAlt;
|
||||
} else if (ResourceMgr_FileExists(rGfxPrintFontData)) {
|
||||
|
||||
+110
-25
@@ -1208,20 +1208,11 @@ void Actor_Init(Actor* actor, PlayState* play) {
|
||||
actor->uncullZoneForward = 1000.0f;
|
||||
actor->uncullZoneScale = 350.0f;
|
||||
actor->uncullZoneDownward = 700.0f;
|
||||
if (CVarGetInteger("gDisableDrawDistance", 0) != 0 && actor->id != ACTOR_EN_TORCH2 && actor->id != ACTOR_EN_BLKOBJ // Extra check for Dark Link and his room
|
||||
&& actor->id != ACTOR_EN_HORSE // Check for Epona, else if we call her she will spawn at the other side of the map + we can hear her during the title screen sequence
|
||||
&& actor->id != ACTOR_EN_HORSE_GANON && actor->id != ACTOR_EN_HORSE_ZELDA // check for Zelda's and Ganondorf's horses that will always be scene during cinematic whith camera paning
|
||||
&& (play->sceneNum != SCENE_DODONGOS_CAVERN && actor->id != ACTOR_EN_ZF)) { // Check for DC and Lizalfos for the case where the miniboss music would still play under certains conditions and changing room
|
||||
actor->uncullZoneForward = 32767.0f;
|
||||
actor->uncullZoneScale = 32767.0f;
|
||||
actor->uncullZoneDownward = 32767.0f;
|
||||
}
|
||||
CollisionCheck_InitInfo(&actor->colChkInfo);
|
||||
actor->floorBgId = BGCHECK_SCENE;
|
||||
ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f);
|
||||
//if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex))
|
||||
{
|
||||
//Actor_SetObjectDependency(play, actor);
|
||||
if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
||||
Actor_SetObjectDependency(play, actor);
|
||||
actor->init(actor, play);
|
||||
actor->init = NULL;
|
||||
|
||||
@@ -2549,6 +2540,13 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
|
||||
Actor_SetObjectDependency(play, actor);
|
||||
actor->init(actor, play);
|
||||
actor->init = NULL;
|
||||
|
||||
GameInteractor_ExecuteOnActorInit(actor);
|
||||
|
||||
// For enemy health bar we need to know the max health during init
|
||||
if (actor->category == ACTORCAT_ENEMY) {
|
||||
actor->maximumHealth = actor->colChkInfo.health;
|
||||
}
|
||||
}
|
||||
actor = actor->next;
|
||||
} else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
||||
@@ -2851,19 +2849,12 @@ s32 func_800314B0(PlayState* play, Actor* actor) {
|
||||
s32 func_800314D4(PlayState* play, Actor* actor, Vec3f* arg2, f32 arg3) {
|
||||
f32 var;
|
||||
|
||||
if (CVarGetInteger("gDisableDrawDistance", 0) != 0 && actor->id != ACTOR_EN_TORCH2 && actor->id != ACTOR_EN_BLKOBJ // Extra check for Dark Link and his room
|
||||
&& actor->id != ACTOR_EN_HORSE // Check for Epona, else if we call her she will spawn at the other side of the map + we can hear her during the title screen sequence
|
||||
&& actor->id != ACTOR_EN_HORSE_GANON && actor->id != ACTOR_EN_HORSE_ZELDA // check for Zelda's and Ganondorf's horses that will always be scene during cinematic whith camera paning
|
||||
&& (play->sceneNum != SCENE_DODONGOS_CAVERN && actor->id != ACTOR_EN_ZF)) { // Check for DC and Lizalfos for the case where the miniboss music would still play under certains conditions and changing room
|
||||
return true;
|
||||
}
|
||||
|
||||
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)) {
|
||||
if ((((fabsf(arg2->x) - actor->uncullZoneScale) * var) < 1.0f) &&
|
||||
(((arg2->y + actor->uncullZoneDownward) * var) > -1.0f) &&
|
||||
(((arg2->y - actor->uncullZoneScale) * var) < 1.0f)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2871,6 +2862,80 @@ s32 func_800314D4(PlayState* play, Actor* actor, Vec3f* arg2, f32 arg3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// #region SOH [Enhancements] Allows us to increase the draw and update distance independently,
|
||||
// mostly a modified version of the function above and additional tweaks for some specfic actors
|
||||
s32 Ship_CalcShouldDrawAndUpdate(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW, bool* shouldDraw,
|
||||
bool* shouldUpdate) {
|
||||
f32 clampedProjectedW;
|
||||
|
||||
// Check if the actor passes its original/vanilla culling requirements
|
||||
if (func_800314D4(play, actor, projectedPos, projectedW)) {
|
||||
*shouldUpdate = true;
|
||||
*shouldDraw = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip cutscne actors that depend on culling to hide from camera pans
|
||||
if (actor->id == ACTOR_EN_VIEWER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s32 multiplier = CVarGetInteger("gDisableDrawDistance", 1);
|
||||
multiplier = MAX(multiplier, 1);
|
||||
|
||||
// Some actors have a really short forward value, so we need to add to it before the multiplier to increase the
|
||||
// final strength of the forward culling
|
||||
f32 adder = (actor->uncullZoneForward < 500) ? 1000.0f : 0.0f;
|
||||
|
||||
if ((projectedPos->z > -actor->uncullZoneScale) &&
|
||||
(projectedPos->z < (((actor->uncullZoneForward + adder) * multiplier) + actor->uncullZoneScale))) {
|
||||
clampedProjectedW = (projectedW < 1.0f) ? 1.0f : 1.0f / projectedW;
|
||||
|
||||
f32 ratioAdjusted = 1.0f;
|
||||
|
||||
if (CVarGetInteger("gEnhancements.WidescreenActorCulling", 0)) {
|
||||
f32 originalAspectRatio = 4.0f / 3.0f;
|
||||
f32 currentAspectRatio = OTRGetAspectRatio();
|
||||
ratioAdjusted = MAX(currentAspectRatio / originalAspectRatio, 1.0f);
|
||||
}
|
||||
|
||||
if ((((fabsf(projectedPos->x) - actor->uncullZoneScale) * (clampedProjectedW / ratioAdjusted)) < 1.0f) &&
|
||||
(((projectedPos->y + actor->uncullZoneDownward) * clampedProjectedW) > -1.0f) &&
|
||||
(((projectedPos->y - actor->uncullZoneScale) * clampedProjectedW) < 1.0f)) {
|
||||
|
||||
if (CVarGetInteger("gEnhancements.ExtendedCullingExcludeGlitchActors", 0)) {
|
||||
// These actors are safe to draw without impacting glitches
|
||||
if ((actor->id == ACTOR_OBJ_BOMBIWA || actor->id == ACTOR_OBJ_HAMISHI ||
|
||||
actor->id == ACTOR_EN_ISHI) || // Boulders (hookshot through collision)
|
||||
actor->id == ACTOR_EN_GS || // Gossip stones (text delay)
|
||||
actor->id == ACTOR_EN_GE1 || // White gerudos (gate clip/archery room transition)
|
||||
actor->id == ACTOR_EN_KZ || // King Zora (unfreeze glitch)
|
||||
actor->id == ACTOR_EN_DU || // Darunia (Fire temple BK skip)
|
||||
actor->id == ACTOR_DOOR_WARP1 // Blue warps (wrong warps)
|
||||
) {
|
||||
*shouldDraw = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip these actors entirely as their draw funcs impacts glitches
|
||||
if ((actor->id == ACTOR_EN_SW &&
|
||||
(((actor->params & 0xE000) >> 0xD) == 1 ||
|
||||
((actor->params & 0xE000) >> 0xD) == 2)) // Gold Skulltulas (hitbox at 0,0)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*shouldDraw = true;
|
||||
*shouldUpdate = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// #endregion
|
||||
|
||||
void func_800315AC(PlayState* play, ActorContext* actorCtx) {
|
||||
s32 invisibleActorCounter;
|
||||
Actor* invisibleActors[INVISIBLE_ACTOR_MAX];
|
||||
@@ -2906,18 +2971,35 @@ void func_800315AC(PlayState* play, ActorContext* actorCtx) {
|
||||
}
|
||||
}
|
||||
|
||||
// #region SOH [Enhancement] Extended culling updates
|
||||
bool shipShouldDraw = false;
|
||||
bool shipShouldUpdate = false;
|
||||
if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(70) == 0)) {
|
||||
if (func_800314B0(play, actor)) {
|
||||
actor->flags |= ACTOR_FLAG_ACTIVE;
|
||||
if (CVarGetInteger("gDisableDrawDistance", 1) > 1 ||
|
||||
CVarGetInteger("gEnhancements.WidescreenActorCulling", 0)) {
|
||||
Ship_CalcShouldDrawAndUpdate(play, actor, &actor->projectedPos, actor->projectedW, &shipShouldDraw,
|
||||
&shipShouldUpdate);
|
||||
|
||||
if (shipShouldUpdate) {
|
||||
actor->flags |= ACTOR_FLAG_ACTIVE;
|
||||
} else {
|
||||
actor->flags &= ~ACTOR_FLAG_ACTIVE;
|
||||
}
|
||||
} else {
|
||||
actor->flags &= ~ACTOR_FLAG_ACTIVE;
|
||||
if (func_800314B0(play, actor)) {
|
||||
actor->flags |= ACTOR_FLAG_ACTIVE;
|
||||
} else {
|
||||
actor->flags &= ~ACTOR_FLAG_ACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actor->isDrawn = false;
|
||||
|
||||
if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(71) == 0)) {
|
||||
if ((actor->init == NULL) && (actor->draw != NULL) && (actor->flags & (ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_ACTIVE))) {
|
||||
if ((actor->init == NULL) && (actor->draw != NULL) &&
|
||||
((actor->flags & (ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_ACTIVE)) || shipShouldDraw)) {
|
||||
// #endregion
|
||||
if ((actor->flags & ACTOR_FLAG_LENS) &&
|
||||
((play->roomCtx.curRoom.lensMode == LENS_MODE_HIDE_ACTORS) ||
|
||||
play->actorCtx.lensActive || (actor->room != play->roomCtx.curRoom.num))) {
|
||||
@@ -3129,6 +3211,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,
|
||||
|
||||
@@ -398,7 +398,7 @@ s32 CollisionPoly_LineVsPoly(CollisionPoly* poly, Vec3s* vtxList, Vec3f* posA, V
|
||||
(poly->normal.x * posB->x + poly->normal.y * posB->y + poly->normal.z * posB->z) * COLPOLY_NORMAL_FRAC +
|
||||
plane.originDist;
|
||||
|
||||
#ifdef __WIIU__
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
// on some platforms this ends up as very small numbers due to rounding issues
|
||||
if (IS_ZERO(planeDistA)) {
|
||||
planeDistA = 0.0f;
|
||||
@@ -1902,7 +1902,7 @@ s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResul
|
||||
s32 bgId2;
|
||||
f32 nx, ny, nz; // unit normal of polygon
|
||||
|
||||
if (CVarGetInteger("gNoClip", 0) != 0) {
|
||||
if (CVarGetInteger("gNoClip", 0) && actor != NULL && actor->id == ACTOR_PLAYER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7887,7 +7887,7 @@ s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear free camera if an action is performed that would move the camera (targeting, first person, talking)
|
||||
// Clear free look if an action is performed that would move the camera (targeting, first person, talking)
|
||||
if (CVarGetInteger("gFreeCamera", 0) && SetCameraManual(camera) == 1 &&
|
||||
((mode >= CAM_MODE_TARGET && mode <= CAM_MODE_BATTLE) ||
|
||||
(mode >= CAM_MODE_FIRSTPERSON && mode <= CAM_MODE_CLIMBZ) || mode == CAM_MODE_HANGZ ||
|
||||
|
||||
@@ -16,9 +16,11 @@ typedef struct {
|
||||
union {
|
||||
u32 unk_00;
|
||||
struct {
|
||||
u32 unk_bit0 : 1;
|
||||
u32 unk_bit1 : 1;
|
||||
u32 validModes : 30;
|
||||
// SoH [Port] These bitfield values are unused and led to shifting in validModes for little endian systems
|
||||
// Removing those so that validModes can be a complete 32 bit value
|
||||
// u32 unk_bit0 : 1;
|
||||
// u32 unk_bit1 : 1;
|
||||
u32 validModes;
|
||||
};
|
||||
};
|
||||
CameraMode* cameraModes;
|
||||
|
||||
@@ -524,7 +524,6 @@ void Map_Init(PlayState* play) {
|
||||
interfaceCtx->unk_25A = -1;
|
||||
|
||||
interfaceCtx->mapSegment = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*));
|
||||
interfaceCtx->mapSegmentName = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*));
|
||||
// "MAP texture initialization scene_data_ID=%d mapSegment=%x"
|
||||
osSyncPrintf("\n\n\nMAP テクスチャ初期化 scene_data_ID=%d\nmapSegment=%x\n\n", play->sceneNum,
|
||||
interfaceCtx->mapSegment, play);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "textures/message_static/message_static.h"
|
||||
#include "textures/message_texture_static/message_texture_static.h"
|
||||
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
@@ -1114,7 +1115,7 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) {
|
||||
}
|
||||
}
|
||||
if (msgCtx->textDelayTimer == 0) {
|
||||
msgCtx->textDrawPos = i + CVarGetInteger("gTextSpeed", 2);
|
||||
msgCtx->textDrawPos = i + CVarGetInteger("gTextSpeed", 1);
|
||||
msgCtx->textDelayTimer = msgCtx->textDelay;
|
||||
} else {
|
||||
msgCtx->textDelayTimer--;
|
||||
@@ -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);
|
||||
|
||||
+196
-104
@@ -1436,64 +1436,62 @@ Gfx* Gfx_TextureI8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 te
|
||||
return displayListHead;
|
||||
}
|
||||
|
||||
void Inventory_SwapAgeEquipment(void) {
|
||||
void Rando_Inventory_SwapAgeEquipment(void) {
|
||||
s16 i;
|
||||
u16 shieldEquipValue;
|
||||
|
||||
if (LINK_AGE_IN_YEARS == YEARS_CHILD) {
|
||||
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
if (i != 0) {
|
||||
gSaveContext.childEquips.buttonItems[i] = gSaveContext.equips.buttonItems[i];
|
||||
gSaveContext.childEquips.buttonItems[i] =
|
||||
gSaveContext.equips.buttonItems[i];
|
||||
} else {
|
||||
gSaveContext.childEquips.buttonItems[i] = ITEM_SWORD_KOKIRI;
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.childEquips.cButtonSlots[i - 1] = gSaveContext.equips.cButtonSlots[i - 1];
|
||||
gSaveContext.childEquips.cButtonSlots[i - 1] =
|
||||
gSaveContext.equips.cButtonSlots[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// When becoming adult, remove swordless flag since we'll get master sword
|
||||
// (Unless Master Sword is shuffled)
|
||||
// Only in rando to keep swordless link bugs in vanilla
|
||||
if (IS_RANDO && !Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD)) {
|
||||
Flags_UnsetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
gSaveContext.childEquips.equipment = gSaveContext.equips.equipment;
|
||||
|
||||
if (gSaveContext.adultEquips.buttonItems[0] == ITEM_NONE && !(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) && gSaveContext.adultEquips.equipment)) {
|
||||
if (!IS_RANDO || !Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD)) {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER;
|
||||
} else {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
// When becoming adult, remove swordless flag since we'll get master sword
|
||||
// This gets set back appropriately later in the case of master sword shuffle
|
||||
Flags_UnsetInfTable(INFTABLE_SWORDLESS);
|
||||
|
||||
// This section sets up the equipment on the first time going adult.
|
||||
// On master sword shuffle the check for the B button is insufficient, and so checking the equipment is completely zero-ed is needed
|
||||
// (Could just always use `gSaveContext.adultEquips.equipment == 0` for rando?)
|
||||
if (gSaveContext.adultEquips.buttonItems[0] == ITEM_NONE && ((IS_RANDO && !Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD)) || (gSaveContext.adultEquips.equipment == 0))) {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER;
|
||||
|
||||
if (gSaveContext.inventory.items[SLOT_NUT] != ITEM_NONE) {
|
||||
gSaveContext.equips.buttonItems[1] = ITEM_NUT;
|
||||
gSaveContext.equips.cButtonSlots[0] = SLOT_NUT;
|
||||
} else {
|
||||
gSaveContext.equips.buttonItems[1] = gSaveContext.equips.cButtonSlots[0] = ITEM_NONE;
|
||||
gSaveContext.equips.buttonItems[1] = gSaveContext.equips.cButtonSlots[0] =
|
||||
ITEM_NONE;
|
||||
}
|
||||
|
||||
gSaveContext.equips.buttonItems[2] = ITEM_BOMB;
|
||||
gSaveContext.equips.buttonItems[3] = gSaveContext.inventory.items[SLOT_OCARINA];
|
||||
gSaveContext.equips.cButtonSlots[1] = SLOT_BOMB;
|
||||
gSaveContext.equips.cButtonSlots[2] = SLOT_OCARINA;
|
||||
|
||||
gSaveContext.equips.equipment = (EQUIP_VALUE_SWORD_MASTER << (EQUIP_TYPE_SWORD * 4)) |
|
||||
(EQUIP_VALUE_SHIELD_HYLIAN << (EQUIP_TYPE_SHIELD * 4)) |
|
||||
(EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4)) |
|
||||
(EQUIP_VALUE_BOOTS_KOKIRI << (EQUIP_TYPE_BOOTS * 4));
|
||||
(EQUIP_VALUE_SHIELD_HYLIAN << (EQUIP_TYPE_SHIELD * 4)) |
|
||||
(EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4)) |
|
||||
(EQUIP_VALUE_BOOTS_KOKIRI << (EQUIP_TYPE_BOOTS * 4));
|
||||
|
||||
// In Master Sword Shuffle we want to override the equip of the master sword from the vanilla code
|
||||
// First check we have the Master sword in our inventory, and if not, then unequip
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_NONE) {
|
||||
!CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER)) {
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
// Set the dpad to nothing
|
||||
gSaveContext.equips.buttonItems[4] = ITEM_NONE;
|
||||
gSaveContext.equips.buttonItems[5] = ITEM_NONE;
|
||||
@@ -1505,10 +1503,170 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
gSaveContext.equips.cButtonSlots[6] = SLOT_NONE;
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.equips.buttonItems[i] = gSaveContext.adultEquips.buttonItems[i];
|
||||
gSaveContext.equips.buttonItems[i] =
|
||||
gSaveContext.adultEquips.buttonItems[i];
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.equips.cButtonSlots[i - 1] = gSaveContext.adultEquips.cButtonSlots[i - 1];
|
||||
gSaveContext.equips.cButtonSlots[i - 1] =
|
||||
gSaveContext.adultEquips.cButtonSlots[i - 1];
|
||||
}
|
||||
|
||||
if (((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) &&
|
||||
(gSaveContext.equips.buttonItems[i] <= ITEM_POE)) ||
|
||||
((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) &&
|
||||
(gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) {
|
||||
gSaveContext.equips.buttonItems[i] =
|
||||
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]];
|
||||
}
|
||||
}
|
||||
|
||||
// In Master Sword Shuffle we want to set the swordless flag if no item is on the B button
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_NONE) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
gSaveContext.equips.equipment = gSaveContext.adultEquips.equipment;
|
||||
}
|
||||
} else {
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.adultEquips.buttonItems[i] = gSaveContext.equips.buttonItems[i];
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.adultEquips.cButtonSlots[i - 1] =
|
||||
gSaveContext.equips.cButtonSlots[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
gSaveContext.adultEquips.equipment = gSaveContext.equips.equipment;
|
||||
|
||||
if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) {
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.equips.buttonItems[i] =
|
||||
gSaveContext.childEquips.buttonItems[i];
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.equips.cButtonSlots[i - 1] =
|
||||
gSaveContext.childEquips.cButtonSlots[i - 1];
|
||||
}
|
||||
|
||||
if (((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) &&
|
||||
(gSaveContext.equips.buttonItems[i] <= ITEM_POE)) ||
|
||||
((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) &&
|
||||
(gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) {
|
||||
gSaveContext.equips.buttonItems[i] =
|
||||
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]];
|
||||
}
|
||||
}
|
||||
|
||||
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
|
||||
}
|
||||
// In Rando we need an extra case to handle starting as adult. We can use the fact that the childEquips will be uninitialised (i.e. 0) at this point
|
||||
else if (gSaveContext.childEquips.equipment == 0) {
|
||||
|
||||
//zero out items
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.equips.buttonItems[i] = ITEM_NONE;
|
||||
if (i != 0) {
|
||||
gSaveContext.equips.cButtonSlots[i-1] = ITEM_NONE;
|
||||
}
|
||||
}
|
||||
gSaveContext.equips.equipment = (EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4)) |
|
||||
(EQUIP_VALUE_SHIELD_DEKU << (EQUIP_TYPE_SHIELD * 4)) |
|
||||
(EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4)) |
|
||||
(EQUIP_VALUE_BOOTS_KOKIRI << (EQUIP_TYPE_BOOTS * 4));
|
||||
}
|
||||
|
||||
// When becoming child in rando, set swordless flag and clear B button if player doesn't have kokiri sword
|
||||
// Otherwise, equip sword and unset flag
|
||||
if (!CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI)) {
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
} else {
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.equipment |= (EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KOKIRI;
|
||||
Flags_UnsetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
}
|
||||
|
||||
shieldEquipValue = gEquipMasks[EQUIP_TYPE_SHIELD] & gSaveContext.equips.equipment;
|
||||
if (shieldEquipValue) {
|
||||
shieldEquipValue >>= gEquipShifts[EQUIP_TYPE_SHIELD];
|
||||
if (!CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SHIELD, shieldEquipValue - 1)) {
|
||||
gSaveContext.equips.equipment &= gEquipNegMasks[EQUIP_TYPE_SHIELD];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Inventory_SwapAgeEquipment(void) {
|
||||
s16 i;
|
||||
u16 shieldEquipValue;
|
||||
|
||||
// Mod Enhancments can utilise the rando flow path
|
||||
if (IS_RANDO || CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) {
|
||||
Rando_Inventory_SwapAgeEquipment();
|
||||
CVarSetInteger("gSwitchTimeline", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (LINK_AGE_IN_YEARS == YEARS_CHILD) {
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
if (i != 0) {
|
||||
gSaveContext.childEquips.buttonItems[i] =
|
||||
gSaveContext.equips.buttonItems[i];
|
||||
} else {
|
||||
gSaveContext.childEquips.buttonItems[i] = ITEM_SWORD_KOKIRI;
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.childEquips.cButtonSlots[i - 1] =
|
||||
gSaveContext.equips.cButtonSlots[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
gSaveContext.childEquips.equipment = gSaveContext.equips.equipment;
|
||||
|
||||
if (gSaveContext.adultEquips.buttonItems[0] == ITEM_NONE) {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER;
|
||||
|
||||
if (gSaveContext.inventory.items[SLOT_NUT] != ITEM_NONE) {
|
||||
gSaveContext.equips.buttonItems[1] = ITEM_NUT;
|
||||
gSaveContext.equips.cButtonSlots[0] = SLOT_NUT;
|
||||
} else {
|
||||
gSaveContext.equips.buttonItems[1] = gSaveContext.equips.cButtonSlots[0] =
|
||||
ITEM_NONE;
|
||||
}
|
||||
|
||||
gSaveContext.equips.buttonItems[2] = ITEM_BOMB;
|
||||
gSaveContext.equips.buttonItems[3] = gSaveContext.inventory.items[SLOT_OCARINA];
|
||||
gSaveContext.equips.cButtonSlots[1] = SLOT_BOMB;
|
||||
gSaveContext.equips.cButtonSlots[2] = SLOT_OCARINA;
|
||||
gSaveContext.equips.equipment = (EQUIP_VALUE_SWORD_MASTER << (EQUIP_TYPE_SWORD * 4)) |
|
||||
(EQUIP_VALUE_SHIELD_HYLIAN << (EQUIP_TYPE_SHIELD * 4)) |
|
||||
(EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4)) |
|
||||
(EQUIP_VALUE_BOOTS_KOKIRI << (EQUIP_TYPE_BOOTS * 4));
|
||||
// Set the dpad to nothing
|
||||
gSaveContext.equips.buttonItems[4] = ITEM_NONE;
|
||||
gSaveContext.equips.buttonItems[5] = ITEM_NONE;
|
||||
gSaveContext.equips.buttonItems[6] = ITEM_NONE;
|
||||
gSaveContext.equips.buttonItems[7] = ITEM_NONE;
|
||||
gSaveContext.equips.cButtonSlots[3] = SLOT_NONE;
|
||||
gSaveContext.equips.cButtonSlots[4] = SLOT_NONE;
|
||||
gSaveContext.equips.cButtonSlots[5] = SLOT_NONE;
|
||||
gSaveContext.equips.cButtonSlots[6] = SLOT_NONE;
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.equips.buttonItems[i] =
|
||||
gSaveContext.adultEquips.buttonItems[i];
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.equips.cButtonSlots[i - 1] =
|
||||
gSaveContext.adultEquips.cButtonSlots[i - 1];
|
||||
}
|
||||
|
||||
if (((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) &&
|
||||
@@ -1524,59 +1682,25 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
gSaveContext.equips.equipment = gSaveContext.adultEquips.equipment;
|
||||
}
|
||||
} else {
|
||||
// When becoming child, set swordless flag if player doesn't have kokiri sword
|
||||
// Only in rando to keep swordless link bugs in vanilla
|
||||
if (IS_RANDO && CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
// When using enhancements, set swordless flag if player doesn't have kokiri sword or hasn't equipped a sword yet.
|
||||
// Then set the child equips button items to item none to ensure kokiri sword is not equipped
|
||||
if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) && (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0 || Flags_GetInfTable(INFTABLE_SWORDLESS))) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
gSaveContext.childEquips.buttonItems[0] = ITEM_NONE;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.adultEquips.buttonItems[i] = gSaveContext.equips.buttonItems[i];
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.adultEquips.cButtonSlots[i - 1] = gSaveContext.equips.cButtonSlots[i - 1];
|
||||
gSaveContext.adultEquips.cButtonSlots[i - 1] =
|
||||
gSaveContext.equips.cButtonSlots[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
gSaveContext.adultEquips.equipment = gSaveContext.equips.equipment;
|
||||
// Switching age using enhancements separated out to make vanilla flow clear
|
||||
if (CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) {
|
||||
|
||||
if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) {
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.equips.buttonItems[i] = gSaveContext.childEquips.buttonItems[i];
|
||||
gSaveContext.equips.buttonItems[i] =
|
||||
gSaveContext.childEquips.buttonItems[i];
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.equips.cButtonSlots[i - 1] = gSaveContext.childEquips.cButtonSlots[i - 1];
|
||||
}
|
||||
|
||||
if (((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) &&
|
||||
(gSaveContext.equips.buttonItems[i] <= ITEM_POE)) ||
|
||||
((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) &&
|
||||
(gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) {
|
||||
osSyncPrintf("Register_Item_Pt(%d)=%d\n", i, gSaveContext.equips.cButtonSlots[i - 1]);
|
||||
gSaveContext.equips.buttonItems[i] =
|
||||
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]];
|
||||
}
|
||||
}
|
||||
|
||||
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
// Equips kokiri sword in the inventory screen only if kokiri sword exists in inventory and a sword has been equipped already
|
||||
if (!(CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) && !Flags_GetInfTable(INFTABLE_SWORDLESS)) {
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
|
||||
}
|
||||
} else if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) {
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.equips.buttonItems[i] = gSaveContext.childEquips.buttonItems[i];
|
||||
|
||||
if (i != 0) {
|
||||
gSaveContext.equips.cButtonSlots[i - 1] = gSaveContext.childEquips.cButtonSlots[i - 1];
|
||||
gSaveContext.equips.cButtonSlots[i - 1] =
|
||||
gSaveContext.childEquips.cButtonSlots[i - 1];
|
||||
}
|
||||
|
||||
if (((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) &&
|
||||
@@ -1592,43 +1716,11 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
|
||||
} else if (IS_RANDO && Randomizer_GetSettingValue(RSK_STARTING_AGE) == RO_AGE_ADULT) {
|
||||
/*If in rando and starting age is adult, childEquips is not initialized and buttonItems[0]
|
||||
will be ITEM_NONE. When changing age from adult -> child, reset equips to "default"
|
||||
(only kokiri tunic/boots equipped, no sword, no C-button items, no D-Pad items).
|
||||
When becoming child, set swordless flag if player doesn't have kokiri sword
|
||||
Only in rando to keep swordless link bugs in vanilla*/
|
||||
if (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
//zero out items
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
gSaveContext.equips.buttonItems[i] = ITEM_NONE;
|
||||
if (i != 0) {
|
||||
gSaveContext.equips.cButtonSlots[i-1] = ITEM_NONE;
|
||||
}
|
||||
}
|
||||
gSaveContext.equips.equipment = (EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4)) |
|
||||
(EQUIP_VALUE_SHIELD_DEKU << (EQUIP_TYPE_SHIELD * 4)) |
|
||||
(EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4)) |
|
||||
(EQUIP_VALUE_BOOTS_KOKIRI << (EQUIP_TYPE_BOOTS * 4));
|
||||
}
|
||||
|
||||
if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) &&
|
||||
(gSaveContext.equips.buttonItems[0] == ITEM_NONE)) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
if (gSaveContext.childEquips.equipment == 0) {
|
||||
// force equip kokiri tunic and boots in scenario gSaveContext.childEquips.equipment is uninitialized
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.equipment |= (EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4)) |
|
||||
(EQUIP_VALUE_BOOTS_KOKIRI << (EQUIP_TYPE_BOOTS * 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
CVarSetInteger("gSwitchTimeline", 0);
|
||||
|
||||
shieldEquipValue = gEquipMasks[EQUIP_TYPE_SHIELD] & gSaveContext.equips.equipment;
|
||||
if (shieldEquipValue != 0) {
|
||||
if (shieldEquipValue) {
|
||||
shieldEquipValue >>= gEquipShifts[EQUIP_TYPE_SHIELD];
|
||||
if (!CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SHIELD, shieldEquipValue - 1)) {
|
||||
gSaveContext.equips.equipment &= gEquipNegMasks[EQUIP_TYPE_SHIELD];
|
||||
|
||||
@@ -2307,10 +2307,12 @@ void Player_DrawPause(PlayState* play, u8* segment, SkelAnime* skelAnime, Vec3f*
|
||||
}
|
||||
|
||||
srcTable = ResourceMgr_LoadArrayByNameAsVec3s(srcTable);
|
||||
Vec3s* ogSrcTable = srcTable;
|
||||
destTable = skelAnime->jointTable;
|
||||
for (i = 0; i < skelAnime->limbCount; i++) {
|
||||
*destTable++ = *srcTable++;
|
||||
}
|
||||
free(ogSrcTable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -78,6 +78,10 @@ void SkelAnime_DrawLod(PlayState* play, void** skeleton, Vec3s* jointTable,
|
||||
Vec3f pos;
|
||||
Vec3s rot;
|
||||
|
||||
if (CVarGetInteger("gDisableLOD", 0)) {
|
||||
lod = 0;
|
||||
}
|
||||
|
||||
if (skeleton == NULL) {
|
||||
osSyncPrintf(VT_FGCOL(RED));
|
||||
osSyncPrintf("Si2_Lod_draw():skelがNULLです。\n"); // "skel is NULL."
|
||||
@@ -144,6 +148,8 @@ void SkelAnime_DrawFlexLimbLod(PlayState* play, s32 limbIndex, void** skeleton,
|
||||
|
||||
newDList = limbDList = limb->dLists[lod];
|
||||
|
||||
play->flexLimbOverrideMTX = mtx;
|
||||
|
||||
if ((overrideLimbDraw == NULL) || !overrideLimbDraw(play, limbIndex, &newDList, &pos, &rot, arg)) {
|
||||
Matrix_TranslateRotateZYX(&pos, &rot);
|
||||
if (newDList != NULL) {
|
||||
@@ -191,6 +197,10 @@ void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable,
|
||||
Vec3s rot;
|
||||
Mtx* mtx = Graph_Alloc(play->state.gfxCtx, dListCount * sizeof(Mtx));
|
||||
|
||||
if (CVarGetInteger("gDisableLOD", 0)) {
|
||||
lod = 0;
|
||||
}
|
||||
|
||||
if (skeleton == NULL) {
|
||||
osSyncPrintf(VT_FGCOL(RED));
|
||||
osSyncPrintf("Si2_Lod_draw_SV():skelがNULLです。\n"); // "skel is NULL."
|
||||
@@ -212,6 +222,8 @@ void SkelAnime_DrawFlexLod(PlayState* play, void** skeleton, Vec3s* jointTable,
|
||||
|
||||
newDList = limbDList = rootLimb->dLists[lod];
|
||||
|
||||
play->flexLimbOverrideMTX = &mtx;
|
||||
|
||||
if ((overrideLimbDraw == 0) || !overrideLimbDraw(play, 1, &newDList, &pos, &rot, arg)) {
|
||||
Matrix_TranslateRotateZYX(&pos, &rot);
|
||||
if (newDList != NULL) {
|
||||
@@ -298,6 +310,20 @@ void SkelAnime_DrawSkeletonOpa(PlayState* play, SkelAnime* skelAnime, OverrideLi
|
||||
}
|
||||
}
|
||||
|
||||
Gfx* SkelAnime_DrawSkeleton2(PlayState* play, SkelAnime* skelAnime, OverrideLimbDrawOpa overrideLimbDraw,
|
||||
PostLimbDrawOpa postLimbDraw, void* arg, Gfx* gfx)
|
||||
{
|
||||
if (skelAnime->skeletonHeader->skeletonType == SKELANIME_TYPE_NORMAL) {
|
||||
return SkelAnime_Draw(play, skelAnime->skeleton, skelAnime->jointTable, overrideLimbDraw, postLimbDraw, arg, gfx);
|
||||
} else if (skelAnime->skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
FlexSkeletonHeader* flexHeader = (FlexSkeletonHeader*)skelAnime->skeletonHeader;
|
||||
return SkelAnime_DrawFlex(play, skelAnime->skeleton, skelAnime->jointTable, flexHeader->dListCount,
|
||||
overrideLimbDraw, postLimbDraw, arg, gfx);
|
||||
}
|
||||
|
||||
return gfx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw all limbs of type `StandardLimb` in a given skeleton to the polyOpa buffer
|
||||
*/
|
||||
@@ -375,6 +401,8 @@ void SkelAnime_DrawFlexLimbOpa(PlayState* play, s32 limbIndex, void** skeleton,
|
||||
|
||||
newDList = limbDList = limb->dList;
|
||||
|
||||
play->flexLimbOverrideMTX = limbMatricies;
|
||||
|
||||
if ((overrideLimbDraw == NULL) || !overrideLimbDraw(play, limbIndex, &newDList, &pos, &rot, arg)) {
|
||||
Matrix_TranslateRotateZYX(&pos, &rot);
|
||||
if (newDList != NULL) {
|
||||
|
||||
+60
-57
@@ -59,65 +59,68 @@ void Sram_OpenSave() {
|
||||
|
||||
Save_LoadFile();
|
||||
|
||||
if (!CVarGetInteger("gRememberSaveLocation", 0) || gSaveContext.savedSceneNum == SCENE_FAIRYS_FOUNTAIN ||
|
||||
gSaveContext.savedSceneNum == SCENE_GROTTOS) {
|
||||
switch (gSaveContext.savedSceneNum) {
|
||||
case SCENE_DEKU_TREE:
|
||||
case SCENE_DODONGOS_CAVERN:
|
||||
case SCENE_JABU_JABU:
|
||||
case SCENE_FOREST_TEMPLE:
|
||||
case SCENE_FIRE_TEMPLE:
|
||||
case SCENE_WATER_TEMPLE:
|
||||
case SCENE_SPIRIT_TEMPLE:
|
||||
case SCENE_SHADOW_TEMPLE:
|
||||
case SCENE_BOTTOM_OF_THE_WELL:
|
||||
case SCENE_ICE_CAVERN:
|
||||
case SCENE_GANONS_TOWER:
|
||||
case SCENE_GERUDO_TRAINING_GROUND:
|
||||
case SCENE_THIEVES_HIDEOUT:
|
||||
case SCENE_INSIDE_GANONS_CASTLE:
|
||||
gSaveContext.entranceIndex = dungeonEntrances[gSaveContext.savedSceneNum];
|
||||
break;
|
||||
case SCENE_DEKU_TREE_BOSS:
|
||||
gSaveContext.entranceIndex = 0;
|
||||
break;
|
||||
case SCENE_DODONGOS_CAVERN_BOSS:
|
||||
gSaveContext.entranceIndex = 4;
|
||||
break;
|
||||
case SCENE_JABU_JABU_BOSS:
|
||||
gSaveContext.entranceIndex = 0x28;
|
||||
break;
|
||||
case SCENE_FOREST_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x169;
|
||||
break;
|
||||
case SCENE_FIRE_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x165;
|
||||
break;
|
||||
case SCENE_WATER_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x10;
|
||||
break;
|
||||
case SCENE_SPIRIT_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x82;
|
||||
break;
|
||||
case SCENE_SHADOW_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x37;
|
||||
break;
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR:
|
||||
case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE:
|
||||
case SCENE_GANONDORF_BOSS:
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR:
|
||||
case SCENE_GANON_BOSS:
|
||||
gSaveContext.entranceIndex = 0x41B;
|
||||
break;
|
||||
switch (gSaveContext.savedSceneNum) {
|
||||
case SCENE_DEKU_TREE:
|
||||
case SCENE_DODONGOS_CAVERN:
|
||||
case SCENE_JABU_JABU:
|
||||
case SCENE_FOREST_TEMPLE:
|
||||
case SCENE_FIRE_TEMPLE:
|
||||
case SCENE_WATER_TEMPLE:
|
||||
case SCENE_SPIRIT_TEMPLE:
|
||||
case SCENE_SHADOW_TEMPLE:
|
||||
case SCENE_BOTTOM_OF_THE_WELL:
|
||||
case SCENE_ICE_CAVERN:
|
||||
case SCENE_GANONS_TOWER:
|
||||
case SCENE_GERUDO_TRAINING_GROUND:
|
||||
case SCENE_THIEVES_HIDEOUT:
|
||||
case SCENE_INSIDE_GANONS_CASTLE:
|
||||
gSaveContext.entranceIndex = dungeonEntrances[gSaveContext.savedSceneNum];
|
||||
break;
|
||||
case SCENE_DEKU_TREE_BOSS:
|
||||
gSaveContext.entranceIndex = 0;
|
||||
break;
|
||||
case SCENE_DODONGOS_CAVERN_BOSS:
|
||||
gSaveContext.entranceIndex = 4;
|
||||
break;
|
||||
case SCENE_JABU_JABU_BOSS:
|
||||
gSaveContext.entranceIndex = 0x28;
|
||||
break;
|
||||
case SCENE_FOREST_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x169;
|
||||
break;
|
||||
case SCENE_FIRE_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x165;
|
||||
break;
|
||||
case SCENE_WATER_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x10;
|
||||
break;
|
||||
case SCENE_SPIRIT_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x82;
|
||||
break;
|
||||
case SCENE_SHADOW_TEMPLE_BOSS:
|
||||
gSaveContext.entranceIndex = 0x37;
|
||||
break;
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR:
|
||||
case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE:
|
||||
case SCENE_GANONDORF_BOSS:
|
||||
case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR:
|
||||
case SCENE_GANON_BOSS:
|
||||
gSaveContext.entranceIndex = 0x41B;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (gSaveContext.savedSceneNum != SCENE_LINKS_HOUSE) {
|
||||
gSaveContext.entranceIndex = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? 0xBB : 0x5F4;
|
||||
} else {
|
||||
gSaveContext.entranceIndex = 0xBB;
|
||||
}
|
||||
default:
|
||||
// Use the saved entrance value with remember save location, except when in grottos/fairy fountains
|
||||
if (CVarGetInteger("gRememberSaveLocation", 0) && gSaveContext.savedSceneNum != SCENE_FAIRYS_FOUNTAIN &&
|
||||
gSaveContext.savedSceneNum != SCENE_GROTTOS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gSaveContext.savedSceneNum != SCENE_LINKS_HOUSE) {
|
||||
gSaveContext.entranceIndex = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? 0xBB : 0x5F4;
|
||||
} else {
|
||||
gSaveContext.entranceIndex = 0xBB;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex);
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
#include <stdlib.h> // malloc
|
||||
#include <string.h> // memcpy
|
||||
|
||||
// OTRTODO: Replace usage of this method when we can clear the cache
|
||||
// for a single texture without the need of a DL opcode in the render code
|
||||
void gfx_texture_cache_clear();
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
#define LAVA_TEX_WIDTH 32
|
||||
@@ -69,7 +65,7 @@ static u8 sMaskTex16x32[16 * 32] = { { 0 } };
|
||||
static u8 sMaskTex32x16[32 * 16] = { { 0 } };
|
||||
static u8 sMaskTex8x8[8 * 8] = { { 0 } };
|
||||
static u8 sMaskTex8x32[8 * 32] = { { 0 } };
|
||||
static u8 sMaskTexLava[32 * 64] = { { 0 } };
|
||||
static u8 sMaskTexLava[LAVA_TEX_WIDTH * LAVA_TEX_HEIGHT] = { { 0 } };
|
||||
|
||||
static u32* sLavaFloorModifiedTexRaw = NULL;
|
||||
static u32* sLavaWavyTexRaw = NULL;
|
||||
@@ -112,6 +108,22 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
|
||||
u32* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
|
||||
size_t lavaSize = ResourceGetSizeByName(sLavaFloorLavaTex);
|
||||
size_t floorSize = ResourceGetSizeByName(gDodongosCavernBossLavaFloorTex);
|
||||
size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex);
|
||||
|
||||
// If the sizes don't match, then don't bother with the blended effect to avoid crashing
|
||||
if (floorSize != lavaSize || floorSize != rockSize) {
|
||||
uint8_t maskVal = !!Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num);
|
||||
|
||||
if (sMaskTexLava[0] != maskVal) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = maskVal;
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, NULL);
|
||||
Gfx_TextureCacheDelete(sMaskTexLava);
|
||||
return;
|
||||
}
|
||||
|
||||
sLavaFloorModifiedTexRaw = malloc(lavaSize);
|
||||
sLavaWavyTexRaw = malloc(floorSize);
|
||||
@@ -121,7 +133,6 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
|
||||
// When KD is dead, just immediately copy the rock texture
|
||||
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
|
||||
u32* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex);
|
||||
memcpy(sLavaFloorModifiedTexRaw, rockTex, rockSize);
|
||||
}
|
||||
|
||||
@@ -145,7 +156,16 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() {
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTex);
|
||||
}
|
||||
|
||||
gfx_texture_cache_clear();
|
||||
// Set all true for the lava as it will always replace the scene texture
|
||||
if (sMaskTexLava[0] == 0) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_TextureCacheDelete(sMaskTexLava);
|
||||
Gfx_TextureCacheDelete(sLavaWavyTex);
|
||||
Gfx_TextureCacheDelete(sLavaFloorModifiedTex);
|
||||
}
|
||||
|
||||
void func_808C12C4(u8* arg1, s16 arg2) {
|
||||
@@ -170,6 +190,11 @@ void func_808C12C4(u8* arg1, s16 arg2) {
|
||||
|
||||
// Same as func_808C1554 but works with u32 values for RGBA32 raw textures
|
||||
void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
// Raw lava not registered, so abort the wave modification
|
||||
if (sLavaWavyTexRaw == NULL || sLavaFloorModifiedTexRaw == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
u16 width = ResourceGetTexWidthByName(arg0);
|
||||
s32 size = ResourceGetTexHeightByName(arg0) * width;
|
||||
|
||||
@@ -203,9 +228,7 @@ void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
}
|
||||
|
||||
free(sp54);
|
||||
|
||||
// Need to clear the cache after updating sLavaWavyTexRaw
|
||||
gfx_texture_cache_clear();
|
||||
Gfx_TextureCacheDelete(sLavaWavyTexRaw);
|
||||
}
|
||||
|
||||
// Modified to support CPU modified texture with the resource system
|
||||
@@ -234,8 +257,7 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
}
|
||||
}
|
||||
|
||||
// Need to clear the cache after updating sLavaWavyTex
|
||||
gfx_texture_cache_clear();
|
||||
Gfx_TextureCacheDelete(sLavaWavyTex);
|
||||
}
|
||||
|
||||
void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) {
|
||||
@@ -325,7 +347,7 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
|
||||
|
||||
// #region SOH [General]
|
||||
// Init mask values for all blended textures
|
||||
// Init mask values for all KD blended textures
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTex8x16); i++) {
|
||||
sMaskTex8x16[i] = 0;
|
||||
}
|
||||
@@ -341,10 +363,6 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTex32x16); i++) {
|
||||
sMaskTex32x16[i] = 0;
|
||||
}
|
||||
// Set all true for the lava as it will always replace the scene texture
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 1;
|
||||
}
|
||||
|
||||
// Register all blended textures
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015890, sMaskTex8x16, NULL);
|
||||
@@ -358,6 +376,13 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL);
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL);
|
||||
|
||||
// Clear cache for masks
|
||||
Gfx_TextureCacheDelete(sMaskTex8x16);
|
||||
Gfx_TextureCacheDelete(sMaskTex8x32);
|
||||
Gfx_TextureCacheDelete(sMaskTex16x16);
|
||||
Gfx_TextureCacheDelete(sMaskTex16x32);
|
||||
Gfx_TextureCacheDelete(sMaskTex32x16);
|
||||
|
||||
BossDodongo_RegisterBlendedLavaTextureUpdate();
|
||||
|
||||
// Register alt listener to update the blended lava for the replacement texture based on alt path
|
||||
@@ -1174,15 +1199,24 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) {
|
||||
|
||||
for (i2 = 0; i2 < 20; i2++) {
|
||||
s16 new_var = this->unk_1C2 & (LAVA_TEX_SIZE - 1);
|
||||
// Compute the index to a scaled position (scaling pseudo x,y as a 1D value)
|
||||
s32 indexStart = ((new_var % LAVA_TEX_WIDTH) * widthScale) + ((new_var / LAVA_TEX_WIDTH) * width * heightScale);
|
||||
|
||||
// From the starting index, apply extra pixels right/down based on the scale
|
||||
for (size_t j = 0; j < heightScale; j++) {
|
||||
for (size_t i3 = 0; i3 < widthScale; i3++) {
|
||||
s32 scaledIndex = (indexStart + i3 + (j * width)) & (size - 1);
|
||||
ptr1[scaledIndex] = ptr2[scaledIndex];
|
||||
// Raw lava must be registered, otherwise skip the effect for incompatible texture pack
|
||||
// and instead set the mask to simulate the lava disappearing by turning black
|
||||
if (sLavaFloorModifiedTexRaw != NULL) {
|
||||
// Compute the index to a scaled position (scaling pseudo x,y as a 1D value)
|
||||
s32 indexStart =
|
||||
((new_var % LAVA_TEX_WIDTH) * widthScale) + ((new_var / LAVA_TEX_WIDTH) * width * heightScale);
|
||||
|
||||
// From the starting index, apply extra pixels right/down based on the scale
|
||||
for (size_t j = 0; j < heightScale; j++) {
|
||||
for (size_t i3 = 0; i3 < widthScale; i3++) {
|
||||
s32 scaledIndex = (indexStart + i3 + (j * width)) & (size - 1);
|
||||
ptr1[scaledIndex] = ptr2[scaledIndex];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sMaskTexLava[new_var] = 1;
|
||||
Gfx_TextureCacheDelete(sMaskTexLava);
|
||||
}
|
||||
|
||||
this->unk_1C2 += 37;
|
||||
@@ -1254,6 +1288,10 @@ block_1:
|
||||
if (*dList != NULL) {
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
if (this->skelAnime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
MATRIX_TOMTX(*play->flexLimbOverrideMTX);
|
||||
}
|
||||
|
||||
mtxScaleZ = 1.0f;
|
||||
mtxScaleY = 1.0f;
|
||||
|
||||
@@ -1274,11 +1312,20 @@ block_1:
|
||||
Matrix_RotateX(-(this->unk_25C[limbIndex] * 0.115f), MTXMODE_APPLY);
|
||||
}
|
||||
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
if (this->skelAnime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
gSPMatrix(POLY_OPA_DISP++, *play->flexLimbOverrideMTX, G_MTX_LOAD);
|
||||
} else {
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
}
|
||||
|
||||
gSPDisplayList(POLY_OPA_DISP++, *dList);
|
||||
Matrix_Pop();
|
||||
|
||||
if (this->skelAnime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
(*play->flexLimbOverrideMTX)++;
|
||||
}
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
{ s32 pad; } // Required to match
|
||||
@@ -1322,10 +1369,6 @@ void BossDodongo_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTex32x16);
|
||||
}
|
||||
|
||||
if (this->unk_1C6 != 0) {
|
||||
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTexLava);
|
||||
}
|
||||
|
||||
if ((this->unk_1C0 >= 2) && (this->unk_1C0 & 1)) {
|
||||
POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 255, 255, 0, 900, 1099);
|
||||
} else {
|
||||
|
||||
@@ -2015,12 +2015,26 @@ s32 BossGoma_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f
|
||||
Matrix_TranslateRotateZYX(pos, rot);
|
||||
|
||||
if (*dList != NULL) {
|
||||
if (this->skelanime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
MATRIX_TOMTX(*play->flexLimbOverrideMTX);
|
||||
}
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_Scale(this->eyeIrisScaleX, this->eyeIrisScaleY, 1.0f, MTXMODE_APPLY);
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
|
||||
if (this->skelanime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
gSPMatrix(POLY_OPA_DISP++, *play->flexLimbOverrideMTX, G_MTX_LOAD);
|
||||
} else {
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
}
|
||||
|
||||
gSPDisplayList(POLY_OPA_DISP++, *dList);
|
||||
Matrix_Pop();
|
||||
|
||||
if (this->skelanime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
(*play->flexLimbOverrideMTX)++;
|
||||
}
|
||||
}
|
||||
|
||||
doNotDrawLimb = true;
|
||||
@@ -2034,14 +2048,28 @@ s32 BossGoma_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f
|
||||
Matrix_TranslateRotateZYX(pos, rot);
|
||||
|
||||
if (*dList != NULL) {
|
||||
if (this->skelanime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
MATRIX_TOMTX(*play->flexLimbOverrideMTX);
|
||||
}
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_Scale(this->tailLimbsScale[limbIndex - BOSSGOMA_LIMB_TAIL4],
|
||||
this->tailLimbsScale[limbIndex - BOSSGOMA_LIMB_TAIL4],
|
||||
this->tailLimbsScale[limbIndex - BOSSGOMA_LIMB_TAIL4], MTXMODE_APPLY);
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
|
||||
if (this->skelanime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
gSPMatrix(POLY_OPA_DISP++, *play->flexLimbOverrideMTX, G_MTX_LOAD);
|
||||
} else {
|
||||
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
}
|
||||
|
||||
gSPDisplayList(POLY_OPA_DISP++, *dList);
|
||||
Matrix_Pop();
|
||||
|
||||
if (this->skelanime.skeletonHeader->skeletonType == SKELANIME_TYPE_FLEX) {
|
||||
(*play->flexLimbOverrideMTX)++;
|
||||
}
|
||||
}
|
||||
|
||||
doNotDrawLimb = true;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1055,7 +1055,7 @@ void DoorWarp1_DrawBlueCrystal(DoorWarp1* this, PlayState* play) {
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0xFF, 0xFF, 200, 255, 255, (u8)this->crystalAlpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, (u8)this->crystalAlpha);
|
||||
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime, NULL, NULL,
|
||||
&this->actor, POLY_XLU_DISP);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
@@ -1079,7 +1079,7 @@ void DoorWarp1_DrawPurpleCrystal(DoorWarp1* this, PlayState* play) {
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (u8)this->crystalAlpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 150, 0, 100, (u8)this->crystalAlpha);
|
||||
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime, NULL, NULL,
|
||||
&this->actor, POLY_XLU_DISP);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -768,7 +768,7 @@ void EnBili_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPSegment(POLY_XLU_DISP++, 0x09, D_809C1700);
|
||||
}
|
||||
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnBili_OverrideLimbDraw, NULL, this, POLY_XLU_DISP);
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ static InitChainEntry sInitChain[] = {
|
||||
};
|
||||
|
||||
static UNK_TYPE sUnused;
|
||||
GetItemEntry sItem;
|
||||
|
||||
Gfx gSkullTreasureChestChestSideAndLidDL[116] = {0};
|
||||
Gfx gGoldTreasureChestChestSideAndLidDL[116] = {0};
|
||||
@@ -472,7 +471,7 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) {
|
||||
func_8002DBD0(&this->dyna.actor, &sp4C, &player->actor.world.pos);
|
||||
if (sp4C.z > -50.0f && sp4C.z < 0.0f && fabsf(sp4C.y) < 10.0f && fabsf(sp4C.x) < 20.0f &&
|
||||
Player_IsFacingActor(&this->dyna.actor, 0x3000, play)) {
|
||||
sItem = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F);
|
||||
GetItemEntry sItem = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F);
|
||||
GetItemEntry blueRupee = ItemTable_RetrieveEntry(MOD_NONE, GI_RUPEE_BLUE);
|
||||
|
||||
// RANDOTODO treasure chest game rando
|
||||
@@ -628,7 +627,7 @@ void EnBox_Update(Actor* thisx, PlayState* play) {
|
||||
}
|
||||
|
||||
if (((!IS_RANDO && ((this->dyna.actor.params >> 5 & 0x7F) == 0x7C)) ||
|
||||
(IS_RANDO && ABS(sItem.getItemId) == RG_ICE_TRAP)) &&
|
||||
(IS_RANDO && this->getItemEntry.getItemId == RG_ICE_TRAP)) &&
|
||||
this->actionFunc == EnBox_Open && this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100) {
|
||||
if (!CVarGetInteger("gAddTraps.enabled", 0)) {
|
||||
EnBox_SpawnIceSmoke(this, play);
|
||||
@@ -948,7 +947,7 @@ void EnBox_Draw(Actor* thisx, PlayState* play) {
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, EnBox_EmptyDList(play->state.gfxCtx));
|
||||
Gfx_SetupDL_25Opa(play->state.gfxCtx);
|
||||
POLY_OPA_DISP = SkelAnime_Draw(play, this->skelanime.skeleton, this->skelanime.jointTable, NULL,
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelanime, NULL,
|
||||
EnBox_PostLimbDraw, this, POLY_OPA_DISP);
|
||||
} else if (this->alpha != 0) {
|
||||
gDPPipeSync(POLY_XLU_DISP++);
|
||||
@@ -959,7 +958,7 @@ void EnBox_Draw(Actor* thisx, PlayState* play) {
|
||||
} else {
|
||||
gSPSegment(POLY_XLU_DISP++, 0x08, func_809CA4A0(play->state.gfxCtx));
|
||||
}
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelanime.skeleton, this->skelanime.jointTable, NULL,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelanime, NULL,
|
||||
EnBox_PostLimbDraw, this, POLY_XLU_DISP);
|
||||
}
|
||||
|
||||
|
||||
@@ -856,7 +856,7 @@ void EnBw_Draw(Actor* thisx, PlayState* play2) {
|
||||
Gfx_SetupDL_25Opa(play->state.gfxCtx);
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, this->color1.r, this->color1.g, this->color1.b, this->color1.a);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]);
|
||||
POLY_OPA_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnBw_OverrideLimbDraw, NULL, this, POLY_OPA_DISP);
|
||||
} else {
|
||||
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
|
||||
@@ -864,7 +864,7 @@ void EnBw_Draw(Actor* thisx, PlayState* play2) {
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 0, 0, 0, this->color1.a);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, this->color1.r, this->color1.g, this->color1.b, this->color1.a);
|
||||
gSPSegment(POLY_XLU_DISP++, 0x08, &D_80116280[0]);
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnBw_OverrideLimbDraw, NULL, this, POLY_XLU_DISP);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -708,14 +708,14 @@ void EnEiyer_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]);
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255);
|
||||
|
||||
POLY_OPA_DISP = SkelAnime_Draw(play, this->skelanime.skeleton, this->skelanime.jointTable,
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelanime,
|
||||
EnEiyer_OverrideLimbDraw, NULL, this, POLY_OPA_DISP);
|
||||
} else {
|
||||
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
|
||||
gSPSegment(POLY_XLU_DISP++, 0x08, D_80116280);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->actor.shape.shadowAlpha);
|
||||
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelanime.skeleton, this->skelanime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelanime,
|
||||
EnEiyer_OverrideLimbDraw, NULL, this, POLY_XLU_DISP);
|
||||
}
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -1540,7 +1540,7 @@ void EnElf_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPEndDisplayList(dListHead++);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, (u8)this->outerColor.r, (u8)this->outerColor.g, (u8)this->outerColor.b,
|
||||
(u8)(envAlpha * alphaScale));
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnElf_OverrideLimbDraw, NULL, this, POLY_XLU_DISP);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -837,8 +837,9 @@ void EnFirefly_Draw(Actor* thisx, PlayState* play) {
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255);
|
||||
}
|
||||
|
||||
POLY_OPA_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
EnFirefly_OverrideLimbDraw, EnFirefly_PostLimbDraw, &this->actor, POLY_OPA_DISP);
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime, EnFirefly_OverrideLimbDraw, EnFirefly_PostLimbDraw,
|
||||
&this->actor, POLY_OPA_DISP);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
@@ -854,7 +855,7 @@ void EnFirefly_DrawInvisible(Actor* thisx, PlayState* play) {
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 255);
|
||||
}
|
||||
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnFirefly_OverrideLimbDraw, EnFirefly_PostLimbDraw, this, POLY_XLU_DISP);
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -858,7 +858,7 @@ void EnPartner_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPEndDisplayList(dListHead++);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, (u8)this->outerColor.r, (u8)this->outerColor.g, (u8)this->outerColor.b,
|
||||
(u8)(envAlpha * alphaScale));
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnPartner_OverrideLimbDraw, NULL, this, POLY_XLU_DISP);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -267,7 +267,7 @@ void EnPoDesert_Draw(Actor* thisx, PlayState* play) {
|
||||
} else {
|
||||
gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280 + 2);
|
||||
}
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnPoDesert_OverrideLimbDraw, EnPoDesert_PostLimbDraw, &this->actor, POLY_XLU_DISP);
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
@@ -944,15 +944,14 @@ void EnPoField_Draw(Actor* thisx, PlayState* play) {
|
||||
this->lightColor.a));
|
||||
gSPSegment(POLY_OPA_DISP++, 0x0C, D_80116280 + 2);
|
||||
POLY_OPA_DISP =
|
||||
SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnPoField_OverrideLimbDraw2, EnPoField_PostLimDraw2, &this->actor, POLY_OPA_DISP);
|
||||
} else {
|
||||
gSPSegment(POLY_XLU_DISP++, 0x08,
|
||||
Gfx_EnvColor(play->state.gfxCtx, this->lightColor.r, this->lightColor.g, this->lightColor.b,
|
||||
this->lightColor.a));
|
||||
gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280);
|
||||
POLY_XLU_DISP =
|
||||
SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnPoField_OverrideLimbDraw2, EnPoField_PostLimDraw2, &this->actor, POLY_XLU_DISP);
|
||||
}
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
|
||||
@@ -1373,14 +1373,12 @@ void EnPoSisters_Draw(Actor* thisx, PlayState* play) {
|
||||
if (this->unk_22E.a == 255 || this->unk_22E.a == 0) {
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, this->unk_22E.r, this->unk_22E.g, this->unk_22E.b, this->unk_22E.a);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x09, D_80116280 + 2);
|
||||
POLY_OPA_DISP =
|
||||
SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
EnPoSisters_OverrideLimbDraw, EnPoSisters_PostLimbDraw, &this->actor, POLY_OPA_DISP);
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime, EnPoSisters_OverrideLimbDraw,
|
||||
EnPoSisters_PostLimbDraw, &this->actor, POLY_OPA_DISP);
|
||||
} else {
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->unk_22E.a);
|
||||
gSPSegment(POLY_XLU_DISP++, 0x09, D_80116280);
|
||||
POLY_XLU_DISP =
|
||||
SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnPoSisters_OverrideLimbDraw, EnPoSisters_PostLimbDraw, &this->actor, POLY_XLU_DISP);
|
||||
}
|
||||
if (!(this->unk_199 & 0x80)) {
|
||||
|
||||
@@ -1085,12 +1085,12 @@ void EnPoh_DrawRegular(Actor* thisx, PlayState* play) {
|
||||
if (this->lightColor.a == 255 || this->lightColor.a == 0) {
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, this->lightColor.r, this->lightColor.g, this->lightColor.b, this->lightColor.a);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, D_80116280 + 2);
|
||||
POLY_OPA_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnPoh_OverrideLimbDraw, EnPoh_PostLimbDraw, &this->actor, POLY_OPA_DISP);
|
||||
} else {
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->lightColor.a);
|
||||
gSPSegment(POLY_XLU_DISP++, 0x08, D_80116280);
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnPoh_OverrideLimbDraw, EnPoh_PostLimbDraw, &this->actor, POLY_XLU_DISP);
|
||||
}
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
|
||||
@@ -821,19 +821,6 @@ void func_80AF3F20(EnRu2* this, PlayState* play) {
|
||||
void EnRu2_Draw(Actor* thisx, PlayState* play) {
|
||||
EnRu2* this = (EnRu2*)thisx;
|
||||
|
||||
// FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings.
|
||||
// Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so
|
||||
// it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the
|
||||
// earring texture loads into the same place in tmem as the brick texture, so when it comes to rendering, TEXEL1
|
||||
// uses the earring texture with diffrent clamp settings, and it displays without noticeable error. However, both
|
||||
// texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses
|
||||
// our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces
|
||||
// TEXEL1 with TEXEL0, which is most likely the original intention, and all is well.
|
||||
Gfx* gfx = ResourceMgr_LoadGfxByName(gAdultRutoHeadDL);
|
||||
Gfx patch = gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED, TEXEL0, 0,
|
||||
PRIM_LOD_FRAC, COMBINED);
|
||||
gfx[0xA2] = patch;
|
||||
|
||||
if ((this->drawConfig < 0) || (this->drawConfig >= ARRAY_COUNT(sDrawFuncs)) ||
|
||||
(sDrawFuncs[this->drawConfig] == 0)) {
|
||||
// "Draw Mode is improper!"
|
||||
|
||||
@@ -16,6 +16,7 @@ void EnSi_Init(Actor* thisx, PlayState* play);
|
||||
void EnSi_Destroy(Actor* thisx, PlayState* play);
|
||||
void EnSi_Update(Actor* thisx, PlayState* play);
|
||||
void EnSi_Draw(Actor* thisx, PlayState* play);
|
||||
void EnSi_Reset();
|
||||
|
||||
s32 func_80AFB748(EnSi* this, PlayState* play);
|
||||
void func_80AFB768(EnSi* this, PlayState* play);
|
||||
@@ -61,7 +62,7 @@ const ActorInit En_Si_InitVars = {
|
||||
(ActorFunc)EnSi_Destroy,
|
||||
(ActorFunc)EnSi_Update,
|
||||
(ActorFunc)EnSi_Draw,
|
||||
NULL,
|
||||
(ActorResetFunc)EnSi_Reset,
|
||||
};
|
||||
|
||||
void EnSi_Init(Actor* thisx, PlayState* play) {
|
||||
@@ -224,6 +225,11 @@ void EnSi_Draw(Actor* thisx, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
void EnSi_Reset() {
|
||||
textId = 0xB4;
|
||||
giveItemId = ITEM_SKULL_TOKEN;
|
||||
}
|
||||
|
||||
void Randomizer_UpdateSkullReward(EnSi* this, PlayState* play) {
|
||||
Player* player = GET_PLAYER(play);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h"
|
||||
#include "objects/object_ossan/object_ossan.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_NO_LOCKON)
|
||||
|
||||
@@ -371,7 +372,8 @@ void EnSyatekiMan_EndGame(EnSyatekiMan* this, PlayState* play) {
|
||||
this->getItemId = GI_RUPEE_PURPLE;
|
||||
}
|
||||
} else {
|
||||
if(IS_RANDO && !Flags_GetTreasure(play, 0x1F)) {
|
||||
// Only give the adult rando reward when the player has a quiver
|
||||
if (IS_RANDO && !Flags_GetTreasure(play, 0x1F) && CUR_UPG_VALUE(UPG_QUIVER) > 0) {
|
||||
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_SHOOTING_GALLERY_REWARD, GI_QUIVER_50);
|
||||
this->getItemId = this->getItemEntry.getItemId;
|
||||
Flags_SetTreasure(play, 0x1F);
|
||||
@@ -448,6 +450,9 @@ void EnSyatekiMan_FinishPrize(EnSyatekiMan* this, PlayState* play) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_0D);
|
||||
} else if ((this->getItemId == GI_QUIVER_40) || (this->getItemId == GI_QUIVER_50)) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_0E);
|
||||
} else if (IS_RANDO && LINK_IS_ADULT && CUR_UPG_VALUE(UPG_QUIVER) == 0) {
|
||||
// In Rando without a quiver, display a message reminding the player to come back with a bow
|
||||
Message_StartTextbox(play, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW, NULL);
|
||||
}
|
||||
this->gameResult = SYATEKI_RESULT_NONE;
|
||||
this->actor.parent = this->tempGallery;
|
||||
|
||||
@@ -814,7 +814,7 @@ void EnVali_Draw(Actor* thisx, PlayState* play) {
|
||||
|
||||
EnVali_DrawBody(this, play);
|
||||
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnVali_OverrideLimbDraw, EnVali_PostLimbDraw, this, POLY_XLU_DISP);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -640,13 +640,13 @@ void EnWeiyer_Draw(Actor* thisx, PlayState* play) {
|
||||
Gfx_SetupDL_25Opa(play->state.gfxCtx);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]);
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255);
|
||||
POLY_OPA_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
EnWeiyer_OverrideLimbDraw, NULL, &this->actor, POLY_OPA_DISP);
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime, EnWeiyer_OverrideLimbDraw, NULL, &this->actor,
|
||||
POLY_OPA_DISP);
|
||||
} else {
|
||||
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
|
||||
gSPSegment(POLY_XLU_DISP++, 0x08, &D_80116280[0]);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->actor.shape.shadowAlpha);
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnWeiyer_OverrideLimbDraw, NULL, &this->actor, POLY_XLU_DISP);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,13 +101,18 @@ static f32 sSpawnSin;
|
||||
s32 EnWood02_SpawnZoneCheck(EnWood02* this, PlayState* play, Vec3f* pos) {
|
||||
f32 phi_f12;
|
||||
|
||||
if (CVarGetInteger("gDisableDrawDistance", 0) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SkinMatrix_Vec3fMtxFMultXYZW(&play->viewProjectionMtxF, pos, &this->actor.projectedPos,
|
||||
&this->actor.projectedW);
|
||||
|
||||
// #region SOH [Enhancement] Use the extended culling calculation
|
||||
if (CVarGetInteger("gDisableDrawDistance", 0) || CVarGetInteger("gEnhancements.WidescreenActorCulling", 0)) {
|
||||
bool shipShouldDraw = false;
|
||||
bool shipShouldUpdate = false;
|
||||
return Ship_CalcShouldDrawAndUpdate(play, &this->actor, &this->actor.projectedPos, this->actor.projectedW,
|
||||
&shipShouldDraw, &shipShouldUpdate);
|
||||
}
|
||||
// #endregion
|
||||
|
||||
phi_f12 = ((this->actor.projectedW == 0.0f) ? 1000.0f : fabsf(1.0f / this->actor.projectedW));
|
||||
|
||||
if ((-this->actor.uncullZoneScale < this->actor.projectedPos.z) &&
|
||||
|
||||
@@ -2252,7 +2252,7 @@ void EnZf_Draw(Actor* thisx, PlayState* play) {
|
||||
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, this->alpha);
|
||||
gSPSegment(POLY_OPA_DISP++, 0x09, &D_80116280[2]);
|
||||
|
||||
POLY_OPA_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_OPA_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnZf_OverrideLimbDraw, EnZf_PostLimbDraw, this, POLY_OPA_DISP);
|
||||
|
||||
if (this->iceTimer != 0) {
|
||||
@@ -2271,7 +2271,7 @@ void EnZf_Draw(Actor* thisx, PlayState* play) {
|
||||
gDPPipeSync(POLY_XLU_DISP++);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha);
|
||||
gSPSegment(POLY_XLU_DISP++, 0x09, &D_80116280[0]);
|
||||
POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
|
||||
POLY_XLU_DISP = SkelAnime_DrawSkeleton2(play, &this->skelAnime,
|
||||
EnZf_OverrideLimbDraw, EnZf_PostLimbDraw, this, POLY_XLU_DISP);
|
||||
}
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -275,7 +275,12 @@ void ObjMure_InitialAction(ObjMure* this, PlayState* play) {
|
||||
}
|
||||
|
||||
void ObjMure_CulledState(ObjMure* this, PlayState* play) {
|
||||
if (fabsf(this->actor.projectedPos.z) < sZClip[this->type] || CVarGetInteger("gDisableDrawDistance", 0) != 0) {
|
||||
// #region SOH [Enhancements] Extended draw distance
|
||||
s32 distanceMultiplier = CVarGetInteger("gDisableDrawDistance", 1);
|
||||
distanceMultiplier = MAX(distanceMultiplier, 1);
|
||||
|
||||
if (fabsf(this->actor.projectedPos.z) < sZClip[this->type] * distanceMultiplier) {
|
||||
// #endregion
|
||||
this->actionFunc = ObjMure_ActiveState;
|
||||
this->actor.flags |= ACTOR_FLAG_UPDATE_WHILE_CULLED;
|
||||
ObjMure_SpawnActors(this, play);
|
||||
@@ -398,8 +403,13 @@ static ObjMureActionFunc sTypeGroupBehaviorFunc[] = {
|
||||
|
||||
void ObjMure_ActiveState(ObjMure* this, PlayState* play) {
|
||||
ObjMure_CheckChildren(this, play);
|
||||
if (sZClip[this->type] + 40.0f <= fabsf(this->actor.projectedPos.z) &&
|
||||
CVarGetInteger("gDisableDrawDistance", 1) != 0) {
|
||||
|
||||
// #region SOH [Enhancements] Extended draw distance
|
||||
s32 distanceMultiplier = CVarGetInteger("gDisableDrawDistance", 1);
|
||||
distanceMultiplier = MAX(distanceMultiplier, 1);
|
||||
|
||||
if ((sZClip[this->type] + 40.0f) * distanceMultiplier <= fabsf(this->actor.projectedPos.z)) {
|
||||
// #endregion
|
||||
this->actionFunc = ObjMure_CulledState;
|
||||
this->actor.flags &= ~ACTOR_FLAG_UPDATE_WHILE_CULLED;
|
||||
ObjMure_KillActors(this, play);
|
||||
|
||||
@@ -190,8 +190,7 @@ void func_80B9A658(ObjMure2* this) {
|
||||
|
||||
void func_80B9A668(ObjMure2* this, PlayState* play) {
|
||||
if (Math3D_Dist1DSq(this->actor.projectedPos.x, this->actor.projectedPos.z) <
|
||||
(sDistSquared1[this->actor.params & 3] * this->unk_184) ||
|
||||
CVarGetInteger("gDisableDrawDistance", 0) != 0) {
|
||||
(sDistSquared1[this->actor.params & 3] * this->unk_184)) {
|
||||
this->actor.flags |= ACTOR_FLAG_UPDATE_WHILE_CULLED;
|
||||
ObjMure2_SpawnActors(this, play);
|
||||
func_80B9A6E8(this);
|
||||
@@ -205,12 +204,8 @@ void func_80B9A6E8(ObjMure2* this) {
|
||||
void func_80B9A6F8(ObjMure2* this, PlayState* play) {
|
||||
func_80B9A534(this);
|
||||
|
||||
if (CVarGetInteger("gDisableDrawDistance", 0) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sDistSquared2[this->actor.params & 3] * this->unk_184) <=
|
||||
Math3D_Dist1DSq(this->actor.projectedPos.x, this->actor.projectedPos.z)) {
|
||||
Math3D_Dist1DSq(this->actor.projectedPos.x, this->actor.projectedPos.z)) {
|
||||
this->actor.flags &= ~ACTOR_FLAG_UPDATE_WHILE_CULLED;
|
||||
ObjMure2_CleanupAndDie(this, play);
|
||||
func_80B9A658(this);
|
||||
@@ -225,5 +220,20 @@ void ObjMure2_Update(Actor* thisx, PlayState* play) {
|
||||
} else {
|
||||
this->unk_184 = 4.0f;
|
||||
}
|
||||
|
||||
// SOH [Enhancements] Extended draw distance
|
||||
s32 distanceMultiplier = CVarGetInteger("gDisableDrawDistance", 1);
|
||||
if (CVarGetInteger("gEnhancements.WidescreenActorCulling", 0) || distanceMultiplier > 1) {
|
||||
f32 originalAspectRatio = 4.0f / 3.0f;
|
||||
f32 currentAspectRatio = OTRGetAspectRatio();
|
||||
// Adjust ratio difference based on field of view testing
|
||||
f32 ratioAdjusted = 1.0f + (MAX(currentAspectRatio / originalAspectRatio, 1.0f) / 1.5f);
|
||||
// Distance multiplier is squared due to the checks above for squared distances
|
||||
distanceMultiplier = SQ(MAX(distanceMultiplier, 1));
|
||||
|
||||
// Prefer the largest of the three values
|
||||
this->unk_184 = MAX(MAX((f32)distanceMultiplier, ratioAdjusted), this->unk_184);
|
||||
}
|
||||
|
||||
this->actionFunc(this, play);
|
||||
}
|
||||
|
||||
@@ -10629,7 +10629,14 @@ void Player_UseTunicBoots(Player* this, PlayState* play) {
|
||||
s32 i;
|
||||
s32 item;
|
||||
s32 actionParam;
|
||||
if (!(this->stateFlags1 & PLAYER_STATE1_INPUT_DISABLED || this->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || this->stateFlags1 & PLAYER_STATE1_IN_CUTSCENE || this->stateFlags1 & PLAYER_STATE1_TEXT_ON_SCREEN || this->stateFlags2 & PLAYER_STATE2_OCARINA_PLAYING)) {
|
||||
if (!(
|
||||
this->stateFlags1 & PLAYER_STATE1_INPUT_DISABLED ||
|
||||
this->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS ||
|
||||
this->stateFlags1 & PLAYER_STATE1_IN_CUTSCENE ||
|
||||
this->stateFlags1 & PLAYER_STATE1_TEXT_ON_SCREEN ||
|
||||
this->stateFlags1 & PLAYER_STATE1_DEAD ||
|
||||
this->stateFlags2 & PLAYER_STATE2_OCARINA_PLAYING
|
||||
)) {
|
||||
for (i = 0; i < ARRAY_COUNT(D_80854388); i++) {
|
||||
if (CHECK_BTN_ALL(sControlInput->press.button, D_80854388[i])) {
|
||||
break;
|
||||
@@ -11332,10 +11339,6 @@ void Player_Draw(Actor* thisx, PlayState* play2) {
|
||||
lod = 1;
|
||||
}
|
||||
|
||||
if (CVarGetInteger("gDisableLOD", 0) != 0) {
|
||||
lod = 0;
|
||||
}
|
||||
|
||||
func_80093C80(play);
|
||||
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
|
||||
|
||||
|
||||
@@ -1030,18 +1030,18 @@ void FileChoose_UpdateRandomizer() {
|
||||
fileSelectSpoilerFileLoaded = false;
|
||||
}
|
||||
|
||||
if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) ||
|
||||
if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) ||
|
||||
(!fileSelectSpoilerFileLoaded && SpoilerFileExists(CVarGetString("gSpoilerLog", "")))) {
|
||||
if (CVarGetInteger("gNewFileDropped", 0) != 0) {
|
||||
CVarSetString("gSpoilerLog", CVarGetString("gDroppedFile", "None"));
|
||||
if (CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) {
|
||||
CVarSetString("gSpoilerLog", CVarGetString("gRandomizerDroppedFile", "None"));
|
||||
}
|
||||
bool silent = true;
|
||||
if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) {
|
||||
if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) {
|
||||
silent = false;
|
||||
}
|
||||
CVarSetInteger("gNewSeedGenerated", 0);
|
||||
CVarSetInteger("gNewFileDropped", 0);
|
||||
CVarSetString("gDroppedFile", "");
|
||||
CVarSetInteger("gRandomizerNewFileDropped", 0);
|
||||
CVarSetString("gRandomizerDroppedFile", "");
|
||||
fileSelectSpoilerFileLoaded = false;
|
||||
const char* fileLoc = CVarGetString("gSpoilerLog", "");
|
||||
Randomizer_LoadSettings(fileLoc);
|
||||
@@ -1076,7 +1076,6 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
|
||||
Input* input = &this->state.input[0];
|
||||
bool dpad = CVarGetInteger("gDpadText", 0);
|
||||
|
||||
SoH_ProcessDroppedFiles();
|
||||
FileChoose_UpdateRandomizer();
|
||||
|
||||
if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
|
||||
@@ -1267,7 +1266,6 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
|
||||
s8 i = 0;
|
||||
bool dpad = CVarGetInteger("gDpadText", 0);
|
||||
|
||||
SoH_ProcessDroppedFiles();
|
||||
FileChoose_UpdateRandomizer();
|
||||
|
||||
if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
|
||||
@@ -3038,11 +3036,7 @@ void FileChoose_LoadGame(GameState* thisx) {
|
||||
Entrance_Init();
|
||||
|
||||
// Handle randomized spawn positions after the save context has been setup from load
|
||||
// When remeber save location is on, set save warp if the save was in an a grotto, or
|
||||
// the entrance index is -1 from shuffle overwarld spawn
|
||||
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES) && ((!CVarGetInteger("gRememberSaveLocation", 0) ||
|
||||
gSaveContext.savedSceneNum == SCENE_FAIRYS_FOUNTAIN || gSaveContext.savedSceneNum == SCENE_GROTTOS) ||
|
||||
(CVarGetInteger("gRememberSaveLocation", 0) && Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_SPAWNS) && gSaveContext.entranceIndex == -1))) {
|
||||
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
|
||||
Entrance_SetSavewarpEntrance();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +456,6 @@ void FileChoose_DrawNameEntry(GameState* thisx) {
|
||||
this->prevConfigMode = CM_MAIN_MENU;
|
||||
this->configMode = CM_NAME_ENTRY_TO_MAIN;
|
||||
CVarSetInteger("gOnFileSelectNameEntry", 0);
|
||||
CVarSetInteger("gNewFileDropped", 0);
|
||||
this->nameBoxAlpha[this->buttonIndex] = this->nameAlpha[this->buttonIndex] = 200;
|
||||
this->connectorAlpha[this->buttonIndex] = 255;
|
||||
func_800AA000(300.0f, 0xB4, 0x14, 0x64);
|
||||
@@ -693,6 +692,7 @@ void FileChoose_UpdateOptionsMenu(GameState* thisx) {
|
||||
sLastOptionButtonIndex = -1;
|
||||
osSyncPrintf("SAVE");
|
||||
Save_SaveGlobal();
|
||||
CVarSave();
|
||||
osSyncPrintf(VT_FGCOL(YELLOW));
|
||||
osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting);
|
||||
osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting);
|
||||
|
||||
@@ -237,8 +237,8 @@ void KaleidoScope_DrawItemSelect(PlayState* play) {
|
||||
s16 oldCursorPoint;
|
||||
s16 moveCursorResult;
|
||||
bool dpad = (CVarGetInteger("gDpadPause", 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP));
|
||||
bool pauseAnyCursor = (CVarGetInteger("gPauseAnyCursor", 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) ||
|
||||
(CVarGetInteger("gPauseAnyCursor", 0) == PAUSE_ANY_CURSOR_ALWAYS_ON);
|
||||
bool pauseAnyCursor = pauseCtx->cursorSpecialPos == 0 && ((CVarGetInteger("gPauseAnyCursor", 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) ||
|
||||
(CVarGetInteger("gPauseAnyCursor", 0) == PAUSE_ANY_CURSOR_ALWAYS_ON));
|
||||
|
||||
// only allow mask select when:
|
||||
// the shop is open:
|
||||
|
||||
@@ -312,6 +312,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gQuestIconGoldSkulltulaTex, 24, 24, 8);
|
||||
}
|
||||
|
||||
// Unique index for both pulse phases
|
||||
uint8_t palettePulseIdx = (mapBgPulseStage ? 40 : 20) - mapBgPulseTimer;
|
||||
|
||||
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
|
||||
stepR = (mapBgPulseR - mapBgPulseColors[mapBgPulseStage][0]) / mapBgPulseTimer;
|
||||
stepG = (mapBgPulseG - mapBgPulseColors[mapBgPulseStage][1]) / mapBgPulseTimer;
|
||||
@@ -324,6 +327,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
interfaceCtx->mapPalette[28] = (rgba16 & 0xFF00) >> 8;
|
||||
interfaceCtx->mapPalette[29] = rgba16 & 0xFF;
|
||||
|
||||
interfaceCtx->mapPalettesPulse[palettePulseIdx][28] = (rgba16 & 0xFF00) >> 8;
|
||||
interfaceCtx->mapPalettesPulse[palettePulseIdx][29] = rgba16 & 0xFF;
|
||||
|
||||
mapBgPulseTimer--;
|
||||
if (mapBgPulseTimer == 0) {
|
||||
mapBgPulseStage ^= 1;
|
||||
@@ -335,7 +341,8 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_POINT);
|
||||
gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha);
|
||||
|
||||
gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalette);
|
||||
// Use a unique palette address per frame so the renderer/shader can cache all variations
|
||||
gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalettesPulse[palettePulseIdx]);
|
||||
gDPSetTextureLUT(POLY_KAL_DISP++, G_TT_RGBA16);
|
||||
|
||||
u8 mirroredWorld = CVarGetInteger("gMirroredWorld", 0);
|
||||
@@ -343,18 +350,15 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
// Offset the U value of each vertex to be in the mirror boundary for the map textures
|
||||
if (mirroredWorld) {
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
pauseCtx->mapPageVtx[60 + i].v.tc[0] += 48 << 5;
|
||||
pauseCtx->mapPageVtx[60 + i].v.tc[0] += MAP_48x85_TEX_WIDTH << 5;
|
||||
}
|
||||
}
|
||||
|
||||
gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[60], 8, 0);
|
||||
|
||||
// The dungeon map textures are recreated each frame, so always invalidate them
|
||||
gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[0]);
|
||||
gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[1]);
|
||||
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, 48, 85, 0, G_TX_WRAP | mirrorMode,
|
||||
G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH,
|
||||
MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK,
|
||||
G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
|
||||
|
||||
// Swap vertices to render left half on the right and vice-versa
|
||||
if (mirroredWorld) {
|
||||
@@ -363,9 +367,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0);
|
||||
}
|
||||
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, 48, 85, 0,
|
||||
G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
|
||||
G_TX_NOLOD);
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH,
|
||||
MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK,
|
||||
G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
|
||||
|
||||
if (mirroredWorld) {
|
||||
gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0);
|
||||
|
||||
@@ -13,6 +13,10 @@ extern u8 gItemAgeReqs[];
|
||||
extern u8 gAreaGsFlags[];
|
||||
extern bool gSelectingMask;
|
||||
|
||||
#define MAP_48x85_TEX_WIDTH 48
|
||||
#define MAP_48x85_TEX_HEIGHT 85
|
||||
#define MAP_48x85_TEX_SIZE ((MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT) / 2) // 48x85 CI4 texture
|
||||
|
||||
#define AGE_REQ_ADULT LINK_AGE_ADULT
|
||||
#define AGE_REQ_CHILD LINK_AGE_CHILD
|
||||
#define AGE_REQ_NONE 9
|
||||
|
||||
@@ -3315,13 +3315,121 @@ void KaleidoScope_UpdateCursorSize(PauseContext* pauseCtx) {
|
||||
pauseCtx->cursorVtx[14].v.ob[1] = pauseCtx->cursorVtx[15].v.ob[1] = pauseCtx->cursorVtx[12].v.ob[1] - 16;
|
||||
}
|
||||
|
||||
// Modifed map texture buffers for registered blend effects and the room indicator color
|
||||
static uint8_t mapLeftTexModified[MAP_48x85_TEX_SIZE];
|
||||
static uint8_t mapRightTexModified[MAP_48x85_TEX_SIZE];
|
||||
static uint8_t* mapLeftTexModifiedRaw = NULL;
|
||||
static uint8_t* mapRightTexModifiedRaw = NULL;
|
||||
static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT];
|
||||
|
||||
// Load dungeon maps into the interface context
|
||||
// SoH [General] - Modified to account for our resource system and HD textures
|
||||
void KaleidoScope_LoadDungeonMap(PlayState* play) {
|
||||
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
||||
|
||||
// Free old textures
|
||||
if (mapLeftTexModifiedRaw != NULL) {
|
||||
free(mapLeftTexModifiedRaw);
|
||||
mapLeftTexModifiedRaw = NULL;
|
||||
}
|
||||
if (mapRightTexModifiedRaw != NULL) {
|
||||
free(mapRightTexModifiedRaw);
|
||||
mapRightTexModifiedRaw = NULL;
|
||||
}
|
||||
|
||||
// Unload original textures to bypass cache result for lookups
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX]);
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]);
|
||||
|
||||
interfaceCtx->mapSegmentName[0] = sDungeonMapTexs[R_MAP_TEX_INDEX];
|
||||
interfaceCtx->mapSegmentName[1] = sDungeonMapTexs[R_MAP_TEX_INDEX + 1];
|
||||
interfaceCtx->mapSegment[0] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX]);
|
||||
interfaceCtx->mapSegment[1] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]);
|
||||
|
||||
// When the texture is HD (raw) we need to copy a dynamic amount of data
|
||||
// Otherwise the original asset has a static size
|
||||
if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) {
|
||||
u32 width = ResourceGetTexWidthByName(interfaceCtx->mapSegmentName[0]);
|
||||
u32 height = ResourceGetTexHeightByName(interfaceCtx->mapSegmentName[0]);
|
||||
size_t size = (width * height) / 2; // account for CI4 size
|
||||
|
||||
// Resource size being larger than the calculated CI size means it is most likely not a CI4 texture
|
||||
// Abort early and unregister the blended effect to avoid crashing
|
||||
if (size < ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0])) {
|
||||
interfaceCtx->mapSegment[0] = NULL;
|
||||
interfaceCtx->mapSegment[1] = NULL;
|
||||
|
||||
Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[0]);
|
||||
Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[1]);
|
||||
|
||||
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]);
|
||||
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
u8* map1TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]);
|
||||
u8* map2TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]);
|
||||
|
||||
mapLeftTexModifiedRaw = malloc(size);
|
||||
mapRightTexModifiedRaw = malloc(size);
|
||||
|
||||
memcpy(mapLeftTexModifiedRaw, map1TexRaw, size);
|
||||
memcpy(mapRightTexModifiedRaw, map2TexRaw, size);
|
||||
|
||||
interfaceCtx->mapSegment[0] = mapLeftTexModifiedRaw;
|
||||
interfaceCtx->mapSegment[1] = mapRightTexModifiedRaw;
|
||||
} else {
|
||||
u8* map1Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]);
|
||||
u8* map2Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]);
|
||||
|
||||
memcpy(mapLeftTexModified, map1Tex, MAP_48x85_TEX_SIZE);
|
||||
memcpy(mapRightTexModified, map2Tex, MAP_48x85_TEX_SIZE);
|
||||
|
||||
interfaceCtx->mapSegment[0] = mapLeftTexModified;
|
||||
interfaceCtx->mapSegment[1] = mapRightTexModified;
|
||||
}
|
||||
|
||||
// Mark and register the blend mask for the copied textures
|
||||
if (mapBlendMask[0] != 1) {
|
||||
for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) {
|
||||
mapBlendMask[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, interfaceCtx->mapSegment[0]);
|
||||
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, interfaceCtx->mapSegment[1]);
|
||||
|
||||
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]);
|
||||
Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]);
|
||||
Gfx_TextureCacheDelete(interfaceCtx->mapSegment[0]);
|
||||
Gfx_TextureCacheDelete(interfaceCtx->mapSegment[1]);
|
||||
}
|
||||
|
||||
static uint8_t registeredDungeonMapTextureHook = false;
|
||||
|
||||
void KaleidoScope_RegisterUpdatedDungeonMapTexture() {
|
||||
if (gPlayState == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
PauseContext* pauseCtx = &gPlayState->pauseCtx;
|
||||
|
||||
// Kaleido is not open in a dungeon so there is nothing to do
|
||||
if (R_PAUSE_MENU_MODE < 3 || pauseCtx->state < 4 || pauseCtx->state > 7 || !sInDungeonScene) {
|
||||
return;
|
||||
}
|
||||
|
||||
KaleidoScope_UpdateDungeonMap(gPlayState);
|
||||
|
||||
// KaleidoScope_UpdateDungeonMap will update the palette index for the current floor if the cursor is on the floor
|
||||
// If the player toggles alt assets while the cursor is not in the floor level, then we handle the palette index here
|
||||
if (gPlayState->sceneNum >= SCENE_DEKU_TREE && gPlayState->sceneNum <= SCENE_TREASURE_BOX_SHOP &&
|
||||
(VREG(30) + 3) == pauseCtx->dungeonMapSlot && (VREG(30) + 3) != pauseCtx->cursorPoint[PAUSE_MAP]) {
|
||||
|
||||
InterfaceContext* interfaceCtx = &gPlayState->interfaceCtx;
|
||||
int32_t size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]);
|
||||
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
}
|
||||
}
|
||||
|
||||
void KaleidoScope_UpdateDungeonMap(PlayState* play) {
|
||||
@@ -3333,19 +3441,34 @@ void KaleidoScope_UpdateDungeonMap(PlayState* play) {
|
||||
KaleidoScope_LoadDungeonMap(play);
|
||||
Map_SetFloorPalettesData(play, pauseCtx->dungeonMapSlot - 3);
|
||||
|
||||
// Copy the map palette values to all pulse palettes
|
||||
for (uint8_t i = 0; i < ARRAY_COUNT(interfaceCtx->mapPalettesPulse); i++) {
|
||||
memcpy(interfaceCtx->mapPalettesPulse[i], interfaceCtx->mapPalette, sizeof(interfaceCtx->mapPalette));
|
||||
}
|
||||
|
||||
s32 size = MAP_48x85_TEX_SIZE;
|
||||
|
||||
if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) {
|
||||
size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]);
|
||||
}
|
||||
|
||||
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
|
||||
if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) {
|
||||
// HDTODO: Handle Runtime Modified Textures (HD)
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], 2040, interfaceCtx->mapPaletteIndex, 14);
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
}
|
||||
}
|
||||
|
||||
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
|
||||
if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) {
|
||||
// HDTODO: Handle Runtime Modified Textures (HD)
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], 2040, interfaceCtx->mapPaletteIndex, 14);
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
}
|
||||
}
|
||||
|
||||
// Register alt listener to update the blended dungeon map textures on alt toggle
|
||||
if (!registeredDungeonMapTextureHook) {
|
||||
registeredDungeonMapTextureHook = true;
|
||||
GameInteractor_RegisterOnAssetAltChange(KaleidoScope_RegisterUpdatedDungeonMapTexture);
|
||||
}
|
||||
}
|
||||
|
||||
void KaleidoScope_Update(PlayState* play)
|
||||
|
||||
@@ -1762,6 +1762,13 @@ static MapMarkData sMapMarkJabuJabuBellyMq[] = {
|
||||
} },
|
||||
{ MAP_MARK_NONE, 0, { 0 } },
|
||||
},
|
||||
// Jabu-Jabu's Belly minimap 16
|
||||
// SoH [General] - This entry corresponds to Big Octorok's room and is missing in the MQ game
|
||||
// N64 hardware does an OoB read and lands on MQ Forest Temple room 0
|
||||
// To avoid UB with OoB for SoH, the correct entry is now added below
|
||||
{
|
||||
{ MAP_MARK_NONE, 0, { 0 } },
|
||||
},
|
||||
};
|
||||
|
||||
static MapMarkData sMapMarkForestTempleMq[] = {
|
||||
|
||||
Reference in New Issue
Block a user