bevy_pixel_map/
multi_tile.rs1use bevy::prelude::*;
2
3use crate::{
4 chunk::Chunk,
5 tile::{DeletingTile, Tile},
6 tilemap::Tilemap,
7 TILE_SIZE,
8};
9
10#[derive(Component, Clone, Debug)]
11pub struct MultiTileMarker {
12 entity: Entity,
13}
14
15#[derive(Component, Clone, Debug)]
16pub struct MultiTile {
17 pos: IVec2,
18 size: IVec2,
19 pixels: Vec<Vec<Color>>,
20 entities: Vec<Entity>,
21}
22
23impl MultiTile {
24 pub fn from_color(color: Color, width: usize, height: usize) -> Self {
25 let data = vec![vec![color; width]; height];
26
27 Self {
28 pixels: data,
29 pos: IVec2::new(0, 0),
30 size: IVec2::new(0, 0),
31 entities: vec![],
32 }
33 }
34
35 pub fn from_image(image: &Image) -> Self {
36 let mut pixels = vec![];
37
38 for y in 0..image.size().y as usize {
39 let mut row = vec![];
40 for x in 0..image.size().x as usize {
41 let pixel_index = y * image.size().x as usize * 4 + x * 4;
42
43 row.push(Color::rgba_u8(
44 image.data[pixel_index],
45 image.data[pixel_index + 1],
46 image.data[pixel_index + 2],
47 image.data[pixel_index + 3],
48 ));
49 }
50 pixels.push(row)
51 }
52
53 let size = IVec2::new(
54 pixels.len() as i32 / TILE_SIZE as i32,
55 pixels[0].len() as i32 / TILE_SIZE as i32,
56 );
57
58 Self {
59 pixels,
60 pos: IVec2::new(0, 0),
61 size,
62 entities: vec![],
63 }
64 }
65
66 #[allow(clippy::needless_range_loop)]
67 pub fn get_tile(&self, offset: IVec2) -> Option<Tile> {
68 if offset.x < 0 || offset.x > self.size.x || offset.y < 0 || offset.y > self.size.y {
69 return None;
70 }
71
72 let mut pixels = [[Color::NONE; TILE_SIZE]; TILE_SIZE];
73
74 for y in 0..TILE_SIZE {
75 for x in 0..TILE_SIZE {
76 pixels[y][x] = self.pixels[y + offset.y as usize * TILE_SIZE]
77 [x + offset.x as usize * TILE_SIZE];
78 }
79 }
80
81 Some(Tile::from_pixels(pixels))
82 }
83
84 pub fn place(mut self, loc: IVec2, tilemap: &mut Tilemap, commands: &mut Commands) -> Entity {
85 self.pos.x = loc.x;
86 self.pos.y = loc.y;
87
88 let mut my_entity = commands.spawn_empty();
89 let entity_id = my_entity.id();
90 for tile_x in 0..self.size.x {
92 for tile_y in 0..self.size.y {
93 let entity = tilemap.set_tile(
94 commands,
95 IVec2::new(loc.x + tile_x, loc.y + tile_y),
96 self.get_tile(IVec2::new(tile_x, self.size.y - 1 - tile_y))
97 .unwrap(),
98 MultiTileMarker { entity: entity_id },
99 );
100 self.entities.push(entity)
101 }
102 }
103
104 my_entity = commands.entity(entity_id);
105
106 my_entity.insert(self);
107 my_entity.id()
108 }
109}
110
111pub fn multi_tile_delete(
112 mut commands: Commands,
113 mut deleting_tiles: Query<(&Parent, &MultiTileMarker), With<DeletingTile>>,
114 multi_tiles: Query<(Entity, &MultiTile)>,
115 chunk_data: Query<&Chunk>,
116 chunks: Query<&Parent, With<Chunk>>,
117 mut tilemaps: Query<&mut Tilemap>,
118) {
119 let mut handled_multi_tiles = vec![];
120 for (chunk, marker) in &mut deleting_tiles {
121 if let Ok((multi_entity, multi_tile)) = multi_tiles.get(marker.entity) {
122 if handled_multi_tiles.contains(&multi_tile.pos) {
123 continue;
124 }
125 handled_multi_tiles.push(multi_tile.pos);
126
127 commands.entity(multi_entity).despawn_recursive();
128
129 if let Ok(tilemap) = chunks.get(chunk.get()) {
130 if let Ok(mut tilemap) = tilemaps.get_mut(tilemap.get()) {
131 for x in 0..multi_tile.size.x {
132 for y in 0..multi_tile.size.y {
133 let loc = IVec2::new(x + multi_tile.pos.x, y + multi_tile.pos.y);
134 if let Some(tile_entity) = tilemap.get_tile(loc, &chunk_data) {
135 if multi_tile.entities.contains(&tile_entity) {
136 tilemap.delete_tile(loc);
137 }
138 }
139 }
140 }
141 }
142 }
143 }
144 }
145}