Skip to content
Snippets Groups Projects
types.rs 17.05 KiB
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::ops::{Deref, DerefMut};
use std::path::Path;

use bevy::math::{IVec2, Rect, UVec2, Vec2};
use bevy::prelude::Event;
use num_traits::AsPrimitive;
use quadtree_rs::area::AreaBuilder;
use quadtree_rs::point::Point;
use quadtree_rs::Quadtree;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Number, Value};

use crate::ldtk::{EntityInstance, FieldInstance, LayerInstance, Level, TileInstance};
use crate::system::Indexer;
use crate::{get_ldtk_tile_scale, px_to_grid, MapQuery};

#[derive(Default, Clone, Debug, Ord, PartialOrd, PartialEq, Eq, Event)]
pub struct LevelDataUpdated(pub String);

pub struct TileRef<'a> {
	pub tile: &'a TileInstance,
}

#[repr(C)]
#[derive(Serialize, Deserialize, 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()
	}
}

#[derive(Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Clone, Copy, Hash, Debug)]
pub struct SpatialIndex(i64, i64);
impl<A, B> From<(A, B)> for SpatialIndex
where
	A: AsPrimitive<i64>,
	B: AsPrimitive<i64>,
{
	fn from(value: (A, B)) -> Self {
		Self(value.0.as_(), value.1.as_())
	}
}
impl From<UVec2> for SpatialIndex {
	fn from(value: UVec2) -> Self {
		Self(value.x as i64, value.y as i64)
	}
}
impl From<IVec2> for SpatialIndex {
	fn from(value: IVec2) -> Self {
		Self(value.x as i64, value.y as i64)
	}
}

pub type ColliderQuadtree = Quadtree<i64, LocatedEntity>;

pub struct LdtkLevel {
	level: Level,
	properties: HashMap<String, Value>,
	processed_layers: Vec<LdtkLayer>,
	colliders: ColliderQuadtree,
}

impl LdtkLevel {
	pub fn width(&self) -> f32 {
		self.level.px_wid as f32
	}
	pub fn width_i(&self) -> i64 {
		self.level.px_wid
	}
	pub fn height(&self) -> f32 {
		self.level.px_hei as f32
	}
	pub fn height_i(&self) -> i64 {
		self.level.px_hei
	}
	pub fn level_ref(&self) -> &Level {
		&self.level
	}
	pub fn level_ref_mut(&mut self) -> &mut Level {
		&mut self.level
	}
	pub fn colliders_ref(&self) -> &ColliderQuadtree {
		&self.colliders
	}
	pub fn colliders_ref_mut(&mut self) -> &mut ColliderQuadtree {
		&mut self.colliders
	}
	pub fn layers(&self) -> impl DoubleEndedIterator<Item = &LdtkLayer> {
		self.processed_layers.iter()
	}
	pub fn layers_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut LdtkLayer> {
		self.processed_layers.iter_mut()
	}
	pub fn properties_ref(&self) -> &HashMap<String, Value> {
		&self.properties
	}
	pub fn properties_mut(&mut self) -> &mut HashMap<String, Value> {
		&mut self.properties
	}
	pub fn property(&self, name: impl ToString) -> Option<&Value> {
		self.properties.get(&name.to_string())
	}
	pub fn property_or_null(&self, name: impl ToString) -> &Value {
		self.property(name).unwrap_or(&Value::Null)
	}
	pub fn get_indexer(&self) -> Indexer {
		Indexer::new(px_to_grid(self.level.px_wid), px_to_grid(self.level.px_hei))
	}
}

#[derive(Debug, Clone, Default)]
pub struct LocatedEntity {
	pub position: IVec2,
	pub size: IVec2,
	pub properties: HashMap<String, Value>,
	pub collides: bool,
}

