Timer, refactor and more

This commit is contained in:
Tobias Wennberg 2025-03-12 17:37:49 +01:00
parent 9517b89479
commit 6e7313a509
2 changed files with 187 additions and 275 deletions

View File

@ -6,13 +6,6 @@ pub struct RelatedMineIterator<'a> {
inner: std::slice::Iter<'a, Option<Entity>>, inner: std::slice::Iter<'a, Option<Entity>>,
} }
#[derive(PartialEq, Eq, Clone)]
pub enum BoxTypeBin {
Safe,
Mine,
Other,
}
impl<'a> Iterator for RelatedMineIterator<'a> { impl<'a> Iterator for RelatedMineIterator<'a> {
type Item = &'a Option<Entity>; type Item = &'a Option<Entity>;
@ -34,30 +27,6 @@ impl RelatedBoxes {
self.dr.clone(), self.dr.clone(),
] ]
} }
// recursive function to add open event to related boxes if it is zero
pub fn iterate_rel_mines(
&self,
commands: &mut Commands,
boxes: &mut Query<&mut Box>,
related_boxes: &Query<&RelatedBoxes>,
) {
for related_box in self.iter() {
let box_entity = match related_box {
Some(b) => {b},
None => {continue},
};
let mut r#box_val = boxes.get_mut(box_entity).unwrap();
if box_val.0 != BoxType::Open {
continue
}
r#box_val.0 = BoxType::Open;
commands.entity(related_box.unwrap()).insert(BoxToOpen);
let related_box = related_boxes.get(box_entity).unwrap();
if related_box.nearby_mines_count_box_mut(boxes) == 0 {
// related_box.iterate_rel_mines(draw_event, boxes, related_boxes)
}
}
}
pub fn relates_with(&self, entity: Entity) -> bool { pub fn relates_with(&self, entity: Entity) -> bool {
for rel_entity in self.iter() { for rel_entity in self.iter() {
if let Some(e) = rel_entity { if let Some(e) = rel_entity {
@ -70,12 +39,12 @@ impl RelatedBoxes {
} }
pub fn nearby_box_unopened_count( pub fn nearby_box_unopened_count(
&self, &self,
boxes: &Query<&Box>, opened_boxes: &Query<&OpenedBox>,
) -> usize { ) -> usize {
let mut i = 0; let mut i = 0;
for ent in self.iter() { for ent in self.iter() {
if let Some(e) = ent { if let Some(e) = ent {
if !boxes.get(e).unwrap().is_opened() { if !opened_boxes.contains(e) {
i += 1; i += 1;
} }
} }
@ -84,156 +53,30 @@ impl RelatedBoxes {
} }
pub fn nearby_box_marked_count( pub fn nearby_box_marked_count(
&self, &self,
boxes: &Query<&Box>, boxes: &Query<&MarkedBox>,
) -> usize { ) -> usize {
let mut i = 0; let mut i = 0;
for ent in self.iter() { for ent in self.iter() {
if let Some(e) = ent { if let Some(e) = ent {
if boxes.get(e).unwrap().is_marked() { if boxes.contains(e) {
i += 1; i += 1;
} }
} }
} }
return i; return i;
} }
pub fn nearby_mines_count_box( pub fn nearby_mines_count(
&self, &self,
boxes: &Query<&Box>, mine_boxes: &Query<&MineBox>,
) -> usize { ) -> usize {
let mut i = 0; let mut i = 0;
for ent in self.iter() { for ent in self.iter() {
if let Some(e) = ent { if let Some(e) = ent {
if boxes.get(e).unwrap().0.is_mine() { if mine_boxes.contains(e) {
i += 1; i += 1;
} }
} }
} }
return i; return i;
} }
pub fn nearby_mines_count_box_mut(
&self,
boxes: &Query<&mut Box>,
) -> usize {
let mut i = 0;
for ent in self.iter() {
if let Some(e) = ent {
if boxes.get(e).unwrap().0.is_mine() {
i += 1;
} }
}
}
return i;
}
pub fn nearby_mines_count_no_hovered(
&self,
boxes: Query<&Box, Without<Hovered>>,
) -> usize {
let mut i = 0;
for ent in self.iter() {
if let Some(e) = ent {
if boxes.get(e).unwrap().0.is_mine() {
i += 1;
}
}
}
return i;
}
}
impl BoxType {
pub fn is_opened(&self) -> bool {
match self {
BoxType::Open => true,
BoxType::Safe => false,
BoxType::MarkedSafe => false,
BoxType::Mine => false,
BoxType::MarkedMine => false,
BoxType::OpenMine => true,
}
}
pub fn box_type_bin(&self) -> BoxTypeBin {
match self {
BoxType::Open => BoxTypeBin::Other,
BoxType::Safe => BoxTypeBin::Safe,
BoxType::MarkedSafe => BoxTypeBin::Other,
BoxType::Mine => BoxTypeBin::Mine,
BoxType::MarkedMine => BoxTypeBin::Other,
BoxType::OpenMine => BoxTypeBin::Other,
}
}
pub fn can_open(&self) -> bool {
match self {
BoxType::Open => false,
BoxType::Safe => true,
BoxType::MarkedSafe => false,
BoxType::Mine => true,
BoxType::MarkedMine => false,
BoxType::OpenMine => false,
}
}
pub fn is_mine(&self) -> bool {
match self {
BoxType::Open => false,
BoxType::Safe => false,
BoxType::MarkedSafe => false,
BoxType::Mine => true,
BoxType::MarkedMine => true,
BoxType::OpenMine => true,
}
}
}
impl Box {
pub fn is_opened(&self) -> bool {
match &self.0 {
BoxType::Open => true,
BoxType::Safe => false,
BoxType::MarkedSafe => false,
BoxType::Mine => false,
BoxType::MarkedMine => false,
BoxType::OpenMine => true,
}
}
pub fn is_marked(&self) -> bool {
match &self.0 {
BoxType::Open => false,
BoxType::Safe => false,
BoxType::MarkedSafe => true,
BoxType::Mine => false,
BoxType::MarkedMine => true,
BoxType::OpenMine => false,
}
}
pub fn open(&self) -> Self {
match &self.0 {
BoxType::Open => Box(BoxType::Open),
BoxType::Safe => Box(BoxType::Open),
BoxType::MarkedSafe => Box(BoxType::MarkedSafe),
BoxType::Mine => Box(BoxType::OpenMine),
BoxType::MarkedMine =>Box(BoxType::MarkedMine),
BoxType::OpenMine => Box(BoxType::OpenMine),
}
}
pub fn mark(&self) -> Self {
match &self.0 {
BoxType::Open => (*self).clone(),
BoxType::Safe => Box(BoxType::MarkedSafe),
BoxType::MarkedSafe => Box(BoxType::MarkedSafe),
BoxType::Mine => Box(BoxType::MarkedMine),
BoxType::MarkedMine =>Box(BoxType::MarkedMine),
BoxType::OpenMine => Box(BoxType::OpenMine),
}
}
pub fn mark_flip(&self) -> Self {
match &self.0 {
BoxType::Open => (*self).clone(),
BoxType::Safe => Box(BoxType::MarkedSafe),
BoxType::MarkedSafe => Box(BoxType::Safe),
BoxType::Mine => Box(BoxType::MarkedMine),
BoxType::MarkedMine =>Box(BoxType::Mine),
BoxType::OpenMine => Box(BoxType::Mine),
}
}
}

View File

@ -1,6 +1,6 @@
mod helpers; mod helpers;
use rand::{rng, Rng}; use rand::{rng, Rng};
use bevy::prelude::*; use bevy::{prelude::*, text::FontSmoothing, time::Stopwatch};
use bevy_pancam::*; use bevy_pancam::*;
const COLOR_OPEN_BOX: Color = Color::srgb(0.749, 0.749, 0.635); const COLOR_OPEN_BOX: Color = Color::srgb(0.749, 0.749, 0.635);
@ -8,10 +8,14 @@ const COLOR_HIDDEN_BOX: Color = Color::srgb(0.459, 0.459, 0.408);
const COLOR_MARKED_BOX: Color = Color::srgb(0.769, 0.435, 0.043); const COLOR_MARKED_BOX: Color = Color::srgb(0.769, 0.435, 0.043);
const COLOR_OPEN_MINE: Color = Color::srgb(1., 0., 0.); const COLOR_OPEN_MINE: Color = Color::srgb(1., 0., 0.);
const BOARD_WIDTH :usize = 16; const BOARD_WIDTH :usize = 9;
const BOARD_HEIGHT :usize = 30; const BOARD_HEIGHT :usize = 9;
const NUMER_OF_MINES :usize = 99; const NUMER_OF_MINES :usize = 10;
#[derive(Component)]
struct GameStopwatch(Stopwatch);
// Actions
#[derive(Component)] #[derive(Component)]
struct BoxToMarkFlip; struct BoxToMarkFlip;
#[derive(Component)] #[derive(Component)]
@ -19,14 +23,23 @@ struct BoxToMark;
#[derive(Component)] #[derive(Component)]
struct BoxToOpen; struct BoxToOpen;
// Box States
#[derive(Component)] #[derive(Component)]
struct SafeUnopenedBox; struct OpenedBox;
#[derive(Component)]
struct MineBox;
#[derive(Component)]
struct MarkedBox;
#[derive(Component)] #[derive(Component)]
struct Hovered; struct Box;
#[derive(Component)]
struct HoveredBox;
#[derive(States, Default, Clone, PartialEq, Eq, Hash, Debug)] #[derive(States, Default, Clone, PartialEq, Eq, Hash, Debug)]
enum GameState { enum GameState {
StartUi,
#[default] #[default]
Start, Start,
Playing, Playing,
@ -34,19 +47,6 @@ enum GameState {
Fail, Fail,
} }
#[derive(Default, Reflect, Clone, PartialEq, Eq)]
enum BoxType {
Open,
#[default]
Safe,
MarkedSafe,
Mine,
MarkedMine,
OpenMine,
}
#[derive(Component, Clone, Copy)] #[derive(Component, Clone, Copy)]
struct RelatedBoxes { struct RelatedBoxes {
ul: Option<Entity>, ul: Option<Entity>,
@ -60,8 +60,6 @@ struct RelatedBoxes {
} }
#[derive(Default, Reflect, Component, Clone)]
struct Box(BoxType);
#[derive(Resource)] #[derive(Resource)]
@ -78,93 +76,168 @@ fn main() {
.insert_resource(MouseWorldCoordinate(None)) .insert_resource(MouseWorldCoordinate(None))
.insert_resource(BoxMatrix([[None; BOARD_WIDTH]; BOARD_HEIGHT])) .insert_resource(BoxMatrix([[None; BOARD_WIDTH]; BOARD_HEIGHT]))
.insert_state(GameState::Start) .insert_state(GameState::Start)
.add_systems(Startup, (draw_boxes, init_camera)) .add_systems(Startup, (
load_initial_entities,
init_camera
))
.add_systems(PreUpdate, (mouse_world_pos, mark_hovered_box)) .add_systems(PreUpdate, (mouse_world_pos, mark_hovered_box))
.add_systems(Update, (init_mines).run_if(in_state(GameState::Start)).run_if(any_with_component::<Hovered>)) .add_systems(Update, (init_mines).run_if(in_state(GameState::Start)).run_if(any_with_component::<HoveredBox>))
.add_systems(Update, ( .add_systems(Update, (
(handle_box).run_if(in_state(GameState::Playing)).run_if(any_with_component::<Hovered>), (
handle_box,
game_stopwatch,
switch_to_win,
).run_if(in_state(GameState::Playing)).run_if(any_with_component::<HoveredBox>),
open_box, open_box,
mark_box, mark_box,
markflip_box, markflip_box,
switch_to_win,
)) ))
.add_systems(OnEnter(GameState::Start), (despawn_boxes.before(draw_boxes), draw_boxes))
.add_systems(OnEnter(GameState::Fail), draw_fail) .add_systems(OnEnter(GameState::Fail), draw_fail)
.add_systems(OnEnter(GameState::Win), draw_win) .add_systems(OnEnter(GameState::Win), draw_win)
.run(); .run();
} }
fn despawn_boxes(
mut commands: Commands,
boxes: Query<Entity, With<Box>>,
) {
for e in boxes.iter() {
commands.entity(e).despawn();
}
}
fn load_initial_entities(
mut commands: Commands,
) {
commands.spawn((
// Accepts a `String` or any type that converts into a `String`, such as `&str`
Text::new("0"),
TextFont {
font_size: 20.,
..default()
},
TextColor(Color::srgb(0.,1.,1.)),
Node {
position_type: PositionType::Absolute,
top: Val::Percent(0.),
right: Val::Percent(50.),
..default()
},
GameStopwatch(Stopwatch::new()),
));
}
fn game_stopwatch(
mut game_stopwatch: Query<(&mut GameStopwatch, &mut Text)>,
time: Res<Time>,
) {
let (mut stopwatch, mut text) = game_stopwatch.single_mut();
stopwatch.0.tick(time.delta());
let seconds = stopwatch.0.elapsed().as_secs_f64();
text.0 = format!("T: {:.3}s", seconds);
}
fn switch_to_win( fn switch_to_win(
mut next_state: ResMut<NextState<GameState>>, mut next_state: ResMut<NextState<GameState>>,
unopened_safes: Query<&SafeUnopenedBox> opened_safes: Query<&OpenedBox, Without<MineBox>>,
) { ) {
if !unopened_safes.is_empty() { if opened_safes.iter().count() != (BOARD_WIDTH*BOARD_HEIGHT - NUMER_OF_MINES) {
return return
} }
next_state.set(GameState::Win); next_state.set(GameState::Win);
} }
fn draw_win( fn draw_win(
mut commands: Commands mut commands: Commands,
mut game_stopwatch: Query<&mut GameStopwatch>,
) { ) {
commands.spawn(Text2d::new("WIN!!")); let mut stopwatch = game_stopwatch.single_mut();
stopwatch.0.pause();
commands.spawn((
// Accepts a `String` or any type that converts into a `String`, such as `&str`
Text::new("WIN!!"),
TextFont {
font_size: 200.,
..default()
},
TextColor(Color::srgb(0.,1.,1.)),
Node {
position_type: PositionType::Absolute,
bottom: Val::Percent(50.),
right: Val::Percent(50.),
..default()
},
));
} }
fn draw_fail( fn draw_fail(
mut commands: Commands mut commands: Commands,
mut game_stopwatch: Query<&mut GameStopwatch>,
mut next_state: ResMut<NextState<GameState>>,
) { ) {
commands.spawn(Text2d::new("FAIL!!")); let mut stopwatch = game_stopwatch.single_mut();
stopwatch.0.pause();
commands.spawn((
// Accepts a `String` or any type that converts into a `String`, such as `&str`
Text::new("FAIL!"),
TextFont {
font_size: 200.,
..default()
},
TextColor(Color::srgb(1.,0.,0.)),
Node {
position_type: PositionType::Absolute,
bottom: Val::Percent(50.),
right: Val::Percent(50.),
..default()
},
));
next_state.set(GameState::Start);
} }
fn markflip_box( fn markflip_box(
mut commands: Commands, mut commands: Commands,
box_entity_to_open: Query<Entity, With<BoxToMarkFlip>>, box_entity_to_open: Query<Entity, With<BoxToMarkFlip>>,
mut boxes: Query<&mut Box>, box_marked: Query<&MarkedBox>,
mut color_material: Query<&mut MeshMaterial2d<ColorMaterial>>, mut color_material: Query<&mut MeshMaterial2d<ColorMaterial>>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
for entity in box_entity_to_open.iter() { for entity in box_entity_to_open.iter() {
println!("FLIP");
commands.entity(entity).remove::<BoxToMarkFlip>(); commands.entity(entity).remove::<BoxToMarkFlip>();
let mut r#box = boxes.get_mut(entity).unwrap(); let is_marked = box_marked.contains(entity);
*r#box = r#box.mark_flip(); *color_material.get_mut(entity).unwrap() = match is_marked {
let mut r#material = color_material.get_mut(entity).unwrap(); true => {
*r#material = match r#box.0 { commands.entity(entity).remove::<(BoxToMarkFlip, MarkedBox)>();
BoxType::Open => MeshMaterial2d(materials.add(COLOR_OPEN_BOX)), MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX))
BoxType::Safe => MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)), },
BoxType::MarkedSafe => MeshMaterial2d(materials.add(COLOR_MARKED_BOX)), false => {
BoxType::Mine => MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)), commands.entity(entity).remove::<BoxToMarkFlip>().insert(MarkedBox);
BoxType::MarkedMine => MeshMaterial2d(materials.add(COLOR_MARKED_BOX)), MeshMaterial2d(materials.add(COLOR_MARKED_BOX))
BoxType::OpenMine => MeshMaterial2d(materials.add(COLOR_OPEN_MINE)), },
}; }
} }
} }
fn mark_box( fn mark_box(
mut commands: Commands, mut commands: Commands,
box_entity_to_open: Query<Entity, With<BoxToMark>>, box_entity_to_open: Query<Entity, (With<BoxToMark>, Without<OpenedBox>)>,
mut boxes: Query<&mut Box>,
mut color_material: Query<&mut MeshMaterial2d<ColorMaterial>>, mut color_material: Query<&mut MeshMaterial2d<ColorMaterial>>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
for entity in box_entity_to_open.iter() { for entity in box_entity_to_open.iter() {
commands.entity(entity).remove::<BoxToMark>(); commands.entity(entity).remove::<BoxToMark>();
let mut r#box = boxes.get_mut(entity).unwrap(); commands.entity(entity).insert(MarkedBox);
*r#box = r#box.mark(); *color_material.get_mut(entity).unwrap() = MeshMaterial2d(materials.add(COLOR_MARKED_BOX));
let mut r#material = color_material.get_mut(entity).unwrap();
*r#material = match r#box.0 {
BoxType::Open => MeshMaterial2d(materials.add(COLOR_OPEN_BOX)),
BoxType::Safe => MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)),
BoxType::MarkedSafe => MeshMaterial2d(materials.add(COLOR_MARKED_BOX)),
BoxType::Mine => MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)),
BoxType::MarkedMine => MeshMaterial2d(materials.add(COLOR_MARKED_BOX)),
BoxType::OpenMine => MeshMaterial2d(materials.add(COLOR_OPEN_MINE)),
};
} }
} }
fn open_box( fn open_box(
mut commands: Commands, mut commands: Commands,
box_entity_to_open: Query<Entity, With<BoxToOpen>>, box_entity_to_open: Query<Entity, (With<BoxToOpen>, Without<OpenedBox>)>,
mut boxes: Query<&mut Box>, marked_boxes: Query<&MarkedBox>,
mine_boxes: Query<&MineBox>,
safe_opened_boxes: Query<&OpenedBox, Without<MineBox>>,
related_boxes: Query<&RelatedBoxes>, related_boxes: Query<&RelatedBoxes>,
transforms: Query<&Transform>,
mut color_material: Query<&mut MeshMaterial2d<ColorMaterial>>, mut color_material: Query<&mut MeshMaterial2d<ColorMaterial>>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
mut next_state: ResMut<NextState<GameState>>, mut next_state: ResMut<NextState<GameState>>,
@ -181,49 +254,39 @@ fn open_box(
let entity = boxes_to_open[counter]; let entity = boxes_to_open[counter];
counter += 1; counter += 1;
commands.entity(entity).remove::<BoxToOpen>(); commands.entity(entity).remove::<BoxToOpen>();
let mut r#box = boxes.get_mut(entity).unwrap(); if marked_boxes.contains(entity) {
if !r#box.0.can_open() {
continue continue
} }
*r#box = r#box.open(); commands.entity(entity).insert(OpenedBox);
commands.entity(entity).remove::<SafeUnopenedBox>();
if r#box.0 == BoxType::OpenMine {
next_state.set(GameState::Fail);
}
let mut r#material = color_material.get_mut(entity).unwrap(); let mut r#material = color_material.get_mut(entity).unwrap();
*r#material = match r#box.0 { if mine_boxes.contains(entity) {
BoxType::Open => MeshMaterial2d(materials.add(COLOR_OPEN_BOX)), next_state.set(GameState::Fail);
BoxType::Safe => MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)), *r#material = MeshMaterial2d(materials.add(COLOR_OPEN_MINE));
BoxType::MarkedSafe => MeshMaterial2d(materials.add(COLOR_MARKED_BOX)), return
BoxType::Mine => MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)), }
BoxType::MarkedMine => MeshMaterial2d(materials.add(COLOR_MARKED_BOX)), *r#material = MeshMaterial2d(materials.add(COLOR_OPEN_BOX));
BoxType::OpenMine => MeshMaterial2d(materials.add(COLOR_OPEN_MINE)), let related_boxes = related_boxes.get(entity).unwrap();
}; let rel_mine_count = related_boxes.nearby_mines_count(&mine_boxes);
let rel_mine = related_boxes.get(entity).unwrap(); let text_entity = commands.spawn((
let rel_mine_count = rel_mine.nearby_mines_count_box_mut(&boxes);
let transform = transforms.get(entity).unwrap();
commands.spawn((
Text2d::new( if rel_mine_count != 0 { rel_mine_count.to_string() } else {"".to_string()}), Text2d::new( if rel_mine_count != 0 { rel_mine_count.to_string() } else {"".to_string()}),
TextLayout::new_with_justify(JustifyText::Center), Transform::default().with_scale(Vec3::new(0.3,0.3,0.)),
transform.clone().with_scale(Vec3::new(0.3,0.3,0.)), )).id();
)); commands.entity(entity).add_child(text_entity);
if rel_mine_count != 0 { if rel_mine_count != 0 {
continue continue
} }
for related_box in rel_mine.iter() { for related_box in related_boxes.iter() {
let box_entity = match related_box { let box_entity = match related_box {
Some(b) => {b}, Some(b) => {b},
None => {continue}, None => {continue},
}; };
let box_val = boxes.get(box_entity).unwrap(); if safe_opened_boxes.contains(box_entity) {
if box_val.0 == BoxType::Open {
continue continue
} }
if boxes_to_open.contains(&related_box.unwrap()) { if boxes_to_open.contains(&related_box.unwrap()) {
continue continue
} }
boxes_to_open.push(related_box.unwrap()); boxes_to_open.push(related_box.unwrap());
//commands.entity(related_box.unwrap()).insert(BoxToOpen);
} }
} }
} }
@ -231,33 +294,38 @@ fn open_box(
fn init_mines( fn init_mines(
mut commands: Commands, mut commands: Commands,
mouse_button_input: Res<ButtonInput<MouseButton>>, mouse_button_input: Res<ButtonInput<MouseButton>>,
mut boxes: Query<(Entity, &mut Box)>, mine_boxes: Query<&MineBox>,
mut hovered_box: Query<(Entity, &RelatedBoxes), With<Hovered>>, mut hovered_box: Query<(Entity, &RelatedBoxes), With<HoveredBox>>,
mut next_state: ResMut<NextState<GameState>>, mut next_state: ResMut<NextState<GameState>>,
box_matrix: Res<BoxMatrix>, box_matrix: Res<BoxMatrix>,
mut game_stopwatch: Query<&mut GameStopwatch>,
) { ) {
let _mouse_key = if mouse_button_input.just_pressed(MouseButton::Left) { MouseButton::Left } let _mouse_key = if mouse_button_input.just_pressed(MouseButton::Left) || mouse_button_input.just_pressed(MouseButton::Right) { MouseButton::Left }
else {return}; else {return};
let (hovered_entity, rel_mine) = hovered_box.single_mut(); let (hovered_entity, rel_mine) = hovered_box.single_mut();
commands.entity(hovered_entity).insert(BoxToOpen); commands.entity(hovered_entity).insert(BoxToOpen);
let mut number_of_mines = 0; let mut number_of_mines = 0;
while number_of_mines <= NUMER_OF_MINES { 'main: loop {
for i in 0..box_matrix.0.len() { for i in 0..box_matrix.0.len() {
for j in 0..box_matrix.0[i].len() { for j in 0..box_matrix.0[i].len() {
let (entity, mut r#box) = boxes.get_mut(box_matrix.0[i][j].unwrap()).unwrap(); let entity = box_matrix.0[i][j].unwrap();
if entity == hovered_entity || rel_mine.relates_with(entity) { if entity == hovered_entity || rel_mine.relates_with(entity) {
continue; continue;
} }
if r#box.0 != BoxType::Mine { if !mine_boxes.contains(entity) {
r#box.0 = match random_bool(NUMER_OF_MINES as f64 / (BOARD_WIDTH*BOARD_HEIGHT) as f64) { if random_bool(NUMER_OF_MINES as f64 / (BOARD_WIDTH*BOARD_HEIGHT) as f64) {
true => { number_of_mines += 1; commands.entity(entity).remove::<SafeUnopenedBox>(); BoxType::Mine}, number_of_mines += 1;
false => BoxType::Safe, commands.entity(entity).insert(MineBox);
} }
}
if number_of_mines >= NUMER_OF_MINES {
break 'main
} }
} }
} }
} }
game_stopwatch.single_mut().0.reset();
next_state.set(GameState::Playing); next_state.set(GameState::Playing);
} }
@ -268,8 +336,8 @@ fn draw_boxes(
mut box_matrix: ResMut<BoxMatrix>, mut box_matrix: ResMut<BoxMatrix>,
) { ) {
let rec = meshes.add(Rectangle::new(5., 5.)); let rec = meshes.add(Rectangle::new(5., 5.));
let offset_x = 6.; let offset_x = 5.9;
let offset_y = 6.; let offset_y = 5.9;
let mut matrix: [[Option<Entity>; BOARD_WIDTH]; BOARD_HEIGHT] = [[None; BOARD_WIDTH]; BOARD_HEIGHT]; let mut matrix: [[Option<Entity>; BOARD_WIDTH]; BOARD_HEIGHT] = [[None; BOARD_WIDTH]; BOARD_HEIGHT];
for i in 0..BOARD_HEIGHT { for i in 0..BOARD_HEIGHT {
@ -280,8 +348,7 @@ fn draw_boxes(
Mesh2d(rec.clone()), Mesh2d(rec.clone()),
MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)), MeshMaterial2d(materials.add(COLOR_HIDDEN_BOX)),
Transform::from_xyz(x, y, 0.), Transform::from_xyz(x, y, 0.),
Box::default(), Box,
SafeUnopenedBox
) )
).id()); ).id());
} }
@ -332,8 +399,11 @@ fn mark_hovered_box(
mut commands: Commands, mut commands: Commands,
mouse_world: Res<MouseWorldCoordinate>, mouse_world: Res<MouseWorldCoordinate>,
mut boxes: Query<(Entity, &Transform), With<Box>>, mut boxes: Query<(Entity, &Transform), With<Box>>,
prev_hovered: Query<Entity, With<Hovered>>, prev_hovered: Query<Entity, With<HoveredBox>>,
) { ) {
for e in prev_hovered.iter() {
commands.entity(e).remove::<HoveredBox>();
}
let mouse = match mouse_world.0 { let mouse = match mouse_world.0 {
Some(x) => {x}, Some(x) => {x},
None => return, None => return,
@ -347,25 +417,24 @@ fn mark_hovered_box(
Some(s) => {s}, Some(s) => {s},
None => {return}, None => {return},
}; };
for e in prev_hovered.iter() { commands.entity(entity).insert(HoveredBox);
commands.entity(e).remove::<Hovered>();
}
commands.entity(entity).insert(Hovered);
} }
fn handle_box( fn handle_box(
mut commands: Commands, mut commands: Commands,
mouse_button_input: Res<ButtonInput<MouseButton>>, mouse_button_input: Res<ButtonInput<MouseButton>>,
hovered_boxes: Query<(Entity, &Box), With<Hovered>>, hovered_boxes: Query<Entity, With<HoveredBox>>,
boxes: Query<&Box>, opened_boxes: Query<&OpenedBox>,
mine_boxes: Query<&MineBox>,
marked_boxes: Query<&MarkedBox>,
related_boxes: Query<&RelatedBoxes>, related_boxes: Query<&RelatedBoxes>,
) { ) {
let mouse_key = if mouse_button_input.just_pressed(MouseButton::Left) { MouseButton::Left } let mouse_key = if mouse_button_input.just_pressed(MouseButton::Left) { MouseButton::Left }
else if mouse_button_input.just_pressed(MouseButton::Right) {MouseButton::Right} else if mouse_button_input.just_pressed(MouseButton::Right) {MouseButton::Right}
else {return}; else {return};
let (entity, r#box) = hovered_boxes.get_single().unwrap(); let entity = hovered_boxes.get_single().unwrap();
if !r#box.0.is_opened() { if !opened_boxes.contains(entity) {
match mouse_key { match mouse_key {
MouseButton::Left => { MouseButton::Left => {
commands.entity(entity).insert(BoxToOpen); commands.entity(entity).insert(BoxToOpen);
@ -377,9 +446,9 @@ fn handle_box(
}; };
} else { } else {
let rel_box = related_boxes.get(entity).unwrap(); let rel_box = related_boxes.get(entity).unwrap();
let box_count = rel_box.nearby_mines_count_box(&boxes); let box_count = rel_box.nearby_mines_count(&mine_boxes);
let rel_box_marked_count = rel_box.nearby_box_marked_count(&boxes); let rel_box_marked_count = rel_box.nearby_box_marked_count(&marked_boxes);
let rel_box_unopened_count = rel_box.nearby_box_unopened_count(&boxes); let rel_box_unopened_count = rel_box.nearby_box_unopened_count(&opened_boxes);
if rel_box_marked_count == box_count { if rel_box_marked_count == box_count {
for rel_box in rel_box.iter() { for rel_box in rel_box.iter() {
if let Some(e) = rel_box { if let Some(e) = rel_box {