diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index 1a9ca3822..c2639b214 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -626,6 +626,14 @@ void DrawEnhancementsMenu() { if (UIWidgets::CVarCheckbox("Time Moves When You Move", "gModes.TimeMovesWhenYouMove")) { RegisterTimeMovesWhenYouMove(); } + if (UIWidgets::CVarCheckbox("Mirrored World", "gModes.MirroredWorld.Mode")) { + if (CVarGetInteger("gModes.MirroredWorld.Mode", 0)) { + CVarSetInteger("gModes.MirroredWorld.State", 1); + } else { + CVarClear("gModes.MirroredWorld.State"); + } + } + ImGui::EndMenu(); } diff --git a/mm/2s2h/BenGui/SearchableMenuItems.h b/mm/2s2h/BenGui/SearchableMenuItems.h index a8d8c846f..a14843174 100644 --- a/mm/2s2h/BenGui/SearchableMenuItems.h +++ b/mm/2s2h/BenGui/SearchableMenuItems.h @@ -878,7 +878,19 @@ void AddEnhancements() { "Time only moves when Link is not standing still.", WIDGET_CVAR_CHECKBOX, {}, - ([](widgetInfo& info) { RegisterTimeMovesWhenYouMove(); }) } }, + ([](widgetInfo& info) { RegisterTimeMovesWhenYouMove(); }) }, + { "Mirrored World", + "gModes.MirroredWorld.Mode", + "Mirrors the world horizontally.", + WIDGET_CVAR_CHECKBOX, + {}, + ([](widgetInfo& info) { + if (CVarGetInteger("gModes.MirroredWorld.Mode", 0)) { + CVarSetInteger("gModes.MirroredWorld.State", 1); + } else { + CVarClear("gModes.MirroredWorld.State"); + } + }) } }, { { .widgetName = "Saving", .widgetType = WIDGET_SEPARATOR_TEXT }, { "Persistent Owl Saves", "gEnhancements.Saving.PersistentOwlSaves", "Continuing a save will not remove the owl save. Playing Song of " diff --git a/mm/2s2h/Enhancements/Camera/DebugCam.cpp b/mm/2s2h/Enhancements/Camera/DebugCam.cpp index 38b9bf6db..eafcaf5ba 100644 --- a/mm/2s2h/Enhancements/Camera/DebugCam.cpp +++ b/mm/2s2h/Enhancements/Camera/DebugCam.cpp @@ -172,7 +172,7 @@ void Camera_DebugCam(Camera* camera) { if (CVarGetInteger("gEnhancements.Camera.DebugCam.6DOF", 0)) { camera->roll += (CHECK_BTN_ANY(sCamPlayState->state.input[controllerPort].cur.button, BTN_DLEFT) - CHECK_BTN_ANY(sCamPlayState->state.input[controllerPort].cur.button, BTN_DRIGHT)) * - 1200.0f * camSpeed; + 1200.0f * camSpeed * GameInteractor_InvertControl(GI_INVERT_DEBUG_DPAD_X); } else { camera->roll = Camera_ScaledStepToCeilS(0, camera->roll, 0.1f, 5); } diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp index aea90a0a9..fbaf19930 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp @@ -268,16 +268,24 @@ int GameInteractor_InvertControl(GIInvertType type) { break; } - /* // Invert all X axis inputs if the Mirrored World mode is enabled if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { switch (type) { case GI_INVERT_CAMERA_RIGHT_STICK_X: + case GI_INVERT_MOVEMENT_X: + case GI_INVERT_FIRST_PERSON_AIM_X: + case GI_INVERT_SHIELD_X: + case GI_INVERT_SHOP_X: + case GI_INVERT_HORSE_X: + case GI_INVERT_ZORA_SWIM_X: + case GI_INVERT_DEBUG_DPAD_X: + case GI_INVERT_TELESCOPE_X: result *= -1; break; + default: + break; } } - */ /* if (CrowdControl::State::InvertedInputs) { diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index e884782b0..c17be750c 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -67,6 +67,14 @@ typedef enum { typedef enum { GI_INVERT_CAMERA_RIGHT_STICK_X, GI_INVERT_CAMERA_RIGHT_STICK_Y, + GI_INVERT_MOVEMENT_X, + GI_INVERT_FIRST_PERSON_AIM_X, + GI_INVERT_SHIELD_X, + GI_INVERT_SHOP_X, + GI_INVERT_HORSE_X, + GI_INVERT_ZORA_SWIM_X, + GI_INVERT_DEBUG_DPAD_X, + GI_INVERT_TELESCOPE_X, } GIInvertType; typedef enum { diff --git a/mm/2s2h/Enhancements/NameTag/NameTag.cpp b/mm/2s2h/Enhancements/NameTag/NameTag.cpp index f20016c0a..19432377b 100644 --- a/mm/2s2h/Enhancements/NameTag/NameTag.cpp +++ b/mm/2s2h/Enhancements/NameTag/NameTag.cpp @@ -31,6 +31,7 @@ typedef struct { static std::vector nameTags; static std::vector nameTagDl; +static bool sMirrorWorldActive = false; void NameTag_RegisterHooks(); @@ -94,8 +95,7 @@ void DrawNameTag(PlayState* play, const NameTag* nameTag) { // Set position, billboard effect, scale (with mirror mode), then center nametag Matrix_Translate(nameTag->actor->world.pos.x, posY, nameTag->actor->world.pos.z, MTXMODE_NEW); Matrix_ReplaceRotation(&play->billboardMtxF); - // BENTODO: Apply mirror world flipping to X scale - Matrix_Scale(scale, -scale, 1.0f, MTXMODE_APPLY); + Matrix_Scale(scale * (sMirrorWorldActive ? -1.0f : 1.0f), -scale, 1.0f, MTXMODE_APPLY); Matrix_Translate(-(float)nameTag->width / 2, -nameTag->height, 0, MTXMODE_APPLY); Matrix_ToMtx(nameTag->mtx); @@ -206,6 +206,8 @@ void UpdateNameTags() { return aDistToCamera > bDistToCamera; }); + + sMirrorWorldActive = CVarGetInteger("gModes.MirroredWorld.State", 0); } extern "C" void NameTag_RegisterForActorWithOptions(Actor* actor, const char* text, NameTagOptions options) { diff --git a/mm/2s2h/Enhancements/Songs/PauseOwlWarp.cpp b/mm/2s2h/Enhancements/Songs/PauseOwlWarp.cpp index a86c1b943..b3f591680 100644 --- a/mm/2s2h/Enhancements/Songs/PauseOwlWarp.cpp +++ b/mm/2s2h/Enhancements/Songs/PauseOwlWarp.cpp @@ -66,71 +66,101 @@ void HandleConfirmingState(PauseContext* pauseCtx, Input* input) { } } +// This is a variation of KaleidoScope_UpdateWorldMapCursor that deals with the warp points instead of region points +// and supports mirror mode void UpdateCursorForOwlWarpPoints(PauseContext* pauseCtx) { - if (pauseCtx->cursorSpecialPos == 0) { - if (pauseCtx->stickAdjX > 30) { + if ((pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && + (pauseCtx->pageIndex == PAUSE_MAP)) { + InterfaceContext* interfaceCtx = &gPlayState->interfaceCtx; + s16 oldCursorPoint = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + bool mirrorWorldActive = CVarGetInteger("gModes.MirroredWorld.State", 0); + bool goingLeft = pauseCtx->stickAdjX < -30; + bool goingRight = pauseCtx->stickAdjX > 30; + + // Handle moving off page buttons + if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT && goingRight) { + pauseCtx->cursorSpecialPos = 0; + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = mirrorWorldActive ? OWL_WARP_STONE_TOWER + 1 : REGION_NONE; + Audio_PlaySfx(NA_SE_SY_CURSOR); + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT && goingLeft) { + pauseCtx->cursorSpecialPos = 0; + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = mirrorWorldActive ? REGION_NONE : OWL_WARP_STONE_TOWER + 1; + Audio_PlaySfx(NA_SE_SY_CURSOR); + } + + // Handle updating cursor color and A button action + if (pauseCtx->cursorSpecialPos == 0) { + pauseCtx->cursorColorSet = PAUSE_CURSOR_COLOR_SET_BLUE; + + if (gSaveContext.buttonStatus[EQUIP_SLOT_A] == BTN_DISABLED) { + gSaveContext.buttonStatus[EQUIP_SLOT_A] = BTN_ENABLED; + gSaveContext.hudVisibility = HUD_VISIBILITY_IDLE; + Interface_SetHudVisibility(HUD_VISIBILITY_ALL); + } + if (!sIsConfirming && interfaceCtx->aButtonHorseDoAction != DO_ACTION_WARP) { + Interface_SetAButtonDoAction(gPlayState, DO_ACTION_WARP); + } + } else { + pauseCtx->cursorColorSet = PAUSE_CURSOR_COLOR_SET_WHITE; + + if (gSaveContext.buttonStatus[EQUIP_SLOT_A] != BTN_DISABLED) { + gSaveContext.buttonStatus[EQUIP_SLOT_A] = BTN_DISABLED; + gSaveContext.hudVisibility = HUD_VISIBILITY_IDLE; + Interface_SetHudVisibility(HUD_VISIBILITY_ALL); + } + if (interfaceCtx->aButtonHorseDoAction != DO_ACTION_INFO) { + Interface_SetAButtonDoAction(gPlayState, DO_ACTION_INFO); + } + } + + // Handle mirror mode flip and starting cursor movement + if (pauseCtx->cursorSpecialPos == 0 && (goingLeft || goingRight)) { pauseCtx->cursorShrinkRate = 4.0f; - sStickAdjTimer = 0; + + if (mirrorWorldActive) { + goingLeft = !goingLeft; + goingRight = !goingRight; + } + } + + // Actually move the cursor + if (goingRight) { do { pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] > OWL_WARP_STONE_TOWER) { - KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_RIGHT); - return; + KaleidoScope_MoveCursorToSpecialPos(gPlayState, mirrorWorldActive ? PAUSE_CURSOR_PAGE_LEFT + : PAUSE_CURSOR_PAGE_RIGHT); + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + break; } } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); - if (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { - pauseCtx->cursorItem[PAUSE_MAP] = - sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; - pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; - Audio_PlaySfx(NA_SE_SY_CURSOR); - } - } else if (pauseCtx->stickAdjX < -30) { - pauseCtx->cursorShrinkRate = 4.0f; - sStickAdjTimer = 0; + } else if (goingLeft) { do { pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; - if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] < OWL_WARP_GREAT_BAY_COAST) { - KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_LEFT); - return; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] <= REGION_NONE) { + KaleidoScope_MoveCursorToSpecialPos(gPlayState, mirrorWorldActive ? PAUSE_CURSOR_PAGE_RIGHT + : PAUSE_CURSOR_PAGE_LEFT); + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + break; } } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); - if (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { - pauseCtx->cursorItem[PAUSE_MAP] = - sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; - pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; - Audio_PlaySfx(NA_SE_SY_CURSOR); - } - } else { - sStickAdjTimer++; } - } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT && pauseCtx->stickAdjX > 30) { - KaleidoScope_MoveCursorFromSpecialPos(gPlayState); - for (int i = OWL_WARP_GREAT_BAY_COAST; i <= OWL_WARP_STONE_TOWER; i++) { - if (pauseCtx->worldMapPoints[i]) { - pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = i; - pauseCtx->cursorItem[PAUSE_MAP] = - sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; - pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; - KaleidoScope_UpdateWorldMapCursor(gPlayState); - KaleidoScope_UpdateNamePanel(gPlayState); - return; - } + + // Updates name panel and cursor slot + if (pauseCtx->cursorSpecialPos == 0) { + // Offset from `ITEM_MAP_POINT_GREAT_BAY` is to get the correct ordering in `map_name_static` + pauseCtx->cursorItem[PAUSE_MAP] = + sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; + // Used as cursor vtxIndex + pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; } - KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_RIGHT); - } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT && pauseCtx->stickAdjX < -30) { - KaleidoScope_MoveCursorFromSpecialPos(gPlayState); - for (int i = OWL_WARP_STONE_TOWER; i >= OWL_WARP_GREAT_BAY_COAST; i--) { - if (pauseCtx->worldMapPoints[i]) { - pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = i; - pauseCtx->cursorItem[PAUSE_MAP] = - sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; - pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; - KaleidoScope_UpdateWorldMapCursor(gPlayState); - KaleidoScope_UpdateNamePanel(gPlayState); - return; - } + + if (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + } + if (oldCursorPoint != pauseCtx->cursorPoint[PAUSE_WORLD_MAP]) { + Audio_PlaySfx(NA_SE_SY_CURSOR); } - KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_LEFT); } } @@ -219,4 +249,4 @@ void RegisterPauseOwlWarp() { HandlePauseOwlWarp(pauseCtx); } }); -} \ No newline at end of file +} diff --git a/mm/include/functions.h b/mm/include/functions.h index 2388f8ca4..c4d91709f 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1330,6 +1330,7 @@ void PadMgr_ThreadEntry(); void Heaps_Alloc(void); void KaleidoScope_UpdateOwlWarpNamePanel(PlayState* play); void KaleidoScope_UpdateNamePanel(PlayState* play); +void SkinMatrix_Clear(MtxF* mf); // #endregion // #region 2S2H [Port] New methods added for porting void AudioSeq_SetPortVolumeScale(u8 seqPlayerIndex, f32 volume); diff --git a/mm/include/z64view.h b/mm/include/z64view.h index 19bb4492c..a1ff7cd5c 100644 --- a/mm/include/z64view.h +++ b/mm/include/z64view.h @@ -41,6 +41,7 @@ typedef struct View { /* 0x15C */ u16 perspNorm; // used to normalize the projection matrix /* 0x160 */ u32 flags; // bit 3: Render to an orthographic perspective /* 0x164 */ s32 unk164; + /* */ Mtx* shipMirrorProjectionPtr; } View; // size = 0x168 #define VIEW_VIEWING (1 << 0) diff --git a/mm/src/audio/lib/synthesis.c b/mm/src/audio/lib/synthesis.c index 8cd0805be..951d52f21 100644 --- a/mm/src/audio/lib/synthesis.c +++ b/mm/src/audio/lib/synthesis.c @@ -867,7 +867,11 @@ Acmd* AudioSynth_ProcessSamples(s16* aiBuf, s32 numSamplesPerUpdate, Acmd* cmd, } size = numSamplesPerUpdate * SAMPLE_SIZE; - aInterleave(cmd++, DMEM_TEMP, DMEM_LEFT_CH, DMEM_RIGHT_CH, size); + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + aInterleave(cmd++, DMEM_TEMP, DMEM_RIGHT_CH, DMEM_LEFT_CH, size); + } else { + aInterleave(cmd++, DMEM_TEMP, DMEM_LEFT_CH, DMEM_RIGHT_CH, size); + } if (gAudioCustomSynthFunction != NULL) { cmd = gAudioCustomSynthFunction(cmd, 2 * size, updateIndex); diff --git a/mm/src/code/z_actor.c b/mm/src/code/z_actor.c index 65f9c9bac..ac9add966 100644 --- a/mm/src/code/z_actor.c +++ b/mm/src/code/z_actor.c @@ -1402,6 +1402,10 @@ void func_800B6F20(PlayState* play, Input* input, f32 magnitude, s16 baseYaw) { s16 relativeYaw = baseYaw - Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); input->cur.stick_x = -Math_SinS(relativeYaw) * magnitude; + // 2S2H [Enhancement] Allow inverting the X axis with GI, primarily for mirror mode, + // otherwise link moves in the opposite direction and likely get soft locked, and + // kafei turns the wrong direction as he paths. + input->cur.stick_x *= GameInteractor_InvertControl(GI_INVERT_MOVEMENT_X); input->rel.stick_x = input->cur.stick_x; input->cur.stick_y = Math_CosS(relativeYaw) * magnitude; input->rel.stick_y = input->cur.stick_y; diff --git a/mm/src/code/z_kankyo.c b/mm/src/code/z_kankyo.c index efeb6084f..f8c6a02a0 100644 --- a/mm/src/code/z_kankyo.c +++ b/mm/src/code/z_kankyo.c @@ -3326,6 +3326,9 @@ void Environment_DrawSkyboxStarsImpl(PlayState* play, Gfx** gfxP) { if ((scale >= 1.0f) && (imgX > -adjustedXBounds) && (imgX < adjustedXBounds) && (imgY > -1.0f) && (imgY < 1.0f)) { + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + imgX *= -1.0f; + } // #endregion imgX = (imgX * (SCREEN_WIDTH / 2)) + (SCREEN_WIDTH / 2); imgY = (imgY * -(SCREEN_HEIGHT / 2)) + (SCREEN_HEIGHT / 2); diff --git a/mm/src/code/z_lib.c b/mm/src/code/z_lib.c index b2ff2c086..5ca4d4f7a 100644 --- a/mm/src/code/z_lib.c +++ b/mm/src/code/z_lib.c @@ -1,5 +1,7 @@ #include "global.h" #include +#include "BenPort.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" f32 Math_CosS(s16 angle) { return coss(angle) * SHT_MINV; @@ -223,12 +225,14 @@ void Lib_GetControlStickData(f32* outMagnitude, s16* outAngle, Input* input) { f32 y = input->rel.stick_y; f32 magnitude; + x *= GameInteractor_InvertControl(GI_INVERT_MOVEMENT_X); magnitude = sqrtf(SQ(x) + SQ(y)); *outMagnitude = (60.0f < magnitude) ? 60.0f : magnitude; if (magnitude > 0.0f) { x = input->cur.stick_x; y = input->cur.stick_y; + x *= GameInteractor_InvertControl(GI_INVERT_MOVEMENT_X); *outAngle = Math_Atan2S_XY(y, -x); } else { *outAngle = 0; diff --git a/mm/src/code/z_map_disp.c b/mm/src/code/z_map_disp.c index e7a0197ae..6f2ce25fb 100644 --- a/mm/src/code/z_map_disp.c +++ b/mm/src/code/z_map_disp.c @@ -156,6 +156,31 @@ static const char* sMapGrandTextures[] = { map_grand_static_Blob_034BB0, map_grand_static_Blob_034F50, }; +static bool sMirrorWorldActive = false; +static u16 sOriginalMapDataRoomFlags[32] = { 0 }; + +s32 Ship_MapModifyPosMirrorMode(s32 pos, s32 offset) { + if (sMirrorWorldActive) { + pos -= offset; + pos *= -1; + pos += offset; + } + return pos; +} + +void Ship_MapDispUpdateMirrorMode() { + sMirrorWorldActive = CVarGetInteger("gModes.MirroredWorld.State", 0); + + for (s32 i = 0; i < sSceneNumRooms; i++) { + sMapDataRooms[i].flags = sOriginalMapDataRoomFlags[i]; + + if (sMirrorWorldActive) { + // Toggle the bit in case the scene already has this flag set, this way the map is still "flipped" visually + sMapDataRooms[i].flags ^= MAP_DATA_ROOM_FLIP_X; + } + } +} + // BENTODO, can we just set dest insetead of doing a memcpy? void MapDisp_GetMapITexture(void* dst, s32 mapCompactId) { s32 mapSize = MapDisp_GetSizeOfMapITex(mapCompactId); @@ -414,6 +439,9 @@ void MapDisp_Minimap_DrawActorIcon(PlayState* play, Actor* actor) { sMapDisp.minimapCurY - sMapDisp.minimapBaseY + texOffsetY; } + posX = Ship_MapModifyPosMirrorMode(posX, sMapDisp.minimapBaseX + sMapDisp.minimapCurX - sMapDisp.minimapBaseX + + texOffsetX); + // 2S2H [Cosmetic] Widescreen support if ((posX > OTRGetRectDimensionFromLeftEdge(0)) && (posX < 0x3FF) && (posY > 0) && (posY < 0x3FF)) { // #region 2S2H [Cosmetic] Hud Editor override actor values and use them below @@ -449,6 +477,9 @@ void MapDisp_Minimap_DrawActorIcon(PlayState* play, Actor* actor) { if (MapDisp_IsDataRotated(play)) { compassRot += 0x7FFF; } + if (sMirrorWorldActive) { + compassRot *= -1; + } Matrix_RotateYF(compassRot / 10.0f, MTXMODE_APPLY); Matrix_Scale(0.4f, 0.4f, 0.4f, MTXMODE_APPLY); // #region 2S2H [Cosmetic] Hud Editor yellow compass matrix scale on x and z @@ -586,6 +617,9 @@ void MapDisp_Minimap_DrawDoorActor(PlayState* play, Actor* actor) { texOffsetY; } + posX = Ship_MapModifyPosMirrorMode(posX, sMapDisp.minimapBaseX + sMapDisp.minimapCurX - sMapDisp.minimapBaseX + + texOffsetX); + // 2S2H [Cosmetic] Widescreen support if ((posX > OTRGetRectDimensionFromLeftEdge(0)) && (posX < 0x3FF) && (posY > 0) && (posY < 0x3FF)) { OPEN_DISPS(play->state.gfxCtx); @@ -893,6 +927,8 @@ void MapDisp_InitMapData(PlayState* play, void* segmentAddress) { for (i = 0; i < sSceneNumRooms; i++) { sMapDataRooms[i] = *mapDataRooms++; + // 2S2H [Enhancement] Track original flags to that we can make modifications mid-scene + sOriginalMapDataRoomFlags[i] = sMapDataRooms[i].flags; } sMapDataScene.rooms = sMapDataRooms; @@ -996,6 +1032,7 @@ void MapDisp_Update(PlayState* play) { s16 targetY; if ((sMapDisp.mapDataScene != NULL) && (sSceneNumRooms != 0)) { + Ship_MapDispUpdateMirrorMode(); // #region 2S2H [Cosmetic] Hud Editor minimap base position // This value is used to determine the relative positioning for all the other elements MapDataRoom* mapDataRoom = &sMapDisp.mapDataScene->rooms[sMapDisp.curRoom]; @@ -1161,6 +1198,8 @@ void MapDisp_SwapRooms(s16 nextRoom) { (f32)prevOffsetY); } + sMapDisp.minimapPrevX = Ship_MapModifyPosMirrorMode(sMapDisp.minimapPrevX, offsetX - prevOffsetX); + sMapDisp.minimapCurX = minimapBaseX - sMapDisp.minimapPrevX; sMapDisp.minimapCurY = minimapBaseY - sMapDisp.minimapPrevY; } @@ -1243,6 +1282,9 @@ void MapDisp_Minimap_DrawRedCompassIcon(PlayState* play, s32 x, s32 z, s32 rot) (sMapDisp.minimapCurY - sMapDisp.minimapBaseY) + texOffsetY; } + posX = Ship_MapModifyPosMirrorMode(posX, sMapDisp.minimapBaseX + sMapDisp.minimapCurX - sMapDisp.minimapBaseX + + texOffsetX); + // 2S2H [Cosmetic] Widescreen support if ((posX > OTRGetRectDimensionFromLeftEdge(0)) && (posX < 0x3FF) && (posY > 0) && (posY < 0x3FF)) { // 2S2H [Cosmetic] Hud Editor red compass position @@ -1264,6 +1306,9 @@ void MapDisp_Minimap_DrawRedCompassIcon(PlayState* play, s32 x, s32 z, s32 rot) if (MapDisp_IsDataRotated(play)) { rot += 0x7FFF; } + if (sMirrorWorldActive) { + rot *= -1; + } Matrix_RotateYF(rot / 10.0f, MTXMODE_APPLY); Matrix_Scale(0.4f, 0.4f, 0.4f, MTXMODE_APPLY); // #region 2S2H [Cosmetic] Hud Editor red compass scale the matrix in x and z @@ -1642,7 +1687,10 @@ void MapDisp_DrawRooms(PlayState* play, s32 viewX, s32 viewY, s32 viewWidth, s32 if (mapDataRoom->flags & MAP_DATA_ROOM_FLIP_X) { offsetX = ((texWidth / 2) - offsetX) + (texWidth / 2); - s = (texWidth - 1) << 5; + // 2S2H [Port] The width originally was subtracted by 1, but this caused the flipped texture to overflow on + // the edge. There are no vanilla scenes that leverage this, so its possibly an authentic bug. + // Removing it will allow the texture to display correctly for things like Mirror Mode. + s = (texWidth - 0) << 5; dsdx = 0xFC00; } else { s = 0; @@ -1665,6 +1713,8 @@ void MapDisp_DrawRooms(PlayState* play, s32 viewX, s32 viewY, s32 viewWidth, s32 texPosY = ((mapDataRoom->centerZ - (f32)sMapDisp.sceneMidZ) * scaleFrac - offsetY) + ((viewHeight / two) + viewY); + texPosX = Ship_MapModifyPosMirrorMode(texPosX, -offsetX + ((viewWidth / two) + viewX)); + if (i == play->roomCtx.curRoom.num) { if (Map_IsInBossScene(play)) { gDPLoadTextureBlock_4b(POLY_OPA_DISP++, roomTexture, G_IM_FMT_CI, texWidth, texHeight, 1, @@ -1781,6 +1831,8 @@ void MapDisp_DrawChests(PlayState* play, s32 viewX, s32 viewY, s32 viewWidth, s3 texPosY = (s32)((((mapDataChests[i].z - (f32)sMapDisp.sceneMidZ) * scaleFrac) - offsetZ) + ((viewHeight / 2) + viewY)); + texPosX = Ship_MapModifyPosMirrorMode(texPosX, -offsetX + ((viewWidth / 2) + viewX)); + gSPTextureRectangle(POLY_OPA_DISP++, texPosX << 2, texPosY << 2, (texPosX + 8) << 2, (texPosY + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); } @@ -1833,6 +1885,9 @@ void MapDisp_DrawRoomExits(PlayState* play, s32 viewX, s32 viewY, s32 viewWidth, ((viewWidth / 2) + viewX); texPosY = ((f32)sTransitionActors[i].pos.z - sMapDisp.sceneMidZ) * scaleFrac + ((viewHeight / 2) + viewY); + + texPosX = Ship_MapModifyPosMirrorMode(texPosX, ((viewWidth / 2) + viewX)); + gSPTextureRectangle(POLY_OPA_DISP++, ((texPosX - 1) << 2), ((texPosY - 1) << 2), ((texPosX + 1) << 2), ((texPosY + 1) << 2), G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); @@ -1894,6 +1949,9 @@ void MapDisp_DrawBossIcon(PlayState* play, s32 viewX, s32 viewY, s32 viewWidth, ((viewWidth / 2) + viewX); texPosY = ((((f32)sTransitionActors[i].pos.z - sMapDisp.sceneMidZ) * scaleFrac) - offsetZ) + ((viewHeight / 2) + viewY); + + texPosX = Ship_MapModifyPosMirrorMode(texPosX, -offsetX + ((viewWidth / 2) + viewX)); + gSPTextureRectangle(POLY_OPA_DISP++, texPosX << 2, texPosY << 2, (texPosX + 8) << 2, (texPosY + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); } diff --git a/mm/src/code/z_parameter.c b/mm/src/code/z_parameter.c index bf3faab58..da73d18fb 100644 --- a/mm/src/code/z_parameter.c +++ b/mm/src/code/z_parameter.c @@ -8610,7 +8610,15 @@ void Interface_Draw(PlayState* play) { Map_DrawMinimap(play); if ((R_PAUSE_BG_PRERENDER_STATE != 2) && (R_PAUSE_BG_PRERENDER_STATE != 3)) { + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.shipMirrorProjectionPtr, + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + } Target_Draw(&play->actorCtx.targetCtx, play); + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionPtr, + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + } } Gfx_SetupDL39_Overlay(play->state.gfxCtx); diff --git a/mm/src/code/z_play.c b/mm/src/code/z_play.c index 024c0fe12..80492c22e 100644 --- a/mm/src/code/z_play.c +++ b/mm/src/code/z_play.c @@ -1248,6 +1248,18 @@ void Play_DrawMain(PlayState* this) { View_Apply(&this->view, 0xF); + // Setup mirror mode matrix handling when we are not drawing kaleido + if (R_PAUSE_BG_PRERENDER_STATE <= PAUSE_BG_PRERENDER_SETUP && CVarGetInteger("gModes.MirroredWorld.State", 0)) { + gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + gSPMatrix(POLY_OPA_DISP++, this->view.shipMirrorProjectionPtr, + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, this->view.shipMirrorProjectionPtr, + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_OPA_DISP++, this->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, this->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + } + // The billboard matrix temporarily stores the viewing matrix Matrix_MtxToMtxF(&this->view.viewing, &this->billboardMtxF); Matrix_MtxToMtxF(&this->view.projection, &this->viewProjectionMtxF); @@ -1523,6 +1535,11 @@ void Play_DrawMain(PlayState* this) { Play_PostWorldDraw(this); } } + + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + gSPClearExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + gSPClearExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + } } SkipPostWorldDraw: diff --git a/mm/src/code/z_view.c b/mm/src/code/z_view.c index 8fedc585f..83801471b 100644 --- a/mm/src/code/z_view.c +++ b/mm/src/code/z_view.c @@ -325,6 +325,7 @@ s32 View_ApplyPerspective(View* view) { s32 height; Vp* vp; Mtx* projection; + Mtx* projectionFlipped; Mtx* viewing; GraphicsContext* gfxCtx = view->gfxCtx; @@ -342,13 +343,26 @@ s32 View_ApplyPerspective(View* view) { // Perspective projection projection = GRAPH_ALLOC(gfxCtx, sizeof(Mtx)); + projectionFlipped = GRAPH_ALLOC(gfxCtx, sizeof(Mtx)); view->projectionPtr = projection; + view->shipMirrorProjectionPtr = projectionFlipped; width = view->viewport.rightX - view->viewport.leftX; height = view->viewport.bottomY - view->viewport.topY; aspect = (f32)width / (f32)height; guPerspective(projection, &view->perspNorm, view->fovy, aspect, view->zNear, view->zFar, view->scale); + + if (CVarGetInteger("gModes.MirroredWorld.State", 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; View_StepDistortion(view, projection); @@ -450,6 +464,7 @@ s32 View_ApplyPerspective(View* view) { s32 View_ApplyOrtho(View* view) { Vp* vp; Mtx* projection; + Mtx* projectionFlipped; GraphicsContext* gfxCtx = view->gfxCtx; OPEN_DISPS(gfxCtx); @@ -465,11 +480,23 @@ s32 View_ApplyOrtho(View* view) { gSPViewport(OVERLAY_DISP++, vp); projection = GRAPH_ALLOC(gfxCtx, sizeof(Mtx)); + projectionFlipped = GRAPH_ALLOC(gfxCtx, sizeof(Mtx)); view->projectionPtr = projection; + view->shipMirrorProjectionPtr = projectionFlipped; guOrtho(projection, gScreenWidth * -0.5f, gScreenWidth * 0.5f, gScreenHeight * -0.5f, gScreenHeight * 0.5f, view->zNear, view->zFar, view->scale); + // if (CVarGetInteger("gModes.MirroredWorld.State", 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(POLY_OPA_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); @@ -486,6 +513,7 @@ s32 View_ApplyOrtho(View* view) { s32 View_ApplyOrthoToOverlay(View* view) { Vp* vp; Mtx* projection; + Mtx* projectionFlipped; GraphicsContext* gfxCtx; gfxCtx = view->gfxCtx; @@ -509,10 +537,23 @@ s32 View_ApplyOrthoToOverlay(View* view) { gSPViewport(OVERLAY_DISP++, vp); projection = GRAPH_ALLOC(gfxCtx, sizeof(Mtx)); + projectionFlipped = GRAPH_ALLOC(gfxCtx, sizeof(Mtx)); view->projectionPtr = projection; + view->shipMirrorProjectionPtr = projectionFlipped; guOrtho(projection, gScreenWidth * -0.5f, gScreenWidth * 0.5f, gScreenHeight * -0.5f, gScreenHeight * 0.5f, view->zNear, view->zFar, view->scale); + + if (CVarGetInteger("gModes.MirroredWorld.State", 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/mm/src/overlays/actors/ovl_En_Fsn/z_en_fsn.c b/mm/src/overlays/actors/ovl_En_Fsn/z_en_fsn.c index 5cd3ce2a6..7ea427bd0 100644 --- a/mm/src/overlays/actors/ovl_En_Fsn/z_en_fsn.c +++ b/mm/src/overlays/actors/ovl_En_Fsn/z_en_fsn.c @@ -6,6 +6,7 @@ #include "z_en_fsn.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_10) @@ -452,6 +453,10 @@ void EnFsn_UpdateCursorPos(EnFsn* this, PlayState* play) { this->cursorPos.x = x + xOffset; this->cursorPos.y = y + yOffset; this->cursorPos.z = 1.2f; + + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + this->cursorPos.x = SCREEN_WIDTH - this->cursorPos.x; + } } s32 EnFsn_FacingShopkeeperDialogResult(EnFsn* this, PlayState* play) { @@ -509,6 +514,8 @@ void EnFsn_UpdateJoystickInputState(EnFsn* this, PlayState* play) { s8 stickX = CONTROLLER1(&play->state)->rel.stick_x; s8 stickY = CONTROLLER1(&play->state)->rel.stick_y; + stickX *= GameInteractor_InvertControl(GI_INVERT_SHOP_X); + if (this->stickAccumX == 0) { if ((stickX > 30) || (stickX < -30)) { this->stickAccumX = stickX; @@ -1602,6 +1609,11 @@ void EnFsn_DrawStickDirectionPrompts(EnFsn* this, PlayState* play) { s32 drawStickRightPrompt = this->stickLeftPrompt.isEnabled; s32 drawStickLeftPrompt = this->stickRightPrompt.isEnabled; + if (CVarGetInteger("gModes.MirroredWorld.State", 0) && (drawStickLeftPrompt != drawStickRightPrompt)) { + drawStickLeftPrompt = !drawStickLeftPrompt; + drawStickRightPrompt = !drawStickRightPrompt; + } + OPEN_DISPS(play->state.gfxCtx); if (drawStickRightPrompt || drawStickLeftPrompt) { diff --git a/mm/src/overlays/actors/ovl_En_Gakufu/z_en_gakufu.c b/mm/src/overlays/actors/ovl_En_Gakufu/z_en_gakufu.c index af3069c51..d67e751d5 100644 --- a/mm/src/overlays/actors/ovl_En_Gakufu/z_en_gakufu.c +++ b/mm/src/overlays/actors/ovl_En_Gakufu/z_en_gakufu.c @@ -267,6 +267,11 @@ void EnGakufu_Draw(Actor* thisx, PlayState* play) { gDPPipeSync(POLY_XLU_DISP++); gSPSegment(POLY_XLU_DISP++, 0x02, play->interfaceCtx.parameterSegment); + // Invert the whole music staff, so the notes appear correctly in mirrored world + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + Matrix_Scale(-1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + } + for (i = 0; (i < ARRAY_COUNT(this->buttonIndex)) && (this->buttonIndex[i] != OCARINA_BTN_INVALID); i++) { Matrix_Push(); Matrix_Translate(30 * i - 105, sOcarinaBtnWallYOffsets[this->buttonIndex[i]] * 7.5f, 1.0f, MTXMODE_APPLY); diff --git a/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c b/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c index a0066f376..a6e7fec72 100644 --- a/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c +++ b/mm/src/overlays/actors/ovl_En_Horse/z_en_horse.c @@ -12,6 +12,7 @@ #include "overlays/actors/ovl_Obj_Um/z_obj_um.h" #include "overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.h" #include "objects/object_horse_link_child/object_horse_link_child.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #define FLAGS (ACTOR_FLAG_10) @@ -3691,6 +3692,8 @@ void EnHorse_UpdateStick(EnHorse* this, PlayState* play) { this->lastStick = this->curStick; this->curStick.x = input->rel.stick_x; this->curStick.z = input->rel.stick_y; + + this->curStick.x *= GameInteractor_InvertControl(GI_INVERT_HORSE_X); } void EnHorse_ResolveCollision(EnHorse* this, PlayState* play, CollisionPoly* colPoly) { diff --git a/mm/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c b/mm/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c index dd24fbe43..3e15732da 100644 --- a/mm/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c +++ b/mm/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c @@ -6,6 +6,7 @@ #include "z_en_ossan.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_10) @@ -298,6 +299,10 @@ void EnOssan_UpdateCursorPos(PlayState* play, EnOssan* this) { Actor_GetScreenPos(play, &this->items[this->cursorIndex]->actor, &x, &y); this->cursorPos.x = x; this->cursorPos.y = y; + + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + this->cursorPos.x = SCREEN_WIDTH - this->cursorPos.x; + } } void EnOssan_EndInteraction(PlayState* play, EnOssan* this) { @@ -465,6 +470,8 @@ void EnOssan_UpdateJoystickInputState(PlayState* play, EnOssan* this) { s8 stickX = CONTROLLER1(&play->state)->rel.stick_x; s8 stickY = CONTROLLER1(&play->state)->rel.stick_y; + stickX *= GameInteractor_InvertControl(GI_INVERT_SHOP_X); + this->moveHorizontal = this->moveVertical = false; if (this->stickAccumX == 0) { @@ -1680,6 +1687,11 @@ void EnOssan_DrawStickDirectionPrompts(PlayState* play, EnOssan* this) { s32 drawStickRightPrompt = this->stickLeftPrompt.isEnabled; s32 drawStickLeftPrompt = this->stickRightPrompt.isEnabled; + if (CVarGetInteger("gModes.MirroredWorld.State", 0) && (drawStickLeftPrompt != drawStickRightPrompt)) { + drawStickLeftPrompt = !drawStickLeftPrompt; + drawStickRightPrompt = !drawStickRightPrompt; + } + (void)"../z_en_oB1.c"; OPEN_DISPS(play->state.gfxCtx); diff --git a/mm/src/overlays/actors/ovl_En_Sob1/z_en_sob1.c b/mm/src/overlays/actors/ovl_En_Sob1/z_en_sob1.c index 761d2f468..13c1eb329 100644 --- a/mm/src/overlays/actors/ovl_En_Sob1/z_en_sob1.c +++ b/mm/src/overlays/actors/ovl_En_Sob1/z_en_sob1.c @@ -8,6 +8,7 @@ #include "objects/object_mastergolon/object_mastergolon.h" #include "objects/object_masterzoora/object_masterzoora.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_10) @@ -456,6 +457,10 @@ void EnSob1_UpdateCursorPos(PlayState* play, EnSob1* this) { this->cursorPos.x = x + xOffset; this->cursorPos.y = y + yOffset; this->cursorPos.z = 1.2f; + + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + this->cursorPos.x = SCREEN_WIDTH - this->cursorPos.x; + } } void EnSob1_EndInteraction(PlayState* play, EnSob1* this) { @@ -584,6 +589,8 @@ void EnSob1_UpdateJoystickInputState(PlayState* play, EnSob1* this) { s8 stickX = CONTROLLER1(&play->state)->rel.stick_x; s8 stickY = CONTROLLER1(&play->state)->rel.stick_y; + stickX *= GameInteractor_InvertControl(GI_INVERT_SHOP_X); + if (this->stickAccumX == 0) { if ((stickX > 30) || (stickX < -30)) { this->stickAccumX = stickX; @@ -1579,6 +1586,11 @@ void EnSob1_DrawStickDirectionPrompt(PlayState* play, EnSob1* this) { s32 drawStickRightPrompt = this->stickLeftPrompt.isEnabled; s32 drawStickLeftPrompt = this->stickRightPrompt.isEnabled; + if (CVarGetInteger("gModes.MirroredWorld.State", 0) && (drawStickLeftPrompt != drawStickRightPrompt)) { + drawStickLeftPrompt = !drawStickLeftPrompt; + drawStickRightPrompt = !drawStickRightPrompt; + } + (void)"../z_en_soB1.c"; OPEN_DISPS(play->state.gfxCtx); diff --git a/mm/src/overlays/actors/ovl_En_Trt/z_en_trt.c b/mm/src/overlays/actors/ovl_En_Trt/z_en_trt.c index 08b977732..40eda53bf 100644 --- a/mm/src/overlays/actors/ovl_En_Trt/z_en_trt.c +++ b/mm/src/overlays/actors/ovl_En_Trt/z_en_trt.c @@ -7,6 +7,7 @@ #include "z_en_trt.h" #include "objects/object_trt/object_trt.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) @@ -166,6 +167,10 @@ void EnTrt_UpdateCursorPos(PlayState* play, EnTrt* this) { this->cursorPos.x = x + xOffset; this->cursorPos.y = y + yOffset; this->cursorPos.z = 1.2f; + + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + this->cursorPos.x = SCREEN_WIDTH - this->cursorPos.x; + } } void EnTrt_SetupGetMushroomCutscene(EnTrt* this) { @@ -273,6 +278,8 @@ void EnTrt_UpdateJoystickInputState(PlayState* play, EnTrt* this) { s8 stickX = CONTROLLER1(&play->state)->rel.stick_x; s8 stickY = CONTROLLER1(&play->state)->rel.stick_y; + stickX *= GameInteractor_InvertControl(GI_INVERT_SHOP_X); + if (this->stickAccumX == 0) { if ((stickX > 30) || (stickX < -30)) { this->stickAccumX = stickX; @@ -1657,6 +1664,11 @@ void EnTrt_DrawStickDirectionPrompt(PlayState* play, EnTrt* this) { s32 drawStickRightPrompt = this->stickLeftPrompt.isEnabled; s32 drawStickLeftPrompt = this->stickRightPrompt.isEnabled; + if (CVarGetInteger("gModes.MirroredWorld.State", 0) && (drawStickLeftPrompt != drawStickRightPrompt)) { + drawStickLeftPrompt = !drawStickLeftPrompt; + drawStickRightPrompt = !drawStickRightPrompt; + } + (void)"../z_en_trt.c"; OPEN_DISPS(play->state.gfxCtx); diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index c728cd4fe..11863f312 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -8330,6 +8330,7 @@ void func_8083A98C(Actor* thisx, PlayState* play2) { // Yaw: shape.rot.y is used as a fixed starting position inputX = sPlayerControlInput->rel.stick_x * -4; + inputX *= GameInteractor_InvertControl(GI_INVERT_TELESCOPE_X); // Start from current position: no input -> no change newYaw = thisx->focus.rot.y - thisx->shape.rot.y; // Add input, clamped to prevent turning too fast @@ -12562,9 +12563,9 @@ s32 Player_UpdateNoclip(Player* this, PlayState* play) { if (CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_DDOWN)) { angle = temp + 0x8000; } else if (CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_DLEFT)) { - angle = temp + 0x4000; + angle = temp + 0x4000 * GameInteractor_InvertControl(GI_INVERT_DEBUG_DPAD_X); } else if (CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_DRIGHT)) { - angle = temp - 0x4000; + angle = temp - 0x4000 * GameInteractor_InvertControl(GI_INVERT_DEBUG_DPAD_X); } this->actor.world.pos.x += speed * Math_SinS(angle); @@ -13006,12 +13007,15 @@ void Player_Destroy(Actor* thisx, PlayState* play) { s32 func_80847190(PlayState* play, Player* this, s32 arg2) { s32 pad; s16 var_s0; + s32 stickX = sPlayerControlInput->rel.stick_x; + + stickX *= GameInteractor_InvertControl(GI_INVERT_FIRST_PERSON_AIM_X); if (!func_800B7128(this) && !func_8082EF20(this) && !arg2) { var_s0 = sPlayerControlInput->rel.stick_y * 0xF0; Math_SmoothStepToS(&this->actor.focus.rot.x, var_s0, 0xE, 0xFA0, 0x1E); - var_s0 = sPlayerControlInput->rel.stick_x * -0x10; + var_s0 = stickX * -0x10; var_s0 = CLAMP(var_s0, -0xBB8, 0xBB8); this->actor.focus.rot.y += var_s0; } else { @@ -13028,8 +13032,7 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) { } var_s0 = this->actor.focus.rot.y - this->actor.shape.rot.y; - temp3 = ((sPlayerControlInput->rel.stick_x >= 0) ? 1 : -1) * - (s32)((1.0f - Math_CosS(sPlayerControlInput->rel.stick_x * 0xC8)) * -1500.0f); + temp3 = ((stickX >= 0) ? 1 : -1) * (s32)((1.0f - Math_CosS(stickX * 0xC8)) * -1500.0f); var_s0 += temp3; this->actor.focus.rot.y = CLAMP(var_s0, -0x4AAA, 0x4AAA) + this->actor.shape.rot.y; @@ -14630,6 +14633,7 @@ void Player_Action_18(Player* this, PlayState* play) { s16 var_a2; s16 var_a3; + xStick *= GameInteractor_InvertControl(GI_INVERT_SHIELD_X); var_a1 = (yStick * Math_CosS(temp_a0)) + (Math_SinS(temp_a0) * xStick); temp_ft5 = (xStick * Math_CosS(temp_a0)) - (Math_SinS(temp_a0) * yStick); @@ -16006,6 +16010,8 @@ void Player_Action_50(Player* this, PlayState* play) { PlayerAnimationHeader* anim1; PlayerAnimationHeader* anim2; + xStick *= GameInteractor_InvertControl(GI_INVERT_MOVEMENT_X); + this->fallStartHeight = this->actor.world.pos.y; this->stateFlags2 |= PLAYER_STATE2_40; @@ -16564,11 +16570,14 @@ void func_80850BA8(Player* this) { void func_80850BF8(Player* this, f32 arg1) { f32 temp_fv0; s16 temp_ft0; + s8 stickX = sPlayerControlInput->rel.stick_x; + + stickX *= GameInteractor_InvertControl(GI_INVERT_ZORA_SWIM_X); Math_AsymStepToF(&this->unk_B48, arg1, 1.0f, (fabsf(this->unk_B48) * 0.01f) + 0.4f); - temp_fv0 = Math_CosS(sPlayerControlInput->rel.stick_x * 0x10E); + temp_fv0 = Math_CosS(stickX * 0x10E); - temp_ft0 = (((sPlayerControlInput->rel.stick_x >= 0) ? 1 : -1) * (1.0f - temp_fv0) * -1100.0f); + temp_ft0 = (((stickX >= 0) ? 1 : -1) * (1.0f - temp_fv0) * -1100.0f); temp_ft0 = CLAMP(temp_ft0, -0x1F40, 0x1F40); this->currentYaw += temp_ft0; @@ -16585,6 +16594,9 @@ void Player_Action_56(Player* this, PlayState* play) { s16 sp3E; s16 sp3C; s16 sp3A; + s8 stickX = sPlayerControlInput->rel.stick_x; + + stickX *= GameInteractor_InvertControl(GI_INVERT_ZORA_SWIM_X); this->stateFlags2 |= PLAYER_STATE2_20; @@ -16634,7 +16646,7 @@ void Player_Action_56(Player* this, PlayState* play) { } } - Math_SmoothStepToS(&this->unk_B86[1], sPlayerControlInput->rel.stick_x * 0xC8, 0xA, 0x3E8, 0x64); + Math_SmoothStepToS(&this->unk_B86[1], stickX * 0xC8, 0xA, 0x3E8, 0x64); Math_SmoothStepToS(&this->unk_B8E, this->unk_B86[1], IREG(40) + 1, IREG(41), IREG(42)); } else if (this->unk_B86[0] == 0) { PlayerAnimation_Update(play, &this->skelAnime); @@ -16662,7 +16674,7 @@ void Player_Action_56(Player* this, PlayState* play) { Math_SmoothStepToS(&this->unk_AAA, sp3E, 4, 0xFA0, 0x190); // X - sp42 = sPlayerControlInput->rel.stick_x * 0x64; + sp42 = stickX * 0x64; if (Math_ScaledStepToS(&this->unk_B8A, sp42, 0x384) && (sp42 == 0)) { Math_SmoothStepToS(&this->unk_B86[1], 0, 4, 0x5DC, 0x64); Math_SmoothStepToS(&this->unk_B8E, this->unk_B86[1], IREG(44) + 1, IREG(45), IREG(46)); diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c index 7915bbab6..9ae8cb101 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c @@ -550,6 +550,18 @@ void KaleidoScope_DrawWorldMap(PlayState* play) { OPEN_DISPS(play->state.gfxCtx); + // Use matrix scaling to flip the entire overworld map for mirror world + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + // Invert culling to counter act the matrix flip + gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + Matrix_Push(); + // We need to shift the matrix a little to the left before flipping it so that it aligns in the same spot + Matrix_Translate(-2.0f, 0.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(-1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + Matrix_Pop(); + } + KaleidoScope_SetCursorVtxPos(pauseCtx, pauseCtx->cursorSlot[PAUSE_MAP] * 4, pauseCtx->mapPageVtx); // Draw the world map image @@ -852,14 +864,24 @@ void KaleidoScope_DrawWorldMap(PlayState* play) { gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); - POLY_OPA_DISP = - Gfx_DrawTexRectRGBA16(POLY_OPA_DISP, gQuestIconLinkHumanFaceTex, 16, 16, sWorldMapCursorsRectLeft[n], - sWorldMapCursorsRectTop[n], 16, 16, 1 << 10, 1 << 10); + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + POLY_OPA_DISP = Gfx_DrawTexRectRGBA16(POLY_OPA_DISP, gQuestIconLinkHumanFaceTex, 16, 16, + (SCREEN_WIDTH - sWorldMapCursorsRectLeft[n] - 16 - 1), + sWorldMapCursorsRectTop[n], 16, 16, 1 << 10, 1 << 10); + } else { + POLY_OPA_DISP = Gfx_DrawTexRectRGBA16(POLY_OPA_DISP, gQuestIconLinkHumanFaceTex, 16, 16, + sWorldMapCursorsRectLeft[n], sWorldMapCursorsRectTop[n], 16, 16, + 1 << 10, 1 << 10); + } } } gDPPipeSync(POLY_OPA_DISP++); + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + gSPClearExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + } + CLOSE_DISPS(play->state.gfxCtx); } @@ -876,11 +898,122 @@ u16 sOwlWarpPauseItems[] = { ITEM_MAP_POINT_STONE_TOWER, // OWL_WARP_STONE_TOWER }; +// 2S2H [Enhancement] Same as KaleidoScope_UpdateWorldMapCursor but with behavior and controls inverted to account for +// mirrored world being used +void Ship_UpdateWorldMapCursorMirrorWorld(PlayState* play) { + PauseContext* pauseCtx = &play->pauseCtx; + s16 oldCursorPoint; + + bool goingLeft = pauseCtx->stickAdjX < -30; + bool goingRight = pauseCtx->stickAdjX > 30; + + if ((pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && + (pauseCtx->pageIndex == PAUSE_MAP) && !PauseOwlWarp_IsOwlWarpEnabled()) { + pauseCtx->cursorColorSet = PAUSE_CURSOR_COLOR_SET_WHITE; + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + + if (gSaveContext.buttonStatus[EQUIP_SLOT_A] != BTN_DISABLED) { + gSaveContext.buttonStatus[EQUIP_SLOT_A] = BTN_DISABLED; + gSaveContext.hudVisibility = HUD_VISIBILITY_IDLE; + Interface_SetHudVisibility(HUD_VISIBILITY_ALL); + } + + if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT && goingRight) { + pauseCtx->cursorSpecialPos = 0; + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = REGION_MAX; + Audio_PlaySfx(NA_SE_SY_CURSOR); + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT && goingLeft) { + pauseCtx->cursorSpecialPos = 0; + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = REGION_NONE; + Audio_PlaySfx(NA_SE_SY_CURSOR); + } + + if (pauseCtx->cursorSpecialPos == 0 && (goingLeft || goingRight)) { + pauseCtx->cursorShrinkRate = 4.0f; + goingLeft = !goingLeft; + goingRight = !goingRight; + } + + if (goingRight) { + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] >= REGION_MAX) { + KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_LEFT); + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + break; + } + } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); + } else if (goingLeft) { + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] <= REGION_NONE) { + KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_RIGHT); + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + break; + } + } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); + } + + if (pauseCtx->cursorSpecialPos == 0) { + pauseCtx->cursorItem[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + // Used as cursor vtxIndex + pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + } + + if (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + } + if (oldCursorPoint != pauseCtx->cursorPoint[PAUSE_WORLD_MAP]) { + Audio_PlaySfx(NA_SE_SY_CURSOR); + } + } else if (pauseCtx->state == PAUSE_STATE_OWLWARP_SELECT) { + pauseCtx->cursorColorSet = PAUSE_CURSOR_COLOR_SET_BLUE; + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + + if (goingLeft || goingRight) { + pauseCtx->cursorShrinkRate = 4.0f; + goingLeft = !goingLeft; + goingRight = !goingRight; + } + + if (goingRight) { + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] > OWL_WARP_STONE_TOWER) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = OWL_WARP_GREAT_BAY_COAST; + } + } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); + } else if (goingLeft) { + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] < OWL_WARP_GREAT_BAY_COAST) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = OWL_WARP_STONE_TOWER; + } + } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); + } + + // Offset from `ITEM_MAP_POINT_GREAT_BAY` is to get the correct ordering in `map_name_static` + pauseCtx->cursorItem[PAUSE_MAP] = + sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; + // Used as cursor vtxIndex + pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + + if (oldCursorPoint != pauseCtx->cursorPoint[PAUSE_WORLD_MAP]) { + Audio_PlaySfx(NA_SE_SY_CURSOR); + } + } +} + void KaleidoScope_UpdateWorldMapCursor(PlayState* play) { static u16 sStickAdjTimer = 0; // unused timer that counts up every frame. Resets on reading a stickAdj. PauseContext* pauseCtx = &play->pauseCtx; s16 oldCursorPoint; + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + Ship_UpdateWorldMapCursorMirrorWorld(play); + return; + } + if ((pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && (pauseCtx->pageIndex == PAUSE_MAP) && !PauseOwlWarp_IsOwlWarpEnabled()) { pauseCtx->cursorColorSet = PAUSE_CURSOR_COLOR_SET_WHITE; diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index f060ed01e..4bcb9ed18 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -2918,13 +2918,17 @@ void KaleidoScope_UpdateCursorSize(PlayState* play) { pauseCtx->cursorX = sWorldMapCursorsX[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]; pauseCtx->cursorY = sWorldMapCursorsY[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]; } - if (!IS_PAUSE_STATE_OWLWARP) { + if (!IS_PAUSE_STATE_OWLWARP && !PauseOwlWarp_IsOwlWarpEnabled()) { pauseCtx->cursorHeight = 10.0f; pauseCtx->cursorWidth = 10.0f; } else { pauseCtx->cursorHeight = 15.0f; pauseCtx->cursorWidth = 15.0f; } + // Flip the position of the cursor with an additional offset to align with flipped map points + if (CVarGetInteger("gModes.MirroredWorld.State", 0)) { + pauseCtx->cursorX = (pauseCtx->cursorX * -1.0) + 5.0f; + } } else { pauseCtx->cursorX = sDungeonMapCursorsX[pauseCtx->cursorPoint[PAUSE_MAP]]; pauseCtx->cursorY = sDungeonMapCursorsY[pauseCtx->cursorPoint[PAUSE_MAP]];