From 6fdfcd2a611a0ce79fbe01fcebfcad56423688e1 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sat, 17 Jun 2023 13:08:20 -0400 Subject: [PATCH] More mirror mode fixes and additions (#3009) * mirror fishing rod * mirror ivan fairy controls * add mirror dungeons only option * mirror sold out GI texture * update dungeons mirror mode for more options * maybe don't include that --- .../cosmetics/authenticGfxPatches.cpp | 64 ++++++++++++++++--- soh/soh/Enhancements/enhancementTypes.h | 5 ++ soh/soh/Enhancements/mods.cpp | 20 ++++-- soh/soh/SohMenuBar.cpp | 10 ++- .../actors/ovl_En_Partner/z_en_partner.c | 2 +- .../overlays/actors/ovl_Fishing/z_fishing.c | 2 +- 6 files changed, 87 insertions(+), 16 deletions(-) diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp index a840ee317..55fbdad8d 100644 --- a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp @@ -5,6 +5,7 @@ extern "C" { #include #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_fz/object_fz.h" +#include "objects/object_gi_soldout/object_gi_soldout.h" #include "objects/object_ik/object_ik.h" #include "objects/object_link_child/object_link_child.h" @@ -192,6 +193,50 @@ void ApplyAuthenticGfxPatches() { PatchIronKnuckleTextureOverflow(); } +// Patches the Sold Out GI DL to render the texture in the mirror boundary +void PatchMirroredSoldOutGI() { + static const char gSoldOutGIVtx[] = "__OTR__objects/object_gi_soldout/object_gi_soldoutVtx_000400"; + + static Vtx* mirroredSoldOutVtx; + + // 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 mirroredSoldOutTex[] = { + gsDPLoadTextureBlock("", G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, G_TX_MIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD), + }; + + if (CVarGetInteger("gMirroredWorld", 0)) { + if (mirroredSoldOutVtx == nullptr) { + // Copy the original vertices that we want to modify (4 at the beginning of the resource) + mirroredSoldOutVtx = (Vtx*)malloc(sizeof(Vtx) * 4); + Vtx* origVtx = (Vtx*)ResourceGetDataByName(gSoldOutGIVtx); + memcpy(mirroredSoldOutVtx, origVtx, sizeof(Vtx) * 4); + + // Offset the vertex U coordinate values by the width of the texture + for (size_t i = 0; i < 4; i++) { + mirroredSoldOutVtx[i].v.tc[0] += 32 << 5; + } + } + + ResourceMgr_PatchGfxByName(gGiSoldOutDL, "SoldOutGITexture_1", 9, mirroredSoldOutTex[1]); + ResourceMgr_PatchGfxByName(gGiSoldOutDL, "SoldOutGITexture_2", 13, mirroredSoldOutTex[5]); + ResourceMgr_PatchGfxByName(gGiSoldOutDL, "SoldOutGITextureCords_1", 17, gsSPVertex(mirroredSoldOutVtx, 4, 0)); + // noop as the original vertex command is 128 bit wide + ResourceMgr_PatchGfxByName(gGiSoldOutDL, "SoldOutGITextureCords_2", 18, gsSPNoOp()); + } else { + if (mirroredSoldOutVtx != nullptr) { + free(mirroredSoldOutVtx); + mirroredSoldOutVtx = nullptr; + } + + ResourceMgr_UnpatchGfxByName(gGiSoldOutDL, "SoldOutGITexture_1"); + ResourceMgr_UnpatchGfxByName(gGiSoldOutDL, "SoldOutGITexture_2"); + ResourceMgr_UnpatchGfxByName(gGiSoldOutDL, "SoldOutGITextureCords_1"); + ResourceMgr_UnpatchGfxByName(gGiSoldOutDL, "SoldOutGITextureCords_2"); + } +} + // 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() { @@ -200,7 +245,7 @@ void PatchMirroredSunSongEtching() { 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; + static Vtx* mirroredSunSongVtx; // 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 @@ -222,27 +267,27 @@ void PatchMirroredSunSongEtching() { } if (CVarGetInteger("gMirroredWorld", 0)) { - if (mirroredVtx == nullptr) { + if (mirroredSunSongVtx == nullptr) { // Copy the original vertices that we want to modify (4 at the beginning of the resource) - mirroredVtx = (Vtx*)malloc(sizeof(Vtx) * 4); + mirroredSunSongVtx = (Vtx*)malloc(sizeof(Vtx) * 4); Vtx* origVtx = (Vtx*)ResourceGetDataByName(royalGraveBackRoomSongVtx); - memcpy(mirroredVtx, origVtx, sizeof(Vtx) * 4); + memcpy(mirroredSunSongVtx, 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; + mirroredSunSongVtx[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)); + ResourceMgr_PatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTextureCords_1", 24, gsSPVertex(mirroredSunSongVtx, 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; + if (mirroredSunSongVtx != nullptr) { + free(mirroredSunSongVtx); + mirroredSunSongVtx = nullptr; } ResourceMgr_UnpatchGfxByName(royalGraveBackRoomDL, "RoyalGraveSunSongTexture_1"); @@ -253,5 +298,6 @@ void PatchMirroredSunSongEtching() { } void ApplyMirrorWorldGfxPatches() { + PatchMirroredSoldOutGI(); PatchMirroredSunSongEtching(); } diff --git a/soh/soh/Enhancements/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h index 1699ddf89..3a73b478b 100644 --- a/soh/soh/Enhancements/enhancementTypes.h +++ b/soh/soh/Enhancements/enhancementTypes.h @@ -16,6 +16,11 @@ typedef enum { MIRRORED_WORLD_ALWAYS, MIRRORED_WORLD_RANDOM, MIRRORED_WORLD_RANDOM_SEEDED, + MIRRORED_WORLD_DUNGEONS_All, + MIRRORED_WORLD_DUNGEONS_VANILLA, + MIRRORED_WORLD_DUNGEONS_MQ, + MIRRORED_WORLD_DUNGEONS_RANDOM, + MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED, } MirroredWorldMode; typedef enum { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index fd19dd35d..2048ead31 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -15,6 +15,8 @@ extern "C" { #include "functions.h" extern SaveContext gSaveContext; extern PlayState* gPlayState; + +uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); } bool performDelayedSave = false; bool performSave = false; @@ -557,16 +559,26 @@ void UpdateMirrorModeState(int32_t sceneNum) { bool nextMirroredWorld = false; int16_t mirroredMode = CVarGetInteger("gMirroredWorldMode", MIRRORED_WORLD_OFF); + int16_t inDungeon = (sceneNum >= SCENE_YDAN && sceneNum <= SCENE_GANONTIKA_SONOGO && sceneNum != SCENE_GERUDOWAY) || + (sceneNum >= SCENE_YDAN_BOSS && sceneNum <= SCENE_GANON_FINAL) || + (sceneNum == SCENE_GANON_DEMO); - 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); + if (mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED || mirroredMode == MIRRORED_WORLD_DUNGEONS_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); + bool randomMirror = Random(0, 2) == 1; + if ( mirroredMode == MIRRORED_WORLD_ALWAYS || - (mirroredMode > MIRRORED_WORLD_ALWAYS && randomNumber == 1) + ((mirroredMode == MIRRORED_WORLD_RANDOM || mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED) && randomMirror) || + // Dungeon modes + (inDungeon && (mirroredMode == MIRRORED_WORLD_DUNGEONS_All || + (mirroredMode == MIRRORED_WORLD_DUNGEONS_VANILLA && !ResourceMgr_IsSceneMasterQuest(sceneNum)) || + (mirroredMode == MIRRORED_WORLD_DUNGEONS_MQ && ResourceMgr_IsSceneMasterQuest(sceneNum)) || + ((mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM || mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED) && randomMirror))) ) { nextMirroredWorld = true; CVarSetInteger("gMirroredWorld", 1); diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 83b3bbbc4..337a848e0 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -62,7 +62,10 @@ std::string GetWindowButtonText(const char* text, bool menuOpen) { static const char* chestStyleMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" }; static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" }; - static const char* mirroredWorldModes[4] = { "Disabled", "Always", "Random", "Random (Seeded)" }; + static const char* mirroredWorldModes[9] = { + "Disabled", "Always", "Random", "Random (Seeded)", "Dungeons", + "Dungeons (Vanilla)", "Dungeons (MQ)", "Dungeons Random", "Dungeons Random (Seeded)", + }; static const char* enemyRandomizerModes[3] = { "Disabled", "Random", "Random (Seeded)" }; static const char* allPowers[9] = { "Vanilla (1x)", @@ -1054,6 +1057,11 @@ void DrawEnhancementsMenu() { "- Always: Always mirror the world\n" "- Random: Randomly decide to mirror the world on each scene change\n" "- Random (Seeded): Scenes are mirrored based on the current randomizer seed/file\n" + "- Dungeons: Mirror the world in Dungeons\n" + "- Dungeons (Vanilla): Mirror the world in vanilla Dungeons\n" + "- Dungeons (MQ): Mirror the world in MQ Dungeons\n" + "- Dungeons Random: Randomly decide to mirror the world in Dungeons\n" + "- Dungeons Random (Seeded): Dungeons are mirrored based on the current randomizer seed/file\n" ); UIWidgets::PaddedText("Enemy Randomizer", true, false); diff --git a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c index 29c04b74a..16168d258 100644 --- a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c +++ b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c @@ -574,7 +574,7 @@ void EnPartner_Update(Actor* thisx, PlayState* play) { Input sControlInput = play->state.input[this->actor.params]; - f32 relX = sControlInput.cur.stick_x / 10.0f; + f32 relX = sControlInput.cur.stick_x / 10.0f * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1); f32 relY = sControlInput.cur.stick_y / 10.0f; Vec3f camForward = { GET_ACTIVE_CAM(play)->at.x - GET_ACTIVE_CAM(play)->eye.x, 0.0f, diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index 8c8dec69b..60fc7fc1c 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -2025,7 +2025,7 @@ void Fishing_DrawRod(PlayState* play) { spC8 = player->unk_85C - spC8; spC4 = player->unk_858; - Math_SmoothStepToF(&player->unk_858, input->rel.stick_x * 0.02f, 0.3f, 5.0f, 0.0f); + Math_SmoothStepToF(&player->unk_858, input->rel.stick_x * 0.02f * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1), 0.3f, 5.0f, 0.0f); spC4 = player->unk_858 - spC4; if (player->unk_858 > 1.0f) {