271 lines
7.2 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|
|
}
|