This commit is like Batman, It has no parents.
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.vscode
|
5004
Cargo.lock
generated
Normal file
5004
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
Cargo.toml
Normal file
28
Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "minesweeper"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# Enable max optimizations for dependencies, but not for our code:
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
|
# Enable only a small amount of optimization in debug mode
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
opt-level = 3
|
||||||
|
codegen-units = 1
|
||||||
|
incremental = false
|
||||||
|
debug = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.14.0" }
|
||||||
|
bevy_ecs_tilemap = { git = "https://github.com/StarArawn/bevy_ecs_tilemap.git" }
|
||||||
|
image = "0.25.1"
|
||||||
|
imageproc = "0.25.0"
|
||||||
|
noise = { version = "0.9.0", features = ["images"] }
|
||||||
|
rand = "0.8.5"
|
||||||
|
rand_chacha = "0.3.1"
|
BIN
assets/bw-tile-hex-col.png
Normal file
BIN
assets/bw-tile-hex-col.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/bw-tile-hex-row.png
Normal file
BIN
assets/bw-tile-hex-row.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 976 B |
BIN
assets/fonts/FiraSans-Bold.ttf
Normal file
BIN
assets/fonts/FiraSans-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/tiles.png
Normal file
BIN
assets/tiles.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
52
src/helpers/camera.rs
Normal file
52
src/helpers/camera.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use bevy::{input::ButtonInput, math::Vec3, prelude::*, render::camera::Camera};
|
||||||
|
|
||||||
|
use crate::TileOffset;
|
||||||
|
|
||||||
|
// Todo: Middle mouse button should drag the playfield around
|
||||||
|
// Zoom with + and -
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn movement(
|
||||||
|
time: Res<Time>,
|
||||||
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut query: Query<(&mut Transform, &mut TileOffset, &mut OrthographicProjection), With<Camera>>,
|
||||||
|
) {
|
||||||
|
for (mut transform, mut offset, mut ortho) in query.iter_mut() {
|
||||||
|
let mut direction = Vec3::ZERO;
|
||||||
|
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyA) {
|
||||||
|
direction -= Vec3::new(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyD) {
|
||||||
|
direction += Vec3::new(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyW) {
|
||||||
|
direction += Vec3::new(0.0, 1.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyS) {
|
||||||
|
direction -= Vec3::new(0.0, 1.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyZ) {
|
||||||
|
ortho.scale += time.delta_seconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyX) {
|
||||||
|
ortho.scale -= time.delta_seconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
ortho.scale = ortho.scale.clamp(0.5, 1.0);
|
||||||
|
|
||||||
|
let z = transform.translation.z;
|
||||||
|
offset.translation += time.delta_seconds() * direction * (16.0 * 8.0);
|
||||||
|
|
||||||
|
offset.x = (offset.translation.x / 16.0) as isize;
|
||||||
|
offset.y = (offset.translation.y / 16.0) as isize;
|
||||||
|
|
||||||
|
transform.translation = offset.translation % 16.0;
|
||||||
|
|
||||||
|
transform.translation.z = z;
|
||||||
|
}
|
||||||
|
}
|
268
src/helpers/grid.rs
Normal file
268
src/helpers/grid.rs
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_ecs_tilemap::prelude::*;
|
||||||
|
|
||||||
|
use super::camera;
|
||||||
|
|
||||||
|
pub struct GridPlugin;
|
||||||
|
|
||||||
|
#[derive(Debug, Component, Reflect)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct Tile {
|
||||||
|
pub x: isize,
|
||||||
|
pub y: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Component, Reflect)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct Flag;
|
||||||
|
|
||||||
|
#[derive(Debug, Component, Reflect)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct Revealed;
|
||||||
|
|
||||||
|
#[derive(Debug, Component, Reflect, 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(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::movement, 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()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is where we check which tile the cursor is hovered over.
|
||||||
|
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() {
|
||||||
|
// Grab the cursor position from the `Res<CursorPos>`
|
||||||
|
let cursor_pos: Vec2 = cursor_pos.0;
|
||||||
|
// We need to make sure that the cursor's world position is correct relative to the map
|
||||||
|
// due to any map transformation.
|
||||||
|
let cursor_in_map_pos: Vec2 = {
|
||||||
|
// Extend the cursor_pos vec3 by 0.0 and 1.0
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Once we have a world position we can transform it into a possible tile position.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
src/helpers/mod.rs
Normal file
2
src/helpers/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod camera;
|
||||||
|
pub mod grid;
|
292
src/main.rs
Normal file
292
src/main.rs
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
#![allow(clippy::type_complexity)]
|
||||||
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::window::WindowResolution;
|
||||||
|
use helpers::grid::{Flag, GridPlugin, Revealed, Tile, TileClickEvent, TileOffset, TileType};
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
#[derive(Deref, Resource)]
|
||||||
|
pub struct PlayerAlive(bool);
|
||||||
|
|
||||||
|
#[derive(Deref, Resource)]
|
||||||
|
pub struct Score(usize);
|
||||||
|
|
||||||
|
impl FromWorld for PlayerAlive {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
|
let alive = world.resource::<PlayerAlive>();
|
||||||
|
Self(**alive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deref, Resource)]
|
||||||
|
pub struct FontHandle(Handle<Font>);
|
||||||
|
|
||||||
|
impl FromWorld for FontHandle {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
|
let asset_server = world.resource::<AssetServer>();
|
||||||
|
Self(asset_server.load("fonts/FiraSans-Bold.ttf"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_camera(mut commands: Commands) {
|
||||||
|
// 0x0 = Center
|
||||||
|
let translation = Vec3 {
|
||||||
|
x: -(1280.0 / 2.0),
|
||||||
|
y: -(720.0 / 2.0),
|
||||||
|
z: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Camera2dBundle {
|
||||||
|
projection: OrthographicProjection {
|
||||||
|
far: 1000.0,
|
||||||
|
near: -1000.0,
|
||||||
|
scaling_mode: bevy::render::camera::ScalingMode::Fixed {
|
||||||
|
width: 1280.0,
|
||||||
|
height: 720.0,
|
||||||
|
},
|
||||||
|
scale: 0.75,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
transform: Transform {
|
||||||
|
translation,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
TileOffset {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
translation,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tile_clicked(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut tiles: Query<(Entity, &Tile, &mut TileType)>,
|
||||||
|
mut tile_flag: Query<&mut Flag>,
|
||||||
|
mut tile_revealed: Query<&mut Revealed>,
|
||||||
|
mut ev_click: EventReader<TileClickEvent>,
|
||||||
|
mut score: ResMut<Score>,
|
||||||
|
mut alive: ResMut<PlayerAlive>,
|
||||||
|
) {
|
||||||
|
if !**alive {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ev in ev_click.read() {
|
||||||
|
if reveal(
|
||||||
|
&mut commands,
|
||||||
|
ev.x,
|
||||||
|
ev.y,
|
||||||
|
ev.button,
|
||||||
|
&mut tiles,
|
||||||
|
&mut tile_flag,
|
||||||
|
&mut tile_revealed,
|
||||||
|
&mut score,
|
||||||
|
10,
|
||||||
|
) && ev.button == MouseButton::Left
|
||||||
|
{
|
||||||
|
*alive = PlayerAlive(false);
|
||||||
|
println!("Score: {}", **score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reveal(
|
||||||
|
commands: &mut Commands,
|
||||||
|
x: isize,
|
||||||
|
y: isize,
|
||||||
|
button: MouseButton,
|
||||||
|
tiles: &mut Query<(Entity, &Tile, &mut TileType)>,
|
||||||
|
tile_flag: &mut Query<&mut Flag>,
|
||||||
|
tile_revealed: &mut Query<&mut Revealed>,
|
||||||
|
score: &mut ResMut<Score>,
|
||||||
|
n: isize,
|
||||||
|
) -> bool {
|
||||||
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
let mut queue = VecDeque::new();
|
||||||
|
queue.push_back((x, y));
|
||||||
|
|
||||||
|
while let Some((cx, cy)) = queue.pop_front() {
|
||||||
|
if (cx - x).abs() > n || (cy - y).abs() > n {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if visited.contains(&(cx, cy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited.insert((cx, cy));
|
||||||
|
|
||||||
|
let result = get_tile(cx, cy);
|
||||||
|
let mut found = false;
|
||||||
|
for (ent, tile, mut kind) in tiles.iter_mut() {
|
||||||
|
if tile.x == cx && tile.y == cy {
|
||||||
|
// tile found
|
||||||
|
*kind = result;
|
||||||
|
|
||||||
|
if button == MouseButton::Right && tile_revealed.get(ent).is_err() {
|
||||||
|
if tile_flag.get(ent).is_ok() {
|
||||||
|
if *kind == TileType::Mine {
|
||||||
|
**score = Score(***score - 1);
|
||||||
|
}
|
||||||
|
commands.entity(ent).remove::<Flag>();
|
||||||
|
} else {
|
||||||
|
if *kind == TileType::Mine {
|
||||||
|
**score = Score(***score + 1);
|
||||||
|
}
|
||||||
|
commands.entity(ent).insert(Flag);
|
||||||
|
}
|
||||||
|
} else if tile_flag.get(ent).is_err() {
|
||||||
|
commands.entity(ent).insert(Revealed);
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
let tile_pos = Tile { x: cx, y: cy };
|
||||||
|
|
||||||
|
let mut ent = commands.spawn(tile_pos);
|
||||||
|
ent.insert(result);
|
||||||
|
|
||||||
|
if button == MouseButton::Left {
|
||||||
|
ent.insert(Revealed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if button == MouseButton::Right {
|
||||||
|
if result == TileType::Mine {
|
||||||
|
**score = Score(***score + 1);
|
||||||
|
}
|
||||||
|
ent.insert(Flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == TileType::Empty && button == MouseButton::Left {
|
||||||
|
for i in cx - 1..=cx + 1 {
|
||||||
|
for j in cy - 1..=cy + 1 {
|
||||||
|
if i != cx || j != cy {
|
||||||
|
queue.push_back((i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == TileType::Mine {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_mine(x: isize, y: isize) -> bool {
|
||||||
|
let value = get_mine_value(x, y);
|
||||||
|
|
||||||
|
let x = x as f64;
|
||||||
|
let y = y as f64;
|
||||||
|
let distance = (x.powf(2.0) + y.powf(2.0)).sqrt();
|
||||||
|
|
||||||
|
value > (1.0 - (distance / 100.0)).min(0.7)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SEED: u32 = 65536;
|
||||||
|
fn get_mine_value(x: isize, y: isize) -> f64 {
|
||||||
|
use noise::{NoiseFn, Perlin};
|
||||||
|
|
||||||
|
let x = x as f64;
|
||||||
|
let y = y as f64;
|
||||||
|
|
||||||
|
const SCALE: f64 = 2.0;
|
||||||
|
|
||||||
|
let result2 = Perlin::new(SEED).get([x / (SCALE * 2.0), y / (SCALE * 2.0), 3.8]);
|
||||||
|
let result1 = Perlin::new(SEED + 1).get([x / (SCALE / 2.0), y / (SCALE / 2.0), 1.5]);
|
||||||
|
|
||||||
|
let result1 = 1.0 - result1.powf(2.5);
|
||||||
|
|
||||||
|
let p = 4.0;
|
||||||
|
|
||||||
|
result1 * (1.0 - (((1.0 - result2) * 2.0).powf(p)) / 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tile(x: isize, y: isize) -> TileType {
|
||||||
|
if is_mine(x, y) {
|
||||||
|
return TileType::Mine;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut surrounding = 0;
|
||||||
|
for i in x - 1..x + 2 {
|
||||||
|
for j in y - 1..y + 2 {
|
||||||
|
if is_mine(i, j) {
|
||||||
|
surrounding += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
surrounding.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// use imageproc::drawing::Canvas;
|
||||||
|
// // prepare an image
|
||||||
|
// let width = 2048;
|
||||||
|
// let height = 2048;
|
||||||
|
|
||||||
|
// let mut canvas = image::DynamicImage::new(width, height, image::ColorType::Rgb8);
|
||||||
|
|
||||||
|
// for x in 0..width {
|
||||||
|
// for y in 0..height {
|
||||||
|
// // let c = (get_mine_value(
|
||||||
|
// // x as isize - (width as isize / 2_isize),
|
||||||
|
// // y as isize - (height as isize / 2_isize),
|
||||||
|
// // ) * 255.0) as u8;
|
||||||
|
// // let color = [c, c, c, 1];
|
||||||
|
// let color = if is_mine(
|
||||||
|
// x as isize - (width as isize / 2_isize),
|
||||||
|
// y as isize - (height as isize / 2_isize),
|
||||||
|
// ) {
|
||||||
|
// [255, 255, 255, 1]
|
||||||
|
// } else {
|
||||||
|
// [0, 0, 0, 1]
|
||||||
|
// };
|
||||||
|
// canvas.draw_pixel(
|
||||||
|
// x,
|
||||||
|
// (-(y as isize) + (height - 1) as isize) as u32,
|
||||||
|
// color.into(),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if let Err(e) = canvas.save("rawr.png") {
|
||||||
|
// eprintln!("Error saving image: {:?}", e);
|
||||||
|
// }
|
||||||
|
|
||||||
|
App::new()
|
||||||
|
.add_plugins((
|
||||||
|
DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
title: String::from("Infinite Minesweeper"),
|
||||||
|
resolution: WindowResolution::new(1280.0, 720.0),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
// bevy_inspector_egui::quick::WorldInspectorPlugin::new(),
|
||||||
|
))
|
||||||
|
.insert_resource(PlayerAlive(true))
|
||||||
|
.insert_resource(Score(0))
|
||||||
|
.init_resource::<FontHandle>()
|
||||||
|
.add_plugins(GridPlugin)
|
||||||
|
.add_systems(Startup, setup_camera)
|
||||||
|
.add_systems(Update, tile_clicked)
|
||||||
|
.run();
|
||||||
|
}
|
25
src/utils.rs
Normal file
25
src/utils.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use noise::utils::{NoiseImage, NoiseMap};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn write_example_to_file(map: &NoiseMap, filename: &str) {
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
let target = Path::new(filename);
|
||||||
|
|
||||||
|
fs::create_dir_all(target.parent().expect("No parent directory found."))
|
||||||
|
.expect("Failed to create directories.");
|
||||||
|
|
||||||
|
map.write_to_file(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn write_image_to_file(image: &NoiseImage, filename: &str) {
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
let target = Path::new(filename);
|
||||||
|
|
||||||
|
fs::create_dir_all(target.parent().expect("No parent directory found."))
|
||||||
|
.expect("Failed to create directories.");
|
||||||
|
|
||||||
|
image.write_to_file(target)
|
||||||
|
}
|
Reference in New Issue
Block a user