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

Extra helpers

- Include Indexer from TraderTales
- Add helpers for TileRef's
- Extra accessors on MapQuery
parent bc63ed68
No related branches found
No related tags found
No related merge requests found
......@@ -8,3 +8,5 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
.idea/
Cargo.lock 0 → 100644
This diff is collapsed.
[toolchain]
channel = "nightly"
\ No newline at end of file
hard_tabs = true
group_imports = "StdExternalCrate"
use_field_init_shorthand = true
use_try_shorthand = true
\ No newline at end of file
......@@ -11,7 +11,7 @@ use serde_json::Value;
use crate::utils::SerdeClone;
#[derive(TypeUuid)]
#[uuid = "eb97c508-61ea-11ed-abd3-db91dd0a6e8b"]
#[uuid = "292a8918-9487-11ed-8dd2-43b6f36cb076"]
pub struct LdtkProject(pub Project);
impl Deref for LdtkProject {
type Target = Project;
......
......@@ -3,6 +3,7 @@ mod camera;
mod locator;
mod map_query;
pub(crate) mod utils;
// mod spawning;
pub static mut LDTK_TILE_SCALE: AtomicU32 = AtomicU32::new(32);
pub fn get_ldtk_tile_scale() -> f32 {
......@@ -65,4 +66,6 @@ use std::sync::atomic::{AtomicU32, Ordering};
pub use __plugin::{MicroLDTKCameraPlugin, MicroLDTKPlugin};
pub use assets::{LdtkLoader, LdtkProject, LevelIndex, TileMetadata, TilesetIndex};
pub use map_query::{CameraBounds, LayerRef, MapQuery, TileRef};
pub use utils::{entity_to_worldspace, grid_to_px, px_to_grid, ActiveLevel, WorldLinked};
pub use utils::{
entity_centre, entity_to_worldspace, grid_to_px, px_to_grid, ActiveLevel, WorldLinked,
};
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::path::Path;
use std::str::FromStr;
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use ldtk_rust::{EntityInstance, LayerInstance, Level, TileInstance};
use num_traits::AsPrimitive;
// use crate::assets::level_index::LevelIndex;
use crate::assets::{LdtkProject, LevelIndex};
use crate::get_ldtk_tile_scale;
use crate::utils::ActiveLevel;
use crate::utils::{ActiveLevel, Indexer, SerdeClone};
use crate::{get_ldtk_tile_scale, px_to_grid};
#[derive(SystemParam)]
pub struct MapQuery<'w, 's> {
......@@ -22,26 +26,100 @@ pub struct TileRef<'a> {
pub tile: &'a TileInstance,
}
impl<'a> TileRef<'a> {
pub fn new(tile: &'a TileInstance) -> Self {
TileRef { tile }
#[repr(C)]
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
pub enum TileFlip {
#[default]
None = 0,
Horizontal = 1,
Vertical = 2,
Both = 3,
}
impl TileFlip {
pub fn is_horizontal(&self) -> bool {
match self {
Self::None | Self::Vertical => false,
Self::Horizontal | Self::Both => true,
}
}
pub fn is_vertical(&self) -> bool {
match self {
Self::None | Self::Horizontal => false,
Self::Vertical | Self::Both => true,
}
}
}
impl<'a> TileRef<'a> {
pub fn new(tile: &'a TileInstance) -> Self {
TileRef { tile }
}
pub fn gid(&self) -> usize {
(self.tile.src[0] * self.tile.src[1]).unsigned_abs() as usize
}
pub fn get_flip(&self) -> TileFlip {
match self.tile.f {
1 => TileFlip::Horizontal,
2 => TileFlip::Vertical,
3 => TileFlip::Both,
_ => TileFlip::None,
}
}
}
impl<'a> Debug for TileRef<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TileRef")
.field("gid", &self.gid())
.field("tile.t", &self.tile.t)
.field("tile.d", &self.tile.d)
.field("tile.px", &self.tile.px)
.field("tile.src", &self.tile.src)
.finish()
}
}
pub struct InstanceRef<'a> {
pub entity: &'a EntityInstance,
}
impl<'a> InstanceRef<'a> {
pub fn get_type(&self) -> &'a String {
&self.entity.identifier
}
pub fn try_get_typed_id<T: FromStr>(&self) -> Result<T, T::Err> {
T::from_str(self.get_type().as_str())
}
pub fn property(&self, name: impl ToString) -> serde_json::Value {
let target = name.to_string();
for field in &self.entity.field_instances {
if field.identifier == target {
return field
.value
.as_ref()
.map(|v| v.clone())
.unwrap_or(serde_json::Value::Null);
}
}
serde_json::Value::Null
}
}
pub struct LayerRef<'a> {
pub idx: usize,
pub indexer: Indexer,
pub layer: &'a LayerInstance,
}
impl<'a> LayerRef<'a> {
pub fn new(idx: usize, layer: &'a LayerInstance) -> Self {
LayerRef { layer, idx }
pub fn new(idx: usize, indexer: Indexer, layer: &'a LayerInstance) -> Self {
LayerRef {
layer,
indexer,
idx,
}
}
pub fn has_tiles(&self) -> bool {
!(self.layer.auto_layer_tiles.is_empty() && self.layer.grid_tiles.is_empty())
......@@ -53,7 +131,7 @@ impl<'a> LayerRef<'a> {
.chain(self.layer.auto_layer_tiles.iter())
.for_each(|tile: &TileInstance| {
let (x, y) = match tile.px.as_slice() {
&[x, y] => (x, y),
&[x, y] => (px_to_grid(x), px_to_grid(y)),
_ => {
return;
}
......@@ -66,9 +144,38 @@ impl<'a> LayerRef<'a> {
(self.idx as f32) / 100.0
}
// pub fn get_tile_metadata_of(&self, tile_id: i64) {
// self.layer.til
// }
pub fn get_tile_at(
&self,
x: impl AsPrimitive<isize>,
y: impl AsPrimitive<isize>,
) -> Option<TileRef> {
let idx = self.indexer.index(x, y);
match self.layer.grid_tiles.is_empty() {
true => self
.layer
.auto_layer_tiles
.get(idx)
.map(|tile| TileRef::new(tile)),
false => self
.layer
.grid_tiles
.get(idx)
.map(|tile| TileRef::new(tile)),
}
}
/// Returns the inferred name of the tileset used for this layer. This is assumed to be the
/// name of the tileset file, without the preceding path segments or the file extension. Case
/// remains unchanged
pub fn infer_tileset_name(&self) -> Option<String> {
self.layer.tileset_rel_path.as_ref().and_then(|path| {
Path::new(path)
.file_stem()
.and_then(|stem| stem.to_str())
.map(String::from)
})
}
}
#[derive(Copy, Clone, Debug)]
......@@ -99,10 +206,18 @@ impl<'w, 's> MapQuery<'w, 's> {
// --- than the currently active one. 'active' methods are a convenience to
// --- call the static accessors on whatever the current level is
pub fn get_indexer_for(level: &Level) -> Indexer {
Indexer::new(px_to_grid(level.px_wid), px_to_grid(level.px_hei))
}
pub fn for_each_layer_of(level: &Level, mut cb: impl FnMut(LayerRef)) {
if let Some(layers) = level.layer_instances.as_ref() {
for layer in layers.iter().rev().enumerate() {
cb(LayerRef::new(layer.0, layer.1));
cb(LayerRef::new(
layer.0,
MapQuery::get_indexer_for(level),
layer.1,
));
}
}
}
......@@ -120,6 +235,48 @@ impl<'w, 's> MapQuery<'w, 's> {
.unwrap_or_default()
}
pub fn get_instance_refs_of(level: &Level) -> Vec<InstanceRef> {
level
.layer_instances
.as_ref()
.map(|layers| {
layers
.iter()
.flat_map(|layer| layer.entity_instances.iter())
.map(|inst| InstanceRef { entity: inst })
.collect()
})
.unwrap_or_default()
}
pub fn get_filtered_entities_of(
level: &Level,
entity_type: impl ToString,
) -> Vec<&EntityInstance> {
let e_type = entity_type.to_string();
match level.layer_instances.as_ref() {
Some(inst) => inst
.iter()
.flat_map(|layer| layer.entity_instances.iter())
.filter(|inst| inst.identifier == e_type)
.collect(),
None => Vec::new(),
}
}
pub fn get_owned_entities_of(level: &Level) -> Vec<EntityInstance> {
level
.layer_instances
.as_ref()
.map(|layers| {
layers
.iter()
.flat_map(|layer| layer.entity_instances.iter().map(|inst| inst.serde_clone()))
.collect()
})
.unwrap_or_default()
}
pub fn get_camera_bounds_of(level: &Level) -> CameraBounds {
CameraBounds {
left: 0.0,
......
// 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);
// }
// }
// }
......@@ -37,6 +37,12 @@ pub fn entity_to_worldspace(level_height: i64, entity: &EntityInstance) -> (f32,
(box_aligned_x, box_aligned_y)
}
pub fn entity_centre(level_height: i64, entity: &EntityInstance) -> (f32, f32) {
let x = entity.px[0] - (entity.width / 2);
let y = (level_height - entity.px[1] - entity.height / 2);
(x as f32, y as f32)
}
#[derive(Component)]
pub struct WorldLinked;
......@@ -54,3 +60,36 @@ impl ActiveLevel {
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Indexer {
width: isize,
height: isize,
}
impl Indexer {
pub fn new(width: impl AsPrimitive<isize>, height: impl AsPrimitive<isize>) -> Self {
Self {
width: width.as_(),
height: height.as_(),
}
}
pub fn index(&self, x: impl AsPrimitive<isize>, y: impl AsPrimitive<isize>) -> usize {
((y.as_() * self.width) + x.as_()).as_()
}
pub fn reverse(&self, index: impl AsPrimitive<isize>) -> (usize, usize) {
(
(index.as_() % self.width) as usize,
(index.as_() / self.width) as usize,
)
}
pub fn width(&self) -> isize {
self.width
}
pub fn height(&self) -> isize {
self.height
}
}
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