Skip to content
Snippets Groups Projects
Verified Commit c75898be authored by Louis's avatar Louis :fire:
Browse files

Support external level files

parent a39c7a0a
No related branches found
No related tags found
No related merge requests found
...@@ -1567,7 +1567,7 @@ dependencies = [ ...@@ -1567,7 +1567,7 @@ dependencies = [
[[package]] [[package]]
name = "micro_ldtk" name = "micro_ldtk"
version = "0.6.0" version = "0.6.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bevy", "bevy",
......
...@@ -3,7 +3,7 @@ use std::collections::HashMap; ...@@ -3,7 +3,7 @@ use std::collections::HashMap;
use bevy::prelude::*; use bevy::prelude::*;
use crate::assets::{LevelIndex, TileMetadata, TilesetIndex}; use crate::assets::{LevelIndex, TileMetadata, TilesetIndex};
use crate::ldtk::Project; use crate::ldtk::{Level, Project};
use crate::{LdtkLevel, LevelDataUpdated}; use crate::{LdtkLevel, LevelDataUpdated};
pub fn handle_ldtk_project_events( pub fn handle_ldtk_project_events(
...@@ -18,9 +18,11 @@ pub fn handle_ldtk_project_events( ...@@ -18,9 +18,11 @@ pub fn handle_ldtk_project_events(
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
if let Some(project) = assets.get(handle) { if let Some(project) = assets.get(handle) {
for level in project.get_all_levels() { for level in project.get_all_levels() {
level_index if level.external_rel_path.is_none() {
.insert(level.identifier.clone(), LdtkLevel::from(level.clone())); level_index
update_events.send(LevelDataUpdated(level.identifier.clone())); .insert(level.identifier.clone(), LdtkLevel::from(level.clone()));
update_events.send(LevelDataUpdated(level.identifier.clone()));
}
} }
for tileset in &project.defs.tilesets { for tileset in &project.defs.tilesets {
...@@ -39,3 +41,22 @@ pub fn handle_ldtk_project_events( ...@@ -39,3 +41,22 @@ pub fn handle_ldtk_project_events(
} }
} }
} }
pub fn handle_ldtk_level_events(
mut events: EventReader<AssetEvent<Level>>,
assets: Res<Assets<Level>>,
mut level_index: ResMut<LevelIndex>,
mut update_events: EventWriter<LevelDataUpdated>,
) {
for event in events.iter() {
match event {
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
if let Some(level) = assets.get(handle) {
level_index.insert(level.identifier.clone(), LdtkLevel::from(level.clone()));
update_events.send(LevelDataUpdated(level.identifier.clone()));
}
}
_ => {}
}
}
}
...@@ -15,7 +15,7 @@ mod data_1_2_5; ...@@ -15,7 +15,7 @@ mod data_1_2_5;
#[cfg(feature = "ldtk_1_3_0")] #[cfg(feature = "ldtk_1_3_0")]
mod data_1_3_0; mod data_1_3_0;
use bevy::asset::{AssetLoader, BoxedFuture, LoadContext, LoadedAsset}; use bevy::asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadedAsset};
use bevy::reflect::{TypePath, TypeUuid, Uuid}; use bevy::reflect::{TypePath, TypeUuid, Uuid};
#[cfg(feature = "ldtk_1_0_0")] #[cfg(feature = "ldtk_1_0_0")]
pub use data_1_0_0::*; pub use data_1_0_0::*;
...@@ -33,6 +33,7 @@ pub use data_1_2_4::*; ...@@ -33,6 +33,7 @@ pub use data_1_2_4::*;
pub use data_1_2_5::*; pub use data_1_2_5::*;
#[cfg(feature = "ldtk_1_3_0")] #[cfg(feature = "ldtk_1_3_0")]
pub use data_1_3_0::*; pub use data_1_3_0::*;
use serde::Deserialize;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum ParseError { pub enum ParseError {
...@@ -40,6 +41,42 @@ pub enum ParseError { ...@@ -40,6 +41,42 @@ pub enum ParseError {
SerdeError(String), SerdeError(String),
} }
pub trait LdtkFromBytes<'a>: Deserialize<'a> {
fn from_bytes(bytes: &'a [u8]) -> Result<Self, ParseError> {
serde_json::from_slice(bytes).map_err(|e| ParseError::SerdeError(format!("{}", e)))
}
}
macro_rules! impl_from_bytes {
($type: tt) => {
impl<'a> From<&'a [u8]> for $type {
fn from(value: &'a [u8]) -> Self {
#[cfg(feature = "no_panic")]
{
match $type::from_bytes(value) {
Ok(val) => val,
Err(e) => {
log::error!("{}", e);
std::process::abort();
}
}
}
#[cfg(not(feature = "no_panic"))]
{
$type::from_bytes(value).expect("Failed to parse ldtk file")
}
}
}
};
}
impl<'a> LdtkFromBytes<'a> for Level {}
impl<'a> LdtkFromBytes<'a> for Project {}
impl_from_bytes!(Level);
impl_from_bytes!(Project);
impl TypeUuid for Project { impl TypeUuid for Project {
const TYPE_UUID: Uuid = Uuid::from_u128(87988914102923589138720617793417023455); const TYPE_UUID: Uuid = Uuid::from_u128(87988914102923589138720617793417023455);
} }
...@@ -53,11 +90,21 @@ impl TypePath for Project { ...@@ -53,11 +90,21 @@ impl TypePath for Project {
} }
} }
impl Project { impl TypeUuid for Level {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> { const TYPE_UUID: Uuid = Uuid::from_u128(18486863672600588966868281477384349187);
serde_json::from_slice(bytes).map_err(|e| ParseError::SerdeError(format!("{}", e))) }
impl TypePath for Level {
fn type_path() -> &'static str {
"micro_ld0tk::ldtk::Level"
} }
fn short_type_path() -> &'static str {
"Level"
}
}
impl Project {
pub fn get_all_levels(&self) -> Vec<&Level> { pub fn get_all_levels(&self) -> Vec<&Level> {
if !self.worlds.is_empty() { if !self.worlds.is_empty() {
self.worlds self.worlds
...@@ -99,26 +146,6 @@ impl Project { ...@@ -99,26 +146,6 @@ impl Project {
pub type LdtkProject = Project; 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)] #[derive(Default)]
pub struct LdtkLoader; pub struct LdtkLoader;
impl AssetLoader for LdtkLoader { impl AssetLoader for LdtkLoader {
...@@ -128,7 +155,27 @@ impl AssetLoader for LdtkLoader { ...@@ -128,7 +155,27 @@ impl AssetLoader for LdtkLoader {
load_context: &'a mut LoadContext, load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, anyhow::Result<(), anyhow::Error>> { ) -> BoxedFuture<'a, anyhow::Result<(), anyhow::Error>> {
Box::pin(async move { Box::pin(async move {
load_context.set_default_asset(LoadedAsset::new(Project::from_bytes(bytes)?)); let project = Project::from_bytes(bytes)?;
let sub_levels = project
.levels
.iter()
.flat_map(|level| {
level
.external_rel_path
.as_ref()
.map(|rel_path| (level.identifier.clone(), rel_path.clone()))
})
.collect::<Vec<(String, String)>>();
let asset = LoadedAsset::new(project).with_dependencies(
sub_levels
.into_iter()
.map(|(id, path)| AssetPath::new(path.into(), Some(id)))
.collect(),
);
load_context.set_default_asset(asset);
Ok(()) Ok(())
}) })
} }
...@@ -137,6 +184,24 @@ impl AssetLoader for LdtkLoader { ...@@ -137,6 +184,24 @@ impl AssetLoader for LdtkLoader {
&["ldtk"] &["ldtk"]
} }
} }
#[derive(Default)]
pub struct LdtkLevelLoader;
impl AssetLoader for LdtkLevelLoader {
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(Level::from_bytes(bytes)?));
Ok(())
})
}
fn extensions(&self) -> &[&str] {
&["ldtkl"]
}
}
#[cfg(feature = "autotile")] #[cfg(feature = "autotile")]
mod autotile_support { mod autotile_support {
...@@ -215,7 +280,7 @@ mod autotile_support { ...@@ -215,7 +280,7 @@ mod autotile_support {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::ldtk::Project; use crate::ldtk::{LdtkFromBytes, Project};
#[cfg_attr(feature = "ldtk_1_2_5", test)] #[cfg_attr(feature = "ldtk_1_2_5", test)]
pub fn load_project() { pub fn load_project() {
......
...@@ -75,10 +75,13 @@ mod __plugin { ...@@ -75,10 +75,13 @@ mod __plugin {
{ {
app.add_event::<super::system::LevelDataUpdated>() app.add_event::<super::system::LevelDataUpdated>()
.add_asset::<super::ldtk::Project>() .add_asset::<super::ldtk::Project>()
.add_asset::<super::ldtk::Level>()
.add_asset_loader(super::ldtk::LdtkLoader) .add_asset_loader(super::ldtk::LdtkLoader)
.add_asset_loader(super::ldtk::LdtkLevelLoader)
.init_resource::<super::assets::TilesetIndex>() .init_resource::<super::assets::TilesetIndex>()
.init_resource::<super::assets::LevelIndex>() .init_resource::<super::assets::LevelIndex>()
.add_systems(Update, super::assets::handle_ldtk_project_events); .add_systems(Update, super::assets::handle_ldtk_project_events)
.add_systems(Update, super::assets::handle_ldtk_level_events);
} }
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment