#[cfg(feature = "ldtk_1_2_5")] mod data_1_2_5; #[cfg(feature = "ldtk_1_2_4")] mod data_1_2_4; use bevy::asset::{AssetLoader, BoxedFuture, LoadContext, LoadedAsset}; use bevy::reflect::{TypeUuid, Uuid}; #[cfg(feature = "ldtk_1_2_4")] pub use data_1_2_4::*; #[cfg(feature = "ldtk_1_2_5")] pub use data_1_2_5::*; #[derive(thiserror::Error, Debug)] pub enum ParseError { #[error("Failed to parse file: {0}")] SerdeError(String), } impl TypeUuid for Project { const TYPE_UUID: Uuid = Uuid::from_u128(87988914102923589138720617793417023455); } impl Project { pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> { serde_json::from_slice(bytes).map_err(|e| ParseError::SerdeError(format!("{}", e))) } } pub type LdtkProject = Project; impl<'a> From<&'a [u8]> for Project { fn from(value: &'a [u8]) -> Self { #[cfg(feature = "no_panic")] { match Project::from_bytes(value) { Ok(val) => val, Err(e) => { log::error!("{}", e); std::process::abort(); } } } #[cfg(not(feature = "no_panic"))] { Project::from_bytes(value).expect("Failed to parse ldtk project file") } } } #[derive(Default)] pub struct LdtkLoader; impl AssetLoader for LdtkLoader { fn load<'a>( &'a self, bytes: &'a [u8], load_context: &'a mut LoadContext, ) -> BoxedFuture<'a, anyhow::Result<(), anyhow::Error>> { Box::pin(async move { load_context.set_default_asset(LoadedAsset::new(Project::from_bytes(bytes)?)); Ok(()) }) } fn extensions(&self) -> &[&str] { &["ldtk"] } } #[cfg(feature = "autotile")] mod autotile_support { use micro_autotile::{AutoRuleSet, AutoTileRule, TileMatcher, TileOutput, TileStatus}; use crate::ldtk::{AutoLayerRuleGroup, Project}; impl From<&AutoLayerRuleGroup> for AutoRuleSet { fn from(value: &AutoLayerRuleGroup) -> Self { let set = value .rules .iter() .filter_map(|rule| match rule.size { 1 => { let rule = AutoTileRule { chance: rule.chance as f32, output: TileOutput::Random( rule.tile_ids.iter().map(|val| *val as usize).collect(), ), matcher: TileMatcher::single_match(TileStatus::from_ldtk_value( rule.pattern[0], )), }; Some(rule) } 3 => { if rule.pattern.len() == 9 { let matcher = TileMatcher([ TileStatus::from_ldtk_value(rule.pattern[0]), TileStatus::from_ldtk_value(rule.pattern[1]), TileStatus::from_ldtk_value(rule.pattern[2]), TileStatus::from_ldtk_value(rule.pattern[3]), TileStatus::from_ldtk_value(rule.pattern[4]), TileStatus::from_ldtk_value(rule.pattern[5]), TileStatus::from_ldtk_value(rule.pattern[6]), TileStatus::from_ldtk_value(rule.pattern[7]), TileStatus::from_ldtk_value(rule.pattern[8]), ]); let rule = AutoTileRule { chance: rule.chance as f32, matcher, output: TileOutput::Random( rule.tile_ids.iter().map(|val| *val as usize).collect(), ), }; Some(rule) } else { None } } _ => None, }) .collect(); AutoRuleSet(set) } } impl From<&Project> for AutoRuleSet { fn from(value: &Project) -> Self { let mut base_set = AutoRuleSet::default(); for layers in value.defs.layers.iter() { for rule_group in layers.auto_rule_groups.iter() { base_set = base_set + rule_group.into(); } } base_set } } } #[cfg(test)] mod test { use crate::ldtk::Project; #[test] pub fn load_project() { const project_data: &[u8] = include_bytes!("./test_data/ver_1_2_5.ldtk"); let project = Project::from_bytes(project_data).expect("Failed to parse project file"); for layer in project.defs.layers.iter() { for auto_rule_group in layer.auto_rule_groups.iter() {} } } }