From 320f4558a8d12eda047a6dab7e3fe175fd3c8293 Mon Sep 17 00:00:00 2001 From: Irastris Date: Mon, 11 May 2026 01:51:11 -0400 Subject: [PATCH 01/21] Untie existing mouse logic from gyro --- files.cmake | 1 + include/d/actor/d_a_alink.h | 2 +- include/dusk/gyro.h | 5 +- include/dusk/mouse.h | 6 +++ include/dusk/settings.h | 4 +- src/d/actor/d_a_alink_dusk.cpp | 2 +- src/d/actor/d_a_alink_link.inc | 27 +++++++--- src/dusk/gyro.cpp | 55 +------------------- src/dusk/imgui/ImGuiConsole.cpp | 2 +- src/dusk/mouse.cpp | 92 +++++++++++++++++++++++++++++++++ src/dusk/settings.cpp | 8 ++- src/dusk/ui/document.cpp | 2 +- src/dusk/ui/settings.cpp | 61 ++++++---------------- src/m_Do/m_Do_main.cpp | 3 ++ 14 files changed, 153 insertions(+), 117 deletions(-) create mode 100644 include/dusk/mouse.h create mode 100644 src/dusk/mouse.cpp diff --git a/files.cmake b/files.cmake index eb8c62bee5..1bcbfaed75 100644 --- a/files.cmake +++ b/files.cmake @@ -1431,6 +1431,7 @@ set(DUSK_FILES src/dusk/game_clock.cpp src/dusk/globals.cpp src/dusk/gyro.cpp + src/dusk/mouse.cpp src/dusk/gamepad_color.cpp src/dusk/autosave.cpp src/dusk/http/http.hpp diff --git a/include/d/actor/d_a_alink.h b/include/d/actor/d_a_alink.h index 107afc1ed3..b3b2afb0f9 100644 --- a/include/d/actor/d_a_alink.h +++ b/include/d/actor/d_a_alink.h @@ -4551,7 +4551,7 @@ class daAlink_c : public daPy_py_c { #if TARGET_PC void handleWolfHowl(); void handleQuickTransform(); - bool checkGyroAimContext(); + bool checkAimContext(); void onIronBallChainInterpCallback(); diff --git a/include/dusk/gyro.h b/include/dusk/gyro.h index a206100739..abb56640e5 100644 --- a/include/dusk/gyro.h +++ b/include/dusk/gyro.h @@ -1,5 +1,4 @@ -#ifndef DUSK_GYRO_H -#define DUSK_GYRO_H +#pragma once namespace dusk::gyro { void read(float dt); @@ -14,5 +13,3 @@ bool get_sensor_keep_alive(); void set_sensor_keep_alive(bool value); bool rollgoal_gyro_enabled(); } // namespace dusk::gyro - -#endif diff --git a/include/dusk/mouse.h b/include/dusk/mouse.h new file mode 100644 index 0000000000..fc402c305c --- /dev/null +++ b/include/dusk/mouse.h @@ -0,0 +1,6 @@ +#pragma once + +namespace dusk::mouse { +void read(); +void getAimDeltas(float& out_yaw, float& out_pitch); +} // namespace dusk::mouse diff --git a/include/dusk/settings.h b/include/dusk/settings.h index fb3704fd30..f2ddffb20d 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -175,7 +175,6 @@ struct UserSettings { ConfigVar midnasLamentNonStop; // Input - ConfigVar gyroMode; ConfigVar enableGyroAim; ConfigVar enableGyroRollgoal; ConfigVar gyroSensitivityX; @@ -185,6 +184,9 @@ struct UserSettings { ConfigVar gyroDeadband; ConfigVar gyroInvertPitch; ConfigVar gyroInvertYaw; + ConfigVar enableMouseAim; + ConfigVar mouseSensitivityX; + ConfigVar mouseSensitivityY; ConfigVar freeCamera; ConfigVar invertCameraXAxis; ConfigVar invertCameraYAxis; diff --git a/src/d/actor/d_a_alink_dusk.cpp b/src/d/actor/d_a_alink_dusk.cpp index 8c1d415ae0..b787eebcaa 100644 --- a/src/d/actor/d_a_alink_dusk.cpp +++ b/src/d/actor/d_a_alink_dusk.cpp @@ -144,7 +144,7 @@ void daAlink_c::handleQuickTransform() { procCoMetamorphoseInit(); } -bool daAlink_c::checkGyroAimContext() { +bool daAlink_c::checkAimContext() { switch (mProcID) { case PROC_SUBJECTIVITY: case PROC_SWIM_SUBJECTIVITY: diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index 636aabdffe..7ff08a76a0 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -11,8 +11,9 @@ #include "d/actor/d_a_tag_mhint.h" #if TARGET_PC -#include "dusk/gyro.h" #include "dusk/action_bindings.h" +#include "dusk/gyro.h" +#include "dusk/mouse.h" #endif bool daAlink_c::checkNoSubjectModeCamera() { @@ -131,7 +132,10 @@ BOOL daAlink_c::setBodyAngleToCamera() { } #if TARGET_PC - if (dusk::getSettings().game.enableGyroAim && checkGyroAimContext()) { + if ((dusk::getSettings().game.enableGyroAim || + dusk::getSettings().game.enableMouseAim) && + checkAimContext()) + { f32 gyro_scale = 1.0f; if (checkWolfEyeUp()) { gyro_scale *= 0.6f; @@ -141,12 +145,21 @@ BOOL daAlink_c::setBodyAngleToCamera() { gyro_scale /= dComIfGp_getCameraZoomScale(field_0x317c); } - f32 gy_yaw = 0.f; - f32 gy_pitch = 0.f; - dusk::gyro::getAimDeltas(gy_yaw, gy_pitch); + f32 final_yaw = 0.f; + f32 final_pitch = 0.f; + if (dusk::getSettings().game.enableMouseAim) { + dusk::mouse::getAimDeltas(final_yaw, final_pitch); + } + if (dusk::getSettings().game.enableGyroAim) { + f32 gyro_yaw = 0.f; + f32 gyro_pitch = 0.f; + dusk::gyro::getAimDeltas(gyro_yaw, gyro_pitch); + final_yaw += gyro_yaw; + final_pitch += gyro_pitch; + } - shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale); - sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale); + shape_angle.y = shape_angle.y + cM_rad2s(final_yaw * gyro_scale * (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f)); + sp8 = sp8 + cM_rad2s(final_pitch * gyro_scale * (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f)); if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) { sp8 = mBodyAngle.x; diff --git a/src/dusk/gyro.cpp b/src/dusk/gyro.cpp index 680d500631..1e2e379960 100644 --- a/src/dusk/gyro.cpp +++ b/src/dusk/gyro.cpp @@ -2,8 +2,6 @@ #include "dusk/ui/ui.hpp" #include "d/actor/d_a_alink.h" -#include -#include #include namespace dusk::gyro { @@ -16,14 +14,11 @@ constexpr float kGravityEmaAlpha = 0.1f; constexpr float kMinGravityProjection = 0.2f; // Let roll contribute more strongly as the pad approaches an upright posture. constexpr float kRollAimBoostMax = 2.0f; -constexpr float kMousePixelToRad = 0.0025f; bool s_sensor_enabled = false; bool s_accel_enabled = false; bool s_was_aiming = false; bool s_have_gravity_baseline = false; -bool s_mouse_enabled = false; -bool s_mouse_relative = false; float s_smooth_gx = 0.0f; float s_smooth_gy = 0.0f; float s_smooth_gz = 0.0f; @@ -43,7 +38,6 @@ void reset_filter_state() { s_baseline_gravity_y = s_baseline_gravity_z = 0.0f; s_was_aiming = false; s_have_gravity_baseline = false; - s_mouse_enabled = false; s_yaw_rad = s_pitch_rad = s_roll_rad = 0.0f; s_rollgoal_ax = s_rollgoal_az = 0; } @@ -72,7 +66,7 @@ bool get_sensor_keep_alive() { return s_sensor_keep_alive; } void set_sensor_keep_alive(bool value) { s_sensor_keep_alive = value; } bool rollgoal_gyro_enabled() { - return getSettings().game.enableGyroRollgoal && getSettings().game.gyroMode.getValue() != GyroMode::Mouse; + return getSettings().game.enableGyroRollgoal; } bool queryGyroAimContext() { @@ -85,7 +79,7 @@ bool queryGyroAimContext() { return false; } - return link->checkGyroAimContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); + return link->checkAimContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); } void read(float dt) { @@ -94,26 +88,6 @@ void read(float dt) { const bool aim_just_ended = !aim_active && s_was_aiming; s_was_aiming = aim_active; - const bool mouse_mode = getSettings().game.gyroMode.getValue() == GyroMode::Mouse; - const bool mouse_gyro_active = !ui::any_document_visible() && mouse_mode && (aim_active || s_sensor_keep_alive); - SDL_Window* window = aurora::window::get_sdl_window(); - if (window != nullptr && mouse_gyro_active != s_mouse_relative && - SDL_SetWindowRelativeMouseMode(window, mouse_gyro_active)) - { - s_mouse_relative = mouse_gyro_active; - } - - if (mouse_gyro_active && !s_mouse_enabled && window != nullptr) { - const AuroraWindowSize sz = aurora::window::get_window_size(); - const float cx = static_cast(sz.width) * 0.5f; - const float cy = static_cast(sz.height) * 0.5f; - SDL_WarpMouseInWindow(window, cx, cy); - float discard_x = 0.0f; - float discard_y = 0.0f; - SDL_GetRelativeMouseState(&discard_x, &discard_y); - } - s_mouse_enabled = mouse_gyro_active; - if (!s_sensor_keep_alive && !aim_active) { disable_pad_sensors(); reset_filter_state(); @@ -126,31 +100,6 @@ void read(float dt) { s_have_gravity_baseline = false; } - if (mouse_mode && !mouse_gyro_active) { - s_pitch_rad = 0.0f; - s_yaw_rad = 0.0f; - s_roll_rad = 0.0f; - return; - } - - if (mouse_mode) { - disable_pad_sensors(); - - float mx_rel = 0.0f; - float my_rel = 0.0f; - SDL_GetRelativeMouseState(&mx_rel, &my_rel); - // Convert pixels to radians - s_pitch_rad = my_rel * kMousePixelToRad * getSettings().game.gyroSensitivityY; - s_yaw_rad = -mx_rel * kMousePixelToRad * getSettings().game.gyroSensitivityX; - s_roll_rad = 0.0f; - - s_pitch_rad = getSettings().game.gyroInvertPitch ? -s_pitch_rad : s_pitch_rad; - s_yaw_rad = getSettings().game.gyroInvertYaw ? -s_yaw_rad : s_yaw_rad; - s_yaw_rad = getSettings().game.enableMirrorMode ? -s_yaw_rad : s_yaw_rad; - - return; - } - if (!s_sensor_enabled) { if (!PADHasSensor(PAD_CHAN0, PAD_SENSOR_GYRO)) { return; diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 077b4c15bc..d83e68b4d8 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -377,7 +377,7 @@ namespace dusk { } // Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds. - if (dusk::getSettings().game.gyroMode.getValue() != GyroMode::Mouse) + if (!dusk::getSettings().game.enableMouseAim) { ImGuiIO& io = ImGui::GetIO(); if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) { diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp new file mode 100644 index 0000000000..0442229b15 --- /dev/null +++ b/src/dusk/mouse.cpp @@ -0,0 +1,92 @@ +#include "dusk/mouse.h" +#include "dusk/gyro.h" +#include "dusk/settings.h" +#include "dusk/ui/ui.hpp" +#include "d/actor/d_a_alink.h" + +#include +#include + +namespace dusk::mouse { +namespace { +constexpr float kMousePixelToRad = 0.0025f; + +bool s_mouse_enabled = false; +bool s_mouse_relative = false; +float s_yaw_rad = 0.0f; +float s_pitch_rad = 0.0f; + +bool queryMouseAimContext() { + if (!static_cast(getSettings().game.enableMouseAim)) { + return false; + } + + daAlink_c* link = daAlink_getAlinkActorClass(); + if (link == nullptr) { + return false; + } + + return link->checkAimContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); +} + +void reset_aim_state() { + s_mouse_enabled = false; + s_yaw_rad = s_pitch_rad = 0.0f; +} +} // namespace + +void read() { + const bool mouse_aim_active = queryMouseAimContext(); + const bool mouse_gyro_active = + !ui::any_document_visible() && getSettings().game.enableMouseAim && mouse_aim_active; + + SDL_Window* window = aurora::window::get_sdl_window(); + if (window != nullptr && mouse_gyro_active != s_mouse_relative && + SDL_SetWindowRelativeMouseMode(window, mouse_gyro_active)) + { + s_mouse_relative = mouse_gyro_active; + } + + if (mouse_gyro_active && !s_mouse_enabled && window != nullptr) { + const AuroraWindowSize sz = aurora::window::get_window_size(); + const float cx = static_cast(sz.width) * 0.5f; + const float cy = static_cast(sz.height) * 0.5f; + SDL_WarpMouseInWindow(window, cx, cy); + float discard_x = 0.0f; + float discard_y = 0.0f; + SDL_GetRelativeMouseState(&discard_x, &discard_y); + } + s_mouse_enabled = mouse_gyro_active; + + if (!dusk::gyro::get_sensor_keep_alive() && !mouse_aim_active) { + reset_aim_state(); + return; + } + + if (getSettings().game.enableMouseAim && !mouse_gyro_active) { + s_pitch_rad = 0.0f; + s_yaw_rad = 0.0f; + return; + } + + if (!getSettings().game.enableMouseAim) { + s_pitch_rad = 0.0f; + s_yaw_rad = 0.0f; + return; + } + + float mx_rel = 0.0f; + float my_rel = 0.0f; + SDL_GetRelativeMouseState(&mx_rel, &my_rel); + + s_pitch_rad = my_rel * kMousePixelToRad * getSettings().game.mouseSensitivityY; + s_yaw_rad = -mx_rel * kMousePixelToRad * getSettings().game.mouseSensitivityX; + + s_yaw_rad = getSettings().game.enableMirrorMode ? -s_yaw_rad : s_yaw_rad; +} + +void getAimDeltas(float& out_yaw, float& out_pitch) { + out_yaw = s_yaw_rad; + out_pitch = s_pitch_rad; +} +} // namespace dusk::mouse diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 4ba40d921b..df9b813739 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -75,7 +75,6 @@ UserSettings g_userSettings = { .midnasLamentNonStop {"game.midnasLamentNonStop", false}, // Input - .gyroMode {"game.gyroMode", GyroMode::Sensor}, .enableGyroAim {"game.enableGyroAim", false}, .enableGyroRollgoal {"game.enableGyroRollgoal", false}, .gyroSensitivityX {"game.gyroSensitivityX", 1.0f}, @@ -85,6 +84,9 @@ UserSettings g_userSettings = { .gyroDeadband {"game.gyroDeadband", 0.04f}, .gyroInvertPitch {"game.gyroInvertPitch", false}, .gyroInvertYaw {"game.gyroInvertYaw", false}, + .enableMouseAim {"game.enableMouseAim", false}, + .mouseSensitivityX {"game.mouseSensitivityX", 1.0f}, + .mouseSensitivityY {"game.mouseSensitivityY", 1.0f}, .freeCamera {"game.freeCamera", false}, .invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraYAxis {"game.invertCameraYAxis", false}, @@ -268,7 +270,6 @@ void registerSettings() { Register(g_userSettings.game.alwaysGreatspin); Register(g_userSettings.game.invincibleEnemies); Register(g_userSettings.game.enableFrameInterpolation); - Register(g_userSettings.game.gyroMode); Register(g_userSettings.game.enableGyroAim); Register(g_userSettings.game.enableGyroRollgoal); Register(g_userSettings.game.gyroSensitivityX); @@ -278,6 +279,9 @@ void registerSettings() { Register(g_userSettings.game.gyroSmoothing); Register(g_userSettings.game.gyroInvertPitch); Register(g_userSettings.game.gyroInvertYaw); + Register(g_userSettings.game.enableMouseAim); + Register(g_userSettings.game.mouseSensitivityX); + Register(g_userSettings.game.mouseSensitivityY); Register(g_userSettings.game.freeCamera); Register(g_userSettings.game.debugFlyCam); Register(g_userSettings.game.debugFlyCamLockEvents); diff --git a/src/dusk/ui/document.cpp b/src/dusk/ui/document.cpp index 4296ac27d9..cb70578e30 100644 --- a/src/dusk/ui/document.cpp +++ b/src/dusk/ui/document.cpp @@ -116,7 +116,7 @@ bool Document::handle_nav_command(Rml::Event& event, NavCommand cmd) { } void Document::toggle_cursor_if_gyro(bool cursor_enabled) { - if (dusk::getSettings().game.gyroMode.getValue() == GyroMode::Mouse) + if (dusk::getSettings().game.enableMouseAim) { if (cursor_enabled) { ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index e009497045..30bf118855 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -379,9 +379,7 @@ int float_setting_percent(ConfigVar& var) { } bool gyro_enabled() { - return getSettings().game.enableGyroAim || - (getSettings().game.enableGyroRollgoal && - getSettings().game.gyroMode.getValue() != GyroMode::Mouse); + return getSettings().game.enableGyroAim || getSettings().game.enableGyroRollgoal; } struct ConfigBoolProps { @@ -945,50 +943,12 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); leftPane.add_section("Gyro"); - leftPane.register_control( - leftPane.add_select_button({ - .key = "Gyro Input Method", - .getValue = - [] { - const auto mode = getSettings().game.gyroMode.getValue(); - const auto idx = static_cast(mode); - return Rml::String{kGyroInputModeLabels[idx]}; - }, - .isModified = - [] { - return getSettings().game.gyroMode.getValue() != - getSettings().game.gyroMode.getDefaultValue(); - }, - }), - rightPane, [](Pane& pane) { - for (size_t i = 0; i < kGyroInputModeLabels.size(); i++) { - pane - .add_button({ - .text = Rml::String{kGyroInputModeLabels[i]}, - .isSelected = - [i] { - return getSettings().game.gyroMode.getValue() == static_cast(i); - }, - }) - .on_pressed([i] { - mDoAud_seStartMenu(kSoundItemChange); - const GyroMode mode = static_cast(i); - getSettings().game.gyroMode.setValue(mode); - config::Save(); - }); - } - pane.add_rml( - "
Sensor reads motion directly from a supported controller's gyro via SDL.
" - "
Mouse treats mouse input as gyro, intended for use with the Steam Deck.
" - "
Mouse input cannot currently be used with Gyro Rollgoal."); - }); addOption("Gyro Aim", getSettings().game.enableGyroAim, "Enables gyro controls while in look mode, aiming a hawk, and aiming " "supported items.

Supported items include the Slingshot, Gale Boomerang, " "Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod."); addOption("Gyro Rollgoal", getSettings().game.enableGyroRollgoal, - "Enables gyro controls for Rollgoal in Hena's Cabin.", - [] { return getSettings().game.gyroMode.getValue() == GyroMode::Mouse; }); + "Enables gyro controls for Rollgoal in Hena's Cabin."); config_percent_select(leftPane, rightPane, getSettings().game.gyroSensitivityY, "Gyro Pitch Sensitivity", "Controls vertical gyro aiming sensitivity.", 25, 400, 5, [] { return !gyro_enabled(); }); @@ -998,10 +958,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { config_percent_select(leftPane, rightPane, getSettings().game.gyroSensitivityRollgoal, "Rollgoal Sensitivity", "Controls how strongly gyro input tilts the Rollgoal table.", 25, 400, 5, - [] { - return !getSettings().game.enableGyroRollgoal || - getSettings().game.gyroMode.getValue() == GyroMode::Mouse; - }); + [] { return !getSettings().game.enableGyroRollgoal; }); config_percent_select(leftPane, rightPane, getSettings().game.gyroDeadband, "Gyro Deadband", "Ignores small gyro movement to reduce drift and jitter.", 0, 50, 1, [] { return !gyro_enabled(); }); @@ -1013,6 +970,18 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { addOption("Invert Gyro Yaw", getSettings().game.gyroInvertYaw, "Invert horizontal gyro aiming.", [] { return !gyro_enabled(); }); + leftPane.add_section("Mouse"); + addOption("Mouse Aim", getSettings().game.enableMouseAim, + "Enables mouse input while in look mode, aiming a hawk, and aiming " + "supported items.

Supported items include the Slingshot, Gale Boomerang, " + "Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod."); + config_percent_select(leftPane, rightPane, getSettings().game.mouseSensitivityX, + "Mouse X Sensitivity", "Controls horizontal mouse sensitivity.", 25, 400, 5, + [] { return !getSettings().game.enableMouseAim; }); + config_percent_select(leftPane, rightPane, getSettings().game.mouseSensitivityY, + "Mouse Y Sensitivity", "Controls vertical mouse sensitivity.", 25, 400, 5, + [] { return !getSettings().game.enableMouseAim; }); + leftPane.add_section("Tools"); addOption("Turbo Key", getSettings().game.enableTurboKeybind, "Hold Tab to increase game speed by up to 4x.", diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 490c0b8980..10bd8f4ad5 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -54,6 +54,7 @@ #include "dusk/frame_interpolation.h" #include "dusk/game_clock.h" #include "dusk/gyro.h" +#include "dusk/mouse.h" #include "dusk/imgui/ImGuiConsole.hpp" #include "dusk/imgui/ImGuiEngine.hpp" #include "dusk/iso_validate.hpp" @@ -286,6 +287,7 @@ void main01(void) { for (int sim_tick = 0; sim_tick < pacing.sim_ticks_to_run; ++sim_tick) { dusk::frame_interp::begin_sim_tick(); mDoCPd_c::read(); + dusk::mouse::read(); dusk::gyro::read(pacing.sim_pace); fapGm_Execute(); mDoAud_Execute(); @@ -308,6 +310,7 @@ void main01(void) { // Game Inputs mDoCPd_c::read(); + dusk::mouse::read(); dusk::gyro::read(pacing.presentation_dt_seconds); // EXECUTE GAME LOGIC & RENDER From 0d6292047e2aa484dd550447ee644b236918568d Mon Sep 17 00:00:00 2001 From: Irastris Date: Mon, 11 May 2026 18:07:24 -0400 Subject: [PATCH 02/21] A bit more mouse cleanup before I start building off it --- src/dusk/mouse.cpp | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 0442229b15..c93cf51928 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -36,18 +36,17 @@ void reset_aim_state() { } // namespace void read() { - const bool mouse_aim_active = queryMouseAimContext(); - const bool mouse_gyro_active = - !ui::any_document_visible() && getSettings().game.enableMouseAim && mouse_aim_active; + const bool aim_active = !ui::any_document_visible() && queryMouseAimContext(); SDL_Window* window = aurora::window::get_sdl_window(); - if (window != nullptr && mouse_gyro_active != s_mouse_relative && - SDL_SetWindowRelativeMouseMode(window, mouse_gyro_active)) + if (window != nullptr && + aim_active != s_mouse_relative && + SDL_SetWindowRelativeMouseMode(window, aim_active)) { - s_mouse_relative = mouse_gyro_active; + s_mouse_relative = aim_active; } - if (mouse_gyro_active && !s_mouse_enabled && window != nullptr) { + if (!s_mouse_enabled && aim_active && window != nullptr) { const AuroraWindowSize sz = aurora::window::get_window_size(); const float cx = static_cast(sz.width) * 0.5f; const float cy = static_cast(sz.height) * 0.5f; @@ -56,20 +55,14 @@ void read() { float discard_y = 0.0f; SDL_GetRelativeMouseState(&discard_x, &discard_y); } - s_mouse_enabled = mouse_gyro_active; + s_mouse_enabled = aim_active; - if (!dusk::gyro::get_sensor_keep_alive() && !mouse_aim_active) { + if (!dusk::gyro::get_sensor_keep_alive() && !aim_active) { reset_aim_state(); return; } - if (getSettings().game.enableMouseAim && !mouse_gyro_active) { - s_pitch_rad = 0.0f; - s_yaw_rad = 0.0f; - return; - } - - if (!getSettings().game.enableMouseAim) { + if (!aim_active) { s_pitch_rad = 0.0f; s_yaw_rad = 0.0f; return; @@ -79,8 +72,8 @@ void read() { float my_rel = 0.0f; SDL_GetRelativeMouseState(&mx_rel, &my_rel); - s_pitch_rad = my_rel * kMousePixelToRad * getSettings().game.mouseSensitivityY; s_yaw_rad = -mx_rel * kMousePixelToRad * getSettings().game.mouseSensitivityX; + s_pitch_rad = my_rel * kMousePixelToRad * getSettings().game.mouseSensitivityY; s_yaw_rad = getSettings().game.enableMirrorMode ? -s_yaw_rad : s_yaw_rad; } From 6c5ea1d6ea1c1479185325206fa5e69d0dd055e3 Mon Sep 17 00:00:00 2001 From: Irastris Date: Mon, 18 May 2026 22:41:54 -0400 Subject: [PATCH 03/21] Rebase and last bit of cleanup --- src/dusk/mouse.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index c93cf51928..4deeb6984e 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -1,5 +1,4 @@ #include "dusk/mouse.h" -#include "dusk/gyro.h" #include "dusk/settings.h" #include "dusk/ui/ui.hpp" #include "d/actor/d_a_alink.h" @@ -57,14 +56,8 @@ void read() { } s_mouse_enabled = aim_active; - if (!dusk::gyro::get_sensor_keep_alive() && !aim_active) { - reset_aim_state(); - return; - } - if (!aim_active) { - s_pitch_rad = 0.0f; - s_yaw_rad = 0.0f; + reset_aim_state(); return; } From 624cbf41102e650cd45d912534a82a93d938183e Mon Sep 17 00:00:00 2001 From: Irastris Date: Mon, 18 May 2026 22:47:11 -0400 Subject: [PATCH 04/21] Fix rebase mistake, don't apply invertFirstPerson to gyro or mouse input --- src/d/actor/d_a_alink_link.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index 7ff08a76a0..eaf5b32af1 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -158,8 +158,8 @@ BOOL daAlink_c::setBodyAngleToCamera() { final_pitch += gyro_pitch; } - shape_angle.y = shape_angle.y + cM_rad2s(final_yaw * gyro_scale * (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f)); - sp8 = sp8 + cM_rad2s(final_pitch * gyro_scale * (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f)); + shape_angle.y = shape_angle.y + cM_rad2s(final_yaw * gyro_scale); + sp8 = sp8 + cM_rad2s(final_pitch * gyro_scale); if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) { sp8 = mBodyAngle.x; From 735335dab63ad1a86f433432df60d1ef6b6895be Mon Sep 17 00:00:00 2001 From: Irastris Date: Tue, 19 May 2026 21:10:59 -0400 Subject: [PATCH 05/21] Remove the deprecated ImGui toast system --- src/dusk/imgui/ImGuiConsole.cpp | 50 --------------------------------- src/dusk/imgui/ImGuiConsole.hpp | 12 -------- 2 files changed, 62 deletions(-) diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index d83e68b4d8..e2dbbf47d4 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -61,10 +61,6 @@ namespace dusk { ImGui::TextUnformatted(text.data(), text.data() + text.size()); } - void DuskToast(std::string_view message, float duration) { - g_imguiConsole.AddToast(message, duration); - } - void ImGuiTextCenter(std::string_view text) { ImGui::NewLine(); float fontSize = ImGui::CalcTextSize( @@ -390,8 +386,6 @@ namespace dusk { SDL_HideCursor(); } } - - ShowToasts(); } void ImGuiConsole::PostDraw() { @@ -545,50 +539,6 @@ namespace dusk { return false; } - void ImGuiConsole::AddToast(std::string_view message, float duration) { - m_toasts.emplace_back(std::string(message), duration); - } - - void ImGuiConsole::ShowToasts() { - if (m_toasts.empty()) { - return; - } - auto& toast = m_toasts.front(); - const float dt = ImGui::GetIO().DeltaTime; - toast.remain -= dt; - toast.current += dt; - - const ImGuiViewport* viewport = ImGui::GetMainViewport(); - const ImVec2 workPos = viewport->WorkPos; - const ImVec2 workSize = viewport->WorkSize; - constexpr float padding = 10.0f; - const ImVec2 windowPos{workPos.x + workSize.x / 2, workPos.y + workSize.y - padding}; - ImGui::SetNextWindowPos(windowPos, ImGuiCond_Always, ImVec2{0.5f, 1.f}); - - const float alpha = std::min({toast.remain, toast.current, 1.f}); - ImGui::SetNextWindowBgAlpha(alpha * 0.65f); - ImVec4 textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); - textColor.w *= alpha; - ImVec4 borderColor = ImGui::GetStyleColorVec4(ImGuiCol_Border); - borderColor.w *= alpha; - ImGui::PushStyleColor(ImGuiCol_Text, textColor); - ImGui::PushStyleColor(ImGuiCol_Border, borderColor); - if (ImGui::Begin("Toast", nullptr, - ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | - ImGuiWindowFlags_NoMove)) - { - ImGuiStringViewText(toast.message); - } - ImGui::End(); - ImGui::PopStyleColor(2); - - if (toast.remain <= 0.f) { - m_toasts.pop_front(); - } - } - void ImGuiConsole::ShowPipelineProgress() { const auto* stats = aurora_get_stats(); const u32 queuedPipelines = stats->queuedPipelines; diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index c1adc427eb..bc9f253156 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -24,29 +24,18 @@ class ImGuiConsole { void PostDraw(); static bool CheckMenuViewToggle(ImGuiKey key, bool& active); - void AddToast(std::string_view message, float duration = 3.f); private: - struct Toast { - std::string message; - float remain; - float current = 0.f; - Toast(std::string message, float duration) noexcept : message(std::move(message)), - remain(duration) {} - }; - float mouseHideTimer = 0.0f; bool m_isHidden = true; bool m_isLaunchInitialized = false; ImGuiWindow* m_dragScrollWindow = nullptr; ImVec2 m_dragScrollLastMousePos = {}; - std::deque m_toasts; // Keep always last ImGuiMenuTools m_menuTools; - void ShowToasts(); void ShowPipelineProgress(); void UpdateDragScroll(); }; @@ -60,7 +49,6 @@ std::string BytesToString(size_t bytes); void SetOverlayWindowLocation(int corner); bool ShowCornerContextMenu(int& corner, int avoidCorner); void ImGuiStringViewText(std::string_view text); -void DuskToast(std::string_view message, float duration = 3.f); void ImGuiBeginGroupPanel(const char* name, const ImVec2& size); void ImGuiEndGroupPanel(); void ImGuiTextCenter(std::string_view text); From 66de7748e8897ba80bd9e3e405ca9ff3316284d5 Mon Sep 17 00:00:00 2001 From: Irastris Date: Tue, 19 May 2026 23:09:08 -0400 Subject: [PATCH 06/21] Add Mouse Camera option in preparation for its use --- include/dusk/settings.h | 1 + src/dusk/settings.cpp | 2 ++ src/dusk/ui/settings.cpp | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index f2ddffb20d..b9f66ddfdc 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -184,6 +184,7 @@ struct UserSettings { ConfigVar gyroDeadband; ConfigVar gyroInvertPitch; ConfigVar gyroInvertYaw; + ConfigVar enableMouseCamera; ConfigVar enableMouseAim; ConfigVar mouseSensitivityX; ConfigVar mouseSensitivityY; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index df9b813739..954e4d36dc 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -84,6 +84,7 @@ UserSettings g_userSettings = { .gyroDeadband {"game.gyroDeadband", 0.04f}, .gyroInvertPitch {"game.gyroInvertPitch", false}, .gyroInvertYaw {"game.gyroInvertYaw", false}, + .enableMouseCamera {"game.enableMouseCamera", false}, .enableMouseAim {"game.enableMouseAim", false}, .mouseSensitivityX {"game.mouseSensitivityX", 1.0f}, .mouseSensitivityY {"game.mouseSensitivityY", 1.0f}, @@ -279,6 +280,7 @@ void registerSettings() { Register(g_userSettings.game.gyroSmoothing); Register(g_userSettings.game.gyroInvertPitch); Register(g_userSettings.game.gyroInvertYaw); + Register(g_userSettings.game.enableMouseCamera); Register(g_userSettings.game.enableMouseAim); Register(g_userSettings.game.mouseSensitivityX); Register(g_userSettings.game.mouseSensitivityY); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 30bf118855..afe8624f36 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -971,6 +971,10 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Invert horizontal gyro aiming.", [] { return !gyro_enabled(); }); leftPane.add_section("Mouse"); + addOption("Mouse Camera", getSettings().game.enableMouseCamera, + "Enables mouse input to rotate the third-person camera.

" + "Requires the Free Camera option to be enabled.", + [] { return !getSettings().game.freeCamera; }); addOption("Mouse Aim", getSettings().game.enableMouseAim, "Enables mouse input while in look mode, aiming a hawk, and aiming " "supported items.

Supported items include the Slingshot, Gale Boomerang, " From 8452c2d05e4b7bf0ada85c5b4eea84354e074ec9 Mon Sep 17 00:00:00 2001 From: Irastris Date: Tue, 19 May 2026 23:10:02 -0400 Subject: [PATCH 07/21] WIP, add mouse controls for the third-person camera --- include/dusk/mouse.h | 6 ++ include/dusk/settings.h | 6 +- src/d/d_camera.cpp | 22 ++++++ src/dusk/mouse.cpp | 158 ++++++++++++++++++++++++++++++++------- src/dusk/settings.cpp | 12 ++- src/dusk/ui/settings.cpp | 18 ++++- src/m_Do/m_Do_main.cpp | 4 + 7 files changed, 190 insertions(+), 36 deletions(-) diff --git a/include/dusk/mouse.h b/include/dusk/mouse.h index fc402c305c..514d14534a 100644 --- a/include/dusk/mouse.h +++ b/include/dusk/mouse.h @@ -1,6 +1,12 @@ #pragma once +#include + namespace dusk::mouse { void read(); void getAimDeltas(float& out_yaw, float& out_pitch); +void getCameraDeltas(float& out_yaw, float& out_pitch); +void handle_event(const SDL_Event& event) noexcept; +void onFocusLost(); +void onFocusGained(); } // namespace dusk::mouse diff --git a/include/dusk/settings.h b/include/dusk/settings.h index b9f66ddfdc..9cc6571552 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -186,8 +186,10 @@ struct UserSettings { ConfigVar gyroInvertYaw; ConfigVar enableMouseCamera; ConfigVar enableMouseAim; - ConfigVar mouseSensitivityX; - ConfigVar mouseSensitivityY; + ConfigVar mouseAimSensitivityX; + ConfigVar mouseAimSensitivityY; + ConfigVar mouseCameraSensitivityX; + ConfigVar mouseCameraSensitivityY; ConfigVar freeCamera; ConfigVar invertCameraXAxis; ConfigVar invertCameraYAxis; diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 0de75747f2..9e6f14fb3c 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -32,6 +32,8 @@ #include "dusk/frame_interpolation.h" #include "dusk/logging.h" #include "dusk/action_bindings.h" +#include "dusk/mouse.h" +#include "dusk/settings.h" #include "imgui.h" #endif @@ -7649,14 +7651,26 @@ bool dCamera_c::freeCamera() { return false; } + const bool mouse_camera_enabled = dusk::getSettings().game.enableMouseCamera.getValue(); + const bool is_targeting = dComIfGp_checkCameraAttentionStatus(dComIfGp_getPlayerCameraID(0), 0x8); + if (!mCamParam.mManualMode) { mCamParam.freeXAngle = mViewCache.mDirection.mAzimuth.Degree(); mCamParam.freeYAngle = mViewCache.mDirection.mInclination.Degree(); } + // Force freecam as active when mouse camera is enabled + if (mouse_camera_enabled && !is_targeting) { + mCamParam.mManualMode = 1; + } else { + mCamParam.mManualMode = 0; + } + cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f}; f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY); + // TODO: Refactor this so mouse and joystick can work together. Disabled for now. + /* // If we aren't in manual cam mode, don't trigger it if the player tries to hit C-up // for first person unless they have first person bound to a custom binding if ((dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID) && mPadInfo.mCStick.mLastPosY != 0) || @@ -7668,12 +7682,20 @@ bool dCamera_c::freeCamera() { mCamParam.freeXAngle += camMovement.x * magnitude * dusk::getSettings().game.freeCameraSensitivity * 5.0f; mCamParam.freeYAngle += camMovement.y * magnitude * dusk::getSettings().game.freeCameraSensitivity * 5.0f; } + */ fopAc_ac_c* player = dComIfGp_getPlayer(0); if (!mCamParam.mManualMode || player == nullptr) { return false; } + f32 yaw_rad = 0.0f; + f32 pitch_rad = 0.0f; + dusk::mouse::getCameraDeltas(yaw_rad, pitch_rad); + + mCamParam.freeXAngle += MTXRadToDeg(yaw_rad) * 1.0f; + mCamParam.freeYAngle += MTXRadToDeg(pitch_rad) * -1.0f; + f32 minYAngle = -30.0f; f32 maxAngle = 50.0f; diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 4deeb6984e..24200ef7f2 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -2,18 +2,25 @@ #include "dusk/settings.h" #include "dusk/ui/ui.hpp" #include "d/actor/d_a_alink.h" +#include "d/d_com_inf_game.h" #include #include +#include namespace dusk::mouse { namespace { constexpr float kMousePixelToRad = 0.0025f; -bool s_mouse_enabled = false; -bool s_mouse_relative = false; -float s_yaw_rad = 0.0f; -float s_pitch_rad = 0.0f; +float s_aim_yaw_rad = 0.0f; +float s_aim_pitch_rad = 0.0f; +float s_camera_yaw_rad = 0.0f; +float s_camera_pitch_rad = 0.0f; + +void reset_deltas() { + s_aim_yaw_rad = s_aim_pitch_rad = 0.0f; + s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; +} bool queryMouseAimContext() { if (!static_cast(getSettings().game.enableMouseAim)) { @@ -28,24 +35,53 @@ bool queryMouseAimContext() { return link->checkAimContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); } -void reset_aim_state() { - s_mouse_enabled = false; - s_yaw_rad = s_pitch_rad = 0.0f; +bool wantMouseCapture() { + return (static_cast(getSettings().game.enableMouseCamera) && + static_cast(getSettings().game.freeCamera)) || + static_cast(getSettings().game.enableMouseAim); } -} // namespace -void read() { - const bool aim_active = !ui::any_document_visible() && queryMouseAimContext(); +bool isWindowFocused(SDL_Window* window) { + if (window == nullptr) { + return false; + } + return (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0; +} - SDL_Window* window = aurora::window::get_sdl_window(); - if (window != nullptr && - aim_active != s_mouse_relative && - SDL_SetWindowRelativeMouseMode(window, aim_active)) - { - s_mouse_relative = aim_active; +bool shouldCaptureMouse(SDL_Window* window) { + if (window == nullptr) { + return false; + } + if (ui::any_document_visible()) { + return false; + } + return wantMouseCapture() && isWindowFocused(window); +} + +bool queryActualCaptureState(SDL_Window* window) { + if (window == nullptr) { + return false; + } + return SDL_GetWindowRelativeMouseMode(window); +} + +bool syncCaptureState(SDL_Window* window, bool should_capture) { + if (window == nullptr) { + reset_deltas(); + return false; + } + + if (!isWindowFocused(window)) { + should_capture = false; + } + + const bool was_captured = queryActualCaptureState(window); + if (was_captured != should_capture) { + SDL_SetWindowRelativeMouseMode(window, should_capture); } - if (!s_mouse_enabled && aim_active && window != nullptr) { + const bool is_captured = queryActualCaptureState(window); + if (is_captured && !was_captured) { const AuroraWindowSize sz = aurora::window::get_window_size(); const float cx = static_cast(sz.width) * 0.5f; const float cy = static_cast(sz.height) * 0.5f; @@ -54,25 +90,95 @@ void read() { float discard_y = 0.0f; SDL_GetRelativeMouseState(&discard_x, &discard_y); } - s_mouse_enabled = aim_active; - if (!aim_active) { - reset_aim_state(); + if (!is_captured) { + reset_deltas(); + } + + return is_captured; +} + +void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_active) { + const auto& game = getSettings().game; + + if (camera_active) { + s_camera_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseCameraSensitivityX.getValue(); + s_camera_pitch_rad = -my_rel * kMousePixelToRad * game.mouseCameraSensitivityY.getValue(); + s_camera_yaw_rad = game.enableMirrorMode.getValue() ? -s_camera_yaw_rad : s_camera_yaw_rad; + } else { + s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; + } + + if (aim_active) { + s_aim_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseAimSensitivityX.getValue(); + s_aim_pitch_rad = my_rel * kMousePixelToRad * game.mouseAimSensitivityY.getValue(); + s_aim_yaw_rad = game.enableMirrorMode.getValue() ? -s_aim_yaw_rad : s_aim_yaw_rad; + } else { + s_aim_yaw_rad = s_aim_pitch_rad = 0.0f; + } +} +} // namespace + +void read() { + SDL_Window* window = aurora::window::get_sdl_window(); + const bool capture_active = syncCaptureState(window, shouldCaptureMouse(window)); + + if (!capture_active) { return; } + const bool aim_active = capture_active && queryMouseAimContext(); + const bool camera_active = capture_active && + static_cast(getSettings().game.enableMouseCamera) && + static_cast(getSettings().game.freeCamera); + float mx_rel = 0.0f; float my_rel = 0.0f; SDL_GetRelativeMouseState(&mx_rel, &my_rel); + accumulateDeltas(mx_rel, my_rel, camera_active, aim_active); +} + +void getAimDeltas(float& out_yaw, float& out_pitch) { + out_yaw = s_aim_yaw_rad; + out_pitch = s_aim_pitch_rad; +} - s_yaw_rad = -mx_rel * kMousePixelToRad * getSettings().game.mouseSensitivityX; - s_pitch_rad = my_rel * kMousePixelToRad * getSettings().game.mouseSensitivityY; +void getCameraDeltas(float& out_yaw, float& out_pitch) { + out_yaw = 0.0f; + out_pitch = 0.0f; - s_yaw_rad = getSettings().game.enableMirrorMode ? -s_yaw_rad : s_yaw_rad; + if (!static_cast(getSettings().game.enableMouseCamera) || + !static_cast(getSettings().game.freeCamera)) + { + return; + } + + out_yaw = s_camera_yaw_rad; + out_pitch = s_camera_pitch_rad; } -void getAimDeltas(float& out_yaw, float& out_pitch) { - out_yaw = s_yaw_rad; - out_pitch = s_pitch_rad; +void handle_event(const SDL_Event& event) noexcept { + switch (event.type) { + case SDL_EVENT_WINDOW_FOCUS_LOST: + onFocusLost(); + break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: + onFocusGained(); + break; + } +} + +void onFocusLost() { + SDL_Window* window = aurora::window::get_sdl_window(); + if (window != nullptr) { + SDL_SetWindowRelativeMouseMode(window, false); + } + SDL_ShowCursor(); + reset_deltas(); +} + +void onFocusGained() { + SDL_Window* window = aurora::window::get_sdl_window(); + syncCaptureState(window, shouldCaptureMouse(window)); } } // namespace dusk::mouse diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 954e4d36dc..c2e699fdc9 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -86,8 +86,10 @@ UserSettings g_userSettings = { .gyroInvertYaw {"game.gyroInvertYaw", false}, .enableMouseCamera {"game.enableMouseCamera", false}, .enableMouseAim {"game.enableMouseAim", false}, - .mouseSensitivityX {"game.mouseSensitivityX", 1.0f}, - .mouseSensitivityY {"game.mouseSensitivityY", 1.0f}, + .mouseAimSensitivityX {"game.mouseAimSensitivityX", 1.0f}, + .mouseAimSensitivityY {"game.mouseAimSensitivityY", 1.0f}, + .mouseCameraSensitivityX {"game.mouseCameraSensitivityX", 1.0f}, + .mouseCameraSensitivityY {"game.mouseCameraSensitivityY", 1.0f}, .freeCamera {"game.freeCamera", false}, .invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraYAxis {"game.invertCameraYAxis", false}, @@ -282,8 +284,10 @@ void registerSettings() { Register(g_userSettings.game.gyroInvertYaw); Register(g_userSettings.game.enableMouseCamera); Register(g_userSettings.game.enableMouseAim); - Register(g_userSettings.game.mouseSensitivityX); - Register(g_userSettings.game.mouseSensitivityY); + Register(g_userSettings.game.mouseAimSensitivityX); + Register(g_userSettings.game.mouseAimSensitivityY); + Register(g_userSettings.game.mouseCameraSensitivityX); + Register(g_userSettings.game.mouseCameraSensitivityY); Register(g_userSettings.game.freeCamera); Register(g_userSettings.game.debugFlyCam); Register(g_userSettings.game.debugFlyCamLockEvents); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index afe8624f36..0e18de537e 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -979,11 +979,21 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Enables mouse input while in look mode, aiming a hawk, and aiming " "supported items.

Supported items include the Slingshot, Gale Boomerang, " "Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod."); - config_percent_select(leftPane, rightPane, getSettings().game.mouseSensitivityX, - "Mouse X Sensitivity", "Controls horizontal mouse sensitivity.", 25, 400, 5, + config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivityX, + "Mouse Camera X Sensitivity", "Controls horizontal mouse camera sensitivity.", 25, 400, 5, + [] { + return !getSettings().game.enableMouseCamera || !getSettings().game.freeCamera; + }); + config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivityY, + "Mouse Camera Y Sensitivity", "Controls vertical mouse camera sensitivity.", 25, 400, 5, + [] { + return !getSettings().game.enableMouseCamera || !getSettings().game.freeCamera; + }); + config_percent_select(leftPane, rightPane, getSettings().game.mouseAimSensitivityX, + "Mouse Aim X Sensitivity", "Controls horizontal mouse aim sensitivity.", 25, 400, 5, [] { return !getSettings().game.enableMouseAim; }); - config_percent_select(leftPane, rightPane, getSettings().game.mouseSensitivityY, - "Mouse Y Sensitivity", "Controls vertical mouse sensitivity.", 25, 400, 5, + config_percent_select(leftPane, rightPane, getSettings().game.mouseAimSensitivityY, + "Mouse Aim Y Sensitivity", "Controls vertical mouse aim sensitivity.", 25, 400, 5, [] { return !getSettings().game.enableMouseAim; }); leftPane.add_section("Tools"); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 10bd8f4ad5..3d3e419bb7 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -167,6 +167,7 @@ bool launchUILoop() { while (event != nullptr && event->type != AURORA_NONE) { switch (event->type) { case AURORA_SDL_EVENT: + dusk::mouse::handle_event(event->sdl); dusk::ui::handle_event(event->sdl); dusk::g_imguiConsole.HandleSDLEvent(event->sdl); break; @@ -245,12 +246,15 @@ void main01(void) { goto eventsDone; case AURORA_PAUSED: dusk::audio::SetPaused(true); + dusk::mouse::onFocusLost(); break; case AURORA_UNPAUSED: dusk::audio::SetPaused(false); dusk::game_clock::reset_frame_timer(); + dusk::mouse::onFocusGained(); break; case AURORA_SDL_EVENT: + dusk::mouse::handle_event(event->sdl); dusk::ui::handle_event(event->sdl); dusk::g_imguiConsole.HandleSDLEvent(event->sdl); break; From 3ec9078b2bd1eca5188d6200a6ccb6c42a34e59e Mon Sep 17 00:00:00 2001 From: Irastris Date: Fri, 22 May 2026 19:48:59 -0400 Subject: [PATCH 08/21] Various helpText revisions --- src/dusk/ui/settings.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 0e18de537e..bdc6f1a568 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -447,7 +447,7 @@ SelectButton& config_percent_select(Pane& leftPane, Pane& rightPane, ConfigVar
Must be enabled in order to use Mouse Camera."); + addOption("Invert Free Camera X Axis", getSettings().game.invertCameraXAxis, + "Invert horizontal free camera movement.

Applies to joystick input only.", [] { return !getSettings().game.freeCamera; }); - config_percent_select(leftPane, rightPane, getSettings().game.freeCameraSensitivity, - "Free Camera Sensitivity", "Adjusts twin-stick camera sensitivity.", 50, 200, 5, + addOption("Invert Free Camera Y Axis", getSettings().game.invertCameraYAxis, + "Invert vertical free camera movement.

Applies to joystick input only.", [] { return !getSettings().game.freeCamera; }); + config_percent_select(leftPane, rightPane, getSettings().game.freeCameraSensitivity, + "Free Camera Sensitivity", + "Adjusts free camera sensitivity.

Applies to joystick input only.", + 50, 200, 5, [] { return !getSettings().game.freeCamera; }); addOption("Invert First Person X Axis", getSettings().game.invertFirstPersonXAxis, "Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); addOption("Invert First Person Y Axis", getSettings().game.invertFirstPersonYAxis, From 7f5a3320d4f1362a154a2044d4363bd06b8031f0 Mon Sep 17 00:00:00 2001 From: Irastris Date: Mon, 25 May 2026 14:00:19 -0400 Subject: [PATCH 09/21] Enable free camera on horseback --- src/d/d_camera.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 9e6f14fb3c..b09b1a4650 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -9372,6 +9372,10 @@ bool dCamera_c::rideCamera(s32 param_0) { mStyleSettle.mFinished = true; } +#if TARGET_PC + freeCamera(); +#endif + return true; } @@ -9501,6 +9505,10 @@ bool dCamera_c::rideCamera(s32 param_0) { setFlag(0x400); } +#if TARGET_PC + freeCamera(); +#endif + return true; } From 271e02036c4b5e6f702b7c2cfbfbbad325528d7b Mon Sep 17 00:00:00 2001 From: Irastris Date: Tue, 26 May 2026 19:01:40 -0400 Subject: [PATCH 10/21] Untie mouse camera and free camera options Either being enabled now allows the underlying freecam logic to run --- include/d/d_camera.h | 1 + src/d/d_camera.cpp | 8 ++++++-- src/dusk/mouse.cpp | 12 ++++-------- src/dusk/ui/settings.cpp | 15 ++++++--------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/include/d/d_camera.h b/include/d/d_camera.h index b694933461..abe97c0f0c 100644 --- a/include/d/d_camera.h +++ b/include/d/d_camera.h @@ -1037,6 +1037,7 @@ class dCamera_c { bool test1Camera(s32); bool test2Camera(s32); #if TARGET_PC + static bool canUseFreeCam(); bool freeCamera(); bool executeDebugFlyCam(); void deactivateDebugFlyCam(); diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index b09b1a4650..3ac2187474 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -7640,12 +7640,16 @@ void dCamera_c::deactivateDebugFlyCam() { mDebugFlyCam.initialized = false; } +bool dCamera_c::canUseFreeCam() { + return dusk::getSettings().game.freeCamera || dusk::getSettings().game.enableMouseCamera; +} + bool dCamera_c::freeCamera() { - if (dusk::getSettings().game.freeCamera && mGear == 1) { + if (canUseFreeCam() && mGear == 1) { mGear = 0; } - if (!dusk::getSettings().game.freeCamera || mCamStyle == 70) + if (!canUseFreeCam() || mCamStyle == 70) { mCamParam.mManualMode = 0; return false; diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 24200ef7f2..f475cc9934 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -36,9 +36,8 @@ bool queryMouseAimContext() { } bool wantMouseCapture() { - return (static_cast(getSettings().game.enableMouseCamera) && - static_cast(getSettings().game.freeCamera)) || - static_cast(getSettings().game.enableMouseAim); + const auto game = getSettings().game; + return game.enableMouseCamera.getValue() || game.enableMouseAim.getValue(); } bool isWindowFocused(SDL_Window* window) { @@ -128,9 +127,7 @@ void read() { } const bool aim_active = capture_active && queryMouseAimContext(); - const bool camera_active = capture_active && - static_cast(getSettings().game.enableMouseCamera) && - static_cast(getSettings().game.freeCamera); + const bool camera_active = capture_active && getSettings().game.enableMouseCamera; float mx_rel = 0.0f; float my_rel = 0.0f; @@ -147,8 +144,7 @@ void getCameraDeltas(float& out_yaw, float& out_pitch) { out_yaw = 0.0f; out_pitch = 0.0f; - if (!static_cast(getSettings().game.enableMouseCamera) || - !static_cast(getSettings().game.freeCamera)) + if (!getSettings().game.enableMouseCamera) { return; } diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index daf63fd0b5..5189f5344c 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -941,13 +941,12 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { leftPane.add_section("Camera"); addOption("Free Camera", getSettings().game.freeCamera, - "Enables free camera control, letting you control the camera fully with the C-Stick." - "

Must be enabled in order to use Mouse Camera."); + "Enables free camera control, letting you control the camera fully with the C-Stick."); addOption("Invert Free Camera X Axis", getSettings().game.invertCameraXAxis, - "Invert horizontal free camera movement.

Applies to joystick input only.", + "Invert horizontal free camera movement.

Applies to the control stick only.", [] { return !getSettings().game.freeCamera; }); addOption("Invert Free Camera Y Axis", getSettings().game.invertCameraYAxis, - "Invert vertical free camera movement.

Applies to joystick input only.", + "Invert vertical free camera movement.

Applies to the control stick only.", [] { return !getSettings().game.freeCamera; }); config_percent_select(leftPane, rightPane, getSettings().game.freeCameraSensitivity, "Free Camera Sensitivity", @@ -992,9 +991,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { leftPane.add_section("Mouse"); addOption("Mouse Camera", getSettings().game.enableMouseCamera, - "Enables mouse input to rotate the third-person camera.

" - "Requires the Free Camera option to be enabled.", - [] { return !getSettings().game.freeCamera; }); + "Enables mouse input for controlling the third-person camera."); addOption("Mouse Aim", getSettings().game.enableMouseAim, "Enables mouse input while in look mode, aiming a hawk, and aiming " "supported items.

Supported items include the Slingshot, Gale Boomerang, " @@ -1002,12 +999,12 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivityX, "Mouse Camera X Sensitivity", "Controls horizontal mouse camera sensitivity.", 25, 400, 5, [] { - return !getSettings().game.enableMouseCamera || !getSettings().game.freeCamera; + return !getSettings().game.enableMouseCamera; }); config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivityY, "Mouse Camera Y Sensitivity", "Controls vertical mouse camera sensitivity.", 25, 400, 5, [] { - return !getSettings().game.enableMouseCamera || !getSettings().game.freeCamera; + return !getSettings().game.enableMouseCamera; }); config_percent_select(leftPane, rightPane, getSettings().game.mouseAimSensitivityX, "Mouse Aim X Sensitivity", "Controls horizontal mouse aim sensitivity.", 25, 400, 5, From 284aec12f4db4a76fc815dc4db5095cecf921331 Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 27 May 2026 02:16:40 -0400 Subject: [PATCH 11/21] Allow simultaneous C-stick and mouse input --- src/d/d_camera.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 3ac2187474..298156742e 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -7655,26 +7655,14 @@ bool dCamera_c::freeCamera() { return false; } - const bool mouse_camera_enabled = dusk::getSettings().game.enableMouseCamera.getValue(); - const bool is_targeting = dComIfGp_checkCameraAttentionStatus(dComIfGp_getPlayerCameraID(0), 0x8); - if (!mCamParam.mManualMode) { mCamParam.freeXAngle = mViewCache.mDirection.mAzimuth.Degree(); mCamParam.freeYAngle = mViewCache.mDirection.mInclination.Degree(); } - // Force freecam as active when mouse camera is enabled - if (mouse_camera_enabled && !is_targeting) { - mCamParam.mManualMode = 1; - } else { - mCamParam.mManualMode = 0; - } - cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f}; f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY); - // TODO: Refactor this so mouse and joystick can work together. Disabled for now. - /* // If we aren't in manual cam mode, don't trigger it if the player tries to hit C-up // for first person unless they have first person bound to a custom binding if ((dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID) && mPadInfo.mCStick.mLastPosY != 0) || @@ -7686,19 +7674,22 @@ bool dCamera_c::freeCamera() { mCamParam.freeXAngle += camMovement.x * magnitude * dusk::getSettings().game.freeCameraSensitivity * 5.0f; mCamParam.freeYAngle += camMovement.y * magnitude * dusk::getSettings().game.freeCameraSensitivity * 5.0f; } - */ - - fopAc_ac_c* player = dComIfGp_getPlayer(0); - if (!mCamParam.mManualMode || player == nullptr) { - return false; - } f32 yaw_rad = 0.0f; f32 pitch_rad = 0.0f; dusk::mouse::getCameraDeltas(yaw_rad, pitch_rad); + if (dusk::getSettings().game.enableMouseCamera && (yaw_rad != 0.0f || pitch_rad != 0.0f) && + !dComIfGp_checkCameraAttentionStatus(dComIfGp_getPlayerCameraID(0), 0x8)) + { + mCamParam.mManualMode = 1; + mCamParam.freeXAngle += MTXRadToDeg(yaw_rad); + mCamParam.freeYAngle += -MTXRadToDeg(pitch_rad); + } - mCamParam.freeXAngle += MTXRadToDeg(yaw_rad) * 1.0f; - mCamParam.freeYAngle += MTXRadToDeg(pitch_rad) * -1.0f; + fopAc_ac_c* player = dComIfGp_getPlayer(0); + if (!mCamParam.mManualMode || player == nullptr) { + return false; + } f32 minYAngle = -30.0f; f32 maxAngle = 50.0f; From 55165679c9522e19faafbd8825fc6e5f30b08073 Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 27 May 2026 14:15:26 -0400 Subject: [PATCH 12/21] Combine mouse sensitivities for both aim and camera --- include/dusk/settings.h | 6 ++---- src/dusk/mouse.cpp | 8 ++++---- src/dusk/settings.cpp | 12 ++++-------- src/dusk/ui/settings.cpp | 24 +++++++----------------- 4 files changed, 17 insertions(+), 33 deletions(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 717a0eeb2f..c23cb6a024 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -198,10 +198,8 @@ struct UserSettings { ConfigVar gyroInvertYaw; ConfigVar enableMouseCamera; ConfigVar enableMouseAim; - ConfigVar mouseAimSensitivityX; - ConfigVar mouseAimSensitivityY; - ConfigVar mouseCameraSensitivityX; - ConfigVar mouseCameraSensitivityY; + ConfigVar mouseAimSensitivity; + ConfigVar mouseCameraSensitivity; ConfigVar freeCamera; ConfigVar invertCameraXAxis; ConfigVar invertCameraYAxis; diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index f475cc9934..22d33df799 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -101,16 +101,16 @@ void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_a const auto& game = getSettings().game; if (camera_active) { - s_camera_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseCameraSensitivityX.getValue(); - s_camera_pitch_rad = -my_rel * kMousePixelToRad * game.mouseCameraSensitivityY.getValue(); + s_camera_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); + s_camera_pitch_rad = -my_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); s_camera_yaw_rad = game.enableMirrorMode.getValue() ? -s_camera_yaw_rad : s_camera_yaw_rad; } else { s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; } if (aim_active) { - s_aim_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseAimSensitivityX.getValue(); - s_aim_pitch_rad = my_rel * kMousePixelToRad * game.mouseAimSensitivityY.getValue(); + s_aim_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseAimSensitivity.getValue(); + s_aim_pitch_rad = my_rel * kMousePixelToRad * game.mouseAimSensitivity.getValue(); s_aim_yaw_rad = game.enableMirrorMode.getValue() ? -s_aim_yaw_rad : s_aim_yaw_rad; } else { s_aim_yaw_rad = s_aim_pitch_rad = 0.0f; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 13d98df14b..043b3c43c8 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -86,10 +86,8 @@ UserSettings g_userSettings = { .gyroInvertYaw {"game.gyroInvertYaw", false}, .enableMouseCamera {"game.enableMouseCamera", false}, .enableMouseAim {"game.enableMouseAim", false}, - .mouseAimSensitivityX {"game.mouseAimSensitivityX", 1.0f}, - .mouseAimSensitivityY {"game.mouseAimSensitivityY", 1.0f}, - .mouseCameraSensitivityX {"game.mouseCameraSensitivityX", 1.0f}, - .mouseCameraSensitivityY {"game.mouseCameraSensitivityY", 1.0f}, + .mouseAimSensitivity {"game.mouseAimSensitivity", 1.0f}, + .mouseCameraSensitivity {"game.mouseCameraSensitivity", 1.0f}, .freeCamera {"game.freeCamera", false}, .invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraYAxis {"game.invertCameraYAxis", false}, @@ -288,10 +286,8 @@ void registerSettings() { Register(g_userSettings.game.gyroInvertYaw); Register(g_userSettings.game.enableMouseCamera); Register(g_userSettings.game.enableMouseAim); - Register(g_userSettings.game.mouseAimSensitivityX); - Register(g_userSettings.game.mouseAimSensitivityY); - Register(g_userSettings.game.mouseCameraSensitivityX); - Register(g_userSettings.game.mouseCameraSensitivityY); + Register(g_userSettings.game.mouseAimSensitivity); + Register(g_userSettings.game.mouseCameraSensitivity); Register(g_userSettings.game.freeCamera); Register(g_userSettings.game.debugFlyCam); Register(g_userSettings.game.debugFlyCamLockEvents); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 255f946122..ee8ba530a6 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -990,28 +990,18 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Invert horizontal gyro aiming.", [] { return !gyro_enabled(); }); leftPane.add_section("Mouse"); - addOption("Mouse Camera", getSettings().game.enableMouseCamera, - "Enables mouse input for controlling the third-person camera."); addOption("Mouse Aim", getSettings().game.enableMouseAim, "Enables mouse input while in look mode, aiming a hawk, and aiming " "supported items.

Supported items include the Slingshot, Gale Boomerang, " "Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod."); - config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivityX, - "Mouse Camera X Sensitivity", "Controls horizontal mouse camera sensitivity.", 25, 400, 5, - [] { - return !getSettings().game.enableMouseCamera; - }); - config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivityY, - "Mouse Camera Y Sensitivity", "Controls vertical mouse camera sensitivity.", 25, 400, 5, - [] { - return !getSettings().game.enableMouseCamera; - }); - config_percent_select(leftPane, rightPane, getSettings().game.mouseAimSensitivityX, - "Mouse Aim X Sensitivity", "Controls horizontal mouse aim sensitivity.", 25, 400, 5, - [] { return !getSettings().game.enableMouseAim; }); - config_percent_select(leftPane, rightPane, getSettings().game.mouseAimSensitivityY, - "Mouse Aim Y Sensitivity", "Controls vertical mouse aim sensitivity.", 25, 400, 5, + addOption("Mouse Camera", getSettings().game.enableMouseCamera, + "Enables mouse input for controlling the third-person camera."); + config_percent_select(leftPane, rightPane, getSettings().game.mouseAimSensitivity, + "Mouse Aim Sensitivity", "Controls mouse aim sensitivity.", 25, 400, 5, [] { return !getSettings().game.enableMouseAim; }); + config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivity, + "Mouse Camera Sensitivity", "Controls mouse camera sensitivity.", 25, 400, 5, + [] { return !getSettings().game.enableMouseCamera; }); leftPane.add_section("Tools"); addOption("Turbo Key", getSettings().game.enableTurboKeybind, From f609ff5bce7d9e7204ae867c019f20d53956288b Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 27 May 2026 14:36:20 -0400 Subject: [PATCH 13/21] Add option for inverting mouse Y --- include/dusk/settings.h | 1 + src/dusk/mouse.cpp | 18 ++++++++++-------- src/dusk/settings.cpp | 2 ++ src/dusk/ui/settings.cpp | 3 +++ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index c23cb6a024..456ad7553c 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -200,6 +200,7 @@ struct UserSettings { ConfigVar enableMouseAim; ConfigVar mouseAimSensitivity; ConfigVar mouseCameraSensitivity; + ConfigVar invertMouseY; ConfigVar freeCamera; ConfigVar invertCameraXAxis; ConfigVar invertCameraYAxis; diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 22d33df799..2f4b8e4e29 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -100,21 +100,23 @@ bool syncCaptureState(SDL_Window* window, bool should_capture) { void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_active) { const auto& game = getSettings().game; - if (camera_active) { - s_camera_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); - s_camera_pitch_rad = -my_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); - s_camera_yaw_rad = game.enableMirrorMode.getValue() ? -s_camera_yaw_rad : s_camera_yaw_rad; - } else { - s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; - } - if (aim_active) { s_aim_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseAimSensitivity.getValue(); s_aim_pitch_rad = my_rel * kMousePixelToRad * game.mouseAimSensitivity.getValue(); s_aim_yaw_rad = game.enableMirrorMode.getValue() ? -s_aim_yaw_rad : s_aim_yaw_rad; + s_aim_pitch_rad = game.invertMouseY.getValue() ? -s_aim_pitch_rad : s_aim_pitch_rad; } else { s_aim_yaw_rad = s_aim_pitch_rad = 0.0f; } + + if (camera_active) { + s_camera_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); + s_camera_pitch_rad = -my_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); + s_camera_yaw_rad = game.enableMirrorMode.getValue() ? -s_camera_yaw_rad : s_camera_yaw_rad; + s_camera_pitch_rad = game.invertMouseY.getValue() ? -s_camera_pitch_rad : s_camera_pitch_rad; + } else { + s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; + } } } // namespace diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 043b3c43c8..3d1e3a70b6 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -88,6 +88,7 @@ UserSettings g_userSettings = { .enableMouseAim {"game.enableMouseAim", false}, .mouseAimSensitivity {"game.mouseAimSensitivity", 1.0f}, .mouseCameraSensitivity {"game.mouseCameraSensitivity", 1.0f}, + .invertMouseY {"game.invertMouseY", false}, .freeCamera {"game.freeCamera", false}, .invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraYAxis {"game.invertCameraYAxis", false}, @@ -288,6 +289,7 @@ void registerSettings() { Register(g_userSettings.game.enableMouseAim); Register(g_userSettings.game.mouseAimSensitivity); Register(g_userSettings.game.mouseCameraSensitivity); + Register(g_userSettings.game.invertMouseY); Register(g_userSettings.game.freeCamera); Register(g_userSettings.game.debugFlyCam); Register(g_userSettings.game.debugFlyCamLockEvents); diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index ee8ba530a6..3eafeaac7a 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -1002,6 +1002,9 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { config_percent_select(leftPane, rightPane, getSettings().game.mouseCameraSensitivity, "Mouse Camera Sensitivity", "Controls mouse camera sensitivity.", 25, 400, 5, [] { return !getSettings().game.enableMouseCamera; }); + addOption("Invert Mouse Y", getSettings().game.invertMouseY, + "Invert vertical mouse control for both aiming and camera.", + [] { return !getSettings().game.enableMouseAim || !getSettings().game.enableMouseCamera; }); leftPane.add_section("Tools"); addOption("Turbo Key", getSettings().game.enableTurboKeybind, From fa6d94e350854a2aa6ca264d6f79fad877aa7d30 Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 27 May 2026 20:39:50 -0400 Subject: [PATCH 14/21] Refactor cursor visibility handling --- src/dusk/imgui/ImGuiConsole.cpp | 15 ------------ src/dusk/imgui/ImGuiConsole.hpp | 1 - src/dusk/mouse.cpp | 43 ++++++++++++++++++++++++++++++++- src/dusk/ui/document.cpp | 15 ------------ src/dusk/ui/document.hpp | 2 -- src/dusk/ui/menu_bar.cpp | 2 -- src/dusk/ui/prelaunch.cpp | 2 -- 7 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index f15377abd1..5248bc9d36 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -12,7 +12,6 @@ #include "ImGuiConsole.hpp" #include "ImGuiEngine.hpp" #include "JSystem/JUtility/JUTGamePad.h" -#include "SDL3/SDL_mouse.h" #include "dusk/action_bindings.h" #include "dusk/audio/DuskAudioSystem.h" #include "dusk/config.hpp" @@ -372,20 +371,6 @@ namespace dusk { m_menuTools.ShowActorSpawner(); } - // Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds. - if (!dusk::getSettings().game.enableMouseAim) - { - ImGuiIO& io = ImGui::GetIO(); - if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) { - mouseHideTimer = 0.0f; - ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; // Imgui will re-show cursor. - } else if (mouseHideTimer <= 3.0f) { - mouseHideTimer += ImGui::GetIO().DeltaTime; - } else { - ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; - SDL_HideCursor(); - } - } } void ImGuiConsole::PostDraw() { diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index bc9f253156..d97f1b00e6 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -26,7 +26,6 @@ class ImGuiConsole { static bool CheckMenuViewToggle(ImGuiKey key, bool& active); private: - float mouseHideTimer = 0.0f; bool m_isHidden = true; bool m_isLaunchInitialized = false; diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 2f4b8e4e29..1a9ce18fc0 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -5,17 +5,20 @@ #include "d/d_com_inf_game.h" #include +#include #include #include namespace dusk::mouse { namespace { constexpr float kMousePixelToRad = 0.0025f; +constexpr int kIdleHideFrames = 99; // Approx. 3 seconds with 33ms ticks float s_aim_yaw_rad = 0.0f; float s_aim_pitch_rad = 0.0f; float s_camera_yaw_rad = 0.0f; float s_camera_pitch_rad = 0.0f; +int s_idle_frames = 0; void reset_deltas() { s_aim_yaw_rad = s_aim_pitch_rad = 0.0f; @@ -118,11 +121,48 @@ void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_a s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; } } + +void set_cursor_visible(bool visible) { + if (visible) { + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; + SDL_ShowCursor(); + } else { + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; + SDL_HideCursor(); + } +} + +void update_cursor_visibility(SDL_Window* window, bool captured) { + if (window == nullptr || !isWindowFocused(window)) { + return; + } + + if (captured) { + s_idle_frames = 0; + set_cursor_visible(false); + return; + } + + const ImGuiIO& io = ImGui::GetIO(); + if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) { + s_idle_frames = 0; + set_cursor_visible(true); + return; + } + + if (s_idle_frames < kIdleHideFrames) { + ++s_idle_frames; + set_cursor_visible(true); + } else { + set_cursor_visible(false); + } +} } // namespace void read() { SDL_Window* window = aurora::window::get_sdl_window(); const bool capture_active = syncCaptureState(window, shouldCaptureMouse(window)); + update_cursor_visibility(window, capture_active); if (!capture_active) { return; @@ -171,7 +211,8 @@ void onFocusLost() { if (window != nullptr) { SDL_SetWindowRelativeMouseMode(window, false); } - SDL_ShowCursor(); + s_idle_frames = 0; + set_cursor_visible(true); reset_deltas(); } diff --git a/src/dusk/ui/document.cpp b/src/dusk/ui/document.cpp index cb70578e30..a7bcc3f9ed 100644 --- a/src/dusk/ui/document.cpp +++ b/src/dusk/ui/document.cpp @@ -5,7 +5,6 @@ #include "Z2AudioLib/Z2SeMgr.h" #include "m_Do/m_Do_audio.h" -#include namespace dusk::ui { namespace { @@ -107,7 +106,6 @@ bool Document::visible() const { bool Document::handle_nav_command(Rml::Event& event, NavCommand cmd) { if (cmd == NavCommand::Menu) { - toggle_cursor_if_gyro(!visible()); mDoAud_seStartMenu(visible() ? kSoundMenuClose : kSoundMenuOpen); toggle(); return true; @@ -115,17 +113,4 @@ bool Document::handle_nav_command(Rml::Event& event, NavCommand cmd) { return false; } -void Document::toggle_cursor_if_gyro(bool cursor_enabled) { - if (dusk::getSettings().game.enableMouseAim) - { - if (cursor_enabled) { - ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; - SDL_ShowCursor(); - } else { - ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; - SDL_HideCursor(); - } - } -} - } // namespace dusk::ui diff --git a/src/dusk/ui/document.hpp b/src/dusk/ui/document.hpp index eed489beb3..d0f4cae841 100644 --- a/src/dusk/ui/document.hpp +++ b/src/dusk/ui/document.hpp @@ -43,8 +43,6 @@ class Document { bool pending_close() const { return mPendingClose; } bool closed() const { return mClosed; } - void toggle_cursor_if_gyro(bool); - protected: virtual bool handle_nav_command(Rml::Event& event, NavCommand cmd); diff --git a/src/dusk/ui/menu_bar.cpp b/src/dusk/ui/menu_bar.cpp index 1be5c7abb0..068913d592 100644 --- a/src/dusk/ui/menu_bar.cpp +++ b/src/dusk/ui/menu_bar.cpp @@ -45,7 +45,6 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById( mTabBar = std::make_unique(mRoot, TabBar::Props{ .onClose = [this] { - toggle_cursor_if_gyro(false); mDoAud_seStartMenu(kSoundMenuClose); hide(false); }, @@ -219,7 +218,6 @@ bool MenuBar::handle_nav_command(Rml::Event& event, NavCommand cmd) { return true; } if (cmd == NavCommand::Cancel && visible()) { - toggle_cursor_if_gyro(false); mDoAud_seStartMenu(kSoundMenuClose); hide(false); return true; diff --git a/src/dusk/ui/prelaunch.cpp b/src/dusk/ui/prelaunch.cpp index ef6b6e5709..44b73b75ef 100644 --- a/src/dusk/ui/prelaunch.cpp +++ b/src/dusk/ui/prelaunch.cpp @@ -699,8 +699,6 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB return; } - toggle_cursor_if_gyro(false); - mDoAud_seStartMenu(kSoundPlay); show_menu_notification(); From 77f6a5abaac759a83fd275ee682f482d2cff9406 Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 27 May 2026 21:08:51 -0400 Subject: [PATCH 15/21] Tighten aim capture condition and constrain cursor to window region --- src/dusk/mouse.cpp | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 1a9ce18fc0..c32f77afb3 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -26,7 +26,7 @@ void reset_deltas() { } bool queryMouseAimContext() { - if (!static_cast(getSettings().game.enableMouseAim)) { + if (!getSettings().game.enableMouseAim) { return false; } @@ -39,8 +39,11 @@ bool queryMouseAimContext() { } bool wantMouseCapture() { - const auto game = getSettings().game; - return game.enableMouseCamera.getValue() || game.enableMouseAim.getValue(); + return getSettings().game.enableMouseCamera.getValue() || queryMouseAimContext(); +} + +bool wantMouseGrab() { + return getSettings().game.enableMouseCamera.getValue() || getSettings().game.enableMouseAim.getValue(); } bool isWindowFocused(SDL_Window* window) { @@ -51,13 +54,17 @@ bool isWindowFocused(SDL_Window* window) { } bool shouldCaptureMouse(SDL_Window* window) { - if (window == nullptr) { + if (window == nullptr || ui::any_document_visible()) { return false; } - if (ui::any_document_visible()) { + return wantMouseCapture() && isWindowFocused(window); +} + +bool shouldGrabMouse(SDL_Window* window) { + if (window == nullptr) { return false; } - return wantMouseCapture() && isWindowFocused(window); + return wantMouseGrab() && isWindowFocused(window); } bool queryActualCaptureState(SDL_Window* window) { @@ -67,6 +74,13 @@ bool queryActualCaptureState(SDL_Window* window) { return SDL_GetWindowRelativeMouseMode(window); } +bool queryActualGrabState(SDL_Window* window) { + if (window == nullptr) { + return false; + } + return SDL_GetWindowMouseGrab(window); +} + bool syncCaptureState(SDL_Window* window, bool should_capture) { if (window == nullptr) { reset_deltas(); @@ -100,6 +114,17 @@ bool syncCaptureState(SDL_Window* window, bool should_capture) { return is_captured; } +void syncGrabState(SDL_Window* window, bool should_grab) { + if (window == nullptr) { + return; + } + + const bool was_grabbed = queryActualGrabState(window); + if (was_grabbed != should_grab) { + SDL_SetWindowMouseGrab(window, should_grab); + } +} + void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_active) { const auto& game = getSettings().game; @@ -163,6 +188,7 @@ void read() { SDL_Window* window = aurora::window::get_sdl_window(); const bool capture_active = syncCaptureState(window, shouldCaptureMouse(window)); update_cursor_visibility(window, capture_active); + syncGrabState(window, shouldGrabMouse(window)); if (!capture_active) { return; @@ -210,6 +236,7 @@ void onFocusLost() { SDL_Window* window = aurora::window::get_sdl_window(); if (window != nullptr) { SDL_SetWindowRelativeMouseMode(window, false); + syncGrabState(window, false); } s_idle_frames = 0; set_cursor_visible(true); @@ -219,5 +246,6 @@ void onFocusLost() { void onFocusGained() { SDL_Window* window = aurora::window::get_sdl_window(); syncCaptureState(window, shouldCaptureMouse(window)); + syncGrabState(window, shouldGrabMouse(window)); } } // namespace dusk::mouse From adede6ab9cee76fbb3150f73e4db6467d6e3674e Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 27 May 2026 21:33:42 -0400 Subject: [PATCH 16/21] Tidying my trash --- src/dusk/mouse.cpp | 47 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index c32f77afb3..362991298a 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -67,36 +67,18 @@ bool shouldGrabMouse(SDL_Window* window) { return wantMouseGrab() && isWindowFocused(window); } -bool queryActualCaptureState(SDL_Window* window) { - if (window == nullptr) { - return false; - } - return SDL_GetWindowRelativeMouseMode(window); -} - -bool queryActualGrabState(SDL_Window* window) { - if (window == nullptr) { - return false; - } - return SDL_GetWindowMouseGrab(window); -} - bool syncCaptureState(SDL_Window* window, bool should_capture) { if (window == nullptr) { reset_deltas(); return false; } - if (!isWindowFocused(window)) { - should_capture = false; - } - - const bool was_captured = queryActualCaptureState(window); + const bool was_captured = SDL_GetWindowRelativeMouseMode(window); if (was_captured != should_capture) { SDL_SetWindowRelativeMouseMode(window, should_capture); } - const bool is_captured = queryActualCaptureState(window); + const bool is_captured = SDL_GetWindowRelativeMouseMode(window); if (is_captured && !was_captured) { const AuroraWindowSize sz = aurora::window::get_window_size(); const float cx = static_cast(sz.width) * 0.5f; @@ -119,7 +101,7 @@ void syncGrabState(SDL_Window* window, bool should_grab) { return; } - const bool was_grabbed = queryActualGrabState(window); + const bool was_grabbed = SDL_GetWindowMouseGrab(window); if (was_grabbed != should_grab) { SDL_SetWindowMouseGrab(window, should_grab); } @@ -127,21 +109,25 @@ void syncGrabState(SDL_Window* window, bool should_grab) { void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_active) { const auto& game = getSettings().game; + const bool mirror_mode = game.enableMirrorMode.getValue(); + const bool invert_y = game.invertMouseY.getValue(); if (aim_active) { - s_aim_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseAimSensitivity.getValue(); - s_aim_pitch_rad = my_rel * kMousePixelToRad * game.mouseAimSensitivity.getValue(); - s_aim_yaw_rad = game.enableMirrorMode.getValue() ? -s_aim_yaw_rad : s_aim_yaw_rad; - s_aim_pitch_rad = game.invertMouseY.getValue() ? -s_aim_pitch_rad : s_aim_pitch_rad; + const bool sensitivity = game.mouseAimSensitivity.getValue(); + s_aim_yaw_rad = -mx_rel * kMousePixelToRad * sensitivity; + s_aim_pitch_rad = my_rel * kMousePixelToRad * sensitivity; + s_aim_yaw_rad = mirror_mode ? -s_aim_yaw_rad : s_aim_yaw_rad; + s_aim_pitch_rad = invert_y ? -s_aim_pitch_rad : s_aim_pitch_rad; } else { s_aim_yaw_rad = s_aim_pitch_rad = 0.0f; } if (camera_active) { - s_camera_yaw_rad = -mx_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); - s_camera_pitch_rad = -my_rel * kMousePixelToRad * game.mouseCameraSensitivity.getValue(); - s_camera_yaw_rad = game.enableMirrorMode.getValue() ? -s_camera_yaw_rad : s_camera_yaw_rad; - s_camera_pitch_rad = game.invertMouseY.getValue() ? -s_camera_pitch_rad : s_camera_pitch_rad; + const bool sensitivity = game.mouseCameraSensitivity.getValue(); + s_camera_yaw_rad = -mx_rel * kMousePixelToRad * sensitivity; + s_camera_pitch_rad = -my_rel * kMousePixelToRad * sensitivity; + s_camera_yaw_rad = mirror_mode ? -s_camera_yaw_rad : s_camera_yaw_rad; + s_camera_pitch_rad = invert_y ? -s_camera_pitch_rad : s_camera_pitch_rad; } else { s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; } @@ -212,8 +198,7 @@ void getCameraDeltas(float& out_yaw, float& out_pitch) { out_yaw = 0.0f; out_pitch = 0.0f; - if (!getSettings().game.enableMouseCamera) - { + if (!getSettings().game.enableMouseCamera) { return; } From bd8fed40ac090d6ffd7bf00466a4483f18ea8773 Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 27 May 2026 21:45:53 -0400 Subject: [PATCH 17/21] Last bit of housekeeping so I'm satisfied --- src/dusk/mouse.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 362991298a..4466d4fed1 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -220,12 +220,11 @@ void handle_event(const SDL_Event& event) noexcept { void onFocusLost() { SDL_Window* window = aurora::window::get_sdl_window(); if (window != nullptr) { - SDL_SetWindowRelativeMouseMode(window, false); + syncCaptureState(window, false); syncGrabState(window, false); } s_idle_frames = 0; set_cursor_visible(true); - reset_deltas(); } void onFocusGained() { From 4db2f237f2c5c11b98e4752b0a75d54351b075df Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 28 May 2026 15:19:12 -0400 Subject: [PATCH 18/21] Don't write code while sleep deprived --- src/dusk/mouse.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 4466d4fed1..4bb77d343b 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -113,9 +113,9 @@ void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_a const bool invert_y = game.invertMouseY.getValue(); if (aim_active) { - const bool sensitivity = game.mouseAimSensitivity.getValue(); - s_aim_yaw_rad = -mx_rel * kMousePixelToRad * sensitivity; - s_aim_pitch_rad = my_rel * kMousePixelToRad * sensitivity; + const float aimSens = game.mouseAimSensitivity.getValue(); + s_aim_yaw_rad = -mx_rel * kMousePixelToRad * aimSens; + s_aim_pitch_rad = my_rel * kMousePixelToRad * aimSens; s_aim_yaw_rad = mirror_mode ? -s_aim_yaw_rad : s_aim_yaw_rad; s_aim_pitch_rad = invert_y ? -s_aim_pitch_rad : s_aim_pitch_rad; } else { @@ -123,9 +123,9 @@ void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_a } if (camera_active) { - const bool sensitivity = game.mouseCameraSensitivity.getValue(); - s_camera_yaw_rad = -mx_rel * kMousePixelToRad * sensitivity; - s_camera_pitch_rad = -my_rel * kMousePixelToRad * sensitivity; + const float camSens = game.mouseCameraSensitivity.getValue(); + s_camera_yaw_rad = -mx_rel * kMousePixelToRad * camSens; + s_camera_pitch_rad = -my_rel * kMousePixelToRad * camSens; s_camera_yaw_rad = mirror_mode ? -s_camera_yaw_rad : s_camera_yaw_rad; s_camera_pitch_rad = invert_y ? -s_camera_pitch_rad : s_camera_pitch_rad; } else { From e545c396fed49b88e7b0315f2ac707b7c9d06ef2 Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 28 May 2026 17:31:39 -0400 Subject: [PATCH 19/21] Fix my sloppy merge and a few helpText updates --- src/dusk/ui/settings.cpp | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 3a8172abe3..f01098a4c3 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -948,25 +948,23 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { leftPane.add_section("Camera"); addOption("Free Camera", getSettings().game.freeCamera, "Enables free camera control, letting you control the camera fully with the C-Stick."); - addOption("Invert Free Camera X Axis", getSettings().game.invertCameraXAxis, - "Invert horizontal free camera movement.

Applies to the control stick only.", - [] { return !getSettings().game.freeCamera; }); - addOption("Invert Free Camera Y Axis", getSettings().game.invertCameraYAxis, - "Invert vertical free camera movement.

Applies to the control stick only.", config_percent_select(leftPane, rightPane, getSettings().game.freeCameraXSensitivity, - "Free Camera X Sensitivity", "Adjusts horizontal free camera sensitivity.

Applies to the control stick only.", 50, 200, 5, - [] { return !getSettings().game.freeCamera; }); + "Free Camera X Sensitivity", + "Adjusts horizontal free camera sensitivity.

Applies to the control stick only.", + 50, 200, 5, [] { return !getSettings().game.freeCamera; }); config_percent_select(leftPane, rightPane, getSettings().game.freeCameraYSensitivity, - "Free Camera Y Sensitivity", "Adjusts vertical free camera sensitivity.

Applies to the control stick only.", 50, 200, 5, + "Free Camera Y Sensitivity", + "Adjusts vertical free camera sensitivity.

Applies to the control stick only.", + 50, 200, 5, [] { return !getSettings().game.freeCamera; }); + addOption("Invert Camera X Axis", getSettings().game.invertCameraXAxis, + "Invert horizontal camera movement.

Applies to the control stick only."); + addOption("Invert Camera Y Axis", getSettings().game.invertCameraYAxis, + "Invert vertical camera movement.

Applies to the control stick only.", [] { return !getSettings().game.freeCamera; }); addOption("Invert First Person X Axis", getSettings().game.invertFirstPersonXAxis, - "Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); + "Invert horizontal movement while aiming with items or first person camera.

Applies to the control stick only."); addOption("Invert First Person Y Axis", getSettings().game.invertFirstPersonYAxis, - "Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); - addOption("Invert Air/Swim X Axis", getSettings().game.invertAirSwimX, - "Invert horizontal movement while flying or swimming."); - addOption("Invert Air/Swim Y Axis", getSettings().game.invertAirSwimY, - "Invert vertical movement while flying or swimming."); + "Invert vertical movement while aiming with items or first person camera.