impl From<Level> for LdtkLevel {
	fn from(mut value: Level) -> Self {
		let layers = value.layer_instances.take();
		let mut properties = HashMap::with_capacity(value.field_instances.len());
		let fields = std::mem::take(&mut value.field_instances);

		for field in fields {
			properties.insert(field.identifier, field.value.unwrap_or(Value::Null));
		}

		let _level_width = value.px_wid;
		let level_height = value.px_hei;

		let mut level = Self {
			colliders: Quadtree::new(0),
			processed_layers: layers
				.unwrap_or_default()
				.into_iter()
				.rev()
				.enumerate()
				.map(|(idx, inst)| LdtkLayer::new(idx, inst))
				.collect(),
			level: value,
			properties,
		};

		let mut collider_quads = Quadtree::<i64, LocatedEntity>::new(32);
		for entity in MapQuery::get_entities_of(&level) {
			if entity.tags.contains(&String::from("Collider")) {
				let mut properties = HashMap::with_capacity(entity.field_instances.len());
				for field in entity.field_instances.iter() {
					properties.insert(
						field.identifier.clone(),
						field.value.as_ref().unwrap_or(&Value::Null).clone(),
					);
				}

				let _width = entity.width;
				let height = entity.height;
				let _left_x = entity.px[0];
				let _bottom_y = level_height - (entity.px[1] + height);

				let size = IVec2::new(entity.width as i32, entity.height as i32);
				let position = IVec2::new(level.level.px_wid as i32, level.level.px_hei as i32)
					- (IVec2::new(entity.px[0] as i32, entity.px[1] as i32) + size);

				let entity = LocatedEntity {
					position,
					size,
					properties,
					collides: true,
				};

				if let Ok(point) = AreaBuilder::default()
					.anchor(Point::from((position.x as i64, position.y as i64)))
					.dimensions((size.x as i64, size.y as i64))
					.build()
				{
					collider_quads.insert(point, entity);
				}
			}
		}

		level.colliders = collider_quads;
		level
	}
}

pub struct LdtkLayer {
	pub level: usize,
	layer: LayerInstance,
	position_lookup: HashMap<SpatialIndex, TileInstance>,
	indexer: Indexer,
}

impl LdtkLayer {
	pub fn new(index: usize, layer: LayerInstance) -> Self {
		let tile_list = layer.grid_tiles.iter().chain(&layer.auto_layer_tiles);
		let mut position_lookup =
			HashMap::with_capacity(layer.grid_tiles.len() + layer.auto_layer_tiles.len());

		let scale = get_ldtk_tile_scale() as i64;
		let indexer = Indexer::new(layer.c_wid, layer.c_hei);

		for tile in tile_list {
			let x = tile.px[0] / scale;
			let y = tile.px[1] / scale;

			position_lookup.insert((x, y).into(), tile.clone());
		}

		Self {
			level: index,
			indexer,
			layer,
			position_lookup,
		}
	}

	pub fn name(&self) -> &String {
		&self.layer.identifier
	}

	pub fn indexer(&self) -> Indexer {
		self.indexer
	}

	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.position_lookup.iter().for_each(|(pos, tile)| {
			cb(pos.0, pos.1, TileRef::new(tile));
		});
	}

	pub fn get_z_delta(&self) -> f32 {
		(self.level as f32) / 100.0
	}

	pub fn get_tile(&self, pos: impl Into<SpatialIndex>) -> Option<TileRef> {
		self.position_lookup.get(&pos.into()).map(TileRef::new)
	}
	pub fn get_tile_at(
		&self,
		a: impl AsPrimitive<i64>,
		b: impl AsPrimitive<i64>,
	) -> Option<TileRef> {
		self.position_lookup
			.get(&SpatialIndex(a.as_(), b.as_()))
			.map(TileRef::new)
	}

	/// 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)
		})
	}

	pub fn layer_ref(&self) -> &LayerInstance {
		&self.layer
	}

	pub fn layer_ref_mut(&mut self) -> &mut LayerInstance {
		&mut self.layer
	}
}

impl AsRef<LayerInstance> for LdtkLayer {
	fn as_ref(&self) -> &LayerInstance {
		&self.layer
	}
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct WorldTile {
	gid: usize,
	rotation: TileFlip,
}

impl From<&TileInstance> for WorldTile {
	fn from(value: &TileInstance) -> Self {
		Self {
			gid: value.t.max(0) as usize,
			rotation: match value.f {
				1 => TileFlip::Horizontal,
				2 => TileFlip::Vertical,
				3 => TileFlip::Both,
				_ => TileFlip::None,
			},
		}
	}
}

#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub enum EntityPosition {
	Point { position: IVec2 },
	Zone { position: IVec2, size: IVec2 },
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct WorldEntity {
	entity_type: String,
	position: Rect,
	properties: Properties,
	tags: Vec<String>,
	colour: String,
}

impl From<&EntityInstance> for WorldEntity {
	fn from(value: &EntityInstance) -> Self {
		let x = value.px[0];
		let y = value.px[1];

		Self {
			entity_type: value.identifier.clone(),
			position: Rect::from_corners(
				Vec2::new(x as f32, y as f32),
				Vec2::new((x + value.width) as f32, (y + value.height) as f32),
			),
			properties: Properties::from(&value.field_instances),
			tags: value.tags.clone(),
			colour: value.smart_color.clone(),
		}
	}
}

#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Properties(HashMap<String, Value>);
impl Deref for Properties {
	type Target = HashMap<String, Value>;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

impl DerefMut for Properties {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.0
	}
}

impl From<HashMap<String, Value>> for Properties {
	fn from(value: HashMap<String, Value>) -> Self {
		Self(value)
	}
}

impl From<Vec<FieldInstance>> for Properties {
	fn from(value: Vec<FieldInstance>) -> Self {
		(&value).into()
	}
}

impl From<&Vec<FieldInstance>> for Properties {
	fn from(value: &Vec<FieldInstance>) -> Self {
		value
			.iter()
			.map(|field| {
				(
					field.identifier.clone(),
					field.value.clone().unwrap_or(Value::Null),
				)
			})
			.collect::<HashMap<String, Value>>()
			.into()
	}
}

fn convert_map_types(map: &Map<String, Value>) -> HashMap<String, Value> {
	map.iter()
		.map(|(name, value)| (name.clone(), value.clone()))
		.collect()
}

impl Properties {
	pub fn as_string(&self, name: impl ToString) -> Option<String> {
		self.get(&name.to_string()).and_then(|value| match value {
			Value::String(value) => Some(value.to_string()),
			Value::Bool(value) => Some(format!("{}", value)),
			Value::Number(value) => Some(format!("{}", value)),
			_ => None,
		})
	}

