Separate controller presets from custom sensitivity

The fine sensitivity slider reused the 1-10 setting, conflating console
preset numbers with a continuous speed control. Selecting a preset must
leave the player's custom slider value available for later use.

Store the selected preset and custom multiplier independently. Add Custom
and integer choices 1 through 10 to the Controller menu. Use the IW4x
pc_options_gamepad.menu preset table: 0.6, 1.0, 1.4, 1.8, 2.0, 2.2, 2.6,
3.0, 3.5 and 4.0. Custom uses the local controller slider's 0.1-5.0 multiplier
range with 0.1 steps. Editing the slider selects Custom; selecting a preset
never overwrites the saved custom value.

Resolve the active multiplier before applying controller aiming. Persist
both settings and migrate the previous pad_sensitivity value to Custom
using its old value/3 speed mapping, preserving existing effective speed.
The acceleration toggle remains independent.

Validation: play build, targeted formatting and diff checks passed.
Temporary probes verified all ten preset multipliers, custom-value retention,
settings save/reload and legacy migration, and menu inheritance/choices.
Aiming probes also passed for acceleration on/off and ADS/FOV scaling at
30, 60 and 144 Hz. Gameplay feel and visual layout were not retested.
This commit is contained in:
awsms
2026-10-01 07:39:30 +02:00
parent e8d07d1a12
commit 72b4c08c33
4 changed files with 190 additions and 43 deletions
+1 -2
View File
@@ -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 {
+27 -4
View File
@@ -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::<f32>() {
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(),
+19 -9
View File
@@ -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);
+143 -28
View File
@@ -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
},