diff --git a/crates/console/src/plugin/mod.rs b/crates/console/src/plugin/mod.rs index 4411ccc..d2ff750 100644 --- a/crates/console/src/plugin/mod.rs +++ b/crates/console/src/plugin/mod.rs @@ -408,8 +408,7 @@ fn publish_client_action_input( physical.movement_ready = false; physical.look_ready = false; } else { - out.pad_sensitivity = - settings.pad_sensitivity / frame::GameSettings::PAD_SENSITIVITY_DEFAULT; + out.pad_sensitivity = settings.pad_look_sensitivity(); out.pad_ads_sensitivity = settings.pad_ads_sensitivity; out.pad_acceleration = settings.pad_acceleration; if let Some(pad) = pad { diff --git a/crates/console/src/user_settings.rs b/crates/console/src/user_settings.rs index 04eb60f..c097e6d 100644 --- a/crates/console/src/user_settings.rs +++ b/crates/console/src/user_settings.rs @@ -354,7 +354,11 @@ fn serialize_settings(settings: &frame::GameSettings, binds: &KeyBinds) -> Strin format!("player_name={safe_name}"), format!("pad_layout={}", settings.pad_layout), format!("pad_stick_layout={}", settings.pad_stick_layout), - format!("pad_sensitivity={:.2}", settings.pad_sensitivity), + format!("pad_sensitivity_preset={}", settings.pad_sensitivity_preset), + format!( + "pad_custom_sensitivity={:.3}", + settings.pad_custom_sensitivity + ), format!("pad_ads_sensitivity={:.2}", settings.pad_ads_sensitivity), format!("pad_invert={}", settings.pad_invert), format!("pad_curve={}", settings.pad_curve), @@ -448,7 +452,15 @@ fn parse_settings(source: &str, settings: &mut frame::GameSettings, binds: &mut "player_name" => settings.player_name = value.to_owned(), "pad_layout" => parse_into(value, &mut settings.pad_layout), "pad_stick_layout" => parse_into(value, &mut settings.pad_stick_layout), - "pad_sensitivity" => parse_into(value, &mut settings.pad_sensitivity), + // Preserve the effective speed of configs saved with the old 1-10 slider. + "pad_sensitivity" => { + if let Ok(old) = value.parse::() { + settings.pad_custom_sensitivity = old.clamp(1.0, 10.0) / 3.0; + settings.pad_sensitivity_preset = 0; + } + } + "pad_sensitivity_preset" => parse_into(value, &mut settings.pad_sensitivity_preset), + "pad_custom_sensitivity" => parse_into(value, &mut settings.pad_custom_sensitivity), "pad_ads_sensitivity" => parse_into(value, &mut settings.pad_ads_sensitivity), "pad_invert" => parse_into(value, &mut settings.pad_invert), "pad_curve" => parse_into(value, &mut settings.pad_curve), @@ -532,7 +544,11 @@ pub(crate) fn native_menu_settings( "ui_bloom" => settings.bloom = value == "1", "ui_pad_layout" => parse_into(value, &mut settings.pad_layout), "ui_pad_stick_layout" => parse_into(value, &mut settings.pad_stick_layout), - "ui_pad_sensitivity" => parse_into(value, &mut settings.pad_sensitivity), + "ui_pad_sensitivity_preset" => parse_into(value, &mut settings.pad_sensitivity_preset), + "ui_pad_custom_sensitivity" => { + parse_into(value, &mut settings.pad_custom_sensitivity); + settings.pad_sensitivity_preset = 0; + } "ui_pad_ads_sensitivity" => parse_into(value, &mut settings.pad_ads_sensitivity), "ui_pad_invert" => settings.pad_invert = value == "1", "ui_pad_curve" => parse_into(value, &mut settings.pad_curve), @@ -572,7 +588,14 @@ pub(crate) fn native_menu_settings( dvars.set("ui_r_vsync", if settings.vsync { "1" } else { "0" }); dvars.set("ui_pad_layout", settings.pad_layout.to_string()); dvars.set("ui_pad_stick_layout", settings.pad_stick_layout.to_string()); - dvars.set("ui_pad_sensitivity", settings.pad_sensitivity.to_string()); + dvars.set( + "ui_pad_sensitivity_preset", + settings.pad_sensitivity_preset.to_string(), + ); + dvars.set( + "ui_pad_custom_sensitivity", + settings.pad_custom_sensitivity.to_string(), + ); dvars.set( "ui_pad_ads_sensitivity", settings.pad_ads_sensitivity.to_string(), diff --git a/crates/frame/src/settings.rs b/crates/frame/src/settings.rs index 1a61f89..1f422df 100644 --- a/crates/frame/src/settings.rs +++ b/crates/frame/src/settings.rs @@ -40,7 +40,9 @@ pub struct GameSettings { pub pad_layout: u8, pub pad_stick_layout: u8, - pub pad_sensitivity: f32, + /// Zero selects the independently saved custom multiplier; 1..=10 select presets. + pub pad_sensitivity_preset: u8, + pub pad_custom_sensitivity: f32, pub pad_ads_sensitivity: f32, pub pad_invert: bool, pub pad_curve: u8, @@ -71,7 +73,8 @@ impl Default for GameSettings { player_name: "Player".to_owned(), pad_layout: 0, pad_stick_layout: 0, - pad_sensitivity: Self::PAD_SENSITIVITY_DEFAULT, + pad_sensitivity_preset: 0, + pad_custom_sensitivity: 1.0, pad_ads_sensitivity: 1.0, pad_invert: false, pad_curve: 0, @@ -90,7 +93,18 @@ impl GameSettings { pub const FOV_DEFAULT: f32 = 65.0; pub const FOV_MIN: f32 = 65.0; pub const FOV_MAX: f32 = 120.0; - pub const PAD_SENSITIVITY_DEFAULT: f32 = 3.0; + // IW4x ui_mp/pc_options_gamepad.menu: SENSITIVITY_1 through SENSITIVITY_10. + pub const PAD_SENSITIVITY_PRESETS: [f32; 10] = + [0.6, 1.0, 1.4, 1.8, 2.0, 2.2, 2.6, 3.0, 3.5, 4.0]; + + pub fn pad_look_sensitivity(&self) -> f32 { + self.pad_sensitivity_preset + .checked_sub(1) + .and_then(|index| Self::PAD_SENSITIVITY_PRESETS.get(usize::from(index))) + .copied() + .unwrap_or(self.pad_custom_sensitivity) + } + pub const PAD_LAYOUT_CUSTOM: u8 = 255; pub fn touch(&mut self) { @@ -126,12 +140,8 @@ impl GameSettings { default } }; - self.pad_sensitivity = finite( - self.pad_sensitivity, - 1.0, - 10.0, - Self::PAD_SENSITIVITY_DEFAULT, - ); + self.pad_sensitivity_preset = self.pad_sensitivity_preset.min(10); + self.pad_custom_sensitivity = finite(self.pad_custom_sensitivity, 0.1, 5.0, 1.0); self.pad_ads_sensitivity = finite(self.pad_ads_sensitivity, 0.5, 1.5, 1.0); self.pad_deadzone_left = finite(self.pad_deadzone_left, 0.0, 0.4, 0.12); self.pad_deadzone_right = finite(self.pad_deadzone_right, 0.0, 0.4, 0.12); diff --git a/crates/ui/menus/controller.json b/crates/ui/menus/controller.json index ff4e9bc..08c2982 100644 --- a/crates/ui/menus/controller.json +++ b/crates/ui/menus/controller.json @@ -262,12 +262,127 @@ "name": "controller_label_3" } }, + { + "name": "video_display_mode", + "fields": { + "item_type": 12, + "static_flags": 0, + "dvar": "ui_pad_sensitivity_preset", + "choices": [ + [ + "Custom", + "0" + ], + [ + "1", + "1" + ], + [ + "2", + "2" + ], + [ + "3", + "3" + ], + [ + "4", + "4" + ], + [ + "5", + "5" + ], + [ + "6", + "6" + ], + [ + "7", + "7" + ], + [ + "8", + "8" + ], + [ + "9", + "9" + ], + [ + "10", + "10" + ] + ], + "vis_exp": "1", + "disabled_exp": "0", + "text_key": "", + "text_exp": "", + "text_align_mode": 10, + "text_align_x": -8, + "text_align_y": 0, + "fore_color": [ + 1, + 1, + 1, + 1 + ], + "rect": { + "x": 232, + "y": 68, + "w": 344, + "h": 20 + }, + "handlers": { + "action": [ + { + "Script": "play mouse_click;" + } + ], + "focus": [ + { + "Script": "play mouse_over; setItemColor self backcolor 0 0 0 1;" + } + ], + "leave_focus": [ + { + "Script": "setItemColor self backcolor 0 0 0 0;" + } + ] + }, + "text_scale": 0.375, + "style": 1, + "background": "menu_button_selection_bar", + "back_color": [ + 0, + 0, + 0, + 0 + ], + "name": "ui_pad_sensitivity_preset" + } + }, + { + "name": "video_display_mode_label", + "fields": { + "vis_exp": "1", + "text_key": "Sensitivity Preset", + "rect": { + "x": 232, + "y": 68, + "w": 344, + "h": 20 + }, + "text_scale": 0.32, + "name": "controller_sensitivity_preset_label" + } + }, { "name": "video_brightness", "fields": { "item_type": 10, "static_flags": 0, - "dvar": "ui_pad_sensitivity", + "dvar": "ui_pad_custom_sensitivity", "choices": [], "vis_exp": "1", "disabled_exp": "0", @@ -284,7 +399,7 @@ ], "rect": { "x": 420, - "y": 68, + "y": 88, "w": 112, "h": 20 }, @@ -305,24 +420,24 @@ 0 ], "slider": { - "min": 1, - "max": 10, + "min": 0.1, + "max": 5.0, "step": 0.1, "display_range": null, - "suffix": "", + "suffix": "x", "decimals": 1 }, - "name": "ui_pad_sensitivity" + "name": "ui_pad_custom_sensitivity" } }, { "name": "video_brightness_label", "fields": { - "text_key": "Look Sensitivity", + "text_key": "Custom Sensitivity", "vis_exp": "1", "rect": { "x": 232, - "y": 68, + "y": 88, "w": 344, "h": 20 }, @@ -351,7 +466,7 @@ ], "rect": { "x": 420, - "y": 88, + "y": 108, "w": 112, "h": 20 }, @@ -391,7 +506,7 @@ "vis_exp": "1", "rect": { "x": 232, - "y": 88, + "y": 108, "w": 344, "h": 20 }, @@ -429,7 +544,7 @@ ], "rect": { "x": 232, - "y": 108, + "y": 128, "w": 344, "h": 20 }, @@ -469,7 +584,7 @@ "text_key": "Look Inversion", "rect": { "x": 232, - "y": 108, + "y": 128, "w": 344, "h": 20 }, @@ -512,7 +627,7 @@ ], "rect": { "x": 232, - "y": 128, + "y": 148, "w": 344, "h": 20 }, @@ -552,7 +667,7 @@ "text_key": "Response Curve", "rect": { "x": 232, - "y": 128, + "y": 148, "w": 344, "h": 20 }, @@ -591,7 +706,7 @@ ], "rect": { "x": 232, - "y": 148, + "y": 168, "w": 344, "h": 20 }, @@ -631,7 +746,7 @@ "text_key": "Look Acceleration", "rect": { "x": 232, - "y": 148, + "y": 168, "w": 344, "h": 20 }, @@ -666,7 +781,7 @@ ], "rect": { "x": 232, - "y": 168, + "y": 188, "w": 344, "h": 20 }, @@ -706,7 +821,7 @@ "text_key": "Aim Assist", "rect": { "x": 232, - "y": 168, + "y": 188, "w": 344, "h": 20 }, @@ -745,7 +860,7 @@ ], "rect": { "x": 232, - "y": 188, + "y": 208, "w": 344, "h": 20 }, @@ -785,7 +900,7 @@ "text_key": "Vibration", "rect": { "x": 232, - "y": 188, + "y": 208, "w": 344, "h": 20 }, @@ -815,7 +930,7 @@ ], "rect": { "x": 420, - "y": 208, + "y": 228, "w": 112, "h": 20 }, @@ -855,7 +970,7 @@ "vis_exp": "1", "rect": { "x": 232, - "y": 208, + "y": 228, "w": 344, "h": 20 }, @@ -884,7 +999,7 @@ ], "rect": { "x": 420, - "y": 228, + "y": 248, "w": 112, "h": 20 }, @@ -924,7 +1039,7 @@ "vis_exp": "1", "rect": { "x": 232, - "y": 228, + "y": 248, "w": 344, "h": 20 }, @@ -970,7 +1085,7 @@ ], "rect": { "x": 232, - "y": 248, + "y": 268, "w": 344, "h": 20 }, @@ -1010,7 +1125,7 @@ "text_key": "Controller Prompts", "rect": { "x": 232, - "y": 248, + "y": 268, "w": 344, "h": 20 }, @@ -1040,7 +1155,7 @@ ], "rect": { "x": 232, - "y": 268, + "y": 288, "w": 344, "h": 20 }, @@ -1080,7 +1195,7 @@ "text_key": "Test Vibration", "rect": { "x": 232, - "y": 268, + "y": 288, "w": 344, "h": 20 },