Applies to the control stick only."); leftPane.add_section("Gyro"); addOption("Gyro Aim", getSettings().game.enableGyroAim, @@ -995,10 +993,6 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Invert vertical gyro aiming.", [] { return !gyro_enabled(); }); addOption("Invert Gyro Yaw", getSettings().game.gyroInvertYaw, "Invert horizontal gyro aiming.", [] { return !gyro_enabled(); }); - - leftPane.add_section("Gameplay"); - addOption("Swap Direct Select Input", getSettings().game.swapDirectSelect, - "Swap the controls for using Direct Select on the item wheel, making Direct Select the default and holding L to scroll the wheel."); leftPane.add_section("Mouse"); addOption("Mouse Aim", getSettings().game.enableMouseAim, @@ -1017,6 +1011,14 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Invert vertical mouse control for both aiming and camera.", [] { return !getSettings().game.enableMouseAim || !getSettings().game.enableMouseCamera; }); + leftPane.add_section("Gameplay"); + addOption("Invert Air/Swim X Axis", getSettings().game.invertAirSwimX, + "Invert horizontal movement while flying or swimming."); + addOption("Invert Air/Swim Y Axis", getSettings().game.invertAirSwimY, + "Invert vertical movement while flying or swimming."); + addOption("Swap Direct Select Input", getSettings().game.swapDirectSelect, + "Swap the controls for using Direct Select on the item wheel, making Direct Select the default and holding L to scroll the wheel."); + leftPane.add_section("Tools"); addOption("Turbo Key", getSettings().game.enableTurboKeybind, "Hold Tab to increase game speed by up to 4x.", From d92a30525d1c6ce7001af970275319d0a8e8fcc2 Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 28 May 2026 18:42:14 -0400 Subject: [PATCH 20/21] Disable control stick aim when mouse aim is active --- src/d/actor/d_a_alink_link.inc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index eaf5b32af1..4d93f39457 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -121,11 +121,18 @@ BOOL daAlink_c::setBodyAngleToCamera() { var_f31 /= dComIfGp_getCameraZoomScale(field_0x317c); } - shape_angle.y = shape_angle.y + (var_f31 * cM_ssin(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f))); - sp8 = mBodyAngle.x + (var_f31 * cM_scos(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f))); - - if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) { +#if TARGET_PC + if (dusk::getSettings().game.enableMouseAim && checkAimContext()) { sp8 = mBodyAngle.x; + } else +#endif + { + shape_angle.y = shape_angle.y + (var_f31 * cM_ssin(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f))); + sp8 = mBodyAngle.x + (var_f31 * cM_scos(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f))); + + if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) { + sp8 = mBodyAngle.x; + } } } else { sp8 = mBodyAngle.x; From c97c216fd689b52a1de3f412f51bdcde5337623d Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 28 May 2026 18:58:58 -0400 Subject: [PATCH 21/21] Use same conditions for cursor grabbing as for capture --- src/dusk/mouse.cpp | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/dusk/mouse.cpp b/src/dusk/mouse.cpp index 4bb77d343b..8ebfaf60cb 100644 --- a/src/dusk/mouse.cpp +++ b/src/dusk/mouse.cpp @@ -42,10 +42,6 @@ bool wantMouseCapture() { return getSettings().game.enableMouseCamera.getValue() || queryMouseAimContext(); } -bool wantMouseGrab() { - return getSettings().game.enableMouseCamera.getValue() || getSettings().game.enableMouseAim.getValue(); -} - bool isWindowFocused(SDL_Window* window) { if (window == nullptr) { return false; @@ -60,13 +56,6 @@ bool shouldCaptureMouse(SDL_Window* window) { return wantMouseCapture() && isWindowFocused(window); } -bool shouldGrabMouse(SDL_Window* window) { - if (window == nullptr) { - return false; - } - return wantMouseGrab() && isWindowFocused(window); -} - bool syncCaptureState(SDL_Window* window, bool should_capture) { if (window == nullptr) { reset_deltas(); @@ -75,6 +64,7 @@ bool syncCaptureState(SDL_Window* window, bool should_capture) { const bool was_captured = SDL_GetWindowRelativeMouseMode(window); if (was_captured != should_capture) { + SDL_SetWindowMouseGrab(window, should_capture); SDL_SetWindowRelativeMouseMode(window, should_capture); } @@ -96,17 +86,6 @@ bool syncCaptureState(SDL_Window* window, bool should_capture) { return is_captured; } -void syncGrabState(SDL_Window* window, bool should_grab) { - if (window == nullptr) { - return; - } - - const bool was_grabbed = SDL_GetWindowMouseGrab(window); - if (was_grabbed != should_grab) { - SDL_SetWindowMouseGrab(window, should_grab); - } -} - void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_active) { const auto& game = getSettings().game; const bool mirror_mode = game.enableMirrorMode.getValue(); @@ -174,7 +153,6 @@ void read() { SDL_Window* window = aurora::window::get_sdl_window(); const bool capture_active = syncCaptureState(window, shouldCaptureMouse(window)); update_cursor_visibility(window, capture_active); - syncGrabState(window, shouldGrabMouse(window)); if (!capture_active) { return; @@ -221,7 +199,6 @@ void onFocusLost() { SDL_Window* window = aurora::window::get_sdl_window(); if (window != nullptr) { syncCaptureState(window, false); - syncGrabState(window, false); } s_idle_frames = 0; set_cursor_visible(true); @@ -230,6 +207,5 @@ void onFocusLost() { void onFocusGained() { SDL_Window* window = aurora::window::get_sdl_window(); syncCaptureState(window, shouldCaptureMouse(window)); - syncGrabState(window, shouldGrabMouse(window)); } } // namespace dusk::mouse