diff --git a/assets/martian.tweak.toml b/assets/martian.tweak.toml index 0808376..4db234c 100644 --- a/assets/martian.tweak.toml +++ b/assets/martian.tweak.toml @@ -92,17 +92,17 @@ directional_light_scale = 1.0 # Only seems to affect objects and not skyboxes ### [display3d.fog] -exponent = 0.0 +directional_light_exponent = 0.0 [display3d.fog.color] Rgba = { red = 1.0, green = 1.0, blue = 1.0, alpha = 0.0 } -[display3d.fog.light_color] +[display3d.fog.directional_light_color] Rgba = { red = 1.0, green = 1.0, blue = 1.0, alpha = 0.0 } ### # Fog Faloff # https://docs.rs/bevy/0.11.3/bevy/pbr/enum.FogFalloff.html ### -[display3d.fog.falloff] -Exponential = { density = 0.005 } +# [display3d.fog.falloff] +# Exponential = { density = 0.005 } # Examples: # * Linear = { start = 1.0, end = 10.0 } # * Exponential = { density = 1.0 } @@ -138,16 +138,17 @@ post_saturation = 1.0 # Sprite file path inside the `assets` folder file = "images/sprites.png" # Size of each tile [x,y] (they have to be the same size) -tile_size = [16, 16] +tile_size_x = 16 +tile_size_y = 16 columns = 8 rows = 1 # The order of each sprite left-to-right, top-to-bottom -sprite_order = [ "LightTile" - , "DarkTile" - , "RedQueen" - , "RedDrone" - , "RedPawn" - , "BlueQueen" - , "BlueDrone" - , "BluePawn" +sprite_order = ["LightTile" + ,"DarkTile" + ,"RedQueen" + ,"RedDrone" + ,"RedPawn" + ,"BlueQueen" + ,"BlueDrone" + ,"BluePawn" ] diff --git a/examples/toml-stuff.rs b/examples/toml-stuff.rs index 99d2bc0..8a4353a 100644 --- a/examples/toml-stuff.rs +++ b/examples/toml-stuff.rs @@ -1,4 +1,4 @@ -use bevy::utils::HashMap; +use bevy::prelude::Color; use serde::Deserialize; use toml::Table; @@ -10,10 +10,64 @@ enum Val { } fn main() { - let s = r#"val = "asdf" -other = 123 -final = false + let s = r#" + [color] + asdf = true + [color.foo] + qwert = false + [color.foo.bar] + hjkl = [1,2,3] + [color.foo.bar.baz] + Rgba = { red = 1.0, blue = 1.0, green = 1.0, alpha = 1.0 } "#; - let v = toml::from_str::(s).unwrap(); - println!("{:?}", v); + let vt = toml::from_str::
(s).unwrap(); + + iter_all(&vt, "") + .iter() + .for_each(|(k, v)| println!("k {:?} :: v {:?}", k, v)); + + let x = get::(&vt, "color_foo_bar_baz").unwrap(); + println!("{:?}", x); + + let y = get::>(&vt, "color_foo_bar_hjkl").unwrap(); + println!("{:?}", y); + + let z = get::(&vt, "color_foo_qwert").unwrap(); + println!("{:?}", z); +} + +fn iter_all(t: &toml::Table, key: &str) -> Vec<(String, toml::Value)> { + t.iter() + .flat_map(|(k, v)| { + let nk = if key == "" { + k.to_string() + } else { + format!("{}_{}", key, k) + }; + match v { + toml::Value::Table(nt) => iter_all(nt, nk.as_str()), + _ => vec![(nk, v.clone())], + } + }) + .collect() +} + +fn locate(t: &toml::Table, key: &str) -> Option { + t.iter().find_map(|(k, v)| { + if key == k { + Some(v.clone()) + } else if key.starts_with(k) { + let prefix = format!("{}_", k); + match v { + toml::Value::Table(nt) => locate(nt, key.strip_prefix(prefix.as_str()).unwrap()), + _ => Some(v.clone()), + } + } else { + None + } + }) +} + +fn get<'de, T: Deserialize<'de>>(t: &toml::Table, key: &str) -> Option { + locate(t, key).map(|val| val.try_into().ok()).flatten() } diff --git a/src/audio.rs b/src/audio.rs index 49fb15a..895793a 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -5,7 +5,6 @@ use crate::prelude::*; use bevy::prelude::*; use bevy_fmod::prelude::AudioSource; use bevy_fmod::prelude::*; -use serde::Deserialize; pub(crate) struct AudioPlugin; @@ -60,33 +59,34 @@ fn audio_trigger( studio: Res, mut commands: Commands, server: Res, - tweakfiles: Res>, + tweaks: Res>, display_state: Res>, ) { - let tweak = tweakfiles + let tweak = tweaks .get(&server.load("martian.tweak.toml")) - .expect("Load tweakfile"); - debug!("Audio tweaks: {:?}", tweak.audio); + .expect("Load tweaks"); let state = display_state.get(); events.read().for_each(|event| { let aud = match event { - AudioEvent::MainMusic | AudioEvent::StopMainMusic => tweak.audio.music.main.clone(), - AudioEvent::MenuSelect => tweak.audio.menu.select.clone(), + AudioEvent::MainMusic | AudioEvent::StopMainMusic => { + tweak.get::("audio_music_main").unwrap() + } + AudioEvent::MenuSelect => tweak.get::("audio_menu_select").unwrap(), AudioEvent::PickUp => match state { - DisplayState::Display2d => tweak.audio.display2d.pick_up.clone(), - DisplayState::Display3d => tweak.audio.display3d.pick_up.clone(), + DisplayState::Display2d => tweak.get::("audio_display2d_pick_up").unwrap(), + DisplayState::Display3d => tweak.get::("audio_display3d_pick_up").unwrap(), }, AudioEvent::PutDown => match state { - DisplayState::Display2d => tweak.audio.display2d.put_down.clone(), - DisplayState::Display3d => tweak.audio.display3d.put_down.clone(), + DisplayState::Display2d => tweak.get::("audio_display2d_put_down").unwrap(), + DisplayState::Display3d => tweak.get::("audio_display3d_put_down").unwrap(), }, AudioEvent::Idle | AudioEvent::StopIdle => match state { - DisplayState::Display2d => tweak.audio.display2d.idle.clone(), - DisplayState::Display3d => tweak.audio.display3d.idle.clone(), + DisplayState::Display2d => tweak.get::("audio_display2d_idle").unwrap(), + DisplayState::Display3d => tweak.get::("audio_display3d_idle").unwrap(), }, AudioEvent::Invalid => match state { - DisplayState::Display2d => tweak.audio.display2d.invalid.clone(), - DisplayState::Display3d => tweak.audio.display3d.invalid.clone(), + DisplayState::Display2d => tweak.get::("audio_display2d_invalid").unwrap(), + DisplayState::Display3d => tweak.get::("audio_display3d_invalid").unwrap(), }, }; // There is an event for this @@ -128,40 +128,3 @@ fn audio_trigger( } }); } - -/// Tweaks made to audio in the Tweakfile -#[derive(Debug, Deserialize, Default)] -pub(crate) struct AudioTweaks { - #[serde(default)] - display2d: PlaySettings, - #[serde(default)] - display3d: PlaySettings, - #[serde(default)] - music: MusicSettings, - #[serde(default)] - menu: MenuSettings, -} - -#[derive(Debug, Deserialize, Default)] -struct MusicSettings { - #[serde(default)] - main: String, -} - -#[derive(Debug, Deserialize, Default)] -struct MenuSettings { - #[serde(default)] - select: String, -} - -#[derive(Debug, Deserialize, Default)] -struct PlaySettings { - #[serde(default)] - pick_up: String, - #[serde(default)] - put_down: String, - #[serde(default)] - idle: String, - #[serde(default)] - invalid: String, -} diff --git a/src/debug.rs b/src/debug.rs index 1d03897..36d531c 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -27,6 +27,9 @@ impl Plugin for DebugPlugin { display_diagnostics.run_if(resource_exists::()), toggle_debug_ui.run_if(resource_changed_or_removed::()), camera_info.run_if(resource_exists::()), + // debug_asset_event::.run_if(on_event::>()), + // debug_asset_event::.run_if(on_event::>()), + debug_asset_event::.run_if(on_event::>()), ), ); } @@ -139,3 +142,9 @@ fn camera_info(mut debug_infos: ResMut, cameras: Query<(&Camera, &Nam .collect::(); debug_infos.set("Cameras".into(), camera_names); } + +fn debug_asset_event(mut reader: EventReader>) { + reader.read().for_each(|e| { + info!("Asset Event: {:?}", e); + }); +} diff --git a/src/display2d.rs b/src/display2d.rs index bce1c61..67e2cd8 100644 --- a/src/display2d.rs +++ b/src/display2d.rs @@ -6,7 +6,7 @@ use bevy::window::{PrimaryWindow, WindowResized}; use crate::{ game::{Board, BoardIndex, Piece, Side, Tile}, prelude::*, - tweak::Tweakfile, + tweak::Tweaks, }; use serde::Deserialize; @@ -36,7 +36,7 @@ impl Plugin for Display2dPlugin { .after(game::update_board) .run_if(any_component_changed::), sync_sprite.run_if(any_component_changed::), - load_spritesheet.run_if(on_event::>()), + load_spritesheet.run_if(on_event::>()), // Set Sprite for Pieces set_sprite.run_if(any_component_changed::), // When tweakfile is updated @@ -121,21 +121,24 @@ fn initialize_camera(mut commands: Commands) { /// STARTUP: Load sprite sheet and insert texture atlas fn load_spritesheet( mut texture_atlases: ResMut>, - server: Res, - tweaks: Res>, + tweaks: Res>, mut commands: Commands, + tweaks_file: Res, ) { info!("Loading spritesheet"); - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfiles"); + let tweak = tweaks + .get(&tweaks_file.handle.clone()) + .expect("Load tweakfiles"); let atlas = TextureAtlas::from_grid( - server.load(tweak.display2d.sprites.file.clone()), + tweak + .get_handle_unchecked::("display2d_sprites_file") + .unwrap(), Vec2::new( - tweak.display2d.sprites.tile_size[0] as f32, - tweak.display2d.sprites.tile_size[1] as f32, + tweak.get::("display2d_sprites_tile_size_x").unwrap(), + tweak.get::("display2d_sprites_tile_size_y").unwrap(), ), - tweak.display2d.sprites.columns, - tweak.display2d.sprites.rows, + tweak.get::("display2d_sprites_columns").unwrap(), + tweak.get::("display2d_sprites_rows").unwrap(), None, None, ); @@ -251,18 +254,22 @@ fn set_sprite( With, >, sprite_sheet: Option>, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); let tweak = tweaks - .get(&handle) + .get(tweaks_file.handle.clone()) .expect("Load tweaksfile in set piece sprite"); let sprite_sheet = sprite_sheet.expect("Sprite sheet"); entities .iter_mut() .for_each(|(mut sprite, mut texture_atlas, game_sprite)| { - if let Some(index) = tweak.display2d.sprites.locate(game_sprite) { + if let Some(index) = tweak + .get::>("display2d_sprites_sprite_order") + .unwrap() + .iter() + .position(|s| s == game_sprite) + { if *texture_atlas != sprite_sheet.handle { *texture_atlas = sprite_sheet.handle.clone(); } @@ -382,33 +389,3 @@ fn move_piece( } }) } - -pub(crate) mod tweaks { - use super::*; - - #[derive(Debug, Deserialize, Default)] - pub(crate) struct Display2dTweaks { - #[serde(default)] - pub sprites: SpriteTweaks, - } - - #[derive(Debug, Deserialize, Default)] - pub(crate) struct SpriteTweaks { - #[serde(default)] - pub file: String, - #[serde(default)] - pub tile_size: [u8; 2], - #[serde(default)] - pub columns: usize, - #[serde(default)] - pub rows: usize, - #[serde(default)] - pub sprite_order: Vec, - } - - impl SpriteTweaks { - pub(crate) fn locate(&self, s: &GameSprite) -> Option { - self.sprite_order.iter().position(|x| x == s) - } - } -} diff --git a/src/display3d.rs b/src/display3d.rs index f7d99f1..ebc183f 100644 --- a/src/display3d.rs +++ b/src/display3d.rs @@ -1,9 +1,7 @@ -use std::path::Path; - use crate::{ game::{Board, BoardIndex, Piece, Side}, prelude::*, - tweak::Tweakfile, + tweak::Tweaks, }; use bevy::{ animation::RepeatAnimation, @@ -14,17 +12,14 @@ use bevy::{ Skybox, }, input::mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, - pbr::{ - ScreenSpaceAmbientOcclusionBundle, ScreenSpaceAmbientOcclusionQualityLevel, - ScreenSpaceAmbientOcclusionSettings, - }, + pbr::{ScreenSpaceAmbientOcclusionBundle, ScreenSpaceAmbientOcclusionSettings}, render::{ render_resource::{TextureViewDescriptor, TextureViewDimension}, view::ColorGrading, }, - window::PrimaryWindow, asset::AssetPath, + window::PrimaryWindow, }; -use serde::Deserialize; +use tweaks::*; pub(crate) struct Display3dPlugin; @@ -41,7 +36,7 @@ impl Plugin for Display3dPlugin { ( load_assets .run_if(in_state(GameState::Loading)) - .run_if(on_event::>()), + .run_if(on_event::>()), hydrate_camera.run_if(any_component_added::), set_piece_model.run_if(any_component_added::), set_board_model.run_if(any_component_added::), @@ -61,7 +56,7 @@ impl Plugin for Display3dPlugin { create_valid_move_entity.run_if(any_component_added::), remove_valid_move_entity.run_if(any_component_removed::()), set_valid_move_model.run_if(any_component_added::), - update_tweaks.run_if(on_event::>()), + update_tweaks.run_if(on_event::>()), ), ) .add_systems( @@ -110,8 +105,6 @@ struct TilesComponent; #[derive(Debug, Resource, Clone)] struct AssetsMap { - models: Handle, - skybox: Handle, hitbox_shape: Handle, hitbox_material: Handle, } @@ -120,12 +113,9 @@ struct AssetsMap { /// This is kind of pulling double duty. /// Both loads the GLTF file _and_ populates the ModelMap once that is loaded. fn load_assets( - server: Res, mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, - tweak_file: Res, - tweak_files: Res>, ) { let hitbox_shape = meshes.add(shape::Box::new(1.0, 0.1, 1.0).into()); let hitbox_material = materials.add(StandardMaterial { @@ -135,16 +125,10 @@ fn load_assets( alpha_mode: AlphaMode::Blend, ..default() }); - - let handle = server.load("martian.tweak.toml"); - let tweak_file = tweak_files.get(handle).unwrap(); - let asset_map = AssetsMap { - models: tweak_file.display3d.models.assets_handle.clone(), - skybox: tweak_file.display3d.models.skybox_handle.clone(), + commands.insert_resource(AssetsMap { hitbox_shape, hitbox_material, - }; - commands.insert_resource(asset_map.clone()); + }); } /// Initialize the 3d board @@ -201,22 +185,24 @@ fn initialize(mut commands: Commands, board: Res, assets: Res>, - assets: Res, - assets_map: Res, gltfs: Res>, state: Res>, mut players: Query<&mut AnimationPlayer>, mut commands: Commands, - tweaks: Res>, - server: Res, + tweaks_file: Res, + tweaks: Res>, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); + let tweak = tweaks + .get(tweaks_file.handle.clone()) + .expect("Load tweakfile"); events .iter() .filter(|(name, _)| name.as_str() == "GameCam") .for_each(|(_, entity)| { info!("Initialize 3d camera"); + let skybox_handle = tweak + .get_handle::("display3d_models_skybox_file") + .unwrap(); // Populate the components for the camera commands.entity(entity).insert(( Display3d, @@ -224,17 +210,17 @@ fn hydrate_camera( Camera3dBundle { camera: Camera { is_active: false, - hdr: tweak.display3d.hdr, + hdr: tweak.get::("display3d_hdr").unwrap(), ..default() }, dither: DebandDither::Enabled, color_grading: ColorGrading { ..default() }, ..default() }, - Skybox(assets.skybox.clone()), + Skybox(skybox_handle.clone()), EnvironmentMapLight { - diffuse_map: assets.skybox.clone(), - specular_map: assets.skybox.clone(), + diffuse_map: skybox_handle.clone(), + specular_map: skybox_handle.clone(), }, UiCameraConfig { show_ui: true }, FogSettings { ..default() }, @@ -244,18 +230,27 @@ fn hydrate_camera( Name::new("3D Camera"), )); - let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); + let gltf = gltfs.get(assets_handle).expect("Load GLTF content"); // Set it to the default position by starting the initial animation if let Ok(mut player) = players.get_mut(entity) { debug!("Animations: {:?}", gltf.named_animations.keys()); // GameCamIntro1, GameCamIntro2, GameCamSide1>2, GameCamSide2>1 let animation = match state.get() { - game::TurnState::SideA => gltf - .named_animations - .get(tweak.display3d.models.animations.intro_a.as_str()), - game::TurnState::SideB => gltf - .named_animations - .get(tweak.display3d.models.animations.intro_b.as_str()), + game::TurnState::SideA => gltf.named_animations.get( + tweak + .get::("display3d_models_animations_intro_a") + .unwrap() + .as_str(), + ), + game::TurnState::SideB => gltf.named_animations.get( + tweak + .get::("display3d_models_animations_intro_b") + .unwrap() + .as_str(), + ), }; player .play(animation.expect("Camera Startup").clone()) @@ -276,7 +271,8 @@ fn update_tweaks( ), With, >, - tweaks: Res>, + tweaks_file: Res, + tweaks: Res>, // mut lights: Query< // ( // Option<&mut SpotLight>, @@ -286,30 +282,40 @@ fn update_tweaks( // Or<(With, With, With)>, // >, mut commands: Commands, - server: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - if let Some(tweak) = tweaks.get(&handle) { + if let Some(tweak) = tweaks.get(tweaks_file.handle.clone()) { camera_settings.iter_mut().for_each( |(entity, mut fog, mut color_grading, mut tonemapping)| { - *fog = tweak.display3d.fog.clone().into(); - *color_grading = tweak.display3d.color.grading.clone().into(); - *tonemapping = tweak.display3d.color.tonemapping.clone().into(); - - let quality_level: Option = - tweak.display3d.ssao.quality_level.clone().into(); + *fog = tweak + .get::("display3d_fog") + .unwrap() + .into(); + *color_grading = tweak + .get::("display3d_color_grading") + .unwrap() + .into(); + *tonemapping = tweak + .get::("display3d_color_tonemapping") + .unwrap() + .into(); + + let quality_level = tweak.get::( + "display3d_ssao_quality_level", + ); match quality_level { Some(quality_level) => { commands .entity(entity) - .insert(ScreenSpaceAmbientOcclusionSettings { quality_level }); + .insert(ScreenSpaceAmbientOcclusionSettings { + quality_level: quality_level.into(), + }); commands.insert_resource(Msaa::Off); } None => { commands .entity(entity) .remove::(); - let msaa: Msaa = tweak.display3d.msaa.clone().into(); + let msaa: Msaa = tweak.get::("display3d_msaa").unwrap().into(); commands.insert_resource(msaa); } } @@ -323,8 +329,17 @@ fn update_tweaks( } } -fn fix_skybox(mut images: ResMut>, assets: Res) { - let image = images.get_mut(&assets.skybox).unwrap(); +fn fix_skybox( + mut images: ResMut>, + tweaks_file: Res, + tweaks: Res>, +) { + let tweak = tweaks.get(tweaks_file.handle.clone()).unwrap(); + let handle = tweak + .get_handle::("display3d_models_skybox_file") + .unwrap(); + let image = images.get_mut(handle).unwrap(); + info!("Loaded skybox image"); // NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture, // so they appear as one texture. The following code reconfigures the texture as necessary. @@ -342,25 +357,37 @@ fn fix_skybox(mut images: ResMut>, assets: Res) { /// Set the model for each piece based on the game::Piece::* marker fn set_piece_model( mut events: Query<(&mut Handle, &Piece), (Added, With)>, - assets_map: Res, gltfs: Res>, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); + let tweak = tweaks + .get(tweaks_file.handle.clone()) + .expect("Load tweakfile"); events.iter_mut().for_each(|(mut handle, piece)| { - let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); + let gltf = gltfs.get(assets_handle).expect("Load GLTF content"); *handle = match piece { - game::Piece::Pawn => gltf - .named_scenes - .get(tweak.display3d.models.scenes.pawn.as_str()), - game::Piece::Drone => gltf - .named_scenes - .get(tweak.display3d.models.scenes.drone.as_str()), - game::Piece::Queen => gltf - .named_scenes - .get(tweak.display3d.models.scenes.queen.as_str()), + game::Piece::Pawn => gltf.named_scenes.get( + tweak + .get::("display3d_models_scenes_pawn") + .unwrap() + .as_str(), + ), + game::Piece::Drone => gltf.named_scenes.get( + tweak + .get::("display3d_models_scenes_drone") + .unwrap() + .as_str(), + ), + game::Piece::Queen => gltf.named_scenes.get( + tweak + .get::("display3d_models_scenes_queen") + .unwrap() + .as_str(), + ), } .expect("Game board model") .clone(); @@ -384,24 +411,35 @@ fn set_board_model( With, ), >, - assets_map: Res, gltfs: Res>, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); + let tweak = tweaks + .get(tweaks_file.handle.clone()) + .expect("Load tweakfile"); boards.iter_mut().for_each(|mut handle| { - let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); + let gltf = gltfs.get(assets_handle).expect("Load GLTF content"); *handle = gltf .named_scenes - .get(tweak.display3d.models.scenes.board.as_str()) + .get( + tweak + .get::("display3d_models_scenes_board") + .unwrap() + .as_str(), + ) .expect("Game board model") .clone(); }); // TODO: Get rid of this -- upstream asset has merged tiles back into gameboard scene + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); tiles.iter_mut().for_each(|mut handle| { - let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); + let gltf = gltfs.get(assets_handle.clone()).expect("Load GLTF content"); *handle = gltf .named_scenes .get("Tiles") @@ -503,17 +541,18 @@ fn set_piece_texture( ), >, gltfs: Res>, - assets_map: Res, children: Query<&Children>, mut models: Query<(&Name, &mut Handle)>, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); events.iter().for_each(|(entity, piece, side)| { debug!("Checking piece texture for {:?}", entity); - if let Some(gltf) = gltfs.get(&assets_map.models) { + let tweak = tweaks.get(tweaks_file.handle.clone()).unwrap(); + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); + if let Some(gltf) = gltfs.get(assets_handle) { children.iter_descendants(entity).for_each(|child| { if let Ok((n, mut m)) = models.get_mut(child) { debug!("Setting piece texture for {:?}", child); @@ -521,84 +560,144 @@ fn set_piece_texture( (Piece::Queen, Side::A, "Queen.0") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.queen_red.as_str()) + .get( + tweak + .get::("display3d_models_materials_queen_red") + .unwrap() + .as_str(), + ) .expect("Load Red Queen texture") .clone() } (Piece::Queen, Side::A, "Queen.1") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.dots_red.as_str()) + .get( + tweak + .get::("display3d_models_materials_dots_red") + .unwrap() + .as_str(), + ) .expect("Load Red Dots texture") .clone() } (Piece::Queen, Side::B, "Queen.0") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.queen_blue.as_str()) + .get( + tweak + .get::("display3d_models_materials_queen_blue") + .unwrap() + .as_str(), + ) .expect("Load Blue Queen texture") .clone() } (Piece::Queen, Side::B, "Queen.1") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.dots_blue.as_str()) + .get( + tweak + .get::("display3d_models_materials_dots_blue") + .unwrap() + .as_str(), + ) .expect("Load Red Dots texture") .clone() } (Piece::Drone, Side::A, "Drone.0") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.drone_red.as_str()) + .get( + tweak + .get::("display3d_models_materials_drone_red") + .unwrap() + .as_str(), + ) .expect("Load Red Drone texture") .clone() } (Piece::Drone, Side::A, "Drone.1") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.dots_red.as_str()) + .get( + tweak + .get::("display3d_models_materials_dots_red") + .unwrap() + .as_str(), + ) .expect("Load Red Dots texture") .clone() } (Piece::Drone, Side::B, "Drone.0") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.drone_blue.as_str()) + .get( + tweak + .get::("display3d_models_materials_drone_blue") + .unwrap() + .as_str(), + ) .expect("Load Blue Drone texture") .clone() } (Piece::Drone, Side::B, "Drone.1") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.dots_blue.as_str()) + .get( + tweak + .get::("display3d_models_materials_dots_blue") + .unwrap() + .as_str(), + ) .expect("Load Blue Dots texture") .clone() } (Piece::Pawn, Side::A, "Pawn.0") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.pawn_red.as_str()) + .get( + tweak + .get::("display3d_models_materials_pawn_red") + .unwrap() + .as_str(), + ) .expect("Load Red Pawn texture") .clone() } (Piece::Pawn, Side::A, "Pawn.1") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.dots_red.as_str()) + .get( + tweak + .get::("display3d_models_materials_dots_red") + .unwrap() + .as_str(), + ) .expect("Load Red Dots texture") .clone() } (Piece::Pawn, Side::B, "Pawn.0") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.pawn_blue.as_str()) + .get( + tweak + .get::("display3d_models_materials_pawn_blue") + .unwrap() + .as_str(), + ) .expect("Load Blue Pawn texture") .clone() } (Piece::Pawn, Side::B, "Pawn.1") => { *m = gltf .named_materials - .get(tweak.display3d.models.materials.dots_blue.as_str()) + .get( + tweak + .get::("display3d_models_materials_dots_blue") + .unwrap() + .as_str(), + ) .expect("Load Blue Dots texture") .clone() } @@ -718,17 +817,25 @@ fn create_valid_move_entity( fn set_valid_move_model( mut events: Query<&mut Handle, (With, Added)>, gltfs: Res>, - assets_map: Res, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); - if let Some(gltf) = gltfs.get(&assets_map.models) { + let tweak = tweaks + .get(tweaks_file.handle.clone()) + .expect("Load tweakfile"); + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); + if let Some(gltf) = gltfs.get(assets_handle) { events.iter_mut().for_each(|mut handle| { *handle = gltf .named_scenes - .get(tweak.display3d.models.scenes.valid_move.as_str()) + .get( + tweak + .get::("display3d_models_scenes_valid_move") + .unwrap() + .as_str(), + ) .unwrap() .clone() }) @@ -758,26 +865,35 @@ fn pick_up( (Entity, &game::Piece), (With, With, Added), >, - assets_map: Res, gltfs: Res>, children: Query<&Children>, mut players: Query<&mut AnimationPlayer>, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); + let tweak = tweaks + .get(tweaks_file.handle.clone()) + .expect("Load tweakfile"); events.iter_mut().for_each(|(entity, piece)| { - let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); + let assets_handle = tweak + .get_handle::("dispaly3d_models_assets_file") + .unwrap(); + let gltf = gltfs.get(assets_handle).expect("Load GLTF content"); children.iter_descendants(entity).for_each(|child| { if let Ok(mut player) = players.get_mut(child) { info!("Picking up {:?} {:?}", entity, piece); - let animation = gltf - .named_animations - .get(tweak.display3d.models.animations.pick_up.as_str()); - let idle = gltf - .named_animations - .get(tweak.display3d.models.animations.idle.as_str()); + let animation = gltf.named_animations.get( + tweak + .get::("display3d_models_animations_pick_up") + .unwrap() + .as_str(), + ); + let idle = gltf.named_animations.get( + tweak + .get::("display3d_models_animations_idle") + .unwrap() + .as_str(), + ); player .start_with_transition( animation.expect("Pickup Animation").clone(), @@ -796,24 +912,30 @@ fn pick_up( fn put_down( mut events: RemovedComponents, mut query: Query<&game::Piece, (With, With, With)>, - assets_map: Res, gltfs: Res>, children: Query<&Children>, mut players: Query<&mut AnimationPlayer>, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); + let tweak = tweaks + .get(&tweaks_file.handle.clone()) + .expect("Load tweakfile"); events.read().for_each(|entity| { if let Ok(_piece) = query.get_mut(entity) { - let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); + let gltf = gltfs.get(assets_handle).expect("Load GLTF content"); children.iter_descendants(entity).for_each(|child| { if let Ok(mut player) = players.get_mut(child) { info!("Putting down {:?}", entity); - let animation = gltf - .named_animations - .get(tweak.display3d.models.animations.put_down.as_str()); + let animation = gltf.named_animations.get( + tweak + .get::("display3d_models_animations_put_down") + .unwrap() + .as_str(), + ); player .start_with_transition( animation.expect("PutDown Animation").clone(), @@ -843,23 +965,32 @@ fn opening_animation(mut players: Query<&mut AnimationPlayer, (With, Wit fn switch_sides( mut players: Query<&mut AnimationPlayer, (With, With)>, - assets_map: Res, gltfs: Res>, state: Res>, - tweaks: Res>, - server: Res, + tweaks: Res>, + tweaks_file: Res, ) { - let handle: Handle = server.load("martian.tweak.toml"); - let tweak = tweaks.get(&handle).expect("Load tweakfile"); - let gltf = gltfs.get(&assets_map.models).expect("Load GLTF content"); + let tweak = tweaks + .get(tweaks_file.handle.clone()) + .expect("Load tweakfile"); + let assets_handle = tweak + .get_handle::("display3d_models_assets_file") + .unwrap(); + let gltf = gltfs.get(assets_handle).expect("Load GLTF content"); players.iter_mut().for_each(|mut player| { let animation = match state.get() { - game::TurnState::SideA => gltf - .named_animations - .get(tweak.display3d.models.animations.turn_a.as_str()), - game::TurnState::SideB => gltf - .named_animations - .get(tweak.display3d.models.animations.turn_b.as_str()), + game::TurnState::SideA => gltf.named_animations.get( + tweak + .get::("display3d_models_animations_turn_a") + .unwrap() + .as_str(), + ), + game::TurnState::SideB => gltf.named_animations.get( + tweak + .get::("display3d_models_animations_turn_b") + .unwrap() + .as_str(), + ), }; player.start_with_transition( animation.expect("Camera Transition Animation").clone(), @@ -868,154 +999,118 @@ fn switch_sides( }); } -pub(crate) mod tweaks { +pub(super) mod tweaks { use bevy::{ - core_pipeline::tonemapping::Tonemapping, pbr::ScreenSpaceAmbientOcclusionQualityLevel, asset::LoadContext, + core_pipeline::tonemapping::Tonemapping, + math::Vec3, + pbr::{FogFalloff, FogSettings, ScreenSpaceAmbientOcclusionQualityLevel}, + render::{ + color::Color, + view::{ColorGrading, Msaa}, + }, }; + use serde::Deserialize; - use super::*; - - #[derive(Debug, Deserialize, Default)] - pub(crate) struct Display3dTweaks { - #[serde(default)] - pub hdr: bool, - #[serde(default)] - pub fog: FogTweaks, - #[serde(default)] - pub color: ColorTweaks, - #[serde(default)] - pub ssao: SsaoTweaks, - #[serde(default)] - pub msaa: MsaaTweaks, - #[serde(default)] - pub models: ModelTweaks, - #[serde(default)] - pub lights: LightTweaks, - } - - impl Display3dTweaks { - pub fn load_dependencies(&mut self, load_context: &mut LoadContext) { - self.models.assets_handle = load_context.load(self.models.assets_file.clone()); - self.models.skybox_handle = load_context.load(self.models.skybox_file.clone()); - - // self.models.scenes.queen_handle = load_context.load(self.models.scenes.queen.clone()); - // self.models.scenes.drone_handle = load_context.load(self.models.scenes.drone.clone()); - // self.models.scenes.pawn_handle = load_context.load(self.models.scenes.pawn.clone()); - // self.models.scenes.board_handle = load_context.load(self.models.scenes.board.clone()); - // self.models.scenes.valid_move_handle = load_context.load(self.models.scenes.valid_move.clone()); - - // self.models.animations.intro_a_handle = load_context.load(self.models.animations.intro_a.clone()); - // self.models.animations.intro_b_handle = load_context.load(self.models.animations.xxx.clone()); - // self.models.animations.turn_a_handle = load_context.load(self.models.animations.turn_a.clone()); - // self.models.animations.turn_b_handle = load_context.load(self.models.animations.turn_b.clone()); - // self.models.animations.pick_up_handle = load_context.load(self.models.animations.pick_up.clone()); - // self.models.animations.put_down_handle = load_context.load(self.models.animations.put_down.clone()); - // self.models.animations.idle_handle = load_context.load(self.models.animations.idle.clone()); - - // self.models.materials.queen_red_handle = load_context.load(self.models.materials.queen_red.clone()); - // self.models.materials.queen_blue_handle = load_context.load(self.models.materials.queen_blue.clone()); - // self.models.materials.drone_red_handle = load_context.load(self.models.materials.drone_red.clone()); - // self.models.materials.drone_blue_handle = load_context.load(self.models.materials.drone_blue.clone()); - // self.models.materials.pawn_red_handle = load_context.load(self.models.materials.xxx.clone()); - // self.models.materials.pawn_blue_handle = load_context.load(self.models.materials.pawn_blue.clone()); - // self.models.materials.dots_red_handle = load_context.load(self.models.materials.dots_red.clone()); - // self.models.materials.dots_blue_handle = load_context.load(self.models.materials.dots_blue.clone()); - } - } - - #[derive(Debug, Deserialize, Default)] - pub(crate) struct ColorTweaks { - #[serde(default)] - pub grading: ColorGradingTweaks, - #[serde(default)] - pub tonemapping: TonemappingTweak, - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) struct ColorGradingTweaks { - pub exposure: f32, - pub gamma: f32, - pub pre_saturation: f32, - pub post_saturation: f32, - } - - impl Into for ColorGradingTweaks { - fn into(self) -> ColorGrading { - ColorGrading { - exposure: self.exposure, - gamma: self.gamma, - pre_saturation: self.pre_saturation, - post_saturation: self.post_saturation, - } - } - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) enum TonemappingTweak { + #[derive(Debug, Deserialize)] + pub enum TweakTonemapping { None, Reinhard, ReinhardLuminance, AcesFitted, AgX, SomewhatBoringDisplayTransform, - #[default] TonyMcMapface, BlenderFilmic, } - impl Into for TonemappingTweak { - fn into(self) -> Tonemapping { - match self { - TonemappingTweak::None => Tonemapping::None, - TonemappingTweak::Reinhard => Tonemapping::Reinhard, - TonemappingTweak::ReinhardLuminance => Tonemapping::ReinhardLuminance, - TonemappingTweak::AcesFitted => Tonemapping::AcesFitted, - TonemappingTweak::AgX => Tonemapping::AgX, - TonemappingTweak::SomewhatBoringDisplayTransform => { + impl From for Tonemapping { + fn from(src: TweakTonemapping) -> Tonemapping { + match src { + TweakTonemapping::None => Tonemapping::None, + TweakTonemapping::Reinhard => Tonemapping::Reinhard, + TweakTonemapping::ReinhardLuminance => Tonemapping::ReinhardLuminance, + TweakTonemapping::AcesFitted => Tonemapping::AcesFitted, + TweakTonemapping::AgX => Tonemapping::AgX, + TweakTonemapping::SomewhatBoringDisplayTransform => { Tonemapping::SomewhatBoringDisplayTransform } - TonemappingTweak::TonyMcMapface => Tonemapping::TonyMcMapface, - TonemappingTweak::BlenderFilmic => Tonemapping::BlenderFilmic, + TweakTonemapping::TonyMcMapface => Tonemapping::TonyMcMapface, + TweakTonemapping::BlenderFilmic => Tonemapping::BlenderFilmic, } } } - #[derive(Debug, Clone, Deserialize)] - pub(crate) struct FogTweaks { - #[serde(default)] - color: Color, - #[serde(default)] - light_color: Color, - #[serde(default)] - exponent: f32, - #[serde(default)] - falloff: FogFalloffTweak, + #[derive(Debug, Deserialize)] + pub enum TweakMsaa { + Off, + Sample2, + Sample4, + Sample8, + } + + impl From for Msaa { + fn from(src: TweakMsaa) -> Msaa { + match src { + TweakMsaa::Off => Msaa::Off, + TweakMsaa::Sample2 => Msaa::Sample2, + TweakMsaa::Sample4 => Msaa::Sample4, + TweakMsaa::Sample8 => Msaa::Sample8, + } + } + } + + #[derive(Deserialize, Debug)] + pub enum TweakScreenSpaceAmbientOcclusionQualityLevel { + Low, + Medium, + High, + Ultra, } - impl Default for FogTweaks { - fn default() -> Self { - FogTweaks { - color: Color::WHITE, - light_color: Color::WHITE, - exponent: 1.0, - falloff: FogFalloffTweak::Exponential { density: 1.0 }, + impl From + for ScreenSpaceAmbientOcclusionQualityLevel + { + fn from( + src: TweakScreenSpaceAmbientOcclusionQualityLevel, + ) -> ScreenSpaceAmbientOcclusionQualityLevel { + match src { + TweakScreenSpaceAmbientOcclusionQualityLevel::Low => { + ScreenSpaceAmbientOcclusionQualityLevel::Low + } + TweakScreenSpaceAmbientOcclusionQualityLevel::Medium => { + ScreenSpaceAmbientOcclusionQualityLevel::Medium + } + TweakScreenSpaceAmbientOcclusionQualityLevel::High => { + ScreenSpaceAmbientOcclusionQualityLevel::High + } + TweakScreenSpaceAmbientOcclusionQualityLevel::Ultra => { + ScreenSpaceAmbientOcclusionQualityLevel::Ultra + } } } } - impl Into for FogTweaks { - fn into(self) -> FogSettings { + #[derive(Debug, Deserialize)] + pub struct TweakFogSettings { + pub color: Color, + pub directional_light_color: Color, + pub directional_light_exponent: f32, + pub falloff: TweakFogFalloff, + } + + impl From for FogSettings { + fn from(src: TweakFogSettings) -> FogSettings { FogSettings { - color: self.color, - directional_light_color: self.light_color, - directional_light_exponent: self.exponent, - falloff: self.falloff.clone().into(), + color: src.color, + directional_light_color: src.directional_light_color, + directional_light_exponent: src.directional_light_exponent, + falloff: src.falloff.into(), } } } - #[derive(Debug, Deserialize, Clone)] - pub(crate) enum FogFalloffTweak { + #[derive(Debug, Deserialize)] + pub enum TweakFogFalloff { Linear { start: f32, end: f32, @@ -1032,21 +1127,15 @@ pub(crate) mod tweaks { }, } - impl Default for FogFalloffTweak { - fn default() -> FogFalloffTweak { - FogFalloffTweak::Exponential { density: 1.0 } - } - } - - impl Into for FogFalloffTweak { - fn into(self) -> FogFalloff { - match self { - FogFalloffTweak::Linear { start, end } => FogFalloff::Linear { start, end }, - FogFalloffTweak::Exponential { density } => FogFalloff::Exponential { density }, - FogFalloffTweak::ExponentialSquared { density } => { + impl From for FogFalloff { + fn from(src: TweakFogFalloff) -> FogFalloff { + match src { + TweakFogFalloff::Linear { start, end } => FogFalloff::Linear { start, end }, + TweakFogFalloff::Exponential { density } => FogFalloff::Exponential { density }, + TweakFogFalloff::ExponentialSquared { density } => { FogFalloff::ExponentialSquared { density } } - FogFalloffTweak::Atmospheric { + TweakFogFalloff::Atmospheric { extinction, inscattering, } => FogFalloff::Atmospheric { @@ -1057,158 +1146,22 @@ pub(crate) mod tweaks { } } - #[derive(Debug, Clone, Default, Deserialize)] - pub(crate) struct SsaoTweaks { - #[serde(default)] - pub quality_level: SsaoQualityLevelTweak, - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) enum SsaoQualityLevelTweak { - #[default] - Off, - Low, - Medium, - High, - Ultra, - Custom { - slice_count: u32, - samples_per_slice_side: u32, - }, - } - - impl Into> for SsaoQualityLevelTweak { - fn into(self) -> Option { - match self { - SsaoQualityLevelTweak::Off => None, - SsaoQualityLevelTweak::Low => Some(ScreenSpaceAmbientOcclusionQualityLevel::Low), - SsaoQualityLevelTweak::Medium => { - Some(ScreenSpaceAmbientOcclusionQualityLevel::Medium) - } - SsaoQualityLevelTweak::High => Some(ScreenSpaceAmbientOcclusionQualityLevel::High), - SsaoQualityLevelTweak::Ultra => { - Some(ScreenSpaceAmbientOcclusionQualityLevel::Ultra) - } - SsaoQualityLevelTweak::Custom { - slice_count, - samples_per_slice_side, - } => Some(ScreenSpaceAmbientOcclusionQualityLevel::Custom { - slice_count, - samples_per_slice_side, - }), - } - } - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) enum MsaaTweaks { - #[default] - Off, - Sample2, - Sample4, - Sample8, + #[derive(Debug, Deserialize)] + pub struct TweakColorGrading { + pub exposure: f32, + pub gamma: f32, + pub pre_saturation: f32, + pub post_saturation: f32, } - impl Into for MsaaTweaks { - fn into(self) -> Msaa { - match self { - MsaaTweaks::Off => Msaa::Off, - MsaaTweaks::Sample2 => Msaa::Sample2, - MsaaTweaks::Sample4 => Msaa::Sample4, - MsaaTweaks::Sample8 => Msaa::Sample4, + impl From for ColorGrading { + fn from(src: TweakColorGrading) -> ColorGrading { + ColorGrading { + exposure: src.exposure, + gamma: src.gamma, + pre_saturation: src.pre_saturation, + post_saturation: src.post_saturation, } } } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) struct ModelTweaks { - pub assets_file: String, - #[serde(skip)] - pub assets_handle: Handle, - pub skybox_file: String, - #[serde(skip)] - pub skybox_handle: Handle, - pub scenes: SceneTweaks, - pub animations: AnimationTweaks, - pub materials: MaterialTweaks, - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) struct SceneTweaks { - pub queen: String, - // #[serde(skip)] - // pub queen_handle: Handle, - pub drone: String, - // #[serde(skip)] - // pub drone_handle: Handle, - pub pawn: String, - // #[serde(skip)] - // pub pawn_handle: Handle, - pub board: String, - // #[serde(skip)] - // pub board_handle: Handle, - pub valid_move: String, - // #[serde(skip)] - // pub valid_move_handle: Handle, - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) struct AnimationTweaks { - pub intro_a: String, - // #[serde(skip)] - // pub intro_a_handle: Handle, - pub intro_b: String, - // #[serde(skip)] - // pub intro_b_handle: Handle, - pub turn_a: String, - // #[serde(skip)] - // pub turn_a_handle: Handle, - pub turn_b: String, - // #[serde(skip)] - // pub turn_b_handle: Handle, - pub pick_up: String, - // #[serde(skip)] - // pub pick_up_handle: Handle, - pub put_down: String, - // #[serde(skip)] - // pub put_down_handle: Handle, - pub idle: String, - // #[serde(skip)] - // pub idle_handle: Handle, - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) struct MaterialTweaks { - pub queen_red: String, - // #[serde(skip)] - // pub queen_red_handle: Handle, - pub queen_blue: String, - // #[serde(skip)] - // pub queen_blue_handle: Handle, - pub drone_red: String, - // #[serde(skip)] - // pub drone_red_handle: Handle, - pub drone_blue: String, - // #[serde(skip)] - // pub drone_blue_handle: Handle, - pub pawn_red: String, - // #[serde(skip)] - // pub pawn_red_handle: Handle, - pub pawn_blue: String, - // #[serde(skip)] - // pub pawn_blue_handle: Handle, - pub dots_red: String, - // #[serde(skip)] - // pub dots_red_handle: Handle, - pub dots_blue: String, - // #[serde(skip)] - // pub dots_blue_handle: Handle, - } - - #[derive(Debug, Deserialize, Default, Clone)] - pub(crate) struct LightTweaks { - pub spot_light_scale: f32, - pub point_light_scale: f32, - pub directional_light_scale: f32, - } } diff --git a/src/loading.rs b/src/loading.rs index ae63db3..647894f 100644 --- a/src/loading.rs +++ b/src/loading.rs @@ -54,7 +54,7 @@ fn loading( server: Res, sprites: Res>, gltfs: Res>, - tweakfile: Res>, + tweaks: Res>, mut next_state: ResMut>, ) { let s = (!sprites.is_empty()) @@ -62,14 +62,15 @@ fn loading( .ids() .all(|id| server.is_loaded_with_dependencies(id)); let g = (!gltfs.is_empty()) && gltfs.ids().all(|id| server.is_loaded_with_dependencies(id)); - let t = (!tweakfile.is_empty()) - && tweakfile + let t = (!tweaks.is_empty()) + && tweaks .ids() .all(|id| server.is_loaded_with_dependencies(id)); info!("s {} g {} t {}", s, g, t); - if s && g && t { + if t { + // s && g && t { next_state.set(GameState::Menu) } } diff --git a/src/tweak.rs b/src/tweak.rs index 2a21af1..5f5583d 100644 --- a/src/tweak.rs +++ b/src/tweak.rs @@ -10,7 +10,7 @@ use serde::Deserialize; use std::str::Utf8Error; use thiserror::Error; -/// A Tweakfile is resource used to specify game customization like asset names, +/// A Tweaks is resource used to specify game customization like asset names, /// and non-user customizations made to the game during development. pub(crate) struct TweakPlugin; @@ -18,156 +18,101 @@ impl Plugin for TweakPlugin { fn build(&self, app: &mut App) { app.add_systems(OnEnter(GameState::Loading), load_tweakfile); - app.init_asset::() - .register_asset_loader(TweakfileLoader); + app.init_asset::() + .register_asset_loader(TweaksLoader); } } fn load_tweakfile(server: Res, mut commands: Commands) { - let handle: Handle = server.load("martian.tweak.toml"); - commands.insert_resource(GameTweakfile { handle }); + let handle: Handle = server.load("martian.tweak.toml"); + commands.insert_resource(GameTweaks { handle }); } #[derive(Debug, Resource)] -pub(crate) struct GameTweakfile { - pub handle: Handle, -} - -/// Tweakfile contains tweaks made to other parts of the game -#[derive(Debug, Deserialize, TypePath, Asset, Default)] -pub struct Tweakfile { - #[serde(default)] - pub audio: audio::AudioTweaks, - #[serde(default)] - pub display3d: display3d::tweaks::Display3dTweaks, - #[serde(default)] - pub display2d: display2d::tweaks::Display2dTweaks, +pub(crate) struct GameTweaks { + pub handle: Handle, } #[derive(Debug, Asset, TypePath)] -pub struct Tweaks(HashMap); +pub struct Tweaks { + table: toml::Table, + handles: HashMap, +} impl Tweaks { - fn from_table( - table: &toml::Table, - load_context: &mut LoadContext, - base: String, - ) -> Vec<(String, TweakValue)> { - table + fn from_table(table: &toml::Table, load_context: &mut LoadContext) -> Tweaks { + let handles = Tweaks::iter_all(table, "") .iter() - .flat_map(|(k, v)| { - let key = if base.len() > 0 { - format!("{}_{}", base, k) - } else { - k.clone() - }; - match v { - toml::Value::Table(t) => Tweaks::from_table(t, load_context, key), - toml::Value::Datetime(_) => panic!("This type is not supported!"), - _ => vec![(key, TweakValue::from_toml(v, load_context))], - } + .filter_map(|(k, v)| match v { + toml::Value::String(s) => std::path::Path::new(format!("assets/{}", s).as_str()) + .exists() + .then(|| (k.clone(), load_context.load_untyped(s).untyped())), + _ => None, }) - .collect() - } - - fn get_handle(&self, key: &str) -> Option> { - self.0.get(key).map(|val| Handle::::from(val.clone())) - } + .collect(); - fn get_int(&self, key: &str) -> Option { - self.0.get(key).map(|val| i64::from(val.clone())) - } - - fn get_float(&self, key: &str) -> Option { - self.0.get(key).map(|val| f64::from(val.clone())) - } -} - -#[derive(Debug, Clone)] -pub enum TweakValue { - Handle(UntypedHandle), - Int(i64), - Float(f64), - Bool(bool), - Str(String), - List(Vec), -} - -impl TweakValue { - fn from_toml(val: &toml::Value, load_context: &mut LoadContext) -> TweakValue { - match val { - toml::Value::String(s) => { - if std::path::Path::new(&format!("assets/{}", s)).exists() { - TweakValue::Handle(load_context.load_untyped(s).untyped()) - } else { - TweakValue::Str(s.clone()) - } - } - toml::Value::Integer(i) => TweakValue::Int(*i), - toml::Value::Float(f) => TweakValue::Float(*f), - toml::Value::Boolean(b) => TweakValue::Bool(*b), - toml::Value::Array(a) => TweakValue::List( - a.iter() - .map(|v| TweakValue::from_toml(v, load_context)) - .collect(), - ), - toml::Value::Table(_) | toml::Value::Datetime(_) => { - panic!("This type is not supported!") - } + Tweaks { + table: table.clone(), + handles, } } -} -impl From for Handle { - fn from(src: TweakValue) -> Handle { - match src { - TweakValue::Handle(h) => h.clone().typed::(), - _ => panic!("{:?} is not a handle", src), - } + pub fn get_handle(&self, key: &str) -> Option> { + self.handles.get(key).map(|h| h.clone().typed::()) } -} -impl From for String { - fn from(src: TweakValue) -> String { - match src { - TweakValue::Str(s) => s, - _ => panic!("{:?} is not a bool", src), - } + pub fn get_handle_unchecked(&self, key: &str) -> Option> { + self.handles + .get(key) + .map(|h| h.clone().typed_unchecked::()) } -} -impl From for i64 { - fn from(src: TweakValue) -> i64 { - match src { - TweakValue::Int(i) => i, - _ => panic!("{:?} is not an integer", src), - } + pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Option { + Tweaks::locate(&self.table, key) + .map(|val| val.try_into().ok()) + .flatten() } -} -impl From for f64 { - fn from(src: TweakValue) -> f64 { - match src { - TweakValue::Float(f) => f, - _ => panic!("{:?} is not a float", src), - } + fn iter_all(t: &toml::Table, key: &str) -> Vec<(String, toml::Value)> { + t.iter() + .flat_map(|(k, v)| { + let nk = if key == "" { + k.to_string() + } else { + format!("{}_{}", key, k) + }; + match v { + toml::Value::Table(nt) => Tweaks::iter_all(nt, nk.as_str()), + _ => vec![(nk, v.clone())], + } + }) + .collect() } -} -impl From for bool { - fn from(src: TweakValue) -> bool { - match src { - TweakValue::Bool(b) => b, - _ => panic!("{:?} is not a bool", src), - } + fn locate(t: &toml::Table, key: &str) -> Option { + t.iter().find_map(|(k, v)| { + if key == k { + Some(v.clone()) + } else if key.starts_with(k) { + let prefix = format!("{}_", k); + match v { + toml::Value::Table(nt) => { + Tweaks::locate(nt, key.strip_prefix(prefix.as_str()).unwrap()) + } + _ => Some(v.clone()), + } + } else { + None + } + }) } } #[derive(Default)] -pub struct TweakfileLoader; +pub struct TweaksLoader; #[derive(Debug, Error)] -pub enum TweakfileError { +pub enum TweaksError { #[error("Failed to read file")] IO(#[from] std::io::Error), #[error("Failed to decode file")] @@ -176,10 +121,10 @@ pub enum TweakfileError { Parse(#[from] toml::de::Error), } -impl AssetLoader for TweakfileLoader { - type Asset = Tweakfile; +impl AssetLoader for TweaksLoader { + type Asset = Tweaks; type Settings = (); - type Error = TweakfileError; + type Error = TweaksError; fn load<'a>( &'a self, @@ -191,11 +136,16 @@ impl AssetLoader for TweakfileLoader { let mut bytes = Vec::new(); reader.read_to_end(&mut bytes).await?; let s = std::str::from_utf8(bytes.as_slice())?; - let parsed = toml::from_str::(s)?; - let list = Tweaks::from_table(&parsed, load_context, String::new()); - let result: HashMap = list.into_iter().collect(); - // Ok(Tweaks(result)) - Ok(Tweakfile::default()) + match toml::from_str::(s) { + Ok(parsed) => { + let result = Tweaks::from_table(&parsed, load_context); + Ok(result) + } + Err(e) => { + println!("Error parsing tweaks: {:?}", e.message()); + Err(TweaksError::Parse(e)) + } + } }) }