wde_terrain_grid/core/
grid.rs1use bevy::prelude::*;
2use std::collections::HashMap;
3use wde_terrain::prelude::{CHUNK_SIZE, ChunkPos};
4
5use crate::prelude::GridEntity;
6
7pub const CHUNK_GRID_SUBDIVISIONS: u32 = CHUNK_SIZE as u32 / 2;
9pub const TILE_SIZE: f32 = CHUNK_SIZE / CHUNK_GRID_SUBDIVISIONS as f32;
11
12pub type GridChunkPos = IVec2;
14pub type GridLocalPos = (u32, u32);
16pub type GridTilePos = (GridChunkPos, GridLocalPos);
18
19#[derive(Debug, Clone, Message)]
21pub enum GridEntityEvent {
22 Placed {
23 entity: Entity,
24 grid_entity: GridEntity
25 },
26 Removed {
27 entity: Entity
28 }
29}
30
31pub struct Chunk {
33 tiles: Vec<Option<Entity>>,
35 entity_to_tile: HashMap<Entity, Vec<GridLocalPos>>
37}
38impl Default for Chunk {
39 fn default() -> Self {
40 Chunk {
41 tiles: vec![None; (CHUNK_GRID_SUBDIVISIONS * CHUNK_GRID_SUBDIVISIONS * 4) as usize],
42 entity_to_tile: HashMap::new()
43 }
44 }
45}
46impl Chunk {
47 pub fn set_entity_at(&mut self, local_pos: GridLocalPos, entity: Entity) {
50 let index = Self::local_pos_to_index(local_pos);
51 if let Some(tile) = self.tiles.get_mut(index) {
52 *tile = Some(entity);
53 self.entity_to_tile
54 .entry(entity)
55 .or_default()
56 .push(local_pos);
57 }
58 }
59 pub fn get_entity_at(&self, local_pos: GridLocalPos) -> Option<Entity> {
61 let index = Self::local_pos_to_index(local_pos);
62 self.tiles.get(index).and_then(|tile| *tile)
63 }
64 pub fn remove_entity(&mut self, entity: Entity) {
66 if let Some(local_positions) = self.entity_to_tile.remove(&entity) {
67 for local_pos in local_positions {
68 let index = Self::local_pos_to_index(local_pos);
69 if let Some(tile) = self.tiles.get_mut(index)
70 && tile.as_ref() == Some(&entity)
71 {
72 *tile = None;
73 }
74 }
75 }
76 }
77
78 pub fn get_nearest_local_tile(world_pos: Vec2) -> GridLocalPos {
81 let half_chunk = CHUNK_SIZE * 0.5;
82 let cell_size = CHUNK_SIZE / CHUNK_GRID_SUBDIVISIONS as f32;
83
84 let local_x = (world_pos.x + half_chunk).rem_euclid(CHUNK_SIZE) / cell_size;
85 let local_y = (world_pos.y + half_chunk).rem_euclid(CHUNK_SIZE) / cell_size;
86
87 (local_x.floor() as u32, local_y.floor() as u32)
88 }
89 pub fn get_tile_world_pos(pos: GridTilePos) -> Vec2 {
91 let (chunk_pos, local_pos) = pos;
92 chunk_pos.as_vec2() * CHUNK_SIZE
93 + Vec2::new(local_pos.0 as f32, local_pos.1 as f32) * TILE_SIZE
94 + TILE_SIZE / 2.0
95 - CHUNK_SIZE / 2.0
96 }
97 pub fn local_pos_to_index(local_pos: GridLocalPos) -> usize {
99 let (local_x, local_y) = local_pos;
100 (local_y * CHUNK_GRID_SUBDIVISIONS + local_x) as usize * 4
101 }
102}
103
104#[derive(Resource, Default)]
106pub struct Grid {
107 chunks: HashMap<GridChunkPos, Chunk>,
108 parent: Option<Entity>
109}
110impl Grid {
111 pub fn get_entity(&self, chunk_pos: GridChunkPos, local_pos: GridLocalPos) -> Option<Entity> {
113 self.chunks
114 .get(&chunk_pos)
115 .and_then(|chunk| chunk.get_entity_at(local_pos))
116 }
117 pub fn set_entity(&mut self, grid_entity: &GridEntity, entity: Entity) {
119 for pos in grid_entity.footprint() {
120 let chunk = self.chunks.entry(pos.0).or_default();
121 chunk.set_entity_at(pos.1, entity);
122 }
123 }
124 pub fn remove_entity(&mut self, entity: Entity) {
126 for chunk in self.chunks.values_mut() {
127 chunk.remove_entity(entity);
128 }
129 }
130 pub fn is_area_free(&self, entity: &GridEntity) -> bool {
132 let footprint = entity.footprint();
133 for pos in footprint {
134 if self.get_entity(pos.0, pos.1).is_some() {
135 return false;
136 }
137 }
138 true
139 }
140
141 pub fn get_nearest_tile(world_pos: Vec2) -> GridTilePos {
144 let half_chunk = CHUNK_SIZE * 0.5;
145 let chunk_pos = ChunkPos::new(
146 (world_pos.x + half_chunk).div_euclid(CHUNK_SIZE) as i32,
147 (world_pos.y + half_chunk).div_euclid(CHUNK_SIZE) as i32
148 );
149 let local_pos = Chunk::get_nearest_local_tile(world_pos);
150 (chunk_pos, local_pos)
151 }
152 pub fn get_chunk_world_pos(chunk_pos: GridChunkPos) -> Vec2 {
154 Vec2::new(
155 chunk_pos.x as f32 * CHUNK_SIZE * 2.0,
156 chunk_pos.y as f32 * CHUNK_SIZE * 2.0
157 )
158 }
159 pub fn get_tile_world_pos(pos: GridTilePos) -> Vec2 {
161 Chunk::get_tile_world_pos(pos)
162 }
163
164 pub fn set_parent(&mut self, parent: Entity) {
166 self.parent = Some(parent);
167 }
168 pub fn get_parent(&self) -> Option<Entity> {
169 self.parent
170 }
171}