use bevy::prelude::*; use crate::{ load_project_settings_from_path, save_project_settings_to_string, PhysicsSettings, ProjectSettings, DEFAULT_PROJECT_PATH, }; /// Runtime copy of physics tuning consumed by `sim` systems. #[derive(Resource, Debug, Clone, Copy)] pub struct SimTuning { pub player_radius: f32, pub player_length: f32, pub eye_offset_stand: f32, pub eye_offset_crouch: f32, pub gravity: f32, pub walk_speed: f32, pub run_speed: f32, pub crouch_speed: f32, pub jump_speed: f32, pub ground_accel: f32, pub air_accel: f32, pub ground_check_dist: f32, pub skin: f32, pub max_slide_iter: u32, pub ground_normal_y: f32, pub coyote_time: f32, pub jump_buffer: f32, } impl SimTuning { pub fn from_physics(physics: &PhysicsSettings) -> Self { Self { player_radius: physics.player_radius, player_length: physics.player_length, eye_offset_stand: physics.eye_offset_stand, eye_offset_crouch: physics.eye_offset_crouch, gravity: physics.gravity, walk_speed: physics.walk_speed, run_speed: physics.run_speed, crouch_speed: physics.crouch_speed, jump_speed: physics.jump_speed, ground_accel: physics.ground_accel, air_accel: physics.air_accel, ground_check_dist: physics.ground_check_dist, skin: physics.skin, max_slide_iter: physics.max_slide_iter, ground_normal_y: physics.ground_normal_y, coyote_time: physics.coyote_time, jump_buffer: physics.jump_buffer, } } } /// Tracks project settings file path and dirty state (editor persists via UI). #[derive(Resource, Debug)] pub struct ProjectSettingsIo { pub path: String, pub dirty: bool, } impl Default for ProjectSettingsIo { fn default() -> Self { Self { path: DEFAULT_PROJECT_PATH.into(), dirty: false, } } } /// Fired after project settings are saved or reloaded from disk. #[derive(Message, Debug, Clone, Copy, Default)] pub struct ProjectSettingsChanged; pub struct ProjectSettingsPlugin; impl Plugin for ProjectSettingsPlugin { fn build(&self, app: &mut App) { app.init_resource::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .add_message::() .add_systems(PreStartup, load_settings); } } fn load_settings(mut commands: Commands, io: Res) { let settings = load_project_settings_from_path(&io.path); let tuning = SimTuning::from_physics(&settings.physics); commands.insert_resource(settings); commands.insert_resource(tuning); } /// Refreshes [`SimTuning`] from the current [`ProjectSettings`] resource. pub fn sync_sim_tuning(settings: &ProjectSettings, mut tuning: ResMut) { *tuning = SimTuning::from_physics(&settings.physics); } /// Persists settings to the path in [`ProjectSettingsIo`]. pub fn save_project_settings( settings: &ProjectSettings, io: &mut ProjectSettingsIo, ) -> Result<(), String> { let text = save_project_settings_to_string(settings).map_err(|error| error.to_string())?; std::fs::write(&io.path, text).map_err(|error| error.to_string())?; io.dirty = false; Ok(()) }