Skip to content
Snippets Groups Projects
map_query.rs 3.93 KiB
Newer Older
Louis's avatar
Louis committed
use std::marker::PhantomData;
Louis's avatar
Louis committed
use std::str::FromStr;
Louis's avatar
Louis committed

use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use ldtk_rust::EntityInstance;
Louis's avatar
Louis committed

use crate::assets::LevelIndex;
use crate::utils::{ActiveLevel, SerdeClone};
use crate::{get_ldtk_tile_scale, LdtkLayer, LdtkLevel};
Louis's avatar
Louis committed

#[derive(SystemParam)]
pub struct MapQuery<'w, 's> {
	active: Option<Res<'w, ActiveLevel>>,
	index: Res<'w, LevelIndex>,
	#[system_param(ignore)]
	_e: PhantomData<&'s ()>,
}

Louis's avatar
Louis committed
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()
Louis's avatar
Louis committed
					.unwrap_or(serde_json::Value::Null);
			}
		}

		serde_json::Value::Null
	}
Louis's avatar
Louis committed
}

#[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().rev() {
			cb(layer);
Louis's avatar
Louis committed
		}
	}

	pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> {
Louis's avatar
Louis committed
		level
Louis's avatar
Louis committed
			.layer_instances
			.as_ref()
			.map(|layers| {
				layers
					.iter()
					.flat_map(|layer| layer.entity_instances.iter())
					.collect()
			})
			.unwrap_or_default()
	}

	pub fn get_instance_refs_of(level: &LdtkLevel) -> Vec<InstanceRef> {
Louis's avatar
Louis committed
		level
Louis's avatar
Louis committed
			.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(
Louis's avatar
Louis committed
		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()
Louis's avatar
Louis committed
	}

	pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> {
Louis's avatar
Louis committed
		level
			.layers()
			.flat_map(|layer| {
				layer
					.as_ref()
					.entity_instances
Louis's avatar
Louis committed
					.iter()
					.map(|inst| inst.serde_clone())
Louis's avatar
Louis committed
			})
Louis's avatar
Louis committed
	}

	pub fn get_camera_bounds_of(level: &LdtkLevel) -> CameraBounds {
		let level = level.level_ref();
Louis's avatar
Louis committed
		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<&LdtkLevel> {
Louis's avatar
Louis committed
		self.active
			.as_ref()
			.and_then(|index| self.index.get(&index.map))
	}

	pub fn get_entities(&self) -> Vec<&EntityInstance> {
		self.get_active_level()
			.map(MapQuery::get_entities_of)
Louis's avatar
Louis committed
			.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)) {
Louis's avatar
Louis committed
		if let Some(level) = self.get_active_level() {
			Self::for_each_layer_of(level, cb);
		}
	}
}