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

Refactor into nested mods, embed LDTK data, support LDTK 1.2.4 & 1.2.5

parent dfb52c4f
No related branches found
No related tags found
No related merge requests found
This diff is collapsed.
[package]
name = "micro_ldtk"
version = "0.2.0"
version = "0.3.0-beta.1"
edition = "2021"
authors = [
......@@ -9,13 +9,19 @@ authors = [
repository = "https://lab.lcr.gr/microhacks/bevy-micro-ldtk.git"
license = "Apache-2.0"
[features]
default = ["ldtk_1_2_5"]
ldtk_1_2_5 = []
ldtk_1_2_4 = []
no_panic = []
[dependencies]
bevy = "0.9.1"
bevy = "0.10.1"
anyhow = "1.0.66"
thiserror = "1.0.40"
log = "0.4.17"
serde = "1.0.147"
serde_json = "1.0.87"
ldtk_rust = "0.6.0"
num-traits = "0.2.15"
quadtree_rs = "0.1.2"
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use anyhow::Error;
use bevy::asset::{AssetEvent, AssetLoader, Assets, BoxedFuture, LoadContext, LoadedAsset};
use bevy::prelude::{EventReader, EventWriter, Res, ResMut, Resource};
use bevy::reflect::TypeUuid;
use ldtk_rust::Project;
use serde_json::Value;
use bevy::prelude::*;
use crate::utils::SerdeClone;
use crate::{LdtkLevel, LevelDataUpdated};
#[derive(TypeUuid)]
#[uuid = "292a8918-9487-11ed-8dd2-43b6f36cb076"]
pub struct LdtkProject(pub Project);
impl Deref for LdtkProject {
type Target = Project;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LdtkProject {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl LdtkProject {}
#[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<(), Error>> {
Box::pin(async move {
let mut ldtk: Project = serde_json::from_slice(bytes)?;
if ldtk.external_levels {
ldtk.load_external_levels(load_context.path());
}
load_context.set_default_asset(LoadedAsset::new(LdtkProject(ldtk)));
Ok(())
})
}
fn extensions(&self) -> &[&str] {
&["ldtk"]
}
}
#[derive(Resource, Default)]
pub struct LevelIndex(pub HashMap<String, LdtkLevel>);
impl Deref for LevelIndex {
type Target = HashMap<String, LdtkLevel>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LevelIndex {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Default)]
pub struct TileMetadata(pub HashMap<i64, Value>);
#[derive(Resource, Default)]
pub struct TilesetIndex(pub HashMap<String, TileMetadata>);
impl Deref for TilesetIndex {
type Target = HashMap<String, TileMetadata>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TilesetIndex {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
use crate::ldtk::Project;
use crate::{LdtkLevel, LevelDataUpdated, LevelIndex, TileMetadata, TilesetIndex};
pub fn handle_ldtk_project_events(
mut events: EventReader<AssetEvent<LdtkProject>>,
assets: Res<Assets<LdtkProject>>,
mut events: EventReader<AssetEvent<Project>>,
assets: Res<Assets<Project>>,
mut level_index: ResMut<LevelIndex>,
mut tilset_index: ResMut<TilesetIndex>,
mut update_events: EventWriter<LevelDataUpdated>,
......@@ -92,7 +15,7 @@ pub fn handle_ldtk_project_events(
for event in events.iter() {
match event {
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
if let Some(LdtkProject(project)) = assets.get(handle) {
if let Some(project) = assets.get(handle) {
for level in &project.levels {
level_index.insert(
level.identifier.clone(),
......
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use bevy::prelude::Resource;
#[derive(Resource, Default)]
pub struct LevelIndex(pub HashMap<String, LdtkLevel>);
impl Deref for LevelIndex {
type Target = HashMap<String, LdtkLevel>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LevelIndex {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Default)]
pub struct TileMetadata(pub HashMap<i64, Value>);
#[derive(Resource, Default)]
pub struct TilesetIndex(pub HashMap<String, TileMetadata>);
impl Deref for TilesetIndex {
type Target = HashMap<String, TileMetadata>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TilesetIndex {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
mod asset_events;
mod asset_storage;
pub use asset_events::*;
pub use asset_storage::*;
This diff is collapsed.
This diff is collapsed.
#[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)]
pub enum ParseError {
#[error("Failed to parse file: {0}")]
SerdeError(#[from] 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> TryFrom<&'a [u8]> for Project {
type Error = ParseError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
Project::from_bytes(value)
}
}
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"]
}
}
mod assets;
mod camera;
mod locator;
mod map_query;
mod pregen;
mod types;
pub(crate) mod utils;
// mod spawning;
mod system;
#[cfg(any(feature = "ldtk_1_2_5", feature = "ldtk_1_2_4"))]
pub mod ldtk;
pub static mut LDTK_TILE_SCALE: AtomicU32 = AtomicU32::new(32);
pub fn get_ldtk_tile_scale() -> f32 {
......@@ -29,17 +29,18 @@ mod __plugin {
use bevy::asset::AddAsset;
use bevy::ecs::query::ReadOnlyWorldQuery;
use crate::LevelDataUpdated;
pub struct MicroLDTKPlugin;
impl Plugin for MicroLDTKPlugin {
fn build(&self, app: &mut App) {
app.add_event::<LevelDataUpdated>()
.init_resource::<super::assets::TilesetIndex>()
.init_resource::<super::assets::LevelIndex>()
.add_asset::<super::assets::LdtkProject>()
.add_asset_loader(super::assets::LdtkLoader)
.add_system(super::assets::handle_ldtk_project_events);
#[cfg(any(feature = "ldtk_1_2_5", feature = "ldtk_1_2_4"))]
{
app.add_event::<super::types::LevelDataUpdated>()
.add_asset::<super::ldtk::Project>()
.add_asset_loader(super::ldtk::LdtkLoader)
.init_resource::<super::assets::TilesetIndex>()
.init_resource::<super::assets::LevelIndex>()
.add_system(super::assets::handle_ldtk_project_events);
}
}
}
......@@ -79,8 +80,5 @@ pub use __plugin::{MicroLDTKCameraPlugin, MicroLDTKPlugin};
pub use assets::{LdtkLoader, LdtkProject, LevelIndex, TileMetadata, TilesetIndex};
pub use camera::CameraBounder;
pub use map_query::{CameraBounds, MapQuery};
pub use pregen::{
write_layer_to_texture, write_map_to_texture, Rasterise, SuppliesImage, SuppliesTextureAtlas,
};
pub use types::{LdtkLayer, LdtkLevel, LevelDataUpdated, SpatialIndex, TileFlip, TileRef};
pub use utils::{entity_centre, grid_to_px, px_to_grid, ActiveLevel, Indexer, WorldLinked};
pub use pregen::{write_layer_to_texture, write_map_to_texture, Rasterise};
pub use system::*;
use bevy::prelude::{Handle, TextureAtlas};
pub trait SuppliesTileAtlas {
fn from_path(path: impl ToString) -> Option<Handle<TextureAtlas>>;
}
......@@ -4,9 +4,9 @@ use std::str::FromStr;
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use ldtk_rust::EntityInstance;
use crate::assets::LevelIndex;
use crate::ldtk::EntityInstance;
use crate::utils::{ActiveLevel, SerdeClone};
use crate::{get_ldtk_tile_scale, LdtkLayer, LdtkLevel};
......
......@@ -2,7 +2,9 @@ use bevy::prelude::{Handle, Image, TextureAtlas};
use bevy::render::render_resource::TextureFormat;
use bevy::render::texture::TextureFormatPixelInfo;
use crate::{get_ldtk_tile_scale, Indexer, LdtkLayer, LdtkLevel};
use crate::{
get_ldtk_tile_scale, Indexer, LdtkLayer, LdtkLevel, SuppliesImage, SuppliesTextureAtlas,
};
pub fn write_layer_to_texture(
layer: &LdtkLayer,
......@@ -70,15 +72,6 @@ pub fn write_map_to_texture(
}
}
pub trait SuppliesTextureAtlas {
fn get_atlas_handle(&self, name: impl ToString) -> Option<&Handle<TextureAtlas>>;
fn get_atlas(&self, name: &Handle<TextureAtlas>) -> Option<&TextureAtlas>;
}
pub trait SuppliesImage {
fn get_image_handle(&self, name: impl ToString) -> Option<&Handle<Image>>;
fn get_image(&self, handle: &Handle<Image>) -> Option<&Image>;
}
pub trait Rasterise {
fn write_to_texture(
&self,
......
// use std::marker::PhantomData;
//
// use bevy::app::{App, Plugin};
// use bevy::ecs::system::{SystemParam, SystemState};
// use bevy::prelude::{
// Commands, Component, DespawnRecursiveExt, Entity, In, Query, Res, ResMut, Resource, System,
// With, World,
// };
// use ldtk_rust::{EntityInstance, Level};
//
// use crate::utils::SerdeClone;
// use crate::{ActiveLevel, LevelIndex, MapQuery};
//
// #[derive(Component, Copy, Clone, Debug)]
// pub struct LdtkDespawnTag;
//
// pub type TileSpawnerInput = (Level, LdtkDespawnTag);
// pub type EntitySpawnerInput = Vec<EntityInstance>;
//
// fn noop_tile_system(_: In<TileSpawnerInput>) {}
// fn noop_entity_system(_: In<EntitySpawnerInput>) {}
//
// #[derive(Resource)]
// pub struct MapSpawner {
// tile_spawner: Option<Box<dyn System<In = TileSpawnerInput, Out = ()>>>,
// entity_spawner: Option<Box<dyn System<In = EntitySpawnerInput, Out = ()>>>,
// }
//
// impl MapSpawner {
// pub fn new(
// tile_spawner: impl System<In = TileSpawnerInput, Out = ()>,
// entity_spawner: impl System<In = EntitySpawnerInput, Out = ()>,
// ) -> Self {
// MapSpawner {
// tile_spawner: Some(Box::new(tile_spawner)),
// entity_spawner: Some(Box::new(entity_spawner)),
// }
// }
//
// pub fn with_tile_spawner(tile_spawner: impl System<In = TileSpawnerInput, Out = ()>) -> Self {
// MapSpawner {
// tile_spawner: Some(Box::new(tile_spawner)),
// entity_spawner: None,
// }
// }
// pub fn with_entity_spawner(
// entity_spawner: impl System<In = EntitySpawnerInput, Out = ()>,
// ) -> Self {
// MapSpawner {
// entity_spawner: Some(Box::new(entity_spawner)),
// tile_spawner: None,
// }
// }
//
// pub fn run_tile_system(&mut self, level: &Level, world: &mut World) {
// match self.tile_spawner {
// Some(ref mut system) => {
// system.run((level.serde_clone(), LdtkDespawnTag), world);
// }
// None => {}
// }
// }
// pub fn run_entity_system(&mut self, level: &Level, world: &mut World) {
// match self.entity_spawner {
// Some(ref mut system) => {
// let entity_list = MapQuery::get_owned_entities_of(level);
// system.run(entity_list, world);
// }
// None => {}
// }
// }
// }
//
// pub fn ldtk_spawning_system(
// world: &mut World,
// systems: &mut SystemState<(
// Commands,
// Option<Res<ActiveLevel>>,
// Option<ResMut<MapSpawner>>,
// Res<LevelIndex>,
// Query<Entity, With<LdtkDespawnTag>>,
// )>,
// ) {
// let (mut commands, active_level, mut spawning_systems, level_index, existing_query) =
// systems.get_mut(world);
//
// let active_level = match active_level {
// Some(res) => res,
// None => return,
// };
//
// if active_level.is_added() || (active_level.is_changed() && active_level.dirty) {
// for entity in &existing_query {
// commands.entity(entity).despawn_recursive();
// }
//
// let level = match level_index.get(&active_level.map) {
// Some(level) => level,
// None => return,
// };
//
// if let Some(mut spawning_systems) = spawning_systems {
// spawning_systems.run_tile_system(level, world);
// spawning_systems.run_entity_system(level, world);
// }
// }
// }
use bevy::prelude::{Handle, Image, TextureAtlas};
pub trait SuppliesTextureAtlas {
fn get_atlas_handle(&self, name: impl ToString) -> Option<&Handle<TextureAtlas>>;
fn get_atlas(&self, name: &Handle<TextureAtlas>) -> Option<&TextureAtlas>;
}
pub trait SuppliesImage {
fn get_image_handle(&self, name: impl ToString) -> Option<&Handle<Image>>;
fn get_image(&self, handle: &Handle<Image>) -> Option<&Image>;
}
mod locator;
mod types;
mod utils;
pub use locator::*;
pub use types::*;
pub use utils::*;
......@@ -12,6 +12,7 @@ use quadtree_rs::Quadtree;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Number, Value};
use crate::ldtk::{EntityInstance, FieldInstance, LayerInstance, Level, TileInstance};
use crate::utils::{Indexer, SerdeClone};
use crate::{get_ldtk_tile_scale, px_to_grid, MapQuery};
......
use bevy::prelude::{Component, Resource};
use ldtk_rust::EntityInstance;
use num_traits::AsPrimitive;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::get_ldtk_tile_scale;
pub trait SerdeClone {
fn serde_clone(&self) -> Self;
}
impl<T> SerdeClone for T
where
T: Serialize + DeserializeOwned,
{
fn serde_clone(&self) -> Self {
serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap()
}
}
use crate::ldtk::EntityInstance;
pub fn px_to_grid<T: AsPrimitive<i64>>(t: T) -> i64 {
t.as_() / (get_ldtk_tile_scale() as i64)
......
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