Skip to content
Snippets Groups Projects
mod.rs 3.72 KiB
Newer Older
#[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() {}
		}
	}
}