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 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::(); app.register_type::(); app.register_type::(); app.register_type::(); app.add_plugins(TilemapPlugin); app.init_resource::(); app.add_event::(); 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) { let texture_handle: Handle = 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>, cursor_pos: Res, tilemap_q: Query<(&TilemapSize, &TilemapGridSize, &TileStorage, &Transform)>, offset: Query<&TileOffset, With>, mut ev_tile_click: EventWriter, ) { 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>, ) { 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, mut cursor_pos: ResMut, ) { 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); } } } }