use std::fmt::Debug; use std::marker::PhantomData; use std::str::FromStr; use bevy::ecs::system::SystemParam; use bevy::prelude::*; use crate::assets::LevelIndex; use crate::ldtk::EntityInstance; use crate::system::ActiveLevel; use crate::{get_ldtk_tile_scale, LdtkLayer, LdtkLevel}; #[derive(SystemParam)] pub struct MapQuery<'w, 's> { active: Option<Res<'w, ActiveLevel>>, index: Res<'w, LevelIndex>, #[system_param(ignore)] _e: PhantomData<&'s ()>, } pub struct InstanceRef<'a> { pub entity: &'a EntityInstance, } impl<'a> InstanceRef<'a> { pub fn x(&self) -> i64 { self.entity.px[0] } pub fn y(&self) -> i64 { self.entity.px[1] } pub fn width(&self) -> i64 { self.entity.width } pub fn height(&self) -> i64 { self.entity.height } 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() .cloned() .unwrap_or(serde_json::Value::Null); } } serde_json::Value::Null } } #[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: &LdtkLevel, mut cb: impl FnMut(&LdtkLayer)) { for layer in level.layers() { cb(layer); } } pub fn get_layers_of(level: &LdtkLevel) -> impl DoubleEndedIterator<Item = &LdtkLayer> { level.layers() } pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> { level .layers() .flat_map(|layer| layer.as_ref().entity_instances.iter()) .collect() } pub fn get_instance_refs_of(level: &LdtkLevel) -> Vec<InstanceRef> { level .layers() .flat_map(|layer| { layer .as_ref() .entity_instances .iter() .map(|inst| InstanceRef { entity: inst }) }) .collect() } pub fn get_filtered_entities_of( level: &LdtkLevel, entity_type: impl ToString, ) -> Vec<&EntityInstance> { let e_type = entity_type.to_string(); level .layers() .flat_map(|layer| layer.as_ref().entity_instances.iter()) .filter(|inst| inst.identifier == e_type) .collect() } pub fn get_filtered_instance_refs_of( level: &LdtkLevel, entity_type: impl ToString, ) -> Vec<InstanceRef> { let e_type = entity_type.to_string(); level .layers() .flat_map(|layer| { layer .as_ref() .entity_instances .iter() .map(|inst| InstanceRef { entity: inst }) }) .filter(|inst| inst.entity.identifier == e_type) .collect() } pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> { level .layers() .flat_map(|layer| layer.as_ref().entity_instances.iter().cloned()) .collect() } pub fn get_camera_bounds_of(level: &LdtkLevel) -> CameraBounds { let level = level.level_ref(); CameraBounds { left: 0.0, top: level.px_hei as f32, bottom: 0.0, right: level.px_wid as f32, } } pub fn active_level_is(&self, name: impl ToString) -> bool { self.active .as_ref() .map(|active_level| active_level.map == name.to_string()) .unwrap_or(false) } pub fn get_active_level_name(&self) -> Option<&String> { self.active.as_ref().map(|m| &m.map) } pub fn get_active_level(&self) -> Option<&LdtkLevel> { self.get_active_level_name() .and_then(|index| self.index.get(index)) } pub fn get_entities(&self) -> Vec<&EntityInstance> { self.get_active_level() .map(MapQuery::get_entities_of) .unwrap_or_default() } pub fn get_instance_refs(&self) -> Vec<InstanceRef> { self.get_active_level() .map(MapQuery::get_instance_refs_of) .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, cb: impl FnMut(&LdtkLayer)) { if let Some(level) = self.get_active_level() { Self::for_each_layer_of(level, cb); } } }