diff --git a/soh/soh/Enhancements/customequipment.cpp b/soh/soh/Enhancements/customequipment.cpp index e8c2721543..0c14be5a0d 100644 --- a/soh/soh/Enhancements/customequipment.cpp +++ b/soh/soh/Enhancements/customequipment.cpp @@ -54,7 +54,7 @@ static Gfx* LoadCustomGfx(const char* path) { if (!path) return nullptr; path = ResolveCustomFPSHand(path); - if (!ResourceGetIsCustomByName(path) && !ResourceMgr_FileAltExists(path)) + if (!ResourceMgr_FileAltExists(path) || !ResourceGetIsCustomByName(path)) return nullptr; return ResourceMgr_LoadGfxByName(path); } diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp index 16c112114e..2818e7e862 100644 --- a/soh/soh/ResourceManagerHelpers.cpp +++ b/soh/soh/ResourceManagerHelpers.cpp @@ -76,8 +76,8 @@ static const char* ResourceMgr_ResolveLinkTunicDListPath(const char* path) { const std::string candidate = fmt::format("__OTR__objects/{}_{}/{}", objectFolder, tunicSuffix, originalPath + objectPrefix.size()); - if (!ResourceGetIsCustomByName(candidate.c_str()) && !ResourceMgr_FileExists(candidate.c_str()) && - !(ResourceMgr_IsAltAssetsEnabled() && ResourceMgr_FileAltExists(candidate.c_str()))) { + if (!ResourceMgr_IsAltAssetsEnabled() || !ResourceMgr_FileAltExists(candidate.c_str()) || + !ResourceGetIsCustomByName(candidate.c_str())) { return path; } @@ -602,33 +602,36 @@ extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) { bool isAlt = ResourceMgr_IsAltAssetsEnabled(); if (isAlt) { - std::string pathStr = std::string(path); - static const std::string sOtr = "__OTR__"; + if (ResourceMgr_FileAltExists(path)) { + std::string pathStr = std::string(path); + static const std::string sOtr = "__OTR__"; - if (pathStr.starts_with(sOtr)) { - pathStr = pathStr.substr(sOtr.length()); - } - - // Try alt/ first - pathStr = Ship::IResource::gAltAssetPrefix + pathStr; - AnimationHeaderCommon* animHeader = (AnimationHeaderCommon*)ResourceGetDataByName(pathStr.c_str()); - - // If alt loaded successfully, verify it has valid data - if (animHeader != NULL) { - // Check for valid frame count (> 0) - if (animHeader->frameCount > 0) { - // For Normal animations: check frameData (comes after frameCount in AnimationHeader) - // For Link animations: check segment (comes after frameCount in LinkAnimationHeader) - // We check both to be safe - if either is valid, the animation is usable - AnimationHeader* normalAnim = (AnimationHeader*)animHeader; - LinkAnimationHeader* linkAnim = (LinkAnimationHeader*)animHeader; - - // Valid if Normal animation has frameData OR Link animation has segment - if (normalAnim->frameData != NULL || linkAnim->segment != NULL) { - return animHeader; - } + if (pathStr.starts_with(sOtr)) { + pathStr = pathStr.substr(sOtr.length()); + } + + // Try alt/ first + pathStr = Ship::IResource::gAltAssetPrefix + pathStr; + + AnimationHeaderCommon* animHeader = (AnimationHeaderCommon*)ResourceGetDataByName(pathStr.c_str()); + + // If alt loaded successfully, verify it has valid data + if (animHeader != NULL) { + // Check for valid frame count (> 0) + if (animHeader->frameCount > 0) { + // For Normal animations: check frameData (comes after frameCount in AnimationHeader) + // For Link animations: check segment (comes after frameCount in LinkAnimationHeader) + // We check both to be safe - if either is valid, the animation is usable + AnimationHeader* normalAnim = (AnimationHeader*)animHeader; + LinkAnimationHeader* linkAnim = (LinkAnimationHeader*)animHeader; + + // Valid if Normal animation has frameData OR Link animation has segment + if (normalAnim->frameData != NULL || linkAnim->segment != NULL) { + return animHeader; + } + } + // Alt loaded but is invalid (broken), fall through to original path } - // Alt loaded but is invalid (broken), fall through to original path } // Fall back to original path diff --git a/soh/soh/resource/type/Skeleton.cpp b/soh/soh/resource/type/Skeleton.cpp index ac83d16615..1871688ff3 100644 --- a/soh/soh/resource/type/Skeleton.cpp +++ b/soh/soh/resource/type/Skeleton.cpp @@ -135,43 +135,73 @@ void SkeletonPatcher::UpdateCustomSkeletons() { void SkeletonPatcher::UpdateTunicSkeletons(SkeletonPatchInfo& skel) { std::string skeletonPath = ""; + s32 tunicID = TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC)); + s32 ageID = 0; // Check if this is one of Link's skeletons if (sOtr + skel.vanillaSkeletonPath == std::string(gLinkAdultSkel)) { + // Adult skeleton + ageID = 2; + } else if (sOtr + skel.vanillaSkeletonPath == std::string(gLinkChildSkel)) { + // Child skeleton + ageID = 1; + } else { + // Incompatible? + return; + } + + // Check if we even need updating + s32 skelID = ageID << 4 | tunicID; + if (skelID == skel.lastSkeletonId) + return; + skel.lastSkeletonId = skelID; + + // Check if this is one of Link's skeletons + if (ageID == 2) { // Check what Link's current tunic is - switch (TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC))) { + switch (tunicID) { case PLAYER_TUNIC_KOKIRI: + if (skel.lastSkeletonId == (ageID << 4 | PLAYER_TUNIC_KOKIRI)) + return; skeletonPath = std::string(gLinkAdultKokiriTunicSkel).substr(sOtr.length()); break; case PLAYER_TUNIC_GORON: + if (skel.lastSkeletonId == (ageID << 4 | PLAYER_TUNIC_GORON)) + return; skeletonPath = std::string(gLinkAdultGoronTunicSkel).substr(sOtr.length()); break; case PLAYER_TUNIC_ZORA: + if (skel.lastSkeletonId == (ageID << 4 | PLAYER_TUNIC_ZORA)) + return; skeletonPath = std::string(gLinkAdultZoraTunicSkel).substr(sOtr.length()); break; default: return; } - - UpdateCustomSkeletonFromPath(skeletonPath, skel); - } else if (sOtr + skel.vanillaSkeletonPath == std::string(gLinkChildSkel)) { + } else if (skelID == 1) { // Check what Link's current tunic is - switch (TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC))) { + switch (tunicID) { case PLAYER_TUNIC_KOKIRI: + if (skel.lastSkeletonId == (ageID << 4 | PLAYER_TUNIC_KOKIRI)) + return; skeletonPath = std::string(gLinkChildKokiriTunicSkel).substr(sOtr.length()); break; case PLAYER_TUNIC_GORON: + if (skel.lastSkeletonId == (ageID << 4 | PLAYER_TUNIC_GORON)) + return; skeletonPath = std::string(gLinkChildGoronTunicSkel).substr(sOtr.length()); break; case PLAYER_TUNIC_ZORA: + if (skel.lastSkeletonId == (ageID << 4 | PLAYER_TUNIC_ZORA)) + return; skeletonPath = std::string(gLinkChildZoraTunicSkel).substr(sOtr.length()); break; default: return; } - - UpdateCustomSkeletonFromPath(skeletonPath, skel); } + + UpdateCustomSkeletonFromPath(skeletonPath, skel); } void SkeletonPatcher::UpdateCustomSkeletonFromPath(const std::string& skeletonPath, SkeletonPatchInfo& skel) { diff --git a/soh/soh/resource/type/Skeleton.h b/soh/soh/resource/type/Skeleton.h index 11e0995c94..8e5805c0ab 100644 --- a/soh/soh/resource/type/Skeleton.h +++ b/soh/soh/resource/type/Skeleton.h @@ -78,6 +78,8 @@ class Skeleton : public Ship::Resource { struct SkeletonPatchInfo { SkelAnime* skelAnime; std::string vanillaSkeletonPath; + + u8 lastSkeletonId; bool isLocalPlayer; };