use std::ops::{Deref, DerefMut};
use bevy::{
ecs::{
entity::Entity,
query::{QueryData, QueryFilter, With, WorldQuery},
system::SystemParam,
},
prelude::Query,
};
use crate::{
chunks::{Chunk, InMap},
coords::{calculate_tile_coordinate, calculate_tile_index, max_tile_index, CoordIterator},
maps::TileMap,
};
use super::{InChunk, TileCoord};
#[derive(SystemParam)]
pub struct TileMapQuery<'w, 's, Q, F = (), const N: usize = 2>
where
Q: QueryData + 'static,
F: QueryFilter + 'static,
{
tile_q: Query<'w, 's, Q, (F, With<InChunk>)>,
chunk_q: Query<'w, 's, &'static Chunk, With<InMap>>,
map_q: Query<'w, 's, &'static TileMap<N>>,
}
impl<'w, 's, Q, F, const N: usize> TileMapQuery<'w, 's, Q, F, N>
where
Q: QueryData + 'static,
F: QueryFilter + 'static,
{
pub fn get_map(
&self,
map_id: Entity,
) -> Option<
TileQuery<
&'_ Query<'w, 's, Q, (F, With<InChunk>)>,
&'_ Query<'w, 's, &'static Chunk, With<InMap>>,
N,
>,
> {
let map = self.map_q.get(map_id).ok()?;
Some(TileQuery {
tile_q: &self.tile_q,
chunk_q: &self.chunk_q,
map,
})
}
pub fn get_map_mut(
&mut self,
map_id: Entity,
) -> Option<
TileQuery<
&'_ mut Query<'w, 's, Q, (F, With<InChunk>)>,
&'_ mut Query<'w, 's, &'static Chunk, With<InMap>>,
N,
>,
> {
let map = self.map_q.get(map_id).ok()?;
Some(TileQuery {
tile_q: &mut self.tile_q,
chunk_q: &mut self.chunk_q,
map,
})
}
}
pub struct TileQuery<'a, T, C, const N: usize> {
tile_q: T,
chunk_q: C,
map: &'a TileMap<N>,
}
impl<'a, 'w: 'a, 's: 'a, Q, F, T, C, const N: usize> TileQuery<'a, T, C, N>
where
T: Deref<Target = Query<'w, 's, Q, (F, With<InChunk>)>>,
C: Deref<Target = Query<'w, 's, &'static Chunk, With<InMap>>>,
Q: QueryData + 'static,
F: QueryFilter + 'static,
{
fn get_tile_entity(&self, tile_c: impl Into<[i32; N]>) -> Option<Entity> {
let tile_c = tile_c.into();
let chunk_size = self.map.get_chunk_size();
let chunk_id = self.map.get_from_tile(TileCoord::<N>(tile_c))?;
let chunk = self.chunk_q.get(chunk_id).ok()?;
let tile_index = calculate_tile_index(tile_c, chunk_size);
chunk.get(tile_index)
}
pub fn get_at(
&self,
tile_c: impl Into<[i32; N]>,
) -> Option<<<Q as QueryData>::ReadOnly as WorldQuery>::Item<'_>> {
let tile_c = tile_c.into();
let tile_e = self.get_tile_entity(tile_c)?;
self.tile_q.get(tile_e).ok()
}
pub unsafe fn get_at_unchecked(
&self,
tile_c: impl Into<[i32; N]>,
) -> Option<<Q as WorldQuery>::Item<'_>> {
let tile_c = tile_c.into();
let tile_e = self.get_tile_entity(tile_c)?;
self.tile_q.get_unchecked(tile_e).ok()
}
pub fn iter_in(
&self,
corner_1: impl Into<[i32; N]>,
corner_2: impl Into<[i32; N]>,
) -> TileQueryIter<'_, 'a, T, C, N> {
let corner_1 = corner_1.into();
let corner_2 = corner_2.into();
TileQueryIter::new(self, corner_1, corner_2)
}
pub fn iter_in_chunk(&self, chunk_c: impl Into<[i32; N]>) -> TileQueryIter<'_, 'a, T, C, N> {
let chunk_c = chunk_c.into();
let chunk_size = self.map.get_chunk_size();
let corner_1 = calculate_tile_coordinate(chunk_c, 0, chunk_size);
let corner_2 =
calculate_tile_coordinate(chunk_c, max_tile_index::<N>(chunk_size), chunk_size);
TileQueryIter::new(self, corner_1, corner_2)
}
pub fn iter_in_chunks(
&mut self,
chunk_c_1: impl Into<[i32; N]>,
chunk_c_2: impl Into<[i32; N]>,
) -> TileQueryIter<'_, 'a, T, C, N> {
let chunk_c_1 = chunk_c_1.into();
let chunk_c_2 = chunk_c_2.into();
let chunk_size = self.map.get_chunk_size();
let corner_1 = calculate_tile_coordinate(chunk_c_1, 0, chunk_size);
let corner_2 =
calculate_tile_coordinate(chunk_c_2, max_tile_index::<N>(chunk_size), chunk_size);
TileQueryIter::new(self, corner_1, corner_2)
}
}
impl<'a, 'w: 'a, 's: 'a, Q, F, T, C, const N: usize> TileQuery<'a, T, C, N>
where
T: DerefMut<Target = Query<'w, 's, Q, (F, With<InChunk>)>>,
C: DerefMut<Target = Query<'w, 's, &'static Chunk, With<InMap>>>,
Q: QueryData + 'static,
F: QueryFilter + 'static,
{
pub fn get_at_mut(
&mut self,
tile_c: impl Into<[i32; N]>,
) -> Option<<Q as WorldQuery>::Item<'_>> {
let tile_e = self.get_tile_entity(tile_c)?;
self.tile_q.get_mut(tile_e).ok()
}
pub fn iter_in_chunks_mut(
&mut self,
chunk_c_1: impl Into<[i32; N]>,
chunk_c_2: impl Into<[i32; N]>,
) -> TileQueryIterMut<'_, 'a, T, C, N> {
let chunk_c_1 = chunk_c_1.into();
let chunk_c_2 = chunk_c_2.into();
let chunk_size = self.map.get_chunk_size();
let corner_1 = calculate_tile_coordinate(chunk_c_1, 0, chunk_size);
let corner_2 =
calculate_tile_coordinate(chunk_c_2, max_tile_index::<N>(chunk_size), chunk_size);
TileQueryIterMut::new(self, corner_1, corner_2)
}
pub fn iter_in_chunk_mut(
&mut self,
chunk_c: impl Into<[i32; N]>,
) -> TileQueryIterMut<'_, 'a, T, C, N> {
let chunk_c = chunk_c.into();
let chunk_size = self.map.get_chunk_size();
let corner_1 = calculate_tile_coordinate(chunk_c, 0, chunk_size);
let corner_2 =
calculate_tile_coordinate(chunk_c, max_tile_index::<N>(chunk_size), chunk_size);
TileQueryIterMut::new(self, corner_1, corner_2)
}
pub fn iter_in_mut(
&mut self,
corner_1: impl Into<[i32; N]>,
corner_2: impl Into<[i32; N]>,
) -> TileQueryIterMut<'_, 'a, T, C, N> {
let corner_1 = corner_1.into();
let corner_2 = corner_2.into();
TileQueryIterMut::new(self, corner_1, corner_2)
}
}
pub struct TileQueryIter<'i, 'a, T, C, const N: usize> {
coord_iter: CoordIterator<N>,
tile_q: &'i TileQuery<'a, T, C, N>,
}
impl<'i, 'a: 'i, T, C, const N: usize> TileQueryIter<'i, 'a, T, C, N> {
fn new(tile_q: &'i TileQuery<'a, T, C, N>, corner_1: [i32; N], corner_2: [i32; N]) -> Self {
Self {
tile_q,
coord_iter: CoordIterator::new(corner_1, corner_2),
}
}
}
impl<'i, 'a: 'i, 'w: 'a, 's: 'a, Q, F, T, C, const N: usize> Iterator
for TileQueryIter<'i, 'a, T, C, N>
where
T: Deref<Target = Query<'w, 's, Q, (F, With<InChunk>)>>,
C: Deref<Target = Query<'w, 's, &'static Chunk, With<InMap>>>,
Q: QueryData + 'static,
F: QueryFilter + 'static,
{
type Item = <<Q as QueryData>::ReadOnly as WorldQuery>::Item<'i>;
#[allow(clippy::while_let_on_iterator)]
fn next(&mut self) -> Option<Self::Item> {
while let Some(target) = self.coord_iter.next() {
let tile = self.tile_q.get_at(target);
if tile.is_some() {
return tile;
}
}
None
}
}
pub struct TileQueryIterMut<'i, 'a, T, C, const N: usize> {
coord_iter: CoordIterator<N>,
tile_q: &'i TileQuery<'a, T, C, N>,
}
impl<'i, 'a: 'i, T, C, const N: usize> TileQueryIterMut<'i, 'a, T, C, N> {
fn new(tile_q: &'i mut TileQuery<'a, T, C, N>, corner_1: [i32; N], corner_2: [i32; N]) -> Self {
Self {
tile_q,
coord_iter: CoordIterator::new(corner_1, corner_2),
}
}
}
impl<'i, 'a: 'i, 'w: 'a, 's: 'a, Q, F, T, C, const N: usize> Iterator
for TileQueryIterMut<'i, 'a, T, C, N>
where
T: DerefMut<Target = Query<'w, 's, Q, (F, With<InChunk>)>>,
C: Deref<Target = Query<'w, 's, &'static Chunk, With<InMap>>>,
Q: QueryData + 'static,
F: QueryFilter + 'static,
{
type Item = <Q as WorldQuery>::Item<'i>;
#[allow(clippy::while_let_on_iterator)]
fn next(&mut self) -> Option<Self::Item> {
while let Some(target) = self.coord_iter.next() {
let tile = unsafe { self.tile_q.get_at_unchecked(target) };
if tile.is_some() {
return tile;
}
}
None
}
}