From 1ba6cf643dedd0bb4e92e20eb2ad5c459cacf731 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Wed, 29 Oct 2025 16:36:29 -0400 Subject: [PATCH] Modularize coloured ToT Medallions hook (#5877) * Modularize colourized ToT Medallions hook * Add overlooked reset of Forest Medallion colour * Move asset variables to hook file * Fix includes * Remove forward declarations * Use data structures to reduceboilerplate code * Simplify data structures, reduce boilerplate even more * Correct patchName2 checks * Clang format * Add brackets on if statements --- .../Enhancements/Graphics/ToTMedallions.cpp | 129 ++++++++++++++++++ soh/soh/Enhancements/mods.cpp | 112 --------------- soh/soh/Enhancements/mods.h | 2 +- soh/soh/SohGui/SohMenuEnhancements.cpp | 2 +- 4 files changed, 131 insertions(+), 114 deletions(-) create mode 100644 soh/soh/Enhancements/Graphics/ToTMedallions.cpp diff --git a/soh/soh/Enhancements/Graphics/ToTMedallions.cpp b/soh/soh/Enhancements/Graphics/ToTMedallions.cpp new file mode 100644 index 000000000..13c752a62 --- /dev/null +++ b/soh/soh/Enhancements/Graphics/ToTMedallions.cpp @@ -0,0 +1,129 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/mods.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "align_asset_macro.h" +#include "macros.h" +#include "variables.h" +#include "soh/ResourceManagerHelpers.h" +extern PlayState* gPlayState; +} + +static constexpr int32_t CVAR_TOT_MEDALLION_COLORS_DEFAULT = 0; +#define CVAR_TOT_MEDALLION_COLORS_NAME CVAR_ENHANCEMENT("ToTMedallionsColors") +#define CVAR_TOT_MEDALLION_COLORS_VALUE \ + CVarGetInteger(CVAR_TOT_MEDALLION_COLORS_NAME, CVAR_TOT_MEDALLION_COLORS_DEFAULT) + +// GreyScaleEndDlist +#define dgEndGrayscaleAndEndDlistDL "__OTR__helpers/cosmetics/gEndGrayscaleAndEndDlistDL" +static const ALIGN_ASSET(2) char gEndGrayscaleAndEndDlistDL[] = dgEndGrayscaleAndEndDlistDL; + +// This is used for the Temple of Time Medalions' color +#define dtokinoma_room_0DL_007A70 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007A70" +static const ALIGN_ASSET(2) char tokinoma_room_0DL_007A70[] = dtokinoma_room_0DL_007A70; +#define dtokinoma_room_0DL_007FD0 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007FD0" +static const ALIGN_ASSET(2) char tokinoma_room_0DL_007FD0[] = dtokinoma_room_0DL_007FD0; + +static Gfx grayscaleWhite = gsDPSetGrayscaleColor(255, 255, 255, 255); + +class ToTPatchSetup { + public: + ToTPatchSetup(Gfx ifColored, const char* patchName, int index, const char* patchName2 = "", int index2 = 0) + : patchName(patchName), index(index), ifColored(ifColored), patchName2(patchName2), index2(index2) { + } + + void ApplyPatch(bool colored = true) { + Gfx colorGfx = colored ? ifColored : grayscaleWhite; + ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, patchName, index, colorGfx); + if (patchName2 && *patchName2) { + ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, patchName2, index2, colorGfx); + } + } + + void RevertPatch() { + ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, patchName); + if (patchName2 && *patchName2) { + ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, patchName2); + } + } + + private: + const char* patchName; + const char* patchName2; + int index; + int index2; + Gfx ifColored; +}; + +typedef struct MedallionColorPatch { + QuestItem questItemId; + ToTPatchSetup patch; +} MedallionColorPatch; + +static ToTPatchSetup startGrayscale = + ToTPatchSetup(gsSPGrayscale(true), "ToTMedallions_StartGrayscale", 7, "ToTMedallions_2_StartGrayscale", 7); + +static MedallionColorPatch medallionColorPatches[] = { + { QUEST_MEDALLION_WATER, ToTPatchSetup(gsDPSetGrayscaleColor(0, 161, 255, 255), "ToTMedallions_MakeBlue", 16) }, + { QUEST_MEDALLION_SPIRIT, ToTPatchSetup(gsDPSetGrayscaleColor(255, 135, 0, 255), "ToTMedallions_MakeOrange", 45) }, + { QUEST_MEDALLION_LIGHT, ToTPatchSetup(gsDPSetGrayscaleColor(255, 255, 0, 255), "ToTMedallions_MakeYellow", 69, + "ToTMedallions_2_MakeYellow", 16) }, + { QUEST_MEDALLION_FOREST, ToTPatchSetup(gsDPSetGrayscaleColor(0, 255, 0, 255), "ToTMedallions_MakeGreen", 94) }, + { QUEST_MEDALLION_FIRE, ToTPatchSetup(gsDPSetGrayscaleColor(255, 0, 0, 255), "ToTMedallions_MakeRed", 118) }, + { QUEST_MEDALLION_SHADOW, ToTPatchSetup(gsDPSetGrayscaleColor(212, 0, 255, 255), "ToTMedallions_MakePurple", 142, + "ToTMedallions_2_MakePurple", 27) }, +}; + +static ToTPatchSetup endGrayscale = + ToTPatchSetup(gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL), "ToTMedallions_EndGrayscaleAndEndDlist", 160, + "ToTMedallions_2_EndGrayscaleAndEndDlist", 51); + +static void PatchToTMedallions() { + // TODO: Refactor the DemoEffect_UpdateJewelAdult and DemoEffect_UpdateJewelChild from z_demo_effect + // effects to take effect in there + startGrayscale.ApplyPatch(); + + for (auto& medallionPatch : medallionColorPatches) { + medallionPatch.patch.ApplyPatch(CHECK_QUEST_ITEM(medallionPatch.questItemId)); + } + + endGrayscale.ApplyPatch(); +} + +static void ResetToTMedallions() { + // Unpatch everything + startGrayscale.RevertPatch(); + + for (auto& medallionPatch : medallionColorPatches) { + medallionPatch.patch.RevertPatch(); + } + + endGrayscale.RevertPatch(); +} + +void UpdateToTMedallions() { + if (CVAR_TOT_MEDALLION_COLORS_VALUE) { + PatchToTMedallions(); + } else { + ResetToTMedallions(); + } +} + +static void CheckTempleOfTime(int16_t sceneNum) { + if (sceneNum != SCENE_TEMPLE_OF_TIME) { + return; + } + PatchToTMedallions(); +} + +static void RegisterToTMedallions() { + COND_HOOK(OnItemReceive, CVAR_TOT_MEDALLION_COLORS_VALUE, [](GetItemEntry) { + if (gPlayState) { + CheckTempleOfTime(gPlayState->sceneNum); + } + }); + COND_HOOK(OnSceneInit, CVAR_TOT_MEDALLION_COLORS_VALUE, CheckTempleOfTime); +} + +static RegisterShipInitFunc initFunc(RegisterToTMedallions, { CVAR_TOT_MEDALLION_COLORS_NAME }); diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 08c226340..2577b7dd2 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -49,16 +49,6 @@ extern SaveContext gSaveContext; extern PlayState* gPlayState; } -// GreyScaleEndDlist -#define dgEndGrayscaleAndEndDlistDL "__OTR__helpers/cosmetics/gEndGrayscaleAndEndDlistDL" -static const ALIGN_ASSET(2) char gEndGrayscaleAndEndDlistDL[] = dgEndGrayscaleAndEndDlistDL; - -// This is used for the Temple of Time Medalions' color -#define dtokinoma_room_0DL_007A70 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007A70" -static const ALIGN_ASSET(2) char tokinoma_room_0DL_007A70[] = dtokinoma_room_0DL_007A70; -#define dtokinoma_room_0DL_007FD0 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007FD0" -static const ALIGN_ASSET(2) char tokinoma_room_0DL_007FD0[] = dtokinoma_room_0DL_007FD0; - /// Switches Link's age and respawns him at the last entrance he entered. void SwitchAge() { if (gPlayState == NULL) @@ -741,107 +731,6 @@ void RegisterRandomizedEnemySizes() { }); } -void PatchToTMedallions() { - // TODO: Refactor the DemoEffect_UpdateJewelAdult and DemoEffect_UpdateJewelChild from z_demo_effect - // effects to take effect in there - if (CVarGetInteger(CVAR_ENHANCEMENT("ToTMedallionsColors"), 0)) { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_StartGrayscale", 7, gsSPGrayscale(true)); - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_StartGrayscale", 7, gsSPGrayscale(true)); - - if (CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER)) { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue", 16, - gsDPSetGrayscaleColor(0, 161, 255, 255)); - } else { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue", 16, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - } - - if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange", 45, - gsDPSetGrayscaleColor(255, 135, 0, 255)); - } else { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange", 45, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - } - - if (CHECK_QUEST_ITEM(QUEST_MEDALLION_LIGHT)) { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow", 69, - gsDPSetGrayscaleColor(255, 255, 0, 255)); - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow", 16, - gsDPSetGrayscaleColor(255, 255, 0, 255)); - } else { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow", 69, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow", 16, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - } - - if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeGreen", 94, - gsDPSetGrayscaleColor(0, 255, 0, 255)); - } else { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeGreen", 94, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - } - - if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed", 118, - gsDPSetGrayscaleColor(255, 0, 0, 255)); - } else { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed", 118, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - } - - if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple", 142, - gsDPSetGrayscaleColor(212, 0, 255, 255)); - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple", 27, - gsDPSetGrayscaleColor(212, 0, 255, 255)); - } else { - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple", 142, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple", 27, - gsDPSetGrayscaleColor(255, 255, 255, 255)); - } - - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_EndGrayscaleAndEndDlist", 160, - gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL)); - ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_EndGrayscaleAndEndDlist", 51, - gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL)); - } else { - // Unpatch everything - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_StartGrayscale"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_StartGrayscale"); - - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple"); - - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_EndGrayscaleAndEndDlist"); - ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_EndGrayscaleAndEndDlist"); - } -} - -void RegisterToTMedallions() { - GameInteractor::Instance->RegisterGameHook([](GetItemEntry _unused) { - if (!CVarGetInteger(CVAR_ENHANCEMENT("ToTMedallionsColors"), 0) || !gPlayState || - gPlayState->sceneNum != SCENE_TEMPLE_OF_TIME) { - return; - } - PatchToTMedallions(); - }); - GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { - if (!CVarGetInteger(CVAR_ENHANCEMENT("ToTMedallionsColors"), 0) || sceneNum != SCENE_TEMPLE_OF_TIME) { - return; - } - PatchToTMedallions(); - }); -} - void RegisterFloorSwitchesHook() { GameInteractor::Instance->RegisterGameHook([](void* refActor) { Actor* actor = static_cast(refActor); @@ -896,7 +785,6 @@ void InitMods() { RegisterEnemyDefeatCounts(); RegisterBossDefeatTimestamps(); RegisterRandomizedEnemySizes(); - RegisterToTMedallions(); RegisterFloorSwitchesHook(); RegisterPatchHandHandler(); RegisterHurtContainerModeHandler(); diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 8ada99f1e..f28ef1e7e 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -10,7 +10,7 @@ extern "C" { void UpdateDirtPathFixState(int32_t sceneNum); void UpdateMirrorModeState(int32_t sceneNum); void UpdateHurtContainerModeState(bool newState); -void PatchToTMedallions(); +void UpdateToTMedallions(); void UpdatePermanentHeartLossState(); void UpdateHyperEnemiesState(); void UpdateHyperBossesState(); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index a48706035..e81478df9 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -602,7 +602,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Color Temple of Time's Medallions", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ToTMedallionsColors")) .RaceDisable(false) - .Callback([](WidgetInfo& info) { PatchToTMedallions(); }) + .Callback([](WidgetInfo& info) { UpdateToTMedallions(); }) .Options(CheckboxOptions().Tooltip( "When Medallions are collected, the Medallion imprints around the Master Sword Pedestal in the Temple " "of Time will become colored-in."));