From 7a41bd3878f5da1be202631e32a1e296bf511cc6 Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Tue, 13 Jun 2023 07:46:15 -0500 Subject: [PATCH] Mirrored world enhancement (#1569) * Mirrored world PoC * invert culling for health meter and A button action * A few more fixes * Fix for item equip animations * Fix for pause triforce * Mirror scenes with static backgrounds * mirror minimap for mirror world * mirror dungeon maps and icons on the pause menu * mirror overworld map and icons on the pause menu * mirror debug world movement * mirror shops cursor and movement * use flip flag * Reverse crouch stab x axis for mirror mode * use invert culling command and clean up culling logic * Move mirror mode handler to mods and support random modes * Small cvar tweaks * mirror billboard score numbers and fix gyro horse mirrored inputs --------- Co-authored-by: Adam Bird --- soh/include/z64.h | 1 + soh/soh/Enhancements/enhancementTypes.h | 7 ++ soh/soh/Enhancements/mods.cpp | 25 +++++ soh/soh/Enhancements/mods.h | 1 + soh/soh/SohMenuBar.cpp | 14 +++ soh/src/code/audio_synthesis.c | 6 +- soh/src/code/z_camera.c | 3 +- soh/src/code/z_lib.c | 4 + soh/src/code/z_map_exp.c | 103 ++++++++++++------ soh/src/code/z_map_mark.c | 28 +++-- soh/src/code/z_parameter.c | 6 + soh/src/code/z_play.c | 17 +++ soh/src/code/z_player_lib.c | 4 +- soh/src/code/z_room.c | 2 +- soh/src/code/z_view.c | 28 +++++ .../overlays/actors/ovl_En_Horse/z_en_horse.c | 2 +- .../overlays/actors/ovl_En_Ossan/z_en_ossan.c | 58 ++++++++-- .../actors/ovl_player_actor/z_player.c | 17 +-- .../ovl_Effect_Ss_Extra/z_eff_ss_extra.c | 13 +++ .../ovl_kaleido_scope/z_kaleido_map_PAL.c | 92 +++++++++++++--- .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 11 +- .../misc/ovl_kaleido_scope/z_lmap_mark.c | 5 +- 22 files changed, 363 insertions(+), 84 deletions(-) diff --git a/soh/include/z64.h b/soh/include/z64.h index ecde677b0..2172dcb74 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -203,6 +203,7 @@ typedef struct { /* 0x0060 */ Mtx projection; /* 0x00A0 */ Mtx viewing; /* 0x00E0 */ Mtx* projectionPtr; + /* 0x00E0 */ Mtx* projectionFlippedPtr; /* 0x00E4 */ Mtx* viewingPtr; /* 0x00E8 */ Vec3f distortionOrientation; /* 0x00F4 */ Vec3f distortionScale; diff --git a/soh/soh/Enhancements/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h index 00ededc44..a13494a3a 100644 --- a/soh/soh/Enhancements/enhancementTypes.h +++ b/soh/soh/Enhancements/enhancementTypes.h @@ -11,6 +11,13 @@ typedef enum { BUNNY_HOOD_FAST } BunnyHoodMode; +typedef enum { + MIRRORED_WORLD_OFF, + MIRRORED_WORLD_ALWAYS, + MIRRORED_WORLD_RANDOM, + MIRRORED_WORLD_RANDOM_SEEDED, +} MirroredWorldMode; + typedef enum { FASTFILE_1, FASTFILE_2, diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index c5903be36..46b0b629c 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -4,6 +4,7 @@ #include "tts/tts.h" #include "soh/Enhancements/boss-rush/BossRushTypes.h" #include "soh/Enhancements/enhancementTypes.h" +#include "soh/Enhancements/randomizer/3drando/random.hpp" extern "C" { #include @@ -550,6 +551,29 @@ void RegisterMenuPathFix() { }); } +void UpdateMirrorModeState(int32_t sceneNum) { + if (CVarGetInteger("gMirroredWorldMode", MIRRORED_WORLD_OFF) == 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 + ) { + CVarSetInteger("gMirroredWorld", 1); + } else { + CVarClear("gMirroredWorld"); + } +} + +void RegisterMirrorModeHandler() { + GameInteractor::Instance->RegisterGameHook([](int32_t sceneNum) { + UpdateMirrorModeState(sceneNum); + }); +} + void InitMods() { RegisterTTS(); RegisterInfiniteMoney(); @@ -571,4 +595,5 @@ void InitMods() { RegisterHyperEnemies(); RegisterBonkDamage(); RegisterMenuPathFix(); + RegisterMirrorModeHandler(); } diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 16effa712..2f0430475 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -8,6 +8,7 @@ extern "C" { #endif void UpdateDirtPathFixState(int32_t sceneNum); +void UpdateMirrorModeState(int32_t sceneNum); void InitMods(); #ifdef __cplusplus diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 884f1bd1d..a312b251c 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -62,6 +62,7 @@ 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* allPowers[9] = { "Vanilla (1x)", "Double (2x)", @@ -1015,6 +1016,19 @@ void DrawEnhancementsMenu() { UIWidgets::Spacer(0); if (ImGui::BeginMenu("Extra Modes")) { + UIWidgets::PaddedText("Mirrored World Mode", true, false); + if (UIWidgets::EnhancementCombobox("gMirroredWorldMode", mirroredWorldModes, MIRRORED_WORLD_OFF) && gPlayState != NULL) { + UpdateMirrorModeState(gPlayState->sceneNum); + } + UIWidgets::Tooltip( + "Mirrors the world horizontally\n\n" + "- 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" + ); + + UIWidgets::Spacer(0); + UIWidgets::PaddedEnhancementCheckbox("Ivan the Fairy (Coop Mode)", "gIvanCoopModeEnabled", true, false); UIWidgets::Tooltip("Enables Ivan the Fairy upon the next map change. Player 2 can control Ivan and " "press the C-Buttons to use items and mess with Player 1!"); diff --git a/soh/src/code/audio_synthesis.c b/soh/src/code/audio_synthesis.c index e1b42185a..f2c526630 100644 --- a/soh/src/code/audio_synthesis.c +++ b/soh/src/code/audio_synthesis.c @@ -653,7 +653,11 @@ Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* cmd, s32 updat } updateIndex = aiBufLen * 2; - aInterleave(cmd++, DMEM_TEMP, DMEM_LEFT_CH, DMEM_RIGHT_CH, updateIndex); + if (CVarGetInteger("gMirroredWorld", 0)) { + aInterleave(cmd++, DMEM_TEMP, DMEM_RIGHT_CH, DMEM_LEFT_CH, updateIndex); + } else { + aInterleave(cmd++, DMEM_TEMP, DMEM_LEFT_CH, DMEM_RIGHT_CH, updateIndex); + } aSaveBuffer(cmd++, DMEM_TEMP, aiBuf, updateIndex * 2); return cmd; diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c index b1fe9987d..e0d827107 100644 --- a/soh/src/code/z_camera.c +++ b/soh/src/code/z_camera.c @@ -1487,8 +1487,9 @@ s32 Camera_Free(Camera* camera) { f32 newCamX = -D_8015BD7C->state.input[0].cur.right_stick_x * 10.0f * (CVarGetFloat("gThirdPersonCameraSensitivityX", 1.0f)); f32 newCamY = D_8015BD7C->state.input[0].cur.right_stick_y * 10.0f * (CVarGetFloat("gThirdPersonCameraSensitivityY", 1.0f)); + bool invertXAxis = (CVarGetInteger("gInvertXAxis", 0) && !CVarGetInteger("gMirroredWorld", 0)) || (!CVarGetInteger("gInvertXAxis", 0) && CVarGetInteger("gMirroredWorld", 0)); - camera->play->camX += newCamX * (CVarGetInteger("gInvertXAxis", 0) ? -1 : 1); + camera->play->camX += newCamX * (invertXAxis ? -1 : 1); camera->play->camY += newCamY * (CVarGetInteger("gInvertYAxis", 1) ? 1 : -1); if (camera->play->camY > 0x32A4) { diff --git a/soh/src/code/z_lib.c b/soh/src/code/z_lib.c index 3508fceb7..820256cbb 100644 --- a/soh/src/code/z_lib.c +++ b/soh/src/code/z_lib.c @@ -201,6 +201,10 @@ void func_80077D10(f32* arg0, s16* arg1, Input* input) { f32 relX = input->rel.stick_x; f32 relY = input->rel.stick_y; + if (CVarGetInteger("gMirroredWorld", 0)) { + relX = -input->rel.stick_x; + } + *arg0 = sqrtf(SQ(relX) + SQ(relY)); *arg0 = (60.0f < *arg0) ? 60.0f : *arg0; diff --git a/soh/src/code/z_map_exp.c b/soh/src/code/z_map_exp.c index 990f1b695..3a758d92a 100644 --- a/soh/src/code/z_map_exp.c +++ b/soh/src/code/z_map_exp.c @@ -635,32 +635,49 @@ void Minimap_DrawCompassIcons(PlayState* play) { gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); gDPSetCombineMode(OVERLAY_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + s16 mapWidth = 0; + s16 mapStartPosX = 0; + if (play->sceneNum >= SCENE_SPOT00 && play->sceneNum <= SCENE_GANON_TOU) { // Overworld + mapStartPosX = R_OW_MINIMAP_X; + mapWidth = gMapData->owMinimapWidth[R_MAP_INDEX]; + } else if (play->sceneNum >= SCENE_YDAN && play->sceneNum <= SCENE_ICE_DOUKUTO) { // Dungeons + mapStartPosX = R_DGN_MINIMAP_X; + mapWidth = 96; + } + + // The compass offset value is a factor of 10 compared to N64 screen pixels and originates in the center of the screen + // Compute the additional mirror offset value by normalizing the original offset position + // and taking it's distance to the center of the map, duplicating that result and casting back to a factor of 10 + s16 mirrorOffset = ((mapWidth / 2) - ((R_COMPASS_OFFSET_X / 10) - (mapStartPosX - SCREEN_WIDTH / 2))) * 2 * 10; + tempX = player->actor.world.pos.x; tempZ = player->actor.world.pos.z; - tempX /= R_COMPASS_SCALE_X; + tempX /= R_COMPASS_SCALE_X * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1); tempZ /= R_COMPASS_SCALE_Y; + + s16 tempXOffset = R_COMPASS_OFFSET_X + (CVarGetInteger("gMirroredWorld", 0) ? mirrorOffset : 0); if (CVarGetInteger("gMinimapPosType", 0) != 0) { if (CVarGetInteger("gMinimapPosType", 0) == 1) {//Anchor Left if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Left_MM_Margin;}; - Matrix_Translate( - OTRGetDimensionFromLeftEdge((R_COMPASS_OFFSET_X + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), - (R_COMPASS_OFFSET_Y + ((Y_Margins_Minimap*10)*-1) - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); + Matrix_Translate( + OTRGetDimensionFromLeftEdge((tempXOffset + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), + (R_COMPASS_OFFSET_Y + ((Y_Margins_Minimap*10)*-1) - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); } else if (CVarGetInteger("gMinimapPosType", 0) == 2) {//Anchor Right if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Right_MM_Margin;}; Matrix_Translate( - OTRGetDimensionFromRightEdge((R_COMPASS_OFFSET_X + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), + OTRGetDimensionFromRightEdge((tempXOffset + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), (R_COMPASS_OFFSET_Y +((Y_Margins_Minimap*10)*-1) - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); } else if (CVarGetInteger("gMinimapPosType", 0) == 3) {//Anchor None Matrix_Translate( - (R_COMPASS_OFFSET_X + tempX + (CVarGetInteger("gMinimapPosX", 0)*10) / 10.0f), + (tempXOffset + tempX + (CVarGetInteger("gMinimapPosX", 0)*10) / 10.0f), (R_COMPASS_OFFSET_Y + ((Y_Margins_Minimap*10)*-1) - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); } } else { - Matrix_Translate(OTRGetDimensionFromRightEdge((R_COMPASS_OFFSET_X+(X_Margins_Minimap*10) + tempX) / 10.0f), (R_COMPASS_OFFSET_Y+((Y_Margins_Minimap*10)*-1) - tempZ) / 10.0f, 0.0f, MTXMODE_NEW); + Matrix_Translate(OTRGetDimensionFromRightEdge((tempXOffset+(X_Margins_Minimap*10) + tempX) / 10.0f), (R_COMPASS_OFFSET_Y+((Y_Margins_Minimap*10)*-1) - tempZ) / 10.0f, 0.0f, MTXMODE_NEW); } Matrix_Scale(0.4f, 0.4f, 0.4f, MTXMODE_APPLY); Matrix_RotateX(-1.6f, MTXMODE_APPLY); - tempX = (0x7FFF - player->actor.shape.rot.y) / 0x400; + tempX = ((0x7FFF - player->actor.shape.rot.y) / 0x400) * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1); Matrix_RotateY(tempX / 10.0f, MTXMODE_APPLY); gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); @@ -669,32 +686,32 @@ void Minimap_DrawCompassIcons(PlayState* play) { gSPDisplayList(OVERLAY_DISP++, gCompassArrowDL); //Player map entry (red arrow) - tempX = sPlayerInitialPosX+X_Margins_Minimap; - tempZ = sPlayerInitialPosZ+Y_Margins_Minimap; - tempX /= R_COMPASS_SCALE_X; + tempX = sPlayerInitialPosX; + tempZ = sPlayerInitialPosZ; + tempX /= R_COMPASS_SCALE_X * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1); tempZ /= R_COMPASS_SCALE_Y; if (CVarGetInteger("gMinimapPosType", 0) != 0) { if (CVarGetInteger("gMinimapPosType", 0) == 1) {//Anchor Left if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Left_MM_Margin;}; - Matrix_Translate( - OTRGetDimensionFromLeftEdge((R_COMPASS_OFFSET_X + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), - (R_COMPASS_OFFSET_Y + ((Y_Margins_Minimap*10)*-1) - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); + Matrix_Translate( + OTRGetDimensionFromLeftEdge((tempXOffset + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), + (R_COMPASS_OFFSET_Y + ((Y_Margins_Minimap*10)*-1) - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); } else if (CVarGetInteger("gMinimapPosType", 0) == 2) {//Anchor Right if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Right_MM_Margin;}; Matrix_Translate( - OTRGetDimensionFromRightEdge((R_COMPASS_OFFSET_X + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), + OTRGetDimensionFromRightEdge((tempXOffset + (X_Margins_Minimap*10) + tempX + (CVarGetInteger("gMinimapPosX", 0)*10)) / 10.0f), (R_COMPASS_OFFSET_Y +((Y_Margins_Minimap*10)*-1) - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); } else if (CVarGetInteger("gMinimapPosType", 0) == 3) {//Anchor None Matrix_Translate( - (R_COMPASS_OFFSET_X + tempX + (CVarGetInteger("gMinimapPosX", 0)*10) / 10.0f), + (tempXOffset + tempX + (CVarGetInteger("gMinimapPosX", 0)*10) / 10.0f), (R_COMPASS_OFFSET_Y - tempZ + ((CVarGetInteger("gMinimapPosY", 0)*10)*-1)) / 10.0f, 0.0f, MTXMODE_NEW); } } else { - Matrix_Translate(OTRGetDimensionFromRightEdge((R_COMPASS_OFFSET_X+(X_Margins_Minimap*10) + tempX) / 10.0f), (R_COMPASS_OFFSET_Y+((Y_Margins_Minimap*10)*-1) - tempZ) / 10.0f, 0.0f, MTXMODE_NEW); + Matrix_Translate(OTRGetDimensionFromRightEdge((tempXOffset+(X_Margins_Minimap*10) + tempX) / 10.0f), (R_COMPASS_OFFSET_Y+((Y_Margins_Minimap*10)*-1) - tempZ) / 10.0f, 0.0f, MTXMODE_NEW); } Matrix_Scale(VREG(9) / 100.0f, VREG(9) / 100.0f, VREG(9) / 100.0f, MTXMODE_APPLY); Matrix_RotateX(VREG(52) / 10.0f, MTXMODE_APPLY); - Matrix_RotateY(sPlayerInitialDirection / 10.0f, MTXMODE_APPLY); + Matrix_RotateY((sPlayerInitialDirection * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1)) / 10.0f, MTXMODE_APPLY); gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); @@ -752,8 +769,9 @@ void Minimap_Draw(PlayState* play) { if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, mapIndex)) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, minimapColor.r, minimapColor.g, minimapColor.b, interfaceCtx->minimapAlpha); + u8 mirrorMode = CVarGetInteger("gMirroredWorld", 0) ? G_TX_MIRROR : G_TX_NOMIRROR; gDPLoadTextureBlock_4b(OVERLAY_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_I, 96, 85, 0, - G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + mirrorMode | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); s16 dgnMiniMapX = OTRGetRectDimensionFromRightEdge(R_DGN_MINIMAP_X + X_Margins_Minimap); @@ -770,9 +788,16 @@ void Minimap_Draw(PlayState* play) { dgnMiniMapX = CVarGetInteger("gMinimapPosX", 0); } } + + s32 sValue = 0; + if (CVarGetInteger("gMirroredWorld", 0)) { + // Flip the minimap on the x-axis (s-axis) by setting s to the textures mirror boundary + sValue = 96 << 5; + } + gSPWideTextureRectangle(OVERLAY_DISP++, dgnMiniMapX << 2, dgnMiniMapY << 2, (dgnMiniMapX + 96) << 2, (dgnMiniMapY + 85) << 2, G_TX_RENDERTILE, - 0, 0, 1 << 10, 1 << 10); + sValue, 0, 1 << 10, 1 << 10); } if (CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, mapIndex)) { @@ -820,9 +845,10 @@ void Minimap_Draw(PlayState* play) { gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, minimapColor.r, minimapColor.g, minimapColor.b, interfaceCtx->minimapAlpha); + u8 mirrorMode = CVarGetInteger("gMirroredWorld", 0) ? G_TX_MIRROR : G_TX_NOMIRROR; gDPLoadTextureBlock_4b(OVERLAY_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_IA, gMapData->owMinimapWidth[mapIndex], gMapData->owMinimapHeight[mapIndex], 0, - G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + mirrorMode | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); s16 oWMiniMapX = OTRGetRectDimensionFromRightEdge(R_OW_MINIMAP_X + X_Margins_Minimap); @@ -839,9 +865,16 @@ void Minimap_Draw(PlayState* play) { oWMiniMapX = CVarGetInteger("gMinimapPosX", 0); } } + + s32 sValue = 0; + if (CVarGetInteger("gMirroredWorld", 0)) { + // Flip the minimap on the x-axis (s-axis) by setting s to the textures mirror boundary + sValue = gMapData->owMinimapWidth[mapIndex] << 5; + } + gSPWideTextureRectangle(OVERLAY_DISP++, oWMiniMapX << 2, oWMiniMapY << 2, (oWMiniMapX + gMapData->owMinimapWidth[mapIndex]) << 2, - (oWMiniMapY + gMapData->owMinimapHeight[mapIndex]) << 2, G_TX_RENDERTILE, 0, + (oWMiniMapY + gMapData->owMinimapHeight[mapIndex]) << 2, G_TX_RENDERTILE, sValue, 0, 1 << 10, 1 << 10); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, minimapColor.r, minimapColor.g, minimapColor.b, interfaceCtx->minimapAlpha); @@ -851,26 +884,33 @@ void Minimap_Draw(PlayState* play) { if (((play->sceneNum != SCENE_SPOT01) && (play->sceneNum != SCENE_SPOT04) && (play->sceneNum != SCENE_SPOT08)) || (LINK_AGE_IN_YEARS != YEARS_ADULT)) { + s16 origX = gMapData->owEntranceIconPosX[sEntranceIconMapIndex]; + + // Compute the distance of the center of the original texture location to the center of the map + // Then duplicate that and right-align the texture (extra 2 pixels are due to the texture being a 6px left-aligned in a 8px tex) + s16 distFromCenter = (R_OW_MINIMAP_X + (gMapData->owMinimapWidth[mapIndex] / 2)) - (origX + (iconSize / 2)); + s16 mirrorOffset = distFromCenter * 2 + (iconSize / 2) - 2; + s16 newX = origX + (CVarGetInteger("gMirroredWorld", 0) ? mirrorOffset : 0); + // The game authentically uses larger negative values for the entrance icon Y pos value. Normally only the first 12 bits // would be read when the final value is passed into `gSPTextureRectangle`, but our cosmetic hud placements requires using // `gSPWideTextureRectangle` which reads the first 24 bits instead. This caused the icon to be placed off screen. // To address this, we take only the first 10 bits (which are later left-shifted by 2 to get our final 12 bits) // to fix the entrance icon position when used with `gSPWideTextureRectangle` s16 newY = gMapData->owEntranceIconPosY[sEntranceIconMapIndex] & 0x3FF; - s16 PosX = gMapData->owEntranceIconPosX[sEntranceIconMapIndex] + X_Margins_Minimap; - s16 entranceX = OTRGetRectDimensionFromRightEdge(PosX); + s16 entranceX = OTRGetRectDimensionFromRightEdge(newX + X_Margins_Minimap); s16 entranceY = newY + Y_Margins_Minimap; if (CVarGetInteger("gMinimapPosType", 0) != 0) { entranceY = newY + CVarGetInteger("gMinimapPosY", 0) + Y_Margins_Minimap; if (CVarGetInteger("gMinimapPosType", 0) == 1) { // Anchor Left if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Left_MM_Margin;}; - entranceX = OTRGetRectDimensionFromLeftEdge(PosX + CVarGetInteger("gMinimapPosX", 0)); + entranceX = OTRGetRectDimensionFromLeftEdge(newX + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0)); } else if (CVarGetInteger("gMinimapPosType", 0) == 2) { // Anchor Right if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Right_MM_Margin;}; - entranceX = OTRGetRectDimensionFromRightEdge(PosX + CVarGetInteger("gMinimapPosX", 0)); + entranceX = OTRGetRectDimensionFromRightEdge(newX + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0)); } else if (CVarGetInteger("gMinimapPosType", 0) == 3) { // Anchor None - entranceX = PosX + CVarGetInteger("gMinimapPosX", 0); + entranceX = newX + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0); } } @@ -894,18 +934,19 @@ void Minimap_Draw(PlayState* play) { } } - s16 entranceX = OTRGetRectDimensionFromRightEdge(270 + X_Margins_Minimap); + s16 origX = CVarGetInteger("gMirroredWorld", 0) ? 256 : 270; + s16 entranceX = OTRGetRectDimensionFromRightEdge(origX + X_Margins_Minimap); s16 entranceY = 154 + Y_Margins_Minimap; if (CVarGetInteger("gMinimapPosType", 0) != 0) { entranceY = 154 + Y_Margins_Minimap + CVarGetInteger("gMinimapPosY", 0); if (CVarGetInteger("gMinimapPosType", 0) == 1) {//Anchor Left if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Left_MM_Margin;}; - entranceX = OTRGetRectDimensionFromLeftEdge(270 + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0)); + entranceX = OTRGetRectDimensionFromLeftEdge(origX + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0)); } else if (CVarGetInteger("gMinimapPosType", 0) == 2) {//Anchor Right if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap = Right_MM_Margin;}; - entranceX = OTRGetRectDimensionFromRightEdge(270 + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0)); + entranceX = OTRGetRectDimensionFromRightEdge(origX + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0)); } else if (CVarGetInteger("gMinimapPosType", 0) == 3) {//Anchor None - entranceX = 270 + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0); + entranceX = origX + X_Margins_Minimap + CVarGetInteger("gMinimapPosX", 0); } } diff --git a/soh/src/code/z_map_mark.c b/soh/src/code/z_map_mark.c index 8d9f025a0..f40ead1b8 100644 --- a/soh/src/code/z_map_mark.c +++ b/soh/src/code/z_map_mark.c @@ -132,8 +132,19 @@ void MapMark_DrawForDungeon(PlayState* play) { //Place each chest / boss room icon for (i = 0; i < mapMarkIconData->count; i++) { if ((mapMarkIconData->markType != MAP_MARK_CHEST) || !Flags_GetTreasure(play, markPoint->chestFlag)) { + markInfo = &sMapMarkInfoTable[mapMarkIconData->markType]; + int height = markInfo->textureHeight * 1.0f; //Adjust Height with scale + int width = markInfo->textureWidth * 1.0f; //Adjust Width with scale + int height_factor = (1 << 10) * markInfo->textureHeight / height; + int width_factor = (1 << 10) * markInfo->textureWidth / width; + + // The original mark point X originates from the left edge of the map + // For mirror mode, we compute the new mark point X by subtracting it from the right side of the + // dungeon map and the textures width + s16 markPointX = CVarGetInteger("gMirroredWorld", 0) ? 96 - markPoint->x - width : markPoint->x; + //Minimap chest / boss icon - const s32 PosX_Minimap_ori = GREG(94) + OTRGetRectDimensionFromRightEdge(markPoint->x+X_Margins_Minimap_ic) + 204; + const s32 PosX_Minimap_ori = GREG(94) + OTRGetRectDimensionFromRightEdge(markPointX+X_Margins_Minimap_ic) + 204; const s32 PosY_Minimap_ori = GREG(95) + markPoint->y + Y_Margins_Minimap_ic + 140; if (CVarGetInteger("gMinimapPosType", 0) != 0) { rectTop = (markPoint->y + Y_Margins_Minimap_ic + 140 + CVarGetInteger("gMinimapPosY", 0)); @@ -143,15 +154,15 @@ void MapMark_DrawForDungeon(PlayState* play) { play->sceneNum == SCENE_BMORI1 || play->sceneNum == SCENE_HIDAN || play->sceneNum == SCENE_MIZUSIN || play->sceneNum == SCENE_JYASINZOU || play->sceneNum == SCENE_HAKADAN || play->sceneNum == SCENE_HAKADANCH || play->sceneNum == SCENE_ICE_DOUKUTO) { - rectLeft = OTRGetRectDimensionFromLeftEdge(markPoint->x+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic); + rectLeft = OTRGetRectDimensionFromLeftEdge(markPointX+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic); } else { - rectLeft = OTRGetRectDimensionFromLeftEdge(markPoint->x+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic); + rectLeft = OTRGetRectDimensionFromLeftEdge(markPointX+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic); } } else if (CVarGetInteger("gMinimapPosType", 0) == 2) {//Anchor Right if (CVarGetInteger("gMinimapUseMargins", 0) != 0) {X_Margins_Minimap_ic = Right_MC_Margin;}; - rectLeft = OTRGetRectDimensionFromRightEdge(markPoint->x+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic); + rectLeft = OTRGetRectDimensionFromRightEdge(markPointX+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic); } else if (CVarGetInteger("gMinimapPosType", 0) == 3) {//Anchor None - rectLeft = markPoint->x+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic; + rectLeft = markPointX+CVarGetInteger("gMinimapPosX", 0)+204+X_Margins_Minimap_ic; } else if (CVarGetInteger("gMinimapPosType", 0) == 4) {//Hidden rectLeft = -9999; } @@ -160,13 +171,6 @@ void MapMark_DrawForDungeon(PlayState* play) { rectTop = PosY_Minimap_ori; } - int height = 8 * 1.0f; //Adjust Height with scale - int width = 8 * 1.0f; //Adjust Width with scale - int height_factor = (1 << 10) * 8 / height; - int width_factor = (1 << 10) * 8 / width; - - markInfo = &sMapMarkInfoTable[mapMarkIconData->markType]; - gDPPipeSync(OVERLAY_DISP++); gDPLoadTextureBlock(OVERLAY_DISP++, markInfo->texture, markInfo->imageFormat, G_IM_SIZ_MARK, diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 756e445e9..43be8b854 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -5090,7 +5090,13 @@ void Interface_Draw(PlayState* play) { Minimap_Draw(play); if ((R_PAUSE_MENU_MODE != 2) && (R_PAUSE_MENU_MODE != 3)) { + if (CVarGetInteger("gMirroredWorld", 0)) { + gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + } func_8002C124(&play->actorCtx.targetCtx, play); // Draw Z-Target + if (CVarGetInteger("gMirroredWorld", 0)) { + gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + } } Gfx_SetupDL_39Overlay(play->state.gfxCtx); diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 59257ad9f..24cef9426 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -1473,6 +1473,17 @@ void Play_Draw(PlayState* play) { func_800AA460(&play->view, play->view.fovy, play->view.zNear, play->lightCtx.fogFar); func_800AAA50(&play->view, 15); + // Flip the projections and invert culling for the OPA and XLU display buffers + // These manage the world and effects + if (CVarGetInteger("gMirroredWorld", 0)) { + gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + gSPMatrix(POLY_OPA_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_OPA_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + } + // The billboard matrix temporarily stores the viewing matrix Matrix_MtxToMtxF(&play->view.viewing, &play->billboardMtxF); Matrix_MtxToMtxF(&play->view.projection, &play->viewProjectionMtxF); @@ -1694,6 +1705,12 @@ void Play_Draw(PlayState* play) { } } } + + // Reset the inverted culling + if (CVarGetInteger("gMirroredWorld", 0)) { + gSPClearExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + gSPClearExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + } } if (play->view.unk_124 != 0) { diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index cb856b870..8048a6097 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -1776,7 +1776,7 @@ void func_80091A24(PlayState* play, void* seg04, void* seg06, SkelAnime* skelAni Matrix_SetTranslateRotateYXZ(pos->x - ((CVarGetInteger("gPauseLiveLink", 0) && LINK_AGE_IN_YEARS == YEARS_ADULT) ? 25 : 0), pos->y - (CVarGetInteger("gPauseTriforce", 0) ? 16 : 0), pos->z, rot); - Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_Scale(scale * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1), scale, scale, MTXMODE_APPLY); gSPSegment(POLY_OPA_DISP++, 0x04, seg04); gSPSegment(POLY_OPA_DISP++, 0x06, seg06); @@ -1798,7 +1798,7 @@ void func_80091A24(PlayState* play, void* seg04, void* seg06, SkelAnime* skelAni Matrix_SetTranslateRotateYXZ(pos->x - (LINK_AGE_IN_YEARS == YEARS_ADULT ? 25 : 0), pos->y + 280 + (LINK_AGE_IN_YEARS == YEARS_ADULT ? 48 : 0), pos->z, rot); - Matrix_Scale(scale * 1, scale * 1, scale * 1, MTXMODE_APPLY); + Matrix_Scale(scale * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1), scale * 1, scale * 1, MTXMODE_APPLY); Gfx* ohNo = POLY_XLU_DISP; POLY_XLU_DISP = POLY_OPA_DISP; diff --git a/soh/src/code/z_room.c b/soh/src/code/z_room.c index 1d4b610c5..17b2feb82 100644 --- a/soh/src/code/z_room.c +++ b/soh/src/code/z_room.c @@ -270,7 +270,7 @@ void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 h bg->b.imageFmt = fmt; bg->b.imageSiz = siz; bg->b.imagePal = 0; - bg->b.imageFlip = 0; + bg->b.imageFlip = CVarGetInteger("gMirroredWorld", 0) ? G_BG_FLAG_FLIPS : 0; if (ResourceMgr_ResourceIsBackground((char*) source)) { char* blob = (char*) ResourceGetDataByName((char *) source); diff --git a/soh/src/code/z_view.c b/soh/src/code/z_view.c index c84634a6b..6ea62941c 100644 --- a/soh/src/code/z_view.c +++ b/soh/src/code/z_view.c @@ -296,6 +296,7 @@ s32 func_800AAA9C(View* view) { s32 height; Vp* vp; Mtx* projection; + Mtx* projectionFlipped; Mtx* viewing; GraphicsContext* gfxCtx = view->gfxCtx; @@ -313,8 +314,11 @@ s32 func_800AAA9C(View* view) { gSPViewport(POLY_KAL_DISP++, vp); projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + projectionFlipped = Graph_Alloc(gfxCtx, sizeof(Mtx)); LOG_CHECK_NULL_POINTER("projection", projection); + LOG_CHECK_NULL_POINTER("projectionFlipped", projectionFlipped); view->projectionPtr = projection; + view->projectionFlippedPtr = projectionFlipped; width = view->viewport.rightX - view->viewport.leftX; height = view->viewport.bottomY - view->viewport.topY; @@ -427,6 +431,15 @@ s32 func_800AAA9C(View* view) { } osSyncPrintf("\n"); } + if (CVarGetInteger("gMirroredWorld", 0)) { + MtxF flipF; + SkinMatrix_Clear(&flipF); + flipF.xx = -1.0; + MtxF projectionF; + Matrix_MtxToMtxF(projection, &projectionF); + SkinMatrix_MtxFMtxFMult(&projectionF, &flipF, &projectionF); + Matrix_MtxFToMtx(&projectionF, projectionFlipped); + } view->projection = *projection; @@ -511,6 +524,7 @@ s32 func_800AB0A8(View* view) { s32 func_800AB2C4(View* view) { Vp* vp; Mtx* projection; + Mtx* projectionFlipped; GraphicsContext* gfxCtx; gfxCtx = view->gfxCtx; @@ -528,12 +542,26 @@ s32 func_800AB2C4(View* view) { gSPViewport(OVERLAY_DISP++, vp); projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + projectionFlipped = Graph_Alloc(gfxCtx, sizeof(Mtx)); LOG_CHECK_NULL_POINTER("projection", projection); + LOG_CHECK_NULL_POINTER("projectionFlipped", projectionFlipped); view->projectionPtr = projection; + view->projectionFlippedPtr = projectionFlipped; guOrtho(projection, -(f32)gScreenWidth * 0.5f, (f32)gScreenWidth * 0.5f, -(f32)gScreenHeight * 0.5f, (f32)gScreenHeight * 0.5f, -30, view->zFar, view->scale); + // This is for z-targeting + if (CVarGetInteger("gMirroredWorld", 0)) { + MtxF flipF; + SkinMatrix_Clear(&flipF); + flipF.xx = -1.0; + MtxF projectionF; + Matrix_MtxToMtxF(projection, &projectionF); + SkinMatrix_MtxFMtxFMult(&projectionF, &flipF, &projectionF); + Matrix_MtxFToMtx(&projectionF, projectionFlipped); + } + view->projection = *projection; gSPMatrix(OVERLAY_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); diff --git a/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c index 7ccb228a6..6fa5f68b6 100644 --- a/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c +++ b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c @@ -3042,7 +3042,7 @@ void EnHorse_StickDirection(Vec2f* curStick, f32* stickMag, s16* angle) { void EnHorse_UpdateStick(EnHorse* this, PlayState* play) { this->lastStick = this->curStick; - this->curStick.x = play->state.input[0].rel.stick_x; + this->curStick.x = play->state.input[0].rel.stick_x * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1); this->curStick.y = play->state.input[0].rel.stick_y; } diff --git a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c index 5be8e1012..e403871b9 100644 --- a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c +++ b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c @@ -661,6 +661,10 @@ void EnOssan_UpdateCursorPos(PlayState* play, EnOssan* this) { Actor_GetScreenPos(play, &this->shelfSlots[this->cursorIndex]->actor, &x, &y); this->cursorX = x; this->cursorY = y; + + if (CVarGetInteger("gMirroredWorld", 0)) { + this->cursorX = SCREEN_WIDTH - x; + } } void EnOssan_EndInteraction(PlayState* play, EnOssan* this) { @@ -769,6 +773,10 @@ void EnOssan_UpdateJoystickInputState(PlayState* play, EnOssan* this) { s8 stickX = input->rel.stick_x; s8 stickY = input->rel.stick_y; + if (CVarGetInteger("gMirroredWorld", 0)) { + stickX = -input->rel.stick_x; + } + this->moveHorizontal = this->moveVertical = false; if (this->stickAccumX == 0) { @@ -979,8 +987,16 @@ void EnOssan_State_FacingShopkeeper(EnOssan* this, PlayState* play, Player* play func_80078884(NA_SE_SY_DECIDE); return; } + + u16 dLeft = BTN_DLEFT; + u16 dRight = BTN_DRIGHT; + if (CVarGetInteger("gMirroredWorld", 0)) { + dLeft = BTN_DRIGHT; + dRight = BTN_DLEFT; + } + // Stick Left - if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, dLeft))) { nextIndex = EnOssan_SetCursorIndexFromNeutral(this, 4); if (nextIndex != CURSOR_INVALID) { this->cursorIndex = nextIndex; @@ -989,7 +1005,7 @@ void EnOssan_State_FacingShopkeeper(EnOssan* this, PlayState* play, Player* play this->stickLeftPrompt.isEnabled = false; func_80078884(NA_SE_SY_CURSOR); } - } else if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + } else if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, dRight))) { nextIndex = EnOssan_SetCursorIndexFromNeutral(this, 0); if (nextIndex != CURSOR_INVALID) { this->cursorIndex = nextIndex; @@ -1219,8 +1235,16 @@ void EnOssan_State_BrowseLeftShelf(EnOssan* this, PlayState* play, Player* playe EnOssan_UpdateCursorPos(play, this); if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && !EnOssan_HasPlayerSelectedItem(play, this, &play->state.input[0])) { + + u16 dLeft = BTN_DLEFT; + u16 dRight = BTN_DRIGHT; + if (CVarGetInteger("gMirroredWorld", 0)) { + dLeft = BTN_DRIGHT; + dRight = BTN_DLEFT; + } + if (this->moveHorizontal) { - if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, dRight))) { a = EnOssan_CursorRight(this, this->cursorIndex, 4); if (a != CURSOR_INVALID) { this->cursorIndex = a; @@ -1228,14 +1252,14 @@ void EnOssan_State_BrowseLeftShelf(EnOssan* this, PlayState* play, Player* playe EnOssan_SetLookToShopkeeperFromShelf(play, this); return; } - } else if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + } else if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, dLeft))) { b = EnOssan_CursorLeft(this, this->cursorIndex, 8); if (b != CURSOR_INVALID) { this->cursorIndex = b; } } } else { - if ((this->stickAccumX > 0 && this->stickAccumX > 500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + if ((this->stickAccumX > 0 && this->stickAccumX > 500) || (dpad && CHECK_BTN_ALL(input->press.button, dRight))) { c = EnOssan_CursorRight(this, this->cursorIndex, 4); if (c != CURSOR_INVALID) { this->cursorIndex = c; @@ -1243,7 +1267,7 @@ void EnOssan_State_BrowseLeftShelf(EnOssan* this, PlayState* play, Player* playe EnOssan_SetLookToShopkeeperFromShelf(play, this); return; } - } else if ((this->stickAccumX < 0 && this->stickAccumX < -500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + } else if ((this->stickAccumX < 0 && this->stickAccumX < -500) || (dpad && CHECK_BTN_ALL(input->press.button, dLeft))) { d = EnOssan_CursorLeft(this, this->cursorIndex, 8); if (d != CURSOR_INVALID) { this->cursorIndex = d; @@ -1280,8 +1304,16 @@ void EnOssan_State_BrowseRightShelf(EnOssan* this, PlayState* play, Player* play EnOssan_UpdateCursorPos(play, this); if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && !EnOssan_HasPlayerSelectedItem(play, this, &play->state.input[0])) { + + u16 dLeft = BTN_DLEFT; + u16 dRight = BTN_DRIGHT; + if (CVarGetInteger("gMirroredWorld", 0)) { + dLeft = BTN_DRIGHT; + dRight = BTN_DLEFT; + } + if (this->moveHorizontal) { - if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, dLeft))) { nextIndex = EnOssan_CursorRight(this, this->cursorIndex, 0); if (nextIndex != CURSOR_INVALID) { this->cursorIndex = nextIndex; @@ -1289,14 +1321,14 @@ void EnOssan_State_BrowseRightShelf(EnOssan* this, PlayState* play, Player* play EnOssan_SetLookToShopkeeperFromShelf(play, this); return; } - } else if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + } else if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, dRight))) { nextIndex = EnOssan_CursorLeft(this, this->cursorIndex, 4); if (nextIndex != CURSOR_INVALID) { this->cursorIndex = nextIndex; } } } else { - if ((this->stickAccumX < 0 && this->stickAccumX < -500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + if ((this->stickAccumX < 0 && this->stickAccumX < -500) || (dpad && CHECK_BTN_ALL(input->press.button, dLeft))) { nextIndex = EnOssan_CursorRight(this, this->cursorIndex, 0); if (nextIndex != CURSOR_INVALID) { this->cursorIndex = nextIndex; @@ -1304,7 +1336,7 @@ void EnOssan_State_BrowseRightShelf(EnOssan* this, PlayState* play, Player* play EnOssan_SetLookToShopkeeperFromShelf(play, this); return; } - } else if ((this->stickAccumX > 0 && this->stickAccumX > 500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + } else if ((this->stickAccumX > 0 && this->stickAccumX > 500) || (dpad && CHECK_BTN_ALL(input->press.button, dRight))) { nextIndex = EnOssan_CursorLeft(this, this->cursorIndex, 4); if (nextIndex != CURSOR_INVALID) { this->cursorIndex = nextIndex; @@ -2375,6 +2407,12 @@ void EnOssan_DrawStickDirectionPrompts(PlayState* play, EnOssan* this) { s32 drawStickLeftPrompt = this->stickLeftPrompt.isEnabled; s32 drawStickRightPrompt = this->stickRightPrompt.isEnabled; + // Invert which stick prompt is active when only one is active + if (CVarGetInteger("gMirroredWorld", 0) && (drawStickLeftPrompt != drawStickRightPrompt)) { + drawStickLeftPrompt = !drawStickLeftPrompt; + drawStickRightPrompt = !drawStickRightPrompt; + } + OPEN_DISPS(play->state.gfxCtx); if (drawStickLeftPrompt || drawStickRightPrompt) { Gfx_SetupDL_39Overlay(play->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index bca9561ed..f8b3f44d3 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -8170,7 +8170,7 @@ void func_80843188(Player* this, PlayState* play) { if (this->unk_850 != 0) { sp54 = sControlInput->rel.stick_y * 100; - sp50 = sControlInput->rel.stick_x * -120; + sp50 = sControlInput->rel.stick_x * (CVarGetInteger("gMirroredWorld", 0) ? 120 : -120); sp4E = this->actor.shape.rot.y - Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); sp40 = Math_CosS(sp4E); @@ -11347,12 +11347,13 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { s32 temp1; s16 temp2; s16 temp3; + bool gInvertAimingXAxis = (CVarGetInteger("gInvertAimingXAxis", 0) && !CVarGetInteger("gMirroredWorld", 0)) || (!CVarGetInteger("gInvertAimingXAxis", 0) && CVarGetInteger("gMirroredWorld", 0)); if (!func_8002DD78(this) && !func_808334B4(this) && (arg2 == 0) && !CVarGetInteger("gDisableAutoCenterViewFirstPerson", 0)) { temp2 = sControlInput->rel.stick_y * 240.0f * (CVarGetInteger("gInvertAimingYAxis", 1) ? 1 : -1); // Sensitivity not applied here because higher than default sensitivies will allow the camera to escape the autocentering, and glitch out massively Math_SmoothStepToS(&this->actor.focus.rot.x, temp2, 14, 4000, 30); - temp2 = sControlInput->rel.stick_x * -16.0f * (CVarGetInteger("gInvertAimingXAxis", 0) ? -1 : 1) * (CVarGetFloat("gFirstPersonCameraSensitivityX", 1.0f)); + temp2 = sControlInput->rel.stick_x * -16.0f * (gInvertAimingXAxis ? -1 : 1) * (CVarGetFloat("gFirstPersonCameraSensitivityX", 1.0f)); temp2 = CLAMP(temp2, -3000, 3000); this->actor.focus.rot.y += temp2; } else { @@ -11377,18 +11378,18 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { temp2 = this->actor.focus.rot.y - this->actor.shape.rot.y; temp3 = ((sControlInput->rel.stick_x >= 0) ? 1 : -1) * (s32)((1.0f - Math_CosS(sControlInput->rel.stick_x * 200)) * -1500.0f * - (CVarGetInteger("gInvertAimingXAxis", 0) ? -1 : 1)) * (CVarGetFloat("gFirstPersonCameraSensitivityX", 1.0f)); + (gInvertAimingXAxis ? -1 : 1)) * (CVarGetFloat("gFirstPersonCameraSensitivityX", 1.0f)); temp2 += temp3; this->actor.focus.rot.y = CLAMP(temp2, -temp1, temp1) + this->actor.shape.rot.y; if (fabsf(sControlInput->cur.gyro_y) > 0.01f) { - this->actor.focus.rot.y += (sControlInput->cur.gyro_y) * 750.0f; + this->actor.focus.rot.y += (sControlInput->cur.gyro_y) * 750.0f * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1); } if (fabsf(sControlInput->cur.right_stick_x) > 15.0f && CVarGetInteger("gRightStickAiming", 0) != 0) { this->actor.focus.rot.y += - (sControlInput->cur.right_stick_x) * 10.0f * (CVarGetInteger("gInvertAimingXAxis", 0) ? 1 : -1) * (CVarGetFloat("gFirstPersonCameraSensitivityX", 1.0f)); + (sControlInput->cur.right_stick_x) * 10.0f * (gInvertAimingXAxis ? 1 : -1) * (CVarGetFloat("gFirstPersonCameraSensitivityX", 1.0f)); } } @@ -11939,7 +11940,7 @@ void func_8084BF1C(Player* this, PlayState* play) { if ((this->unk_84F != 0) && (sp80 != 0)) { anim2 = this->ageProperties->unk_BC[this->unk_850]; - if (sp80 > 0) { + if (CVarGetInteger("gMirroredWorld", 0) ? (sp80 < 0) : (sp80 > 0)) { this->skelAnime.prevTransl = this->ageProperties->unk_7A[this->unk_850]; func_80832264(play, this, anim2); } else { @@ -13619,9 +13620,9 @@ s32 func_8084FCAC(Player* this, PlayState* play) { if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_DDOWN)) { angle = temp + 0x8000; } else if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_DLEFT)) { - angle = temp + 0x4000; + angle = temp + (0x4000 * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1)); } else if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_DRIGHT)) { - angle = temp - 0x4000; + angle = temp - (0x4000 * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1)); } this->actor.world.pos.x += speed * Math_SinS(angle); diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.c b/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.c index 711769bd2..fe92d8a3f 100644 --- a/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.c +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.c @@ -62,6 +62,7 @@ void EffectSsExtra_Draw(PlayState* play, u32 index, EffectSs* this) { s32 pad; f32 scale = this->rScale / 100.0f; void* object = play->objectCtx.status[this->rObjBankIdx].segment; + u8 mirroredWorld = CVarGetInteger("gMirroredWorld", 0); OPEN_DISPS(play->state.gfxCtx); @@ -71,11 +72,23 @@ void EffectSsExtra_Draw(PlayState* play, u32 index, EffectSs* this) { Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); Gfx_SetupDL_25Xlu(play->state.gfxCtx); Matrix_ReplaceRotation(&play->billboardMtxF); + + // Flip the score texture matrix and un-invert the culling to display it normally + if (mirroredWorld) { + gSPClearExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + Matrix_Scale(-1, 1, 1, MTXMODE_APPLY); + } + gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sTextures[this->rScoreIdx])); gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_yabusame_point_DL_000DC0)); + // Set back inverted culling + if (mirroredWorld) { + gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + } + CLOSE_DISPS(play->state.gfxCtx); } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c index 4d1c65ad4..a50bcc76f 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c @@ -338,22 +338,40 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalette); gDPSetTextureLUT(POLY_KAL_DISP++, G_TT_RGBA16); + u8 mirroredWorld = CVarGetInteger("gMirroredWorld", 0); + u8 mirrorMode = mirroredWorld ? G_TX_MIRROR : G_TX_NOMIRROR; + // Offset the U value of each vertex to be in the mirror boundary for the map textures + if (mirroredWorld) { + for (size_t i = 0; i < 8; i++) { + pauseCtx->mapPageVtx[60 + i].v.tc[0] += 48 << 5; + } + } + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[60], 8, 0); // The dungeon map textures are recreated each frame, so always invalidate them gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[0]); gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[1]); - gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, 48, 85, 0, G_TX_WRAP | G_TX_NOMIRROR, + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, 48, 85, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); - gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + // Swap vertices to render left half on the right and vice-versa + if (mirroredWorld) { + gSP1Quadrangle(POLY_KAL_DISP++, 4, 6, 7, 5, 0); + } else { + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, 48, 85, 0, - G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); - gSP1Quadrangle(POLY_KAL_DISP++, 4, 6, 7, 5, 0); + if (mirroredWorld) { + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } else { + gSP1Quadrangle(POLY_KAL_DISP++, 4, 6, 7, 5, 0); + } gDPPipeSync(POLY_KAL_DISP++); gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_BILERP); @@ -420,6 +438,8 @@ void KaleidoScope_DrawWorldMap(PlayState* play, GraphicsContext* gfxCtx) { s16 stepG; s16 stepB; bool dpad = CVarGetInteger("gDpadPause", 0); + u8 mirroredWorld = CVarGetInteger("gMirroredWorld", 0); + u8 mirrorMode = mirroredWorld ? G_TX_MIRROR : G_TX_NOMIRROR; OPEN_DISPS(gfxCtx); @@ -428,25 +448,27 @@ void KaleidoScope_DrawWorldMap(PlayState* play, GraphicsContext* gfxCtx) { oldCursorPoint = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; if (pauseCtx->cursorSpecialPos == 0) { - if ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + if ((!mirroredWorld && ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)))) || + (mirroredWorld && ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))))) { D_8082A6D4 = 0; do { pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] > 11) { pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 11; - KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_RIGHT); + KaleidoScope_MoveCursorToSpecialPos(play, !mirroredWorld ? PAUSE_CURSOR_PAGE_RIGHT : PAUSE_CURSOR_PAGE_LEFT); break; } } while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0); - } else if ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + } else if ((!mirroredWorld && ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT)))) || + (mirroredWorld && ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))))) { D_8082A6D4 = 0; do { pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] < 0) { pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 0; - KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_LEFT); + KaleidoScope_MoveCursorToSpecialPos(play, !mirroredWorld ? PAUSE_CURSOR_PAGE_LEFT : PAUSE_CURSOR_PAGE_RIGHT); break; } } while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0); @@ -461,11 +483,18 @@ void KaleidoScope_DrawWorldMap(PlayState* play, GraphicsContext* gfxCtx) { pauseCtx->cursorItem[PAUSE_MAP] = gSaveContext.worldMapArea + 0x18; if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { if ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { - pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 0; pauseCtx->cursorSpecialPos = 0; - while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { - pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + if (!mirroredWorld) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 0; + while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + } + } else { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 11; + while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + } } pauseCtx->cursorItem[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; @@ -476,11 +505,18 @@ void KaleidoScope_DrawWorldMap(PlayState* play, GraphicsContext* gfxCtx) { } } else { if ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { - pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 11; pauseCtx->cursorSpecialPos = 0; - while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { - pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + if (!mirroredWorld) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 11; + while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + } + } else { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 0; + while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + } } pauseCtx->cursorItem[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; @@ -501,6 +537,16 @@ void KaleidoScope_DrawWorldMap(PlayState* play, GraphicsContext* gfxCtx) { } } + // Use matrix scaling to flip the entire overworld map for mirror world + if (mirroredWorld) { + // Invert culling to counter act the matrix flip + gSPSetExtraGeometryMode(POLY_KAL_DISP++, G_EX_INVERT_CULLING); + Matrix_Push(); + Matrix_Scale(-1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_KAL_DISP++, MATRIX_NEWMTX(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + Matrix_Pop(); + } + gDPPipeSync(POLY_KAL_DISP++); if (HREG(15) == 0) { @@ -708,6 +754,17 @@ void KaleidoScope_DrawWorldMap(PlayState* play, GraphicsContext* gfxCtx) { KaleidoScope_DrawCursor(play, PAUSE_MAP); } + if (mirroredWorld) { + // Offset U value for current position area name texture into mirror boundary + for (i = 0; i < 4; i++) { + pauseCtx->mapPageVtx[176 + 4 + i].v.tc[0] += 80 << 5; + } + // Offset U value for "current position" texture into mirror boundary + for (i = 0; i < 4; i++) { + pauseCtx->mapPageVtx[176 + 8 + i].v.tc[0] += 64 << 5; + } + } + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[176], 16, 0); if (pauseCtx->tradeQuestLocation != 0xFF) { @@ -739,12 +796,17 @@ void KaleidoScope_DrawWorldMap(PlayState* play, GraphicsContext* gfxCtx) { gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 0, 0, 0, pauseCtx->alpha); gDPLoadTextureBlock_4b(POLY_KAL_DISP++, currentPosTitleTexs[gSaveContext.language], G_IM_FMT_I, 64, 8, 0, - G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); gSP1Quadrangle(POLY_KAL_DISP++, 8, 10, 11, 9, 0); gDPPipeSync(POLY_KAL_DISP++); + if (mirroredWorld) { + // Revert the inversion + gSPClearExtraGeometryMode(POLY_KAL_DISP++, G_EX_INVERT_CULLING); + } + CLOSE_DISPS(gfxCtx); } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 6ec602abb..e8478e00a 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -814,7 +814,8 @@ Gfx* KaleidoScope_QuadTextureIA4(Gfx* gfx, void* texture, s16 width, s16 height, } Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point) { - gDPLoadTextureBlock(gfx++, texture, G_IM_FMT_IA, G_IM_SIZ_8b, width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, + u8 mirrorMode = CVarGetInteger("gMirroredWorld", 0) ? G_TX_MIRROR : G_TX_NOMIRROR; + gDPLoadTextureBlock(gfx++, texture, G_IM_FMT_IA, G_IM_SIZ_8b, width, height, 0, mirrorMode | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); gSP1Quadrangle(gfx++, point, point + 2, point + 3, point + 1, 0); @@ -3042,6 +3043,14 @@ void KaleidoScope_Draw(PlayState* play) { func_800AAA50(&play->view, 15); + // Flip the OPA and XLU projections again as the set view call above reset the original flips from z_play + if (CVarGetInteger("gMirroredWorld", 0)) { + gSPMatrix(POLY_OPA_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_OPA_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + } + CLOSE_DISPS(play->state.gfxCtx); } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark.c index 5a464f4dd..cc34dcbb6 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark.c @@ -131,8 +131,11 @@ void PauseMapMark_DrawForDungeon(PlayState* play) { markInfo->textureWidth, markInfo->textureHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + // Compute the offset to mirror icons over the map center (48) as an axis line + s16 mirrorOffset = CVarGetInteger("gMirroredWorld", 0) ? mirrorOffset = (48 - markPoint->x) * 2 + 1 : 0; + Matrix_Push(); - Matrix_Translate(GREG(92) + markPoint->x, GREG(93) + markPoint->y, 0.0f, MTXMODE_APPLY); + Matrix_Translate(GREG(92) + markPoint->x + mirrorOffset, GREG(93) + markPoint->y, 0.0f, MTXMODE_APPLY); Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); gSPMatrix(POLY_KAL_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);