Mostly working, battling handle types

main
Elijah Voigt 2 years ago
parent cef69b81cc
commit de30eaf06a

@ -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"
]

@ -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::<Table>(s).unwrap();
println!("{:?}", v);
let vt = toml::from_str::<Table>(s).unwrap();
iter_all(&vt, "")
.iter()
.for_each(|(k, v)| println!("k {:?} :: v {:?}", k, v));
let x = get::<Color>(&vt, "color_foo_bar_baz").unwrap();
println!("{:?}", x);
let y = get::<Vec<usize>>(&vt, "color_foo_bar_hjkl").unwrap();
println!("{:?}", y);
let z = get::<bool>(&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<toml::Value> {
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<T> {
locate(t, key).map(|val| val.try_into().ok()).flatten()
}

@ -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<FmodStudio>,
mut commands: Commands,
server: Res<AssetServer>,
tweakfiles: Res<Assets<tweak::Tweakfile>>,
tweaks: Res<Assets<tweak::Tweaks>>,
display_state: Res<State<DisplayState>>,
) {
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::<String>("audio_music_main").unwrap()
}
AudioEvent::MenuSelect => tweak.get::<String>("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::<String>("audio_display2d_pick_up").unwrap(),
DisplayState::Display3d => tweak.get::<String>("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::<String>("audio_display2d_put_down").unwrap(),
DisplayState::Display3d => tweak.get::<String>("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::<String>("audio_display2d_idle").unwrap(),
DisplayState::Display3d => tweak.get::<String>("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::<String>("audio_display2d_invalid").unwrap(),
DisplayState::Display3d => tweak.get::<String>("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,
}

@ -27,6 +27,9 @@ impl Plugin for DebugPlugin {
display_diagnostics.run_if(resource_exists::<DebugEnabled>()),
toggle_debug_ui.run_if(resource_changed_or_removed::<DebugEnabled>()),
camera_info.run_if(resource_exists::<DebugEnabled>()),
// debug_asset_event::<Gltf>.run_if(on_event::<AssetEvent<Gltf>>()),
// debug_asset_event::<Image>.run_if(on_event::<AssetEvent<Image>>()),
debug_asset_event::<tweak::Tweaks>.run_if(on_event::<AssetEvent<tweak::Tweaks>>()),
),
);
}
@ -139,3 +142,9 @@ fn camera_info(mut debug_infos: ResMut<DebugInfo>, cameras: Query<(&Camera, &Nam
.collect::<String>();
debug_infos.set("Cameras".into(), camera_names);
}
fn debug_asset_event<T: Asset>(mut reader: EventReader<AssetEvent<T>>) {
reader.read().for_each(|e| {
info!("Asset Event: {:?}", e);
});
}

@ -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::<BoardIndex>),
sync_sprite.run_if(any_component_changed::<Side>),
load_spritesheet.run_if(on_event::<AssetEvent<Tweakfile>>()),
load_spritesheet.run_if(on_event::<AssetEvent<Tweaks>>()),
// Set Sprite for Pieces
set_sprite.run_if(any_component_changed::<GameSprite>),
// 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<Assets<TextureAtlas>>,
server: Res<AssetServer>,
tweaks: Res<Assets<tweak::Tweakfile>>,
tweaks: Res<Assets<tweak::Tweaks>>,
mut commands: Commands,
tweaks_file: Res<tweak::GameTweaks>,
) {
info!("Loading spritesheet");
let handle: Handle<tweak::Tweakfile> = 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::<Image>("display2d_sprites_file")
.unwrap(),
Vec2::new(
tweak.display2d.sprites.tile_size[0] as f32,
tweak.display2d.sprites.tile_size[1] as f32,
tweak.get::<f32>("display2d_sprites_tile_size_x").unwrap(),
tweak.get::<f32>("display2d_sprites_tile_size_y").unwrap(),
),
tweak.display2d.sprites.columns,
tweak.display2d.sprites.rows,
tweak.get::<usize>("display2d_sprites_columns").unwrap(),
tweak.get::<usize>("display2d_sprites_rows").unwrap(),
None,
None,
);
@ -251,18 +254,22 @@ fn set_sprite(
With<Display2d>,
>,
sprite_sheet: Option<Res<SpriteSheet>>,
tweaks: Res<Assets<Tweakfile>>,
server: Res<AssetServer>,
tweaks: Res<Assets<Tweaks>>,
tweaks_file: Res<tweak::GameTweaks>,
) {
let handle: Handle<Tweakfile> = 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::<Vec<GameSprite>>("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<GameSprite>,
}
impl SpriteTweaks {
pub(crate) fn locate(&self, s: &GameSprite) -> Option<usize> {
self.sprite_order.iter().position(|x| x == s)
}
}
}

File diff suppressed because it is too large Load Diff

@ -54,7 +54,7 @@ fn loading(
server: Res<AssetServer>,
sprites: Res<Assets<Image>>,
gltfs: Res<Assets<Gltf>>,
tweakfile: Res<Assets<tweak::Tweakfile>>,
tweaks: Res<Assets<tweak::Tweaks>>,
mut next_state: ResMut<NextState<GameState>>,
) {
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)
}
}

