Files
minesweeper/src/helpers/grid.rs
2024-07-12 16:33:46 +02:00

271 lines
7.2 KiB
Rust

use bevy::prelude::*;
use bevy_ecs_tilemap::prelude::*;
use serde::{Deserialize, Serialize};
use super::camera;
pub struct GridPlugin;
#[derive(Debug, Component, Reflect, Serialize, Deserialize)]
#[reflect(Component)]
pub struct Tile {
pub x: isize,
pub y: isize,
}
#[derive(Debug, Component, Reflect, Serialize, Deserialize)]
#[reflect(Component)]
pub struct Flag;
#[derive(Debug, Component, Reflect, Serialize, Deserialize)]
#[reflect(Component)]
pub struct Revealed;
#[derive(Debug, Component, Reflect, Serialize, Deserialize, Copy, Clone, PartialEq, Eq)]
#[reflect(Component)]
pub enum TileType {
Empty,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Mine,
}
impl From<i32> for TileType {
fn from(value: i32) -> Self {
match value {
1 => TileType::One,
2 => TileType::Two,
3 => TileType::Three,
4 => TileType::Four,
5 => TileType::Five,
6 => TileType::Six,
7 => TileType::Seven,
8 => TileType::Eight,
_ => TileType::Empty,
}
}
}
#[derive(Debug, Component, Reflect, Serialize, Deserialize)]
#[reflect(Component)]
pub struct TileOffset {
pub translation: Vec3,
pub x: isize,
pub y: isize,
}
#[derive(Resource)]
pub struct CursorPos(Vec2);
impl Default for CursorPos {
fn default() -> Self {
Self(Vec2::new(-1000.0, -1000.0))
}
}
#[derive(Debug, Event)]
pub struct TileClickEvent {
pub x: isize,
pub y: isize,
pub button: MouseButton,
}
impl Plugin for GridPlugin {
fn build(&self, app: &mut App) {
app.register_type::<Tile>();
app.register_type::<Flag>();
app.register_type::<Revealed>();
app.register_type::<TileType>();
app.add_plugins(TilemapPlugin);
app.init_resource::<CursorPos>();
app.add_event::<TileClickEvent>();
app.add_systems(Startup, generate_grid);
app.add_systems(
First,
(
camera::mouse_movement,
camera::mouse_zoom,
update_cursor_pos,
click_tile,
)
.chain(),
);
app.add_systems(Update, update_tiles);
}
}
fn generate_grid(mut commands: Commands, asset_server: Res<AssetServer>) {
let texture_handle: Handle<Image> = asset_server.load("tiles.png");
let map_size = TilemapSize { x: 82, y: 47 };
let mut tile_storage = TileStorage::empty(map_size);
let tilemap_entity = commands.spawn_empty().id();
for x in 0..map_size.x {
for y in 0..map_size.y {
let tile_pos = TilePos { x, y };
let tile_entity = commands
.spawn(TileBundle {
position: tile_pos,
tilemap_id: TilemapId(tilemap_entity),
visible: TileVisible(true),
texture_index: TileTextureIndex(1),
..Default::default()
})
.id();
tile_storage.set(&tile_pos, tile_entity);
}
}
let tile_size = TilemapTileSize { x: 16.0, y: 16.0 };
let grid_size = TilemapTileSize { x: 16.0, y: 16.0 }.into();
let map_type = TilemapType::Square;
commands.entity(tilemap_entity).insert(TilemapBundle {
grid_size,
map_type,
size: map_size,
storage: tile_storage,
texture: TilemapTexture::Single(texture_handle),
tile_size,
transform: get_tilemap_center_transform(&map_size, &grid_size, &map_type, 0.0),
..Default::default()
});
}
fn click_tile(
buttons: Res<ButtonInput<MouseButton>>,
cursor_pos: Res<CursorPos>,
tilemap_q: Query<(&TilemapSize, &TilemapGridSize, &TileStorage, &Transform)>,
offset: Query<&TileOffset, With<Camera>>,
mut ev_tile_click: EventWriter<TileClickEvent>,
) {
if !buttons.just_pressed(MouseButton::Left) && !buttons.just_pressed(MouseButton::Right) {
return;
}
let offset = offset.single();
for (map_size, grid_size, tile_storage, map_transform) in tilemap_q.iter() {
let cursor_pos: Vec2 = cursor_pos.0;
let cursor_in_map_pos: Vec2 = {
let cursor_pos = Vec4::from((cursor_pos, 0.0, 1.0));
let cursor_in_map_pos = map_transform.compute_matrix().inverse() * cursor_pos;
cursor_in_map_pos.xy()
};
let Some(tile_pos) = TilePos::from_world_pos(
&cursor_in_map_pos,
map_size,
grid_size,
&TilemapType::Square,
) else {
continue;
};
if tile_storage.get(&tile_pos).is_none() {
continue;
};
ev_tile_click.send(TileClickEvent {
x: tile_pos.x as isize + offset.x,
y: tile_pos.y as isize + offset.y,
button: if buttons.just_pressed(MouseButton::Left) {
MouseButton::Left
} else {
MouseButton::Right
},
});
}
}
fn update_tiles(
tiles: Query<(Entity, &Tile, &TileType)>,
tile_flag: Query<&Flag>,
tile_revealed: Query<&Revealed>,
tilemap_q: Query<&TileStorage>,
mut texture_index: Query<&mut TileTextureIndex>,
offset: Query<&TileOffset, With<Camera>>,
) {
let offset = offset.single();
for mut index in texture_index.iter_mut() {
*index = TileTextureIndex(1);
}
for (ent, tile, kind) in tiles.iter() {
if (tile.x - offset.x) < 0 || (tile.y - offset.y) < 0 {
continue;
}
let x = tile.x - offset.x;
let y = tile.y - offset.y;
if x < 0 || y < 0 {
continue;
}
if x > 81 || y > 46 {
continue;
}
let tile_pos = TilePos {
x: x as u32,
y: y as u32,
};
for tile_storage in tilemap_q.iter() {
let Some(tile_entity) = tile_storage.get(&tile_pos) else {
continue;
};
let Ok(mut index) = texture_index.get_mut(tile_entity) else {
continue;
};
if tile_flag.get(ent).is_ok() {
*index = TileTextureIndex(11);
continue;
}
if tile_revealed.get(ent).is_err() {
*index = TileTextureIndex(1);
continue;
}
*index = TileTextureIndex(match kind {
TileType::One => 2,
TileType::Empty => 0,
TileType::Two => 3,
TileType::Three => 4,
TileType::Four => 5,
TileType::Five => 6,
TileType::Six => 7,
TileType::Seven => 8,
TileType::Eight => 9,
TileType::Mine => 10,
});
}
}
}
pub fn update_cursor_pos(
camera_q: Query<(&GlobalTransform, &Camera)>,
mut cursor_moved_events: EventReader<CursorMoved>,
mut cursor_pos: ResMut<CursorPos>,
) {
for cursor_moved in cursor_moved_events.read() {
for (cam_t, cam) in camera_q.iter() {
if let Some(pos) = cam.viewport_to_world_2d(cam_t, cursor_moved.position) {
*cursor_pos = CursorPos(pos);
}
}
}
}