From a842894d0ddca0fa1fff2b8cb26aa969cf51b773 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Wed, 14 Jun 2023 23:22:04 -0400 Subject: [PATCH] Add Mirror World gfx patches and patch Royal Grave Sun Song etching (#3005) * add mirror world gfx patches and patch royal grave sun song * simplify nonmq vs mq --- .../cosmetics/authenticGfxPatches.cpp | 66 +++++++++++++++++++ .../cosmetics/authenticGfxPatches.h | 1 + soh/soh/Enhancements/mods.cpp | 19 +++++- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp index 1eec0c096..a840ee317 100644 --- a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp @@ -8,6 +8,8 @@ extern "C" { #include "objects/object_ik/object_ik.h" #include "objects/object_link_child/object_link_child.h" +uint32_t ResourceMgr_GameHasMasterQuest(); +uint32_t ResourceMgr_GameHasOriginal(); void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction); void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName); } @@ -189,3 +191,67 @@ void ApplyAuthenticGfxPatches() { PatchFreezardTextureOverflow(); PatchIronKnuckleTextureOverflow(); } + +// Patches the Sun Song Etching in the Royal Grave to be mirrored in mirror mode +// This is achieved by mirroring the texture at the boundary and overriding the vertex texture coordinates +void PatchMirroredSunSongEtching() { + static const char gMqRoyalGraveBackRoomDL[] = "__OTR__scenes/mq/hakaana_ouke_scene/hakaana_ouke_room_2DL_005040"; + static const char gNonMqRoyalGraveBackRoomDL[] = "__OTR__scenes/nonmq/hakaana_ouke_scene/hakaana_ouke_room_2DL_005040"; + static const char gMqRoyalGraveBackRoomSongVtx[] = "__OTR__scenes/mq/hakaana_ouke_scene/hakaana_ouke_room_2Vtx_004F80"; + static const char gNonMqRoyalGraveBackRoomSongVtx[] = "__OTR__scenes/nonmq/hakaana_ouke_scene/hakaana_ouke_room_2Vtx_004F80"; + + static Vtx* mirroredVtx; + + // Using a dummy texture here, but will be ignoring the texture command itself + // Only need to patch over the two SetTile commands to get the MIRROR effect + Gfx mirroredSunSongTex[] = { + gsDPLoadTextureBlock("", G_IM_FMT_IA, G_IM_SIZ_8b, 128, 32, 0, G_TX_MIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_CLAMP, 7, 5, G_TX_NOLOD, G_TX_NOLOD) + }; + + const char* royalGraveBackRoomDL; + const char* royalGraveBackRoomSongVtx; + + // If we have the original game, then always prefer the nonmq paths as that is what will be used in game + if (ResourceMgr_GameHasOriginal()) { + royalGraveBackRoomDL = gNonMqRoyalGraveBackRoomDL; + royalGraveBackRoomSongVtx = gNonMqRoyalGraveBackRoomSongVtx; + } else { + royalGraveBackRoomDL = gMqRoyalGraveBackRoomDL; + royalGraveBackRoomSongVtx = gMqRoyalGraveBackRoomSongVtx; + } + + if (CVarGetInteger("gMirroredWorld", 0)) { + if (mirroredVtx == nullptr) { + // Copy the original vertices that we want to modify (4 at the beginning of the resource) + mirroredVtx = (Vtx*)malloc(sizeof(Vtx) * 4); + Vtx* origVtx = (Vtx*)ResourceGetDataByName(royalGraveBackRoomSongVtx); + memcpy(mirroredVtx, origVtx, sizeof(Vtx) * 4); + + // Offset the vertex U coordinate values by the width of the texture + for (size_t i = 0; i < 4; i++) { + mirroredVtx[i].v.tc[0] += 128 << 5; + } + } + + ResourceMgr_PatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTexture_1", 13, mirroredSunSongTex[1]); + ResourceMgr_PatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTexture_2", 17, mirroredSunSongTex[5]); + ResourceMgr_PatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTextureCords_1", 24, gsSPVertex(mirroredVtx, 4, 0)); + // noop as the original vertex command is 128 bit wide + ResourceMgr_PatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTextureCords_2", 25, gsSPNoOp()); + } else { + if (mirroredVtx != nullptr) { + free(mirroredVtx); + mirroredVtx = nullptr; + } + + ResourceMgr_UnpatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTexture_1"); + ResourceMgr_UnpatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTexture_2"); + ResourceMgr_UnpatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTextureCords_1"); + ResourceMgr_UnpatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTextureCords_2"); + } +} + +void ApplyMirrorWorldGfxPatches() { + PatchMirroredSunSongEtching(); +} diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.h b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.h index fda0f5096..d37d198e4 100644 --- a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.h +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.h @@ -1,3 +1,4 @@ #pragma once void ApplyAuthenticGfxPatches(); +void ApplyMirrorWorldGfxPatches(); diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 46b0b629c..0ee5eec04 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -5,6 +5,7 @@ #include "soh/Enhancements/boss-rush/BossRushTypes.h" #include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/randomizer/3drando/random.hpp" +#include "soh/Enhancements/cosmetics/authenticGfxPatches.h" extern "C" { #include @@ -552,20 +553,32 @@ void RegisterMenuPathFix() { } void UpdateMirrorModeState(int32_t sceneNum) { - if (CVarGetInteger("gMirroredWorldMode", MIRRORED_WORLD_OFF) == MIRRORED_WORLD_RANDOM_SEEDED) { + static bool prevMirroredWorld = false; + bool nextMirroredWorld = false; + + int16_t mirroredMode = CVarGetInteger("gMirroredWorldMode", MIRRORED_WORLD_OFF); + + if (mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED) { uint32_t seed = sceneNum + (gSaveContext.n64ddFlag ? (gSaveContext.seedIcons[0] + gSaveContext.seedIcons[1] + gSaveContext.seedIcons[2] + gSaveContext.seedIcons[3] + gSaveContext.seedIcons[4]) : gSaveContext.sohStats.fileCreatedAt); Random_Init(seed); } uint8_t randomNumber = Random(0, 2); if ( - CVarGetInteger("gMirroredWorldMode", MIRRORED_WORLD_OFF) == MIRRORED_WORLD_ALWAYS || - CVarGetInteger("gMirroredWorldMode", MIRRORED_WORLD_OFF) > MIRRORED_WORLD_ALWAYS && randomNumber == 1 + mirroredMode == MIRRORED_WORLD_ALWAYS || + (mirroredMode > MIRRORED_WORLD_ALWAYS && randomNumber == 1) ) { + nextMirroredWorld = true; CVarSetInteger("gMirroredWorld", 1); } else { + nextMirroredWorld = false; CVarClear("gMirroredWorld"); } + + if (prevMirroredWorld != nextMirroredWorld) { + prevMirroredWorld = nextMirroredWorld; + ApplyMirrorWorldGfxPatches(); + } } void RegisterMirrorModeHandler() {