Skip to content
Snippets Groups Projects
map_query.rs 3.67 KiB
Newer Older
Louis's avatar
Louis committed
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);
		}
	}
}