	pub fn as_number_cast<T>(&self, name: impl ToString) -> Option<T>
	where
		T: 'static + Copy,
		f64: AsPrimitive<T>,
	{
		self.get(&name.to_string()).and_then(|value| match value {
			Value::Number(number) => {
				let num = number.as_f64();
				num.map(|val| val.as_())
			}
			_ => None,
		})
	}

	pub fn as_bool(&self, name: impl ToString) -> Option<bool> {
		self.get(&name.to_string()).and_then(|value| match value {
			Value::Bool(value) => Some(*value),
			_ => None,
		})
	}

	pub fn as_vec(&self, name: impl ToString) -> Option<&Vec<Value>> {
		self.get(&name.to_string()).and_then(|value| match value {
			Value::Array(value) => Some(value),
			_ => None,
		})
	}

	pub fn as_vec_owned(&self, name: impl ToString) -> Option<Vec<Value>> {
		self.get(&name.to_string()).and_then(|value| match value {
			Value::Array(value) => Some(value.clone()),
			_ => None,
		})
	}

	pub fn as_map(&self, name: impl ToString) -> Option<&Map<String, Value>> {
		self.get(&name.to_string()).and_then(|value| match value {
			Value::Object(value) => Some(value),
			_ => None,
		})
	}

	pub fn as_map_owned(&self, name: impl ToString) -> Option<Map<String, Value>> {
		self.get(&name.to_string()).and_then(|value| match value {
			Value::Object(value) => Some(value.clone()),
			_ => None,
		})
	}

	pub fn matches(&self, name: impl ToString, value: Value) -> bool {
		match (self.0.get(&name.to_string()), value) {
			(Some(Value::String(a)), Value::String(ref b)) => a == b,
			(Some(Value::Number(a)), Value::Number(ref b)) => a == b,
			(Some(Value::Bool(a)), Value::Bool(ref b)) => a == b,
			(Some(Value::Array(a)), Value::Array(ref b)) => a == b,
			(Some(Value::Object(a)), Value::Object(ref b)) => a == b,
			(Some(Value::Null), Value::Null) => true,
			_ => false,
		}
	}
	pub fn is_null(&self, name: impl ToString) -> bool {
		matches!(self.0.get(&name.to_string()), Some(Value::Null))
	}

	pub fn is_null_or_undefined(&self, name: impl ToString) -> bool {
		matches!(self.0.get(&name.to_string()), None | Some(Value::Null))
	}

