Merge pull request #5 from chasmlol/lol/video-fov-slider

Add an FOV slider to video options
This commit is contained in:
vc
2026-09-25 23:45:34 +03:00
committed by GitHub
6 changed files with 47 additions and 9 deletions
+6
View File
@@ -219,6 +219,7 @@ fn serialize_settings(settings: &frame::GameSettings, binds: &KeyBinds) -> Strin
),
format!("fullscreen={}", settings.fullscreen),
format!("vsync={}", settings.vsync),
format!("fov={:.0}", settings.fov),
format!("master_volume={:.3}", settings.master_volume),
format!("sensitivity={:.3}", settings.sensitivity),
format!("invert_mouse={}", settings.invert_mouse),
@@ -269,6 +270,11 @@ fn parse_settings(source: &str, settings: &mut frame::GameSettings, binds: &mut
settings.master_volume = value;
}
}
"fov" => {
if let Ok(value) = value.parse() {
settings.fov = value;
}
}
"sensitivity" => {
if let Ok(value) = value.parse() {
settings.sensitivity = value;
+11
View File
@@ -28,6 +28,7 @@ pub struct GameSettings {
pub resolution: DisplayResolution,
pub fullscreen: bool,
pub vsync: bool,
pub fov: f32,
pub master_volume: f32,
pub sensitivity: f32,
pub invert_mouse: bool,
@@ -42,6 +43,7 @@ impl Default for GameSettings {
resolution: DisplayResolution::HD,
fullscreen: false,
vsync: true,
fov: Self::FOV_DEFAULT,
master_volume: 1.0,
sensitivity: 5.0,
invert_mouse: false,
@@ -52,6 +54,10 @@ impl Default for GameSettings {
}
impl GameSettings {
pub const FOV_DEFAULT: f32 = 65.0;
pub const FOV_MIN: f32 = 65.0;
pub const FOV_MAX: f32 = 120.0;
pub fn touch(&mut self) {
self.revision = self.revision.wrapping_add(1);
}
@@ -59,6 +65,11 @@ impl GameSettings {
pub fn sanitize(&mut self) {
self.resolution.width = self.resolution.width.clamp(640, 7680);
self.resolution.height = self.resolution.height.clamp(480, 4320);
self.fov = if self.fov.is_finite() {
self.fov.clamp(Self::FOV_MIN, Self::FOV_MAX)
} else {
Self::FOV_DEFAULT
};
self.master_volume = self.master_volume.clamp(0.0, 1.0);
self.sensitivity = self.sensitivity.clamp(0.1, 30.0);
self.player_name = self.player_name.trim().chars().take(16).collect();
@@ -2,9 +2,8 @@ use assets::{PreparedWeapons, WeaponBodyFacts, WeaponKickFacts};
use bevy::prelude::*;
use frame::{LifeStarted, ViewSubject};
use hud_iw4::{
CG_FOV_DEFAULT, CG_FOV_MIN_DEFAULT, CG_FOV_SCALE_DEFAULT, CgCalcFovInputs,
WeaponAdsOverlayFacts, cg_calc_fov_from_ads, cg_horizontal_to_vertical_fov_deg,
cg_zoom_sensitivity,
CG_FOV_MIN_DEFAULT, CG_FOV_SCALE_DEFAULT, CgCalcFovInputs, WeaponAdsOverlayFacts,
cg_calc_fov_from_ads, cg_horizontal_to_vertical_fov_deg, cg_zoom_sensitivity,
};
use math_iw4::{add_lean_to_position, angle_vectors};
use net::{
@@ -240,6 +239,7 @@ pub fn sync_camera_from_presented(
presented: Res<PresentedSnapshot>,
local: Res<LocalPresentClient>,
sim_cam: Res<SimCamera>,
settings: Res<frame::GameSettings>,
mut kick: ResMut<SessionViewKick>,
mut hurt: ResMut<PendingViewHurt>,
weapons: Option<Res<PreparedWeapons>>,
@@ -319,6 +319,7 @@ pub fn sync_camera_from_presented(
}
apply_fpv_lens_fov(
&mut lenses,
settings.fov,
ps.pm_type,
ps.link_flags,
ps.e_flags,
@@ -439,6 +440,7 @@ pub fn sync_camera_from_presented(
kick.last_weapon_pos_frac = ps.f_weapon_pos_frac;
if let Some(horiz) = apply_fpv_lens_fov(
&mut lenses,
settings.fov,
ps.pm_type,
ps.link_flags,
ps.e_flags,
@@ -454,6 +456,7 @@ pub fn sync_camera_from_presented(
fn apply_fpv_lens_fov(
lenses: &mut Query<&mut Projection, With<FpvLens>>,
base_fov: f32,
pm_type: i32,
link_flags: u32,
e_flags: u32,
@@ -463,9 +466,7 @@ fn apply_fpv_lens_fov(
b_position_to_ads: bool,
actions: Option<&mut ClientActionInput>,
) -> Option<f32> {
let Some(facts) = facts.filter(|f| f.body_resolved) else {
return None;
};
let facts = facts.filter(|f| f.body_resolved).unwrap_or_default();
let overlay = WeaponAdsOverlayFacts {
ads_zoom_in_frac: facts.ads_zoom_in_frac,
ads_zoom_out_frac: facts.ads_zoom_out_frac,
@@ -475,10 +476,10 @@ fn apply_fpv_lens_fov(
let ads_target = if facts.ads_zoom_fov > 0.0 {
facts.ads_zoom_fov
} else {
CG_FOV_DEFAULT
base_fov
};
let inputs = CgCalcFovInputs {
cg_fov: CG_FOV_DEFAULT,
cg_fov: base_fov,
pm_type,
link_flags,
e_flags,
+1
View File
@@ -143,6 +143,7 @@ pub enum SettingKey {
Resolution,
Fullscreen,
Vsync,
Fov,
MasterVolume,
Sensitivity,
InvertMouse,
+8 -1
View File
@@ -167,7 +167,7 @@ pub(crate) fn options_widget_is_active(state: &OptionsState, id: &str) -> bool {
OptionsDepth::SectionRows => match state.tab {
OptionsTab::Video => matches!(
id,
"options/resolution" | "options/fullscreen" | "options/vsync"
"options/resolution" | "options/fullscreen" | "options/vsync" | "options/fov"
),
OptionsTab::Audio => id == "options/volume",
OptionsTab::Controls => OptionsControlGroup::from_widget_id(id).is_some(),
@@ -273,6 +273,7 @@ pub(crate) fn drive_options_navigation(
focus.widget.as_deref(),
Some(
"options/volume"
| "options/fov"
| "options/sensitivity"
| "options/fullscreen"
| "options/vsync"
@@ -445,6 +446,12 @@ pub(crate) fn apply_option_intents(
(SettingKey::Vsync, SettingValue::Bool(value)) => {
replace(&mut settings.vsync, *value)
}
(SettingKey::Fov, SettingValue::Float(value)) if value.is_finite() => {
let value = value
.clamp(frame::GameSettings::FOV_MIN, frame::GameSettings::FOV_MAX)
.round();
replace(&mut settings.fov, value)
}
(SettingKey::MasterVolume, SettingValue::Float(value)) => {
let value = value.clamp(0.0, 1.0);
replace(&mut settings.master_volume, value)
+12
View File
@@ -1343,6 +1343,18 @@ pub fn options(host: Host<'_>) -> Screen {
None,
"Synchronize presentation to the display refresh.",
),
slider(
"options/fov",
238.0,
122.0,
"Field of View",
settings.fov,
frame::GameSettings::FOV_MIN,
frame::GameSettings::FOV_MAX,
1.0,
crate::SettingKey::Fov,
"Show more of your surroundings. Default: 65.",
),
]);
}
OptionsTab::Audio => {