diff --git a/mm/2s2h/BenGui/BenMenu.cpp b/mm/2s2h/BenGui/BenMenu.cpp index 14dd551042..d747fc162d 100644 --- a/mm/2s2h/BenGui/BenMenu.cpp +++ b/mm/2s2h/BenGui/BenMenu.cpp @@ -602,6 +602,102 @@ void BenMenu::AddEnhancements() { .DefaultValue(0.5f) .IsPercentage()); + AddWidget(path, "Mouse", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Mouse Enabled", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Camera.Mouse.Enabled") + .Options(CheckboxOptions().DefaultValue(false)) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_ON).active) + info.activeDisables.push_back(DISABLE_FOR_MOUSE_ON); + }); + AddWidget(path, "Invert Camera X Axis", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Camera.Mouse.InvertX") + .Options(CheckboxOptions().Tooltip("Inverts the Camera X Axis.")) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + AddWidget(path, "Invert Camera Y Axis", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Camera.Mouse.InvertY") + .Options(CheckboxOptions().Tooltip("Inverts the Camera Y Axis.").DefaultValue(true)) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + AddWidget(path, "Third-Person Horizontal Sensitivity: %.0f", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gEnhancements.Camera.Mouse.CameraSensitivity.X") + .Options(FloatSliderOptions() + .Tooltip("Adjust the mouse sensitivity of the x axis when in Third Person.") + .Format("%.0f%%") + .Min(0.01f) + .Max(5.0f) + .DefaultValue(1.0f) + .IsPercentage()) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + AddWidget(path, "Third-Person Vertical Sensitivity: %.0f", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gEnhancements.Camera.Mouse.CameraSensitivity.Y") + .Options(FloatSliderOptions() + .Tooltip("Adjust the mouse sensitivity of the y axis when in Third Person.") + .Format("%.0f%%") + .Min(0.01f) + .Max(5.0f) + .DefaultValue(1.0f) + .IsPercentage()) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + AddWidget(path, "First-Person invert X Axis", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Camera.Mouse.FirstPerson.InvertX") + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + AddWidget(path, "First-Person invert Y Axis", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Camera.Mouse.FirstPerson.InvertY") + .Options(CheckboxOptions().DefaultValue(true)) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + AddWidget(path, "First-Person Horizontal Sensitivity: %.0f", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gEnhancements.Camera.Mouse.FirstPerson.SensitivityX") + .Options(FloatSliderOptions() + .Tooltip("Adjust the mouse sensitivity of the x axis when in First Person.") + .Format("%.0f%%") + .Min(0.01f) + .Max(5.0f) + .DefaultValue(1.0f) + .IsPercentage()) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + AddWidget(path, "First-Person Vertical Sensitivity: %.0f", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gEnhancements.Camera.Mouse.FirstPerson.SensitivityY") + .Options(FloatSliderOptions() + .Tooltip("Adjust the mouse sensitivity of the y axis when in First Person.") + .Format("%.0f%%") + .Min(0.01f) + .Max(5.0f) + .DefaultValue(1.0f) + .IsPercentage()) + .PreFunc([](WidgetInfo& info) { + if (mBenMenu->disabledMap.at(DISABLE_FOR_MOUSE_OFF).active) { + info.activeDisables.push_back(DISABLE_FOR_MOUSE_OFF); + } + }); + path = { "Enhancements", "Cheats", 1 }; AddSidebarEntry("Enhancements", "Cheats", 3); AddWidget(path, "Infinite Health", WIDGET_CVAR_CHECKBOX) @@ -1470,6 +1566,12 @@ void BenMenu::InitElement() { { DISABLE_FOR_FREE_LOOK_OFF, { [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Camera.FreeLook.Enable", 0); }, "Free Look is Disabled" } }, + { DISABLE_FOR_MOUSE_ON, + { [](disabledInfo& info) -> bool { return CVarGetInteger("gEnhancements.Camera.Mouse.Enabled", 0); }, + "Mouse is Enabled" } }, + { DISABLE_FOR_MOUSE_OFF, + { [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Camera.Mouse.Enabled", 0); }, + "Mouse is Disabled" } }, { DISABLE_FOR_GYRO_OFF, { [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Camera.FirstPerson.GyroEnabled", 0); diff --git a/mm/2s2h/BenGui/MenuTypes.h b/mm/2s2h/BenGui/MenuTypes.h index a7d16b4fb1..29e7f2dc52 100644 --- a/mm/2s2h/BenGui/MenuTypes.h +++ b/mm/2s2h/BenGui/MenuTypes.h @@ -10,6 +10,8 @@ typedef enum { DISABLE_FOR_DEBUG_CAM_OFF, DISABLE_FOR_FREE_LOOK_ON, DISABLE_FOR_FREE_LOOK_OFF, + DISABLE_FOR_MOUSE_ON, + DISABLE_FOR_MOUSE_OFF, DISABLE_FOR_GYRO_OFF, DISABLE_FOR_GYRO_ON, DISABLE_FOR_RIGHT_STICK_OFF, diff --git a/mm/2s2h/Enhancements/Camera/FreeLook.cpp b/mm/2s2h/Enhancements/Camera/FreeLook.cpp index 2b72dc71d6..736b72647b 100644 --- a/mm/2s2h/Enhancements/Camera/FreeLook.cpp +++ b/mm/2s2h/Enhancements/Camera/FreeLook.cpp @@ -2,6 +2,7 @@ #include "2s2h/GameInteractor/GameInteractor.h" #include "2s2h/ShipInit.hpp" #include "CameraUtils.h" +#include "Mouse.h" extern "C" { #include "macros.h" @@ -100,6 +101,18 @@ bool Camera_FreeLook(Camera* camera) { yaw += yawDiff * GameInteractor_InvertControl(GI_INVERT_CAMERA_RIGHT_STICK_X); pitch += pitchDiff * -GameInteractor_InvertControl(GI_INVERT_CAMERA_RIGHT_STICK_Y); + if (CVarGetInteger("gEnhancements.Camera.Mouse.Enabled", 0) && Mouse_IsCaptured() + // Disable mouse movement when holding down the shield + // TODO: test if this check still needed + // !(player->stateFlags1 & 0x400000) + ) { + MouseDelta mouseDelta = Mouse_GetDelta(); + yaw -= mouseDelta.x * 40.0f * CVarGetFloat("gEnhancements.Camera.Mouse.CameraSensitivity.X", 1.0f) * + GameInteractor_InvertControl(GI_INVERT_CAMERA_MOUSE_X); + pitch += mouseDelta.y * 40.0f * CVarGetFloat("gEnhancements.Camera.Mouse.CameraSensitivity.Y", 1.0f) * + -GameInteractor_InvertControl(GI_INVERT_CAMERA_MOUSE_Y); + } + s16 maxPitch = DEG_TO_BINANG(CVarGetFloat("gEnhancements.Camera.FreeLook.MaxPitch", 72.0f)); s16 minPitch = DEG_TO_BINANG(CVarGetFloat("gEnhancements.Camera.FreeLook.MinPitch", -49.0f)); @@ -140,9 +153,17 @@ bool Camera_FreeLook(Camera* camera) { bool Camera_CanFreeLook(Camera* camera) { f32 camX = sCamPlayState->state.input[0].cur.right_stick_x * 10.0f; f32 camY = sCamPlayState->state.input[0].cur.right_stick_y * 10.0f; + + if (CVarGetInteger("gEnhancements.Camera.Mouse.Enabled", 0) && Mouse_IsCaptured()) { + MouseDelta mouseDelta = Mouse_GetDelta(); + camX -= mouseDelta.x * 40.0f; + camY += mouseDelta.y * 40.0f; + } + if (!sCanFreeLook && (fabsf(camX) >= 15.0f || fabsf(camY) >= 15.0f)) { sCanFreeLook = true; } + // TODO: check Z target mode potential // Pressing Z will "Reset" Camera if (CHECK_BTN_ALL(sCamPlayState->state.input[0].press.button, BTN_Z)) { sCanFreeLook = false; diff --git a/mm/2s2h/Enhancements/Camera/Mouse.cpp b/mm/2s2h/Enhancements/Camera/Mouse.cpp new file mode 100644 index 0000000000..6d4fb644b5 --- /dev/null +++ b/mm/2s2h/Enhancements/Camera/Mouse.cpp @@ -0,0 +1,31 @@ +#include "Mouse.h" + +#include "Context.h" + +static MouseDelta current; + +#ifdef __cplusplus +extern "C" { +#endif + +void Mouse_Update() { + Ship::Coords coords = Ship::Context::GetInstance()->GetWindow()->GetMouseDelta(); + current.x = coords.x; + current.y = coords.y; +} + +MouseDelta Mouse_GetDelta() { + return current; +} + +void Mouse_SetCursorPos(s32 x, s32 y) { + Ship::Context::GetInstance()->GetWindow()->SetMousePos({ x, y }); +} + +bool Mouse_IsCaptured() { + return Ship::Context::GetInstance()->GetWindow()->IsMouseCaptured(); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/mm/2s2h/Enhancements/Camera/Mouse.h b/mm/2s2h/Enhancements/Camera/Mouse.h new file mode 100644 index 0000000000..15a1b56fc4 --- /dev/null +++ b/mm/2s2h/Enhancements/Camera/Mouse.h @@ -0,0 +1,22 @@ +// TODO: Maybe this file should be part of BenPort +#pragma once + +#include "ultratypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MouseDelta { + s32 x; + s32 y; +} MouseDelta; + +void Mouse_Update(); +MouseDelta Mouse_GetDelta(); +void Mouse_SetCursorPos(s32 x, s32 y); +bool Mouse_IsCaptured(); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/mm/2s2h/GameInteractor/GameInteractor.cpp b/mm/2s2h/GameInteractor/GameInteractor.cpp index 71d0002988..348ad00a80 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.cpp +++ b/mm/2s2h/GameInteractor/GameInteractor.cpp @@ -278,6 +278,16 @@ int GameInteractor_InvertControl(GIInvertType type) { result *= -1; } break; + case GI_INVERT_CAMERA_MOUSE_X: + if (CVarGetInteger("gEnhancements.Camera.Mouse.InvertX", 0)) { + result *= -1; + } + break; + case GI_INVERT_CAMERA_MOUSE_Y: + if (CVarGetInteger("gEnhancements.Camera.Mouse.InvertY", 1)) { + result *= -1; + } + break; case GI_INVERT_FIRST_PERSON_AIM_X: if (CVarGetInteger("gEnhancements.Camera.FirstPerson.InvertX", 0)) { result *= -1; @@ -308,12 +318,23 @@ int GameInteractor_InvertControl(GIInvertType type) { result *= -1; } break; + case GI_INVERT_FIRST_PERSON_MOUSE_X: + if (CVarGetInteger("gEnhancements.Camera.Mouse.FirstPerson.InvertX", 0)) { + result *= -1; + } + break; + case GI_INVERT_FIRST_PERSON_MOUSE_Y: + if (CVarGetInteger("gEnhancements.Camera.Mouse.FirstPerson.InvertY", 1)) { + result *= -1; + } + 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_CAMERA_MOUSE_X: case GI_INVERT_MOVEMENT_X: case GI_INVERT_SHIELD_X: case GI_INVERT_SHOP_X: @@ -324,6 +345,7 @@ int GameInteractor_InvertControl(GIInvertType type) { case GI_INVERT_FIRST_PERSON_AIM_X: case GI_INVERT_FIRST_PERSON_GYRO_X: case GI_INVERT_FIRST_PERSON_RIGHT_STICK_X: + case GI_INVERT_FIRST_PERSON_MOUSE_X: case GI_INVERT_FIRST_PERSON_MOVING_X: result *= -1; break; diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index 5fa2e81bfa..3bda50398e 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -103,6 +103,8 @@ typedef enum { typedef enum { GI_INVERT_CAMERA_RIGHT_STICK_X, GI_INVERT_CAMERA_RIGHT_STICK_Y, + GI_INVERT_CAMERA_MOUSE_X, + GI_INVERT_CAMERA_MOUSE_Y, GI_INVERT_MOVEMENT_X, GI_INVERT_SHIELD_X, GI_INVERT_SHOP_X, @@ -116,6 +118,8 @@ typedef enum { GI_INVERT_FIRST_PERSON_GYRO_Y, GI_INVERT_FIRST_PERSON_RIGHT_STICK_X, GI_INVERT_FIRST_PERSON_RIGHT_STICK_Y, + GI_INVERT_FIRST_PERSON_MOUSE_X, + GI_INVERT_FIRST_PERSON_MOUSE_Y, GI_INVERT_FIRST_PERSON_MOVING_X, } GIInvertType; diff --git a/mm/src/code/padmgr.c b/mm/src/code/padmgr.c index 1beb8bfea2..407f1d5870 100644 --- a/mm/src/code/padmgr.c +++ b/mm/src/code/padmgr.c @@ -34,6 +34,7 @@ #include "global.h" #include "PR/controller.h" #include "PR/os_motor.h" +#include "2s2h/Enhancements/Camera/Mouse.h" #include "fault.h" #include #include @@ -603,6 +604,7 @@ void PadMgr_HandleRetrace(void) { // Begin reading controller data osContStartReadData(serialEventQueue); + Mouse_Update(); // Execute rumble callback if (sPadMgrInstance->rumbleRetraceCallback != NULL) { 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 5a4be6e68f..041c073672 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -47,6 +47,7 @@ #include "2s2h/BenPort.h" #include "2s2h/GameInteractor/GameInteractor.h" +#include "2s2h/Enhancements/Camera/Mouse.h" #define THIS ((Player*)thisx) @@ -13038,6 +13039,21 @@ s32 Ship_HandleFirstPersonAiming(PlayState* play, Player* this, s32 arg2) { stickY += leftStickY * CVarGetFloat("gEnhancements.Camera.FirstPerson.SensitivityY", 1.0f); } + if (CVarGetInteger("gEnhancements.Camera.Mouse.Enabled", 0) && Mouse_IsCaptured()) { + MouseDelta mouseDelta = Mouse_GetDelta(); + + if (mouseDelta.x != 0) { + this->actor.focus.rot.y += mouseDelta.x * 12.0f * + CVarGetFloat("gEnhancements.Camera.Mouse.FirstPerson.SensitivityX", 1.0f) * + -GameInteractor_InvertControl(GI_INVERT_FIRST_PERSON_MOUSE_X); + } + if (mouseDelta.y != 0) { + this->actor.focus.rot.x += mouseDelta.y * 12.0f * + CVarGetFloat("gEnhancements.Camera.Mouse.FirstPerson.SensitivityY", 1.0f) * + -GameInteractor_InvertControl(GI_INVERT_FIRST_PERSON_MOUSE_Y); + } + } + if (CVarGetInteger("gEnhancements.Camera.FirstPerson.GyroEnabled", 0)) { gyroX = sPlayerControlInput->cur.gyro_y * 720; // -40 to 40, avg -4 to 4 gyroY = sPlayerControlInput->cur.gyro_x * 720; // -20 to 20, avg -2 to 2