	pub fn is_null_or_falsy(&self, name: impl ToString) -> bool {
		match self.0.get(&name.to_string()) {
			None | Some(Value::Null) => true,
			Some(Value::Bool(value)) => !*value,
			Some(Value::String(value)) => value.is_empty(),
			Some(Value::Number(num)) => num == &Number::from(0),
			Some(Value::Array(value)) => value.is_empty(),
			Some(Value::Object(value)) => value.is_empty(),
		}
	}
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum LayerType {
	Entities(Vec<WorldEntity>),
	GidTiles(HashMap<SpatialIndex, WorldTile>),
	IntTiles(Vec<usize>),
}

fn infer_tileset_name(layer: &LayerInstance) -> Option<String> {
	layer.tileset_rel_path.as_ref().and_then(|path| {
		Path::new(path)
			.file_stem()
			.and_then(|stem| stem.to_str())
			.map(String::from)
	})
}

impl From<&LayerInstance> for LayerType {
	fn from(value: &LayerInstance) -> Self {
		match value.layer_instance_type.as_str() {
			"IntGrid" => LayerType::IntTiles(
				value
					.int_grid_csv
					.iter()
					.map(|num| (*num).max(0) as usize)
					.collect(),
			),
			"Entities" => LayerType::Entities(
				value
					.entity_instances
					.iter()
					.map(|entity| entity.into())
					.collect(),
			),
			"Tiles" => LayerType::GidTiles(
				value
					.grid_tiles
					.iter()
					.map(|tile| (SpatialIndex(tile.px[0], tile.px[1]), tile.into()))
					.collect(),
			),
			"AutoLayer" => LayerType::GidTiles(
				value
					.auto_layer_tiles
					.iter()
					.map(|tile| (SpatialIndex(tile.px[0], tile.px[1]), tile.into()))
					.collect(),
			),
			_ => {
				panic!("Invalid layer type {}", value.layer_instance_type.as_str());
			}
		}
	}
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct WorldLayer {
	id: String,
	depth: usize,
	world_width: i64,
	world_height: i64,
	tile_width: i64,
	tile_height: i64,
	layer_data: LayerType,
	tileset_name: Option<String>,
}

impl WorldLayer {
	pub fn from_layer(depth: usize, layer: &LayerInstance) -> Self {
		let tile_size = get_ldtk_tile_scale() as i64;

		Self {
			depth,
			id: layer.identifier.clone(),
			tileset_name: infer_tileset_name(layer),
			world_width: layer.c_wid * tile_size,
			world_height: layer.c_hei * tile_size,
			tile_width: layer.c_wid,
			tile_height: layer.c_hei,
			layer_data: layer.into(),
		}
	}

	pub fn has_tiles(&self) -> bool {
		match &self.layer_data {
			LayerType::GidTiles(map) => !map.is_empty(),
			_ => false,
		}
	}

	pub fn for_each_tile(&self, mut cb: impl FnMut(i64, i64, &WorldTile)) {
		if let LayerType::GidTiles(map) = &self.layer_data {
			map.iter().for_each(|(pos, tile)| {
				cb(pos.0, pos.1, tile);
			});
		}
	}

	pub fn for_each_tile_mut(&mut self, mut cb: impl FnMut(i64, i64, &mut WorldTile)) {
		if let LayerType::GidTiles(map) = &mut self.layer_data {
			map.iter_mut().for_each(|(pos, tile)| {
				cb(pos.0, pos.1, tile);
			});
		}
	}

	pub fn get_z_delta(&self) -> f32 {
		(self.depth as f32) / 100.0
	}

	pub fn get_tile(
		&self,
		x: impl AsPrimitive<i32>,
		y: impl AsPrimitive<i32>,
	) -> Option<&WorldTile> {
		match &self.layer_data {
			LayerType::GidTiles(map) => map.get(&IVec2::from((x.as_(), y.as_())).into()),
			_ => None,
		}
	}

	pub fn get_tile_mut(
		&mut self,
		x: impl AsPrimitive<i32>,
		y: impl AsPrimitive<i32>,
	) -> Option<&mut WorldTile> {
		match &mut self.layer_data {
			LayerType::GidTiles(map) => map.get_mut(&IVec2::from((x.as_(), y.as_())).into()),
			_ => None,
		}
	}
}

#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct WorldData {
	layers: Vec<WorldLayer>,
	properties: Properties,
	meta: Properties,
}

impl From<&Level> for WorldData {
	fn from(value: &Level) -> Self {
		let mut properties: Properties = (&value.field_instances).into();
		let meta: Properties = properties
			.as_map("meta")
			.map(convert_map_types)
			.unwrap_or_default()
			.into();
		properties.remove("meta");

		Self {
			properties,
			meta,
			layers: value
				.layer_instances
				.as_ref()
				.map(|insts| {
					insts
						.iter()
						.enumerate()
						.map(|(idx, layer)| WorldLayer::from_layer(idx, layer))
						.collect()
				})
				.unwrap_or_default(),
		}
	}
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LayerSet(pub Vec<WorldLayer>);

trait AsWorldLayer {
	fn as_world_layer(&self, depth: usize) -> WorldLayer;
}

// impl<T, V> From<V> for LayerSet
// where
// 	T: AsWorldLayer,
// 	V: Iterator<Item = T>,
// {
// 	fn from(value: V) -> Self {
// 		Self(value.enumerate().map(T::as_world_layer).collect())
// 	}
// }

// impl<T> From<T> for WorldData
// where
// 	T: Into<LayerSet>,
// {
// 	fn from(value: T) -> Self {
// 		let layers = value.into();
// 		WorldData {
// 			layers: layers.0,
// 			..Default::default()
// 		}
// 	}
// }