bevy_pixel_map/
multi_tile.rs

1use 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        // Create the tiles
91        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}