summaryrefslogtreecommitdiff
path: root/mm/src/code
diff options
context:
space:
mode:
authorGarrett Cox <garrettjcox@gmail.com>2024-10-02 22:44:36 -0500
committerGitHub <noreply@github.com>2024-10-02 23:44:36 -0400
commitc474a82a94db5ad2fb97dcc55db2b491906a653f (patch)
tree04ece7d80243de4437b536a9b6bcf3bb866a2189 /mm/src/code
parent71c2c94ece12c664fe54f9959cce40776ba36dcd (diff)
Mirror Mode (#535)
* Mirror mode initial version * Update mm/src/code/z_actor.c Co-authored-by: Archez <Archez@users.noreply.github.com> * mirror mode support for minimaps and dungeon pause map * mirror mode support overworld pause map and song of soaring * mirror mode support for various remaining things * mirror mode support pause warp enhancement * Add searchable menu item --------- Co-authored-by: Archez <Archez@users.noreply.github.com> Co-authored-by: Archez <archez39@me.com>
Diffstat (limited to 'mm/src/code')
-rw-r--r--mm/src/code/z_actor.c4
-rw-r--r--mm/src/code/z_kankyo.c3
-rw-r--r--mm/src/code/z_lib.c4
-rw-r--r--mm/src/code/z_map_disp.c60
-rw-r--r--mm/src/code/z_parameter.c8
-rw-r--r--mm/src/code/z_play.c17
-rw-r--r--mm/src/code/z_view.c41
7 files changed, 136 insertions, 1 deletions
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 <libultraship/bridge.h>
+#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);