use crate::map::chunk::{ChunkCell, ChunkLayer, ChunkLayerType};
use bevy::ecs::entity::{EntityMapper, MapEntities};
use bevy::math::UVec2;
use bevy::prelude::{Component, Entity};
use bevy::utils::HashMap;
use lettuces::storage::grid::Grid;
use std::hash::{Hash, Hasher};
#[cfg(feature = "reflect")]
use bevy::ecs::reflect::ReflectMapEntities;
#[cfg(feature = "reflect")]
use bevy::prelude::{Reflect, ReflectComponent};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "reflect", reflect(Hash))]
pub struct SquareChunkSettings {
pub max_chunk_size: UVec2,
}
impl Default for SquareChunkSettings {
fn default() -> Self {
Self {
max_chunk_size: UVec2 { x: 10, y: 10 },
}
}
}
#[derive(Clone, Component, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "reflect", reflect(Hash, MapEntities, Component))]
pub struct SquareChunkLayer<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
layer_type_data: SquareChunkLayerData<T>,
tile_entities: HashMap<u64, Entity>,
}
impl<T> MapEntities for SquareChunkLayer<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
for tile_entity in self.tile_entities.iter_mut() {
*tile_entity.1 = entity_mapper.map_entity(*tile_entity.1);
}
}
}
impl<T> Hash for SquareChunkLayer<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
fn hash<H: Hasher>(&self, h: &mut H) {
let mut pairs: Vec<_> = self.tile_entities.iter().collect();
pairs.sort_by_key(|i| i.0);
Hash::hash(&pairs, h);
Hash::hash(&self.layer_type_data, h);
}
}
impl<T> ChunkLayer<T> for SquareChunkLayer<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
type ChunkSettings = SquareChunkSettings;
fn into_chunk_cell(
cell: lettuces::cell::Cell,
chunk_settings: &Self::ChunkSettings,
) -> ChunkCell {
let chunk_pos_x = cell.x / chunk_settings.max_chunk_size.x as i32;
let chunk_pos_y = cell.y / chunk_settings.max_chunk_size.y as i32;
ChunkCell::new(
cell.x - (chunk_pos_x * chunk_settings.max_chunk_size.x as i32),
cell.y - (chunk_pos_y * chunk_settings.max_chunk_size.y as i32),
)
}
fn new(
layer_type: ChunkLayerType<T>,
chunk_dimensions: UVec2,
_: &Self::ChunkSettings,
) -> Self {
match layer_type {
ChunkLayerType::Dense(dense_data) => Self {
layer_type_data: SquareChunkLayerData::new_dense_from_vecs(&dense_data),
tile_entities: Default::default(),
},
ChunkLayerType::Sparse(hashmap) => {
let sparse_data = hashmap
.iter()
.map(|(chunk_tile_pos, tile_data)| {
let number =
((chunk_tile_pos.x() as u64) << 32) | chunk_tile_pos.y() as u64;
(number, *tile_data)
})
.collect();
SquareChunkLayer {
layer_type_data: SquareChunkLayerData::Sparse(sparse_data, chunk_dimensions),
tile_entities: Default::default(),
}
}
}
}
fn get_chunk_dimensions(&self) -> UVec2 {
self.layer_type_data.get_dimensions()
}
fn get_tile_data_mut(&mut self, chunk_tile_pos: ChunkCell) -> Option<&mut T> {
self.layer_type_data.get_tile_data_mut(chunk_tile_pos)
}
fn get_tile_data(&self, chunk_tile_pos: ChunkCell) -> Option<&T> {
self.layer_type_data.get_tile_data(chunk_tile_pos)
}
fn set_tile_data(&mut self, chunk_tile_pos: ChunkCell, tile_data: T) {
self.layer_type_data
.set_tile_data(chunk_tile_pos, tile_data);
}
fn get_tile_entity(&self, chunk_tile_pos: ChunkCell) -> Option<Entity> {
let number = ((chunk_tile_pos.x() as u64) << 32) | chunk_tile_pos.y() as u64;
self.tile_entities.get(&number).cloned()
}
fn set_tile_entity(&mut self, chunk_tile_pos: ChunkCell, entity: Entity) {
let number = ((chunk_tile_pos.x() as u64) << 32) | chunk_tile_pos.y() as u64;
self.tile_entities.insert(number, entity);
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "reflect", reflect(Hash))]
pub enum SquareChunkLayerData<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
Sparse(HashMap<u64, T>, UVec2),
Dense(Grid<T>),
}
impl<T> Hash for SquareChunkLayerData<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
fn hash<H: Hasher>(&self, h: &mut H) {
match self {
SquareChunkLayerData::Sparse(hashmap, chunk_size) => {
let mut pairs: Vec<_> = hashmap.iter().collect();
pairs.sort_by_key(|i| i.0);
Hash::hash(&pairs, h);
Hash::hash(&chunk_size, h);
}
SquareChunkLayerData::Dense(grid) => {
Hash::hash(grid, h);
}
}
}
}
impl<T> Default for SquareChunkLayerData<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
fn default() -> Self {
Self::Dense(Grid::<T>::new(0, 0))
}
}
impl<T> SquareChunkLayerData<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
pub fn new_dense_default(chunk_size_x: usize, chunk_size_y: usize) -> Self {
let grid: Grid<T> = Grid::new(chunk_size_x, chunk_size_y);
Self::Dense(grid)
}
pub fn new_dense_uniform(chunk_size_x: usize, chunk_size_y: usize, tile_data: T) -> Self {
let grid: Grid<T> = Grid::init(chunk_size_x, chunk_size_y, tile_data);
Self::Dense(grid)
}
pub fn new_dense_from_vecs(tile_data: &[Vec<T>]) -> Self {
let mut given_tile_count = 0u64;
for tile_data in tile_data.iter() {
given_tile_count += tile_data.len() as u64;
}
assert_eq!(
(tile_data[0].len() * tile_data.len()) as u64,
given_tile_count
);
let mut grid: Grid<T> = Grid::init(tile_data.len(), tile_data[0].len(), T::default());
let mut current_x = 0usize;
let mut current_y = 0usize;
let row_length = tile_data[0].len();
grid.fill_with(|| {
let tile = tile_data[current_y][current_x];
current_x += 1;
if current_x == row_length {
current_x = 0;
current_y += 1;
}
tile
});
Self::Dense(grid)
}
}
impl<T> SquareChunkLayerData<T>
where
T: Hash + Clone + Copy + Sized + Default + Send + Sync,
{
pub fn get_dimensions(&self) -> UVec2 {
match self {
SquareChunkLayerData::Sparse(_, dimensions) => *dimensions,
SquareChunkLayerData::Dense(grid) => {
UVec2::new(grid.size().1 as u32, grid.size().0 as u32)
}
}
}
pub fn set_tile_data(&mut self, chunk_tile_pos: ChunkCell, tile_data: T) {
match self {
SquareChunkLayerData::Sparse(layer_data, ..) => {
let number = ((chunk_tile_pos.x() as u64) << 32) | chunk_tile_pos.y() as u64;
layer_data.insert(number, tile_data);
}
SquareChunkLayerData::Dense(layer_data) => {
if let Some(tile) =
layer_data.get_mut(chunk_tile_pos.y() as usize, chunk_tile_pos.x() as usize)
{
*tile = tile_data
};
}
};
}
pub fn get_tile_data_mut(&mut self, chunk_tile_pos: ChunkCell) -> Option<&mut T> {
match self {
SquareChunkLayerData::Sparse(layer_data, ..) => {
let number = ((chunk_tile_pos.x() as u64) << 32) | chunk_tile_pos.y() as u64;
layer_data.get_mut(&number)
}
SquareChunkLayerData::Dense(layer_data) => {
layer_data.get_mut(chunk_tile_pos.y() as usize, chunk_tile_pos.x() as usize)
}
}
}
pub fn get_tile_data(&self, chunk_tile_pos: ChunkCell) -> Option<&T> {
match self {
SquareChunkLayerData::Sparse(layer_data, ..) => {
let number = ((chunk_tile_pos.x() as u64) << 32) | chunk_tile_pos.y() as u64;
layer_data.get(&number)
}
SquareChunkLayerData::Dense(layer_data) => {
layer_data.get(chunk_tile_pos.y() as usize, chunk_tile_pos.x() as usize)
}
}
}
}