@ -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::<Tweakfile>()
.register_asset_loader(TweakfileLoader);
app.init_asset::<Tweaks>()
.register_asset_loader(TweaksLoader);
}
}
fn load_tweakfile(server: Res<AssetServer>, mut commands: Commands) {
let handle: Handle<Tweakfile> = server.load("martian.tweak.toml");
commands.insert_resource(GameTweakfile { handle });
let handle: Handle<Tweaks> = server.load("martian.tweak.toml");
commands.insert_resource(GameTweaks { handle });
}
#[derive(Debug, Resource)]
pub(crate) struct GameTweakfile {
pub handle: Handle<Tweakfile>,
}
/// 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<Tweaks>,
}
#[derive(Debug, Asset, TypePath)]
pub struct Tweaks(HashMap<String, TweakValue>);
pub struct Tweaks {
table: toml::Table,
handles: HashMap<String, UntypedHandle>,
}
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<T: Asset>(&self, key: &str) -> Option<Handle<T>> {
self.0.get(key).map(|val| Handle::<T>::from(val.clone()))
}
.collect();
fn get_int(&self, key: &str) -> Option<i64> {
self.0.get(key).map(|val| i64::from(val.clone()))
}
fn get_float(&self, key: &str) -> Option<f64> {
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<TweakValue>),
}
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<T: Asset> From<TweakValue> for Handle<T> {
fn from(src: TweakValue) -> Handle<T> {
match src {
TweakValue::Handle(h) => h.clone().typed::<T>(),
_ => panic!("{:?} is not a handle", src),
}
pub fn get_handle<T: Asset>(&self, key: &str) -> Option<Handle<T>> {
self.handles.get(key).map(|h| h.clone().typed::<T>())
}
}
impl From<TweakValue> for String {
fn from(src: TweakValue) -> String {
match src {
TweakValue::Str(s) => s,
_ => panic!("{:?} is not a bool", src),
}
pub fn get_handle_unchecked<T: Asset>(&self, key: &str) -> Option<Handle<T>> {
self.handles
.get(key)
.map(|h| h.clone().typed_unchecked::<T>())
}
}
impl From<TweakValue> 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<T> {
Tweaks::locate(&self.table, key)
.map(|val| val.try_into().ok())
.flatten()
}
}
impl From<TweakValue> 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<TweakValue> 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<toml::Value> {
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::<toml::Table>(s)?;
let list = Tweaks::from_table(&parsed, load_context, String::new());
let result: HashMap<String, TweakValue> = list.into_iter().collect();
// Ok(Tweaks(result))
Ok(Tweakfile::default())
match toml::from_str::<toml::Table>(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))
}
}
})
}

Loading…
Cancel
Save