use std::marker::PhantomData; use bevy::ecs::system::SystemParam; use bevy::prelude::*; use ldtk_rust::{EntityInstance, LayerInstance, Level, TileInstance}; // use crate::assets::level_index::LevelIndex; use crate::assets::{LdtkProject, LevelIndex}; use crate::get_ldtk_tile_scale; use crate::utils::ActiveLevel; #[derive(SystemParam)] pub struct MapQuery<'w, 's> { assets: Res<'w, Assets<LdtkProject>>, active: Option<Res<'w, ActiveLevel>>, index: Res<'w, LevelIndex>, #[system_param(ignore)] _e: PhantomData<&'s ()>, } pub struct TileRef<'a> { pub tile: &'a TileInstance, } impl<'a> TileRef<'a> { pub fn new(tile: &'a TileInstance) -> Self { TileRef { tile } } } impl<'a> TileRef<'a> { pub fn gid(&self) -> usize { (self.tile.src[0] * self.tile.src[1]).unsigned_abs() as usize } } pub struct LayerRef<'a> { pub idx: usize, pub layer: &'a LayerInstance, } impl<'a> LayerRef<'a> { pub fn new(idx: usize, layer: &'a LayerInstance) -> Self { LayerRef { layer, idx } } pub fn has_tiles(&self) -> bool { !(self.layer.auto_layer_tiles.is_empty() && self.layer.grid_tiles.is_empty()) } pub fn for_each_tile(&self, mut cb: impl FnMut(i64, i64, TileRef)) { self.layer .grid_tiles .iter() .chain(self.layer.auto_layer_tiles.iter()) .for_each(|tile: &TileInstance| { let (x, y) = match tile.px.as_slice() { &[x, y] => (x, y), _ => { return; } }; cb(x, y, TileRef::new(tile)); }); } pub fn get_z_delta(&self) -> f32 { (self.idx as f32) / 100.0 } // pub fn get_tile_metadata_of(&self, tile_id: i64) { // self.layer.til // } } #[derive(Copy, Clone, Debug)] pub struct CameraBounds { pub left: f32, pub top: f32, pub bottom: f32, pub right: f32, } impl CameraBounds { pub fn get_min_x(&self, camera_width: f32) -> f32 { self.left + (camera_width / 2.0) - (get_ldtk_tile_scale() / 2.0) } pub fn get_max_x(&self, camera_width: f32) -> f32 { self.right - (camera_width / 2.0) - (get_ldtk_tile_scale() / 2.0) } pub fn get_min_y(&self, camera_height: f32) -> f32 { self.bottom + (camera_height / 2.0) - (get_ldtk_tile_scale() / 2.0) } pub fn get_max_y(&self, camera_height: f32) -> f32 { self.top - (camera_height / 2.0) - (get_ldtk_tile_scale() / 2.0) } } impl<'w, 's> MapQuery<'w, 's> { // --- We put our logic in static accessors because we might source a level other // --- than the currently active one. 'active' methods are a convenience to // --- call the static accessors on whatever the current level is 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)); } } } pub fn get_entities_of(level: &Level) -> Vec<&EntityInstance> { level .layer_instances .as_ref() .map(|layers| { layers .iter() .flat_map(|layer| layer.entity_instances.iter()) .collect() }) .unwrap_or_default() } pub fn get_camera_bounds_of(level: &Level) -> CameraBounds { CameraBounds { left: 0.0, top: level.px_hei as f32, bottom: 0.0, right: level.px_wid as f32, } } pub fn get_active_level(&self) -> Option<&Level> { self.active .as_ref() .and_then(|index| self.index.get(&index.map)) } pub fn get_entities(&self) -> Vec<&EntityInstance> { self.get_active_level() .map(|level| MapQuery::get_entities_of(level)) .unwrap_or_default() } pub fn get_camera_bounds(&self) -> Option<CameraBounds> { self.get_active_level().map(MapQuery::get_camera_bounds_of) } pub fn for_each_layer(&self, mut cb: impl FnMut(LayerRef)) { if let Some(level) = self.get_active_level() { Self::for_each_layer_of(level, cb); } } }