From f0b02d6c7e584a4a72ca1cfdfcc4e8d24232ac12 Mon Sep 17 00:00:00 2001 From: inspectredc <78732756+inspectredc@users.noreply.github.com> Date: Sun, 5 Jan 2025 09:05:12 +0000 Subject: [PATCH] Custom Tunics (#4774) * Custom Tunics Co-authored-by: MoriyaFaith 46070717+MoriyaFaith@users.noreply.github.com * Works for necessary scenarios * make variable static again --- soh/assets/soh_assets.h | 19 ++++++ soh/soh/Enhancements/mods.cpp | 27 ++++++++ soh/soh/resource/type/Skeleton.cpp | 100 ++++++++++++++++++++++++++++- soh/soh/resource/type/Skeleton.h | 5 ++ 4 files changed, 149 insertions(+), 2 deletions(-) diff --git a/soh/assets/soh_assets.h b/soh/assets/soh_assets.h index 3d3cfab2e..894be4106 100644 --- a/soh/assets/soh_assets.h +++ b/soh/assets/soh_assets.h @@ -162,3 +162,22 @@ static const ALIGN_ASSET(2) char gFileSelLanguageGERTex[] = dgFileSelLanguageGER #define dgEmptyTexture "__OTR__textures/virtual/gEmptyTexture" static const ALIGN_ASSET(2) char gEmptyTexture[] = dgEmptyTexture; + +// Custom Tunic Models +#define dgLinkChildKokiriTunicSkel "__OTR__objects/object_link_child_kokiri/gLinkChildKokiriTunicSkel" +static const ALIGN_ASSET(2) char gLinkChildKokiriTunicSkel[] = dgLinkChildKokiriTunicSkel; + +#define dgLinkChildGoronTunicSkel "__OTR__objects/object_link_child_goron/gLinkChildGoronTunicSkel" +static const ALIGN_ASSET(2) char gLinkChildGoronTunicSkel[] = dgLinkChildGoronTunicSkel; + +#define dgLinkChildZoraTunicSkel "__OTR__objects/object_link_child_zora/gLinkChildZoraTunicSkel" +static const ALIGN_ASSET(2) char gLinkChildZoraTunicSkel[] = dgLinkChildZoraTunicSkel; + +#define dgLinkAdultKokiriTunicSkel "__OTR__objects/object_link_boy_kokiri/gLinkAdultKokiriTunicSkel" +static const ALIGN_ASSET(2) char gLinkAdultKokiriTunicSkel[] = dgLinkAdultKokiriTunicSkel; + +#define dgLinkAdultGoronTunicSkel "__OTR__objects/object_link_boy_goron/gLinkAdultGoronTunicSkel" +static const ALIGN_ASSET(2) char gLinkAdultGoronTunicSkel[] = dgLinkAdultGoronTunicSkel; + +#define dgLinkAdultZoraTunicSkel "__OTR__objects/object_link_boy_zora/gLinkAdultZoraTunicSkel" +static const ALIGN_ASSET(2) char gLinkAdultZoraTunicSkel[] = dgLinkAdultZoraTunicSkel; diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 0c42ebe1d..76cb6c00d 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -5,6 +5,7 @@ #include "soh/OTRGlobals.h" #include "soh/SaveManager.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/resource/type/Skeleton.h" #include "soh/Enhancements/boss-rush/BossRushTypes.h" #include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/enhancementTypes.h" @@ -38,6 +39,7 @@ #include "src/overlays/actors/ovl_En_Door/z_en_door.h" #include "objects/object_link_boy/object_link_boy.h" #include "objects/object_link_child/object_link_child.h" +#include "soh_assets.h" #include "kaleido.h" extern "C" { @@ -1435,6 +1437,30 @@ void RegisterRandomizerCompasses() { }); } +void RegisterCustomSkeletons() { + static int8_t previousTunic = -1; + + GameInteractor::Instance->RegisterGameHook([]() { + + if (!GameInteractor::IsSaveLoaded() || gPlayState == NULL) { + return; + } + + if (CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC) != previousTunic) { + SOH::SkeletonPatcher::UpdateCustomSkeletons(); + } + previousTunic = CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC); + }); + + GameInteractor::Instance->RegisterGameHook([]() { + if (!GameInteractor::IsSaveLoaded() || gPlayState == NULL) { + return; + } + + SOH::SkeletonPatcher::UpdateCustomSkeletons(); + }); +} + void InitMods() { BossRush_RegisterHooks(); RandomizerRegisterHooks(); @@ -1479,4 +1505,5 @@ void InitMods() { RegisterHurtContainerModeHandler(); RegisterPauseMenuHooks(); RandoKaleido_RegisterHooks(); + RegisterCustomSkeletons(); } diff --git a/soh/soh/resource/type/Skeleton.cpp b/soh/soh/resource/type/Skeleton.cpp index 057f24b65..883eacd19 100644 --- a/soh/soh/resource/type/Skeleton.cpp +++ b/soh/soh/resource/type/Skeleton.cpp @@ -2,6 +2,13 @@ #include "Skeleton.h" #include "soh/OTRGlobals.h" #include "libultraship/libultraship.h" +#include +#include +#include + +extern "C" SaveContext gSaveContext; +extern "C" u16 gEquipMasks[4]; +extern "C" u8 gEquipShifts[4]; namespace SOH { SkeletonData* Skeleton::GetPointer() { @@ -66,10 +73,10 @@ void SkeletonPatcher::ClearSkeletons() void SkeletonPatcher::UpdateSkeletons() { auto resourceMgr = Ship::Context::GetInstance()->GetResourceManager(); - bool isHD = resourceMgr->IsAltAssetsEnabled(); + bool isAlt = resourceMgr->IsAltAssetsEnabled(); for (auto skel : skeletons) { Skeleton* newSkel = - (Skeleton*)resourceMgr->LoadResource((isHD ? Ship::IResource::gAltAssetPrefix : "") + skel.vanillaSkeletonPath, true).get(); + (Skeleton*)resourceMgr->LoadResource((isAlt ? Ship::IResource::gAltAssetPrefix : "") + skel.vanillaSkeletonPath, true).get(); if (newSkel != nullptr) { skel.skelAnime->skeleton = newSkel->skeletonData.skeletonHeader.segment; @@ -78,4 +85,93 @@ void SkeletonPatcher::UpdateSkeletons() { } } } + +void SkeletonPatcher::UpdateCustomSkeletons() { + for (auto skel : skeletons) { + UpdateTunicSkeletons(skel); + } +} + +void SkeletonPatcher::UpdateTunicSkeletons(SkeletonPatchInfo& skel) { + std::string skeletonPath = ""; + + // Check if this is one of Link's skeletons + if (sOtr + skel.vanillaSkeletonPath == std::string(gLinkAdultSkel)) { + // Check what Link's current tunic is + switch (TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC))) { + case PLAYER_TUNIC_KOKIRI: + skeletonPath = std::string(gLinkAdultKokiriTunicSkel).substr(sOtr.length()); + break; + case PLAYER_TUNIC_GORON: + skeletonPath = std::string(gLinkAdultGoronTunicSkel).substr(sOtr.length()); + break; + case PLAYER_TUNIC_ZORA: + skeletonPath = std::string(gLinkAdultZoraTunicSkel).substr(sOtr.length()); + break; + default: + return; + } + + UpdateCustomSkeletonFromPath(skeletonPath, skel); + } else if (sOtr + skel.vanillaSkeletonPath == std::string(gLinkChildSkel)) { + // Check what Link's current tunic is + switch (TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC))) { + case PLAYER_TUNIC_KOKIRI: + skeletonPath = std::string(gLinkChildKokiriTunicSkel).substr(sOtr.length()); + break; + case PLAYER_TUNIC_GORON: + skeletonPath = std::string(gLinkChildGoronTunicSkel).substr(sOtr.length()); + break; + case PLAYER_TUNIC_ZORA: + skeletonPath = std::string(gLinkChildZoraTunicSkel).substr(sOtr.length()); + break; + default: + return; + } + + UpdateCustomSkeletonFromPath(skeletonPath, skel); + } +} + +void SkeletonPatcher::UpdateCustomSkeletonFromPath(const std::string& skeletonPath, SkeletonPatchInfo& skel) { + Skeleton* newSkel = nullptr; + Skeleton* altSkel = nullptr; + auto resourceMgr = Ship::Context::GetInstance()->GetResourceManager(); + bool isAlt = resourceMgr->IsAltAssetsEnabled(); + + // If alt assets are on, look for alt tagged skeletons + if (isAlt) { + altSkel = + (Skeleton*)Ship::Context::GetInstance() + ->GetResourceManager() + ->LoadResource(Ship::IResource::gAltAssetPrefix + skeletonPath, true) + .get(); + + // Override non-alt skeleton if necessary + if (altSkel != nullptr) { + newSkel = altSkel; + } + } + + // Load new skeleton based on the custom model if it exists + if (altSkel == nullptr) { + newSkel = + (Skeleton*)Ship::Context::GetInstance() + ->GetResourceManager() + ->LoadResource(skeletonPath, true) + .get(); + } + + // Change back to the original skeleton if no skeleton's were found + if (newSkel == nullptr && skeletonPath != skel.vanillaSkeletonPath) { + UpdateCustomSkeletonFromPath(skel.vanillaSkeletonPath, skel); + return; + } + + if (newSkel != nullptr) { + skel.skelAnime->skeleton = newSkel->skeletonData.skeletonHeader.segment; + uintptr_t skelPtr = (uintptr_t)newSkel->GetPointer(); + memcpy(&skel.skelAnime->skeletonHeader, &skelPtr, sizeof(uintptr_t)); + } +} } // namespace SOH diff --git a/soh/soh/resource/type/Skeleton.h b/soh/soh/resource/type/Skeleton.h index 72d81c5a5..fce931c79 100644 --- a/soh/soh/resource/type/Skeleton.h +++ b/soh/soh/resource/type/Skeleton.h @@ -86,8 +86,13 @@ class SkeletonPatcher { static void UnregisterSkeleton(SkelAnime* skelAnime); static void ClearSkeletons(); static void UpdateSkeletons(); + static void UpdateCustomSkeletons(); static std::vector skeletons; + private: + inline static const std::string sOtr = "__OTR__"; + static void UpdateTunicSkeletons(SkeletonPatchInfo& skel); + static void UpdateCustomSkeletonFromPath(const std::string& skeletonPath, SkeletonPatchInfo& skel); };