diff --git a/soh/assets/objects/object_custom_equip/object_custom_equip.h b/soh/assets/objects/object_custom_equip/object_custom_equip.h index 380116a0f99..788973ded73 100644 --- a/soh/assets/objects/object_custom_equip/object_custom_equip.h +++ b/soh/assets/objects/object_custom_equip/object_custom_equip.h @@ -113,6 +113,16 @@ static const ALIGN_ASSET(2) char gCustomAdultFPSHandDL[] = dgCustomAdultFPSHandD #define dgCustomChildFPSHandDL "__OTR__objects/object_custom_equip/gCustomChildFPSHandDL" static const ALIGN_ASSET(2) char gCustomChildFPSHandDL[] = dgCustomChildFPSHandDL; +#define dgCustomAdultGoronFPSHandDL "__OTR__objects/object_custom_equip/gCustomAdultGoronFPSHandDL" +static const ALIGN_ASSET(2) char gCustomAdultGoronFPSHandDL[] = dgCustomAdultGoronFPSHandDL; +#define dgCustomChildGoronFPSHandDL "__OTR__objects/object_custom_equip/gCustomChildGoronFPSHandDL" +static const ALIGN_ASSET(2) char gCustomChildGoronFPSHandDL[] = dgCustomChildGoronFPSHandDL; + +#define dgCustomAdultZoraFPSHandDL "__OTR__objects/object_custom_equip/gCustomAdultZoraFPSHandDL" +static const ALIGN_ASSET(2) char gCustomAdultZoraFPSHandDL[] = dgCustomAdultZoraFPSHandDL; + +#define dgCustomChildZoraFPSHandDL "__OTR__objects/object_custom_equip/gCustomChildZoraFPSHandDL" +static const ALIGN_ASSET(2) char gCustomChildZoraFPSHandDL[] = dgCustomChildZoraFPSHandDL; #endif // OBJECTS_OBJECT_CUSTOM_EQUIP_H diff --git a/soh/soh/Enhancements/cosmetics/CustomTunicDLs.cpp b/soh/soh/Enhancements/cosmetics/CustomTunicDLs.cpp new file mode 100644 index 00000000000..94166dcac3d --- /dev/null +++ b/soh/soh/Enhancements/cosmetics/CustomTunicDLs.cpp @@ -0,0 +1,173 @@ +#include "soh/Enhancements/cosmetics/CustomTunicDLs.h" + +#include "soh/ResourceManagerHelpers.h" + +#include "variables.h" +#include "z64item.h" + +#include +#include +#include + +namespace { +constexpr std::string_view kOtrPrefix = "__OTR__"; + +enum class TunicType { + None, + Goron, + Zora, +}; + +TunicType GetTunicType() { + s32 tunicEquip = (gSaveContext.equips.equipment & gEquipMasks[EQUIP_TYPE_TUNIC]) >> gEquipShifts[EQUIP_TYPE_TUNIC]; + switch (tunicEquip) { + case EQUIP_VALUE_TUNIC_GORON: + return TunicType::Goron; + case EQUIP_VALUE_TUNIC_ZORA: + return TunicType::Zora; + default: + return TunicType::None; + } +} + +struct RemapEntry { + std::string_view base; + std::string_view goron; + std::string_view zora; +}; + +constexpr RemapEntry kRemapEntries[] = { + { "objects/object_link_boy/gLinkAdultLeftHandClosedNearDL", + "objects/object_link_boy_goron/gLinkAdultLeftHandClosedNearDL", + "objects/object_link_boy_zora/gLinkAdultLeftHandClosedNearDL" }, + { "objects/object_link_boy/gLinkAdultLeftHandNearDL", "objects/object_link_boy_goron/gLinkAdultLeftHandNearDL", + "objects/object_link_boy_zora/gLinkAdultLeftHandNearDL" }, + { "objects/object_link_boy/gLinkAdultLeftHandOutNearDL", + "objects/object_link_boy_goron/gLinkAdultLeftHandOutNearDL", + "objects/object_link_boy_zora/gLinkAdultLeftHandOutNearDL" }, + { "objects/object_link_boy/gLinkAdultRightHandClosedNearDL", + "objects/object_link_boy_goron/gLinkAdultRightHandClosedNearDL", + "objects/object_link_boy_zora/gLinkAdultRightHandClosedNearDL" }, + { "objects/object_link_boy/gLinkAdultRightHandHoldingOotNearDL", + "objects/object_link_boy_goron/gLinkAdultRightHandHoldingOotNearDL", + "objects/object_link_boy_zora/gLinkAdultRightHandHoldingOotNearDL" }, + { "objects/object_link_boy/gLinkAdultRightHandNearDL", "objects/object_link_boy_goron/gLinkAdultRightHandNearDL", + "objects/object_link_boy_zora/gLinkAdultRightHandNearDL" }, + { "objects/object_link_boy/gLinkAdultRightArmOutNearDL", + "objects/object_link_boy_goron/gLinkAdultRightArmOutNearDL", + "objects/object_link_boy_zora/gLinkAdultRightArmOutNearDL" }, + { "objects/object_link_boy/gLinkAdultRightHandOutNearDL", + "objects/object_link_boy_goron/gLinkAdultRightHandOutNearDL", + "objects/object_link_boy_zora/gLinkAdultRightHandOutNearDL" }, + { "objects/object_link_boy/gLinkAdultRightHandHoldingHookshotFarDL", + "objects/object_link_boy_goron/gLinkAdultRightHandHoldingHookshotFarDL", + "objects/object_link_boy_zora/gLinkAdultRightHandHoldingHookshotFarDL" }, + { "objects/object_link_boy/gLinkAdultRightHandHoldingBowFirstPersonDL", + "objects/object_link_boy_goron/gLinkAdultRightHandHoldingBowFirstPersonDL", + "objects/object_link_boy_zora/gLinkAdultRightHandHoldingBowFirstPersonDL" }, + { "objects/object_link_child/gLinkChildLeftFistNearDL", "objects/object_link_child_goron/gLinkChildLeftFistNearDL", + "objects/object_link_child_zora/gLinkChildLeftFistNearDL" }, + { "objects/object_link_child/gLinkChildLeftHandHoldingMasterSwordDL", + "objects/object_link_child_goron/gLinkChildLeftHandHoldingMasterSwordDL", + "objects/object_link_child_zora/gLinkChildLeftHandHoldingMasterSwordDL" }, + { "objects/object_link_child/gLinkChildLeftHandNearDL", "objects/object_link_child_goron/gLinkChildLeftHandNearDL", + "objects/object_link_child_zora/gLinkChildLeftHandNearDL" }, + { "objects/object_link_child/gLinkChildLeftHandUpNearDL", + "objects/object_link_child_goron/gLinkChildLeftHandUpNearDL", + "objects/object_link_child_zora/gLinkChildLeftHandUpNearDL" }, + { "objects/object_link_child/gLinkChildRightHandClosedNearDL", + "objects/object_link_child_goron/gLinkChildRightHandClosedNearDL", + "objects/object_link_child_zora/gLinkChildRightHandClosedNearDL" }, + { "objects/object_link_child/gLinkChildRightArmStretchedSlingshotDL", + "objects/object_link_child_goron/gLinkChildRightArmStretchedSlingshotDL", + "objects/object_link_child_zora/gLinkChildRightArmStretchedSlingshotDL" }, + { "objects/object_link_child/gLinkChildRightHandNearDL", + "objects/object_link_child_goron/gLinkChildRightHandNearDL", + "objects/object_link_child_zora/gLinkChildRightHandNearDL" }, + { "objects/object_link_child/gLinkChildRightHandHoldingFairyOcarinaNearDL", + "objects/object_link_child_goron/gLinkChildRightHandHoldingFairyOcarinaNearDL", + "objects/object_link_child_zora/gLinkChildRightHandHoldingFairyOcarinaNearDL" }, + { "objects/object_link_child/gLinkChildRightHandAndOotNearDL", + "objects/object_link_child_goron/gLinkChildRightHandAndOotNearDL", + "objects/object_link_child_zora/gLinkChildRightHandAndOotNearDL" }, + { "objects/object_custom_equip/gCustomAdultFPSHandDL", "objects/object_custom_equip/gCustomAdultGoronFPSHandDL", + "objects/object_custom_equip/gCustomAdultZoraFPSHandDL" }, + { "objects/object_custom_equip/gCustomChildFPSHandDL", "objects/object_custom_equip/gCustomChildGoronFPSHandDL", + "objects/object_custom_equip/gCustomChildZoraFPSHandDL" }, + { "objects/object_link_boy/gLinkAdultLeftIronBootDL", "objects/object_link_boy_goron/gLinkAdultLeftIronBootDL", + "objects/object_link_boy_zora/gLinkAdultLeftIronBootDL" }, + { "objects/object_link_boy/gLinkAdultRightIronBootDL", "objects/object_link_boy_goron/gLinkAdultRightIronBootDL", + "objects/object_link_boy_zora/gLinkAdultRightIronBootDL" }, + { "objects/object_link_boy/gLinkAdultLeftHoverBootDL", "objects/object_link_boy_goron/gLinkAdultLeftHoverBootDL", + "objects/object_link_boy_zora/gLinkAdultLeftHoverBootDL" }, + { "objects/object_link_boy/gLinkAdultRightHoverBootDL", "objects/object_link_boy_goron/gLinkAdultRightHoverBootDL", + "objects/object_link_boy_zora/gLinkAdultRightHoverBootDL" }, +}; + +std::string_view StripOtrPrefix(std::string_view path) { + if (path.starts_with(kOtrPrefix)) { + return path.substr(kOtrPrefix.size()); + } + return path; +} + +bool TargetExists(std::string_view path) { + return ResourceMgr_FileExists(path.data()) || + (ResourceMgr_IsAltAssetsEnabled() && ResourceMgr_FileAltExists(path.data())); +} + +std::string_view GetRemapTarget(std::string_view view, TunicType tunicType) { + if (tunicType == TunicType::None) { + return {}; + } + + for (const auto& entry : kRemapEntries) { + if (view == entry.base) { + return (tunicType == TunicType::Goron) ? entry.goron : entry.zora; + } + } + + return {}; +} + +const char* MakeRemappedPath(std::string_view target, bool hasOtrPrefix) { + std::string fullPath; + if (hasOtrPrefix) { + fullPath.reserve(kOtrPrefix.size() + target.size()); + fullPath.append(kOtrPrefix); + fullPath.append(target); + } else { + fullPath.assign(target); + } + + static thread_local std::unordered_set remappedCache; + auto [it, inserted] = remappedCache.emplace(fullPath); + return it->c_str(); +} +} // namespace + +const char* CustomTunicDLs_RemapPath(const char* path) { + if (path == nullptr) { + return path; + } + + std::string_view view(path); + bool hasOtrPrefix = view.starts_with(kOtrPrefix); + view = StripOtrPrefix(view); + + TunicType tunicType = GetTunicType(); + if (tunicType == TunicType::None) { + return path; + } + + std::string_view target = GetRemapTarget(view, tunicType); + if (target.empty()) { + return path; + } + + if (!TargetExists(target)) { + return path; + } + + return MakeRemappedPath(target, hasOtrPrefix); +} diff --git a/soh/soh/Enhancements/cosmetics/CustomTunicDLs.h b/soh/soh/Enhancements/cosmetics/CustomTunicDLs.h new file mode 100644 index 00000000000..9e201b1a85d --- /dev/null +++ b/soh/soh/Enhancements/cosmetics/CustomTunicDLs.h @@ -0,0 +1,3 @@ +#pragma once + +const char* CustomTunicDLs_RemapPath(const char* path); diff --git a/soh/soh/Enhancements/customequipment.cpp b/soh/soh/Enhancements/customequipment.cpp index 9834836fbc8..02a7f8277c7 100644 --- a/soh/soh/Enhancements/customequipment.cpp +++ b/soh/soh/Enhancements/customequipment.cpp @@ -3,6 +3,7 @@ #include "objects/object_link_boy/object_link_boy.h" #include "objects/object_link_child/object_link_child.h" #include "objects/object_custom_equip/object_custom_equip.h" +#include "soh/Enhancements/cosmetics/CustomTunicDLs.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/ShipInit.hpp" #include "soh/ResourceManagerHelpers.h" @@ -373,6 +374,11 @@ static void ApplyCommonEquipmentPatches() { const char* fpsHand = isChild ? gCustomChildFPSHandDL : gCustomAdultFPSHandDL; const char* rightHandNear = isChild ? gLinkChildRightHandNearDL : gLinkAdultRightHandNearDL; + rightHandClosed = CustomTunicDLs_RemapPath(rightHandClosed); + leftHandClosed = CustomTunicDLs_RemapPath(leftHandClosed); + fpsHand = CustomTunicDLs_RemapPath(fpsHand); + rightHandNear = CustomTunicDLs_RemapPath(rightHandNear); + ApplyPatchEntries({ { gLinkAdultLeftHandHoldingMasterSwordNearDL, gCustomMasterSwordDL, "customMasterSword1", "customMasterSword2", "customMasterSword3", leftHandClosed }, diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp index 329aed6a6da..4d894fee41c 100644 --- a/soh/soh/ResourceManagerHelpers.cpp +++ b/soh/soh/ResourceManagerHelpers.cpp @@ -5,6 +5,7 @@ #include "cvar_prefixes.h" #include "Enhancements/enhancementTypes.h" #include "Enhancements/randomizer/dungeon.h" +#include "Enhancements/cosmetics/CustomTunicDLs.h" #include #include #include "resource/type/SohResourceType.h" @@ -297,6 +298,7 @@ extern "C" void ResourceMgr_PushCurrentDirectory(char* path) { } extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path) { + path = CustomTunicDLs_RemapPath(path); // When an alt resource exists for the DL, we need to unload the original asset // to clear the cache so the alt asset will be loaded instead // OTRTODO: If Alt loading over original cache is fixed, this line can most likely be removed