summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJameriquiah <42100286+Jameriquiah@users.noreply.github.com>2026-06-21 16:46:47 -0500
committerGitHub <noreply@github.com>2026-06-21 21:46:47 +0000
commit6a51720194a6ffca9d059c60bfe0b48debd45d05 (patch)
tree0d3e353c2437b734cb0e8e97c20f684524a10557
parente4b6f1ab87040f3ce7eb8c02c103c9a642364d1e (diff)
Custom Hand DL's for each tunic take 3 (#6754)
-rw-r--r--soh/assets/objects/object_custom_equip/object_custom_equip.h12
-rw-r--r--soh/soh/Enhancements/customequipment.cpp21
-rw-r--r--soh/soh/ResourceManagerHelpers.cpp64
3 files changed, 97 insertions, 0 deletions
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 dd4cfb1f7..f86061a9a 100644
--- a/soh/assets/objects/object_custom_equip/object_custom_equip.h
+++ b/soh/assets/objects/object_custom_equip/object_custom_equip.h
@@ -128,9 +128,21 @@ static const ALIGN_ASSET(2) char gCustomMirrorShieldOnBackDL[] = dgCustomMirrorS
#define dgCustomAdultFPSHandDL "__OTR__objects/object_custom_equip/gCustomAdultFPSHandDL"
static const ALIGN_ASSET(2) char gCustomAdultFPSHandDL[] = dgCustomAdultFPSHandDL;
+#define dgCustomAdultGoronFPSHandDL "__OTR__objects/object_custom_equip/gCustomAdultGoronFPSHandDL"
+static const ALIGN_ASSET(2) char gCustomAdultGoronFPSHandDL[] = dgCustomAdultGoronFPSHandDL;
+
+#define dgCustomAdultZoraFPSHandDL "__OTR__objects/object_custom_equip/gCustomAdultZoraFPSHandDL"
+static const ALIGN_ASSET(2) char gCustomAdultZoraFPSHandDL[] = dgCustomAdultZoraFPSHandDL;
+
#define dgCustomChildFPSHandDL "__OTR__objects/object_custom_equip/gCustomChildFPSHandDL"
static const ALIGN_ASSET(2) char gCustomChildFPSHandDL[] = dgCustomChildFPSHandDL;
+#define dgCustomChildGoronFPSHandDL "__OTR__objects/object_custom_equip/gCustomChildGoronFPSHandDL"
+static const ALIGN_ASSET(2) char gCustomChildGoronFPSHandDL[] = dgCustomChildGoronFPSHandDL;
+
+#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/customequipment.cpp b/soh/soh/Enhancements/customequipment.cpp
index 282bccc2e..4038c5bc8 100644
--- a/soh/soh/Enhancements/customequipment.cpp
+++ b/soh/soh/Enhancements/customequipment.cpp
@@ -25,6 +25,26 @@ static const char* ResolveCustomChain(std::initializer_list<const char*> paths)
return fallback;
}
+static const char* ResolveCustomFPSHand(const char* path) {
+ const bool isAdult = path == gCustomAdultFPSHandDL;
+ const bool isChild = path == gCustomChildFPSHandDL;
+
+ if (!isAdult && !isChild) {
+ return path;
+ }
+
+ switch (TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC))) {
+ case PLAYER_TUNIC_GORON:
+ return ResolveCustomChain(
+ { isAdult ? gCustomAdultGoronFPSHandDL : gCustomChildGoronFPSHandDL, path, nullptr });
+ case PLAYER_TUNIC_ZORA:
+ return ResolveCustomChain(
+ { isAdult ? gCustomAdultZoraFPSHandDL : gCustomChildZoraFPSHandDL, path, nullptr });
+ default:
+ return path;
+ }
+}
+
static Gfx* LoadGfxByName(const char* path) {
return path ? ResourceMgr_LoadGfxByName(path) : nullptr;
}
@@ -32,6 +52,7 @@ static Gfx* LoadGfxByName(const char* path) {
static Gfx* LoadCustomGfx(const char* path) {
if (!path)
return nullptr;
+ path = ResolveCustomFPSHand(path);
if (!ResourceGetIsCustomByName(path) && !ResourceMgr_FileAltExists(path))
return nullptr;
return ResourceMgr_LoadGfxByName(path);
diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp
index 618ac98f6..0d745dd08 100644
--- a/soh/soh/ResourceManagerHelpers.cpp
+++ b/soh/soh/ResourceManagerHelpers.cpp
@@ -2,6 +2,7 @@
#include "OTRGlobals.h"
#include "variables.h"
#include "z64.h"
+#include "macros.h"
#include "cvar_prefixes.h"
#include "Enhancements/enhancementTypes.h"
#include "Enhancements/randomizer/dungeon.h"
@@ -20,6 +21,68 @@
extern "C" PlayState* gPlayState;
+struct LinkTunicDListCacheKey {
+ size_t operator()(const std::pair<std::string, const char*>& key) const {
+ return std::hash<std::string>{}(key.first) ^ std::hash<const char*>{}(key.second);
+ }
+};
+
+static const char* ResourceMgr_ResolveLinkTunicDListPath(const char* path) {
+ if (path == nullptr) {
+ return nullptr;
+ }
+
+ const char* originalPath = path;
+ constexpr std::string_view adultPrefix = "__OTR__objects/object_link_boy/";
+ constexpr std::string_view childPrefix = "__OTR__objects/object_link_child/";
+
+ std::string_view objectPrefix;
+ const char* objectFolder;
+
+ if (std::string_view(originalPath).starts_with(adultPrefix)) {
+ objectPrefix = adultPrefix;
+ objectFolder = "object_link_boy";
+ } else if (std::string_view(originalPath).starts_with(childPrefix)) {
+ objectPrefix = childPrefix;
+ objectFolder = "object_link_child";
+ } else {
+ return path;
+ }
+
+ const char* tunicSuffix = nullptr;
+ switch (TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC))) {
+ case PLAYER_TUNIC_KOKIRI:
+ tunicSuffix = "kokiri";
+ break;
+ case PLAYER_TUNIC_GORON:
+ tunicSuffix = "goron";
+ break;
+ case PLAYER_TUNIC_ZORA:
+ tunicSuffix = "zora";
+ break;
+ default:
+ return path;
+ }
+
+ static std::unordered_map<std::pair<std::string, const char*>, std::string, LinkTunicDListCacheKey>
+ sResolvedLinkTunicDListPaths;
+ std::pair<std::string, const char*> cacheKey{ originalPath, tunicSuffix };
+ if (auto it = sResolvedLinkTunicDListPaths.find(cacheKey); it != sResolvedLinkTunicDListPaths.end()) {
+ return it->second.c_str();
+ }
+
+ 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()))) {
+ return path;
+ }
+
+ auto it = sResolvedLinkTunicDListPaths.emplace(std::move(cacheKey), candidate).first;
+ return it->second.c_str();
+}
+
extern "C" uint32_t ResourceMgr_GetNumGameVersions() {
return static_cast<u32>(
Ship::Context::GetRawInstance()->GetResourceManager()->GetArchiveManager()->GetGameVersions().size());
@@ -307,6 +370,7 @@ extern "C" void ResourceMgr_PushCurrentDirectory(char* path) {
}
extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path) {
+ path = ResourceMgr_ResolveLinkTunicDListPath(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