Newer
Older
use std::fmt::Debug;
use crate::assets::LevelIndex;
use crate::ldtk::EntityInstance;
active: Option<Res<'w, ActiveLevel>>,
index: Res<'w, LevelIndex>,
}
pub struct InstanceRef<'a> {
pub entity: &'a EntityInstance,
}
impl<'a> InstanceRef<'a> {
/// Get the leftmost pixel of this entity's anchor point
pub fn x(&self) -> i64 {
self.entity.px[0]
}
/// Get the topmost pixel of this entity's anchor point
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
}
/// Get the category that this instance belongs to. Exactly matches the string name
/// found in the LDTK entities list
pub fn get_type(&self) -> &'a String {
&self.entity.identifier
}
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/// Try to get a type safe representation of this entity's type, as long as the target type
/// can be produced from a [str] representation
///
/// ## Example
///
/// ```
/// # use std::str::FromStr;
/// # use micro_ldtk::InstanceRef;
/// # use micro_ldtk::ldtk::EntityInstance;
///
/// #[derive(PartialEq, Debug)]
/// enum MyEntityType {
/// Player,
/// Monster,
/// }
///
/// impl FromStr for MyEntityType {
/// # type Err = ();
/// fn from_str(s: &str) -> Result<Self, Self::Err> {
/// match s {
/// "player" => Ok(Self::Player),
/// "monster" => Ok(Self::Monster),
/// # _ => panic!("Oh no")
/// }
/// }
/// }
///
/// let data_from_ldtk: EntityInstance = EntityInstance {
/// identifier: "player".to_string(),
/// // ...other properties
/// # smart_color: "".to_string(),
/// # grid: vec![],
/// # pivot: vec![],
/// # tags: vec![],
/// # tile: None,
/// # world_x: None,
/// # world_y: None,
/// # def_uid: 0,
/// # field_instances: vec![],
/// # height: 0,
/// # iid: "".to_string(),
/// # px: vec![],
/// # width: 0,
/// };
/// #
/// # let process_ldtk_data = || -> InstanceRef<'_> {
/// # InstanceRef {
/// # entity: &data_from_ldtk,
/// # }
/// # };
///
/// let my_entity_type: InstanceRef<'_> = process_ldtk_data();
/// assert_eq!(my_entity_type.try_get_typed_id(), Ok(MyEntityType::Player));
/// ```
pub fn try_get_typed_id<T: FromStr>(&self) -> Result<T, T::Err> {
T::from_str(self.get_type().as_str())
}
/// Retrieve an associated property from this instance. Will return [serde_json::Value::Null]
/// if there is no property with the given name
pub fn property(&self, name: impl ToString) -> serde_json::Value {
self[name].clone()
}
/// Get a reference to the inner instance of this instance ref
pub fn instance_ref(&self) -> &EntityInstance {
self.entity
}
}
type Output = serde_json::Value;
fn index(&self, index: T) -> &Self::Output {
let name = index.to_string();
if field.identifier == name {
return field.value.as_ref().unwrap_or(&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)
self.right - (camera_width / 2.0) // - (get_ldtk_tile_scale() / 2.0)
self.bottom + (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0)
self.top - (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0)
// --- 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
/// Perform an action on each layer of the given LDTK level
pub fn for_each_layer_of(level: &LdtkLevel, mut cb: impl FnMut(&LdtkLayer)) {
for layer in level.layers() {
/// Retrieve an iterator over every layer in the given level, regardless of type
pub fn get_layers_of(level: &LdtkLevel) -> impl DoubleEndedIterator<Item = &LdtkLayer> {
level.layers()
}
/// Retrieve a reference to every entity stored in the given level, regardless of which layer it is found on
pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> {
.layers()
.flat_map(|layer| layer.as_ref().entity_instances.iter())
.collect()
/// Retrieve an enhanced wrapper to every entity stored in the given level, regardless of which layer it is found on
pub fn get_instance_refs_of(level: &LdtkLevel) -> Vec<InstanceRef> {
.layers()
.flat_map(|layer| {
layer
.as_ref()
.entity_instances
/// Retrieve a reference to every entity stored in the given level that matches the specified type name.
/// This must exactly match the name shown in the LDTK entity list
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()
/// Retrieve an enhanced wrapper to every entity stored in the given level that matches the specified type name.
/// This must exactly match the name shown in the LDTK entity list
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()
}
/// Retrieve an owned copy of all entity data in the given level
pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> {
.flat_map(|layer| layer.as_ref().entity_instances.iter().cloned())
/// Use the size of the level to create a zero-based rectangle indicating the boundaries that a camera should
///stay within to avoid showing any out-of-level space
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,
}
}
/// Check to see if the currently active level has the given name. Will always return false if there
/// is no active level
pub fn active_level_is(&self, name: impl ToString) -> bool {
.map(|active_level| active_level.map == name.to_string())
.unwrap_or(false)
}
/// Get the name of the currently active level, or `None` if no level is active
pub fn get_active_level_name(&self) -> Option<&String> {
self.active.as_ref().map(|m| &m.map)
}
/// Get a reference to the currently active level data
pub fn get_active_level(&self) -> Option<&LdtkLevel> {
self.get_active_level_name()
.and_then(|index| self.index.get(index))
/// Get a list of references to entities in the currently active level
/// See [MapQuery::get_entities_of]
pub fn get_entities(&self) -> Vec<&EntityInstance> {
self.get_active_level()
.map(MapQuery::get_entities_of)
/// Get a list of enhanced wrapper to entities in the currently active level
/// See [MapQuery::get_instance_refs_of]
pub fn get_instance_refs(&self) -> Vec<InstanceRef> {
self.get_active_level()
.map(MapQuery::get_instance_refs_of)
.unwrap_or_default()
}
/// Get a zero based rect that indicated pixel bounds for a camera
/// See [MapQuery::get_camera_bounds_of]
pub fn get_camera_bounds(&self) -> Option<CameraBounds> {
self.get_active_level().map(MapQuery::get_camera_bounds_of)
}
/// Perform an action for every layer in the currently active level
/// See [MapQuery::for_each_layer_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);
}
}
/// Search for the first listed instance ref of a given entity type in any layer within the
/// currently active level
pub fn find_one(&self, entity_type: impl AsRef<str>) -> Option<InstanceRef> {
let name = entity_type.as_ref();
self.get_instance_refs()
.iter()
.find(|ir| ir.get_type() == name)
.cloned()
}
/// Search for an instance ref with a specific IID within the currently active level
pub fn find_entity_by_iid(&self, iid: impl ToString) -> Option<InstanceRef> {
let iid = iid.to_string();
self.get_instance_refs()
.iter()
.find(|ir| ir.instance_ref().iid == iid)
.cloned()
}