1use bevy::{prelude::*, render::render_resource::Extent3d};
2
3use crate::{
4 tile::{DeletingTile, Tile},
5 CHUNK_SIZE, TILE_SIZE,
6};
7
8#[derive(Bundle)]
9pub struct ChunkBundle {
10 sprite: SpriteBundle,
11 chunk: Chunk,
12}
13
14impl ChunkBundle {
15 pub fn new(loc: IVec2, chunk: Chunk) -> Self {
16 Self {
17 sprite: SpriteBundle {
18 texture: chunk.image_handle.clone_weak(),
19 transform: Transform::from_xyz(
20 loc.x as f32 * CHUNK_SIZE as f32,
21 loc.y as f32 * CHUNK_SIZE as f32,
22 0.0,
23 )
24 .with_scale(Vec3::splat(1.0 / TILE_SIZE as f32)),
25 ..Default::default()
26 },
27 chunk,
28 }
29 }
30}
31
32#[derive(Component)]
33pub struct Chunk {
34 tiles: [[Option<Entity>; CHUNK_SIZE]; CHUNK_SIZE],
35 image: Image,
36 image_handle: Handle<Image>,
37 dirty_tiles: Vec<IVec2>,
38}
39
40impl Chunk {
41 pub fn new(images: &mut ResMut<Assets<Image>>) -> Self {
42 let mut data = vec![];
43
44 for _ in 0..((CHUNK_SIZE * CHUNK_SIZE) * TILE_SIZE * TILE_SIZE) {
45 data.append(&mut Color::rgba(0.0, 0.0, 0.0, 0.0).as_rgba_u8().to_vec());
46 }
47
48 let image = Image::new(
49 Extent3d {
50 width: CHUNK_SIZE as u32 * TILE_SIZE as u32,
51 height: CHUNK_SIZE as u32 * TILE_SIZE as u32,
52 ..default()
53 },
54 bevy::render::render_resource::TextureDimension::D2,
55 data,
56 bevy::render::render_resource::TextureFormat::Rgba8Unorm,
57 );
58
59 Self {
60 tiles: [[None; CHUNK_SIZE]; CHUNK_SIZE],
61 image: image.clone(),
62 image_handle: images.add(image),
63 dirty_tiles: vec![],
64 }
65 }
66
67 pub fn get_tile(&self, loc: IVec2) -> Option<Entity> {
68 if !verify_chunk_loc(loc) {
69 return None;
70 }
71
72 self.tiles[loc.x as usize][loc.y as usize]
73 }
74
75 pub fn set_tile(
76 &mut self,
77 my_entity: Entity,
78 loc: IVec2,
79 tile: Tile,
80 additional_components: impl Bundle,
81 commands: &mut Commands,
82 ) {
83 if !verify_chunk_loc(loc) {
84 return;
85 }
86
87 self.delete_tile(loc, commands);
88
89 self.tiles[loc.x as usize][loc.y as usize] = Some(
90 commands
91 .spawn((tile, additional_components))
92 .set_parent(my_entity)
93 .id(),
94 );
95
96 self.update_tile(loc)
97 }
98
99 pub fn set_tile_entity(&mut self, loc: IVec2, entity: Entity, commands: &mut Commands) {
100 self.delete_tile(loc, commands);
101 self.tiles[loc.x as usize][loc.y as usize] = Some(entity);
102
103 self.update_tile(loc)
104 }
105
106 pub fn delete_tile(&mut self, loc: IVec2, commands: &mut Commands) {
107 if let Some(entity) = self.get_tile(loc) {
108 commands.entity(entity).insert(DeletingTile);
109 self.tiles[loc.x as usize][loc.y as usize] = None;
110 self.update_tile(loc);
111 }
112 }
113
114 pub fn delete_unmarked(&mut self, loc: IVec2, commands: &mut Commands) {
115 if let Some(entity) = self.get_tile(loc) {
116 commands.entity(entity).despawn_recursive();
117 self.tiles[loc.x as usize][loc.y as usize] = None;
118 self.update_tile(loc);
119 }
120 }
121
122 pub fn update_tile(&mut self, loc: IVec2) {
123 if !verify_chunk_loc(loc) {
124 return;
125 }
126 if self.dirty_tiles.contains(&loc) {
127 return;
128 }
129
130 self.dirty_tiles.push(loc)
131 }
132
133 pub fn update_texture(
134 &mut self,
135 images: &mut ResMut<Assets<Image>>,
136 tiles: &mut Query<&mut Tile>,
137 ) {
138 if self.dirty_tiles.is_empty() {
139 return;
140 }
141
142 let mut data = self.image.data.clone();
143
144 for loc in &self.dirty_tiles {
145 if let Some(tile) = self.get_tile(*loc) {
146 if let Ok(tile) = tiles.get(tile) {
147 for pixel_x in 0..TILE_SIZE {
148 for pixel_y in 0..TILE_SIZE {
149 let pixel_index_y =
151 ((CHUNK_SIZE - 1 - loc.y as usize) * TILE_SIZE) + pixel_y;
152 let pixel_index_x = (loc.x as usize * TILE_SIZE) + pixel_x;
154
155 let color = tile
156 .get_pixel(IVec2::new(pixel_x as i32, pixel_y as i32))
157 .expect("Pixel should be in range")
158 .as_rgba_u8();
159
160 data[pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4
162 + pixel_index_x * 4] = color[0];
163
164 data[(pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4
165 + pixel_index_x * 4)
166 + 1] = color[1];
167
168 data[(pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4
169 + pixel_index_x * 4)
170 + 2] = color[2];
171
172 data[(pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4
173 + pixel_index_x * 4)
174 + 3] = color[3];
175 }
176 }
177 }
178 } else {
179 for pixel_x in 0..TILE_SIZE {
181 for pixel_y in 0..TILE_SIZE {
182 let pixel_index_y =
184 ((CHUNK_SIZE - 1 - loc.y as usize) * TILE_SIZE) + pixel_y;
185
186 let pixel_index_x = (loc.x as usize * TILE_SIZE) + pixel_x;
188
189 data[pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4 + pixel_index_x * 4] = 0;
191
192 data[(pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4 + pixel_index_x * 4)
193 + 1] = 0;
194
195 data[(pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4 + pixel_index_x * 4)
196 + 2] = 0;
197
198 data[(pixel_index_y * (CHUNK_SIZE * TILE_SIZE) * 4 + pixel_index_x * 4)
199 + 3] = 0;
200 }
201 }
202 }
203 }
204
205 self.dirty_tiles.clear();
206
207 self.image.data = data;
208
209 images.insert(self.image_handle.clone(), self.image.clone());
210 }
211}
212
213fn verify_chunk_loc(loc: IVec2) -> bool {
214 if loc.x < 0 || loc.x >= CHUNK_SIZE as i32 || loc.y < 0 || loc.y >= CHUNK_SIZE as i32 {
215 return false;
216 }
217 true
218}
219
220pub fn chunk_texture_update(
221 mut images: ResMut<Assets<Image>>,
222 mut tiles: Query<&mut Tile>,
223 mut chunks: Query<&mut Chunk>,
224) {
225 for mut chunk in &mut chunks {
226 chunk.update_texture(&mut images, &mut tiles)
227 }
228}
229
230pub fn chunk_deleter(
231 mut commands: Commands,
232 deleting_tiles: Query<Entity, (With<Tile>, With<DeletingTile>)>,
233) {
234 for entity in &deleting_tiles {
235 commands.entity(entity).despawn_recursive();
236 }
237}