use std::{collections::HashMap, fs::File, io::Write, path::Path};
use bevy::prelude::*;
use bevy_assets_extensions::{assets::bundle::BundleEntry, static_bundle::StaticAssetsBundle};
use ccutils::containers::Apply;
use crate::error;
#[derive(StaticAssetsBundle, Asset, TypePath)]
#[static_assets_bundle(extensions = ["configuration.ron"])]
pub struct Configuration {
#[asset(key = "layers")]
layers: Vec<String>,
}
impl Configuration {
pub fn new(layers: Vec<String>) -> Self {
Self { layers }
}
pub fn layers(&self) -> &Vec<String> {
&self.layers
}
pub fn set_layer(&mut self, idx: usize, name: impl Into<String>) {
self.layers.get_mut(idx).apply(|l| *l = name.into());
}
pub fn save_to_file(&self, path: impl AsRef<Path>) -> Result<()> {
let mut s = HashMap::<String, BundleEntry>::new();
s.insert(
"layers".to_string(),
BundleEntry::StringList(self.layers.clone()),
);
let bytes = ron::ser::to_string(&s)?;
File::create(path)?.write_all(bytes.as_bytes())?;
Ok(())
}
pub fn load_from_file(path: impl AsRef<Path>) -> Result<Configuration> {
let mut bundle: HashMap<String, BundleEntry> =
ron::from_str(&std::fs::read_to_string(path)?)?;
if let Some(layers) = bundle.remove("layers") {
match layers {
BundleEntry::StringList(list) => Ok(Configuration { layers: list }),
_ => Err(error::Error::InvalidConfigurationError(
"Extpected 'layers' to be a StringList".into(),
))?,
}
} else {
Err(error::Error::InvalidConfigurationError(
"Missing 'layers'".into(),
))?
}
}
}
#[derive(Debug, StaticAssetsBundle, Asset, TypePath)]
pub struct LayersSource {
#[asset(key = "layers")]
layers: HashMap<String, Handle<Image>>,
#[asset(key = "order")]
order: Vec<String>,
#[asset(key = "default_layers")]
default_layers: Vec<String>,
#[asset(key = "atlas_layout")]
atlas_layout: Handle<TextureAtlasLayout>,
#[asset(key = "sprite_sheet")]
sprite_sheet: Handle<crate::Sheet>,
}
impl LayersSource {
pub fn order(&self) -> &Vec<String> {
&self.order
}
pub fn layers_names(&self) -> Vec<String> {
self.layers.keys().map(|k| k.to_owned()).collect()
}
pub fn layers_names_for(&self, name: impl AsRef<str>) -> Vec<String> {
let name = name.as_ref();
self.layers
.keys()
.filter(|n| n.starts_with(name))
.map(|k| k.to_owned())
.collect()
}
pub fn sprite_sheet(&self) -> &Handle<crate::Sheet> {
&self.sprite_sheet
}
pub fn default_layers(&self) -> &Vec<String> {
&self.default_layers
}
pub fn atlas_layout(&self) -> &Handle<TextureAtlasLayout> {
&self.atlas_layout
}
}
pub trait EntityCommandsExt {
fn spawn_layers(
&mut self,
configuration: &Configuration,
source: &LayersSource,
offset: Option<&Vec3>,
) -> &mut Self;
}
impl<'a> EntityCommandsExt for EntityCommands<'a> {
fn spawn_layers(
&mut self,
configuration: &Configuration,
source: &LayersSource,
offset: Option<&Vec3>,
) -> &mut Self {
self.with_children(|c| {
for layer in &configuration.layers {
match source.layers.get(layer) {
Some(handle) => {
let mut se = c.spawn(Sprite {
texture_atlas: Some(TextureAtlas {
layout: source.atlas_layout.clone(),
index: 0,
}),
image: handle.to_owned(),
..Default::default()
});
if let Some(offset) = offset {
se.insert(Transform::from_translation(offset.to_owned()));
}
}
None => bevy::log::error!("Cannot find layer '{}'", layer),
}
}
})
}
}
pub fn apply_configuration(
entity: Entity,
children_query: &Query<&Children>,
sprite_query: &mut Query<&mut Sprite>,
configuration: &Configuration,
layers_source: &LayersSource,
) -> Result<()> {
let children = children_query.get(entity)?;
if children.len() != configuration.layers.len() {
Err(format!(
"Number of children ({}) must match number of layers ({}) in configuration",
children.len(),
configuration.layers.len()
))?;
}
for (child, layer) in children.into_iter().zip(configuration.layers()) {
let mut sprite = sprite_query.get_mut(child.to_owned())?;
match layers_source.layers.get(layer) {
Some(handle) => sprite.image = handle.to_owned(),
None => bevy::log::error!("Cannot find layer '{}'", layer),
}
}
Ok(())
}