Compare commits
3 commits
761b9ba295
...
31c6c4858f
Author | SHA1 | Date | |
---|---|---|---|
31c6c4858f | |||
864a9ca3d5 | |||
993f608cfa |
BIN
assets/faces.png
BIN
assets/faces.png
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
@ -32,7 +32,7 @@
|
||||||
<script src="https://not-fl3.github.io/miniquad-samples/mq_js_bundle.js"></script>
|
<script src="https://not-fl3.github.io/miniquad-samples/mq_js_bundle.js"></script>
|
||||||
<script>
|
<script>
|
||||||
load("./minesweeper.wasm");
|
load("./minesweeper.wasm");
|
||||||
</script> <!-- Your compiled wasm file -->
|
</script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("contextmenu", (e) => {
|
document.addEventListener("contextmenu", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
43
src/gui.rs
43
src/gui.rs
|
@ -1,13 +1,18 @@
|
||||||
mod board_render;
|
mod board_render;
|
||||||
mod highlighter;
|
mod highlighter;
|
||||||
pub mod settings_menu;
|
pub mod settings_menu;
|
||||||
|
mod seven_segment;
|
||||||
pub mod texture_store;
|
pub mod texture_store;
|
||||||
mod tile_render;
|
mod tile_render;
|
||||||
pub mod top_menu;
|
pub mod top_menu;
|
||||||
pub mod ui_event;
|
|
||||||
|
use crate::{logic::game_board::ModifyMode, util::Events};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
highlighter::Highlighter, settings_menu::SettingsMenu, texture_store::TextureStore, top_menu::GUITop, ui_event::GUIEvents,
|
highlighter::Highlighter,
|
||||||
|
settings_menu::SettingsMenu,
|
||||||
|
texture_store::TextureStore,
|
||||||
|
top_menu::{smile::SmileyState, GUITop},
|
||||||
};
|
};
|
||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
#[derive(Default, Copy, Clone, Debug)]
|
#[derive(Default, Copy, Clone, Debug)]
|
||||||
|
@ -16,6 +21,21 @@ pub enum Language {
|
||||||
English,
|
English,
|
||||||
Japanese,
|
Japanese,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum GUIEvent {
|
||||||
|
ClickReset,
|
||||||
|
OpenSettings,
|
||||||
|
CloseSettings,
|
||||||
|
SwitchLanguage(Language),
|
||||||
|
ClickTile(usize, usize),
|
||||||
|
ModifyTile(usize, usize),
|
||||||
|
HighlightTile(usize, usize),
|
||||||
|
UnHighlightTile(usize, usize),
|
||||||
|
CreateNewGame(usize, usize, usize),
|
||||||
|
SetQuestionMode(ModifyMode),
|
||||||
|
SetSmileyState(SmileyState),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct UIState {
|
pub struct UIState {
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
|
@ -33,13 +53,13 @@ pub struct UIState {
|
||||||
}
|
}
|
||||||
impl UIState {
|
impl UIState {
|
||||||
pub fn new(width: usize, height: usize, tile_size: usize, top_offset: usize) -> Self {
|
pub fn new(width: usize, height: usize, tile_size: usize, top_offset: usize) -> Self {
|
||||||
return Self {
|
Self {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
tile_size,
|
tile_size,
|
||||||
top_offset,
|
top_offset,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
pub fn update_dimensions(&mut self, width: usize, height: usize) {
|
pub fn update_dimensions(&mut self, width: usize, height: usize) {
|
||||||
self.width = width;
|
self.width = width;
|
||||||
|
@ -67,25 +87,22 @@ impl UIState {
|
||||||
self.letterbox.1 = 0f32;
|
self.letterbox.1 = 0f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl UIState {
|
|
||||||
pub fn pixel_screen_offset(&self, x: usize, y: usize) -> (f32, f32) {
|
pub fn pixel_screen_offset(&self, x: usize, y: usize) -> (f32, f32) {
|
||||||
let (x, y) = self.pixel_screen_scale(x, y);
|
let (x, y) = self.pixel_screen_scale(x, y);
|
||||||
let x = x + self.letterbox.0;
|
let x = x + self.letterbox.0;
|
||||||
let y = y + self.letterbox.1;
|
let y = y + self.letterbox.1;
|
||||||
return (x, y);
|
(x, y)
|
||||||
}
|
}
|
||||||
pub fn pixel_screen_scale(&self, x: usize, y: usize) -> (f32, f32) {
|
pub fn pixel_screen_scale(&self, x: usize, y: usize) -> (f32, f32) {
|
||||||
let x = x as f32;
|
let x = x as f32;
|
||||||
let y = y as f32;
|
let y = y as f32;
|
||||||
return (x * self.scale, y * self.scale);
|
(x * self.scale, y * self.scale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GameUI {
|
pub struct GameUI {
|
||||||
pub event_handler: GUIEvents,
|
pub event_handler: Events<GUIEvent>,
|
||||||
pub highlighter: Highlighter,
|
pub highlighter: Highlighter,
|
||||||
pub state: UIState,
|
pub state: UIState,
|
||||||
pub settings_menu: SettingsMenu,
|
pub settings_menu: SettingsMenu,
|
||||||
|
@ -96,10 +113,10 @@ pub struct GameUI {
|
||||||
impl GameUI {
|
impl GameUI {
|
||||||
pub fn new(settings: UIState) -> Self {
|
pub fn new(settings: UIState) -> Self {
|
||||||
let set = settings;
|
let set = settings;
|
||||||
return Self {
|
Self {
|
||||||
state: set,
|
state: set,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
pub fn is_valid_position(&self, x: usize, y: usize) -> bool {
|
pub fn is_valid_position(&self, x: usize, y: usize) -> bool {
|
||||||
if x < self.state.width && y < self.state.height {
|
if x < self.state.width && y < self.state.height {
|
||||||
|
@ -128,6 +145,6 @@ impl GameUI {
|
||||||
if !self.is_valid_position(x, y) {
|
if !self.is_valid_position(x, y) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
return Some((x, y));
|
Some((x, y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::logic::game_board::GameBoard;
|
use crate::{logic::game_board::GameBoard, util::Events};
|
||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
|
|
||||||
use super::{texture_store::TextureStore, ui_event::GUIEvents, UIState};
|
use super::{texture_store::TextureStore, GUIEvent, UIState};
|
||||||
impl GameBoard {
|
impl GameBoard {
|
||||||
pub fn render(&self, textures: &TextureStore, settings: &UIState) {
|
pub fn render(&self, textures: &TextureStore, settings: &UIState) {
|
||||||
// dbg!(&settings.top_offset, &settings.render_scale);
|
// dbg!(&settings.top_offset, &settings.render_scale);
|
||||||
|
@ -32,13 +32,13 @@ impl GameBoard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn events(&self, settings: &UIState, event_handler: &mut GUIEvents) {
|
pub fn events(&self, settings: &UIState, event_handler: &mut Events<GUIEvent>) {
|
||||||
if settings.mouse_in_minefield && !settings.frozen {
|
if settings.mouse_in_minefield && !settings.frozen {
|
||||||
if is_mouse_button_released(MouseButton::Left) {
|
if is_mouse_button_released(MouseButton::Left) {
|
||||||
event_handler.add(super::ui_event::GUIEvent::ClickTile(settings.cursor.0, settings.cursor.1))
|
event_handler.add(GUIEvent::ClickTile(settings.cursor.0, settings.cursor.1))
|
||||||
}
|
}
|
||||||
if is_mouse_button_released(MouseButton::Right) {
|
if is_mouse_button_released(MouseButton::Right) {
|
||||||
event_handler.add(super::ui_event::GUIEvent::ModifyTile(settings.cursor.0, settings.cursor.1))
|
event_handler.add(GUIEvent::ModifyTile(settings.cursor.0, settings.cursor.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,11 @@ use crate::{
|
||||||
game_board::GameBoard,
|
game_board::GameBoard,
|
||||||
tile::{TileModifier, TileState},
|
tile::{TileModifier, TileState},
|
||||||
},
|
},
|
||||||
|
util::Events,
|
||||||
util::{ADJACENT_WITHOUT_CENTER, ADJACENT_WITH_CENTER},
|
util::{ADJACENT_WITHOUT_CENTER, ADJACENT_WITH_CENTER},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{top_menu::smile::SmileyState, ui_event::GUIEvent, ui_event::GUIEvents, UIState};
|
use super::{top_menu::smile::SmileyState, GUIEvent, UIState};
|
||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
use std::default;
|
use std::default;
|
||||||
|
|
||||||
|
@ -25,9 +26,11 @@ pub enum Highlight {
|
||||||
Wide,
|
Wide,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The highlighter is responsible for the selection when a tile is clicked and held either with left or middle click
|
||||||
impl Highlighter {
|
impl Highlighter {
|
||||||
pub fn events(&mut self, ui_state: &UIState, event_handler: &mut GUIEvents, game_board: &mut GameBoard) {
|
pub fn events(&mut self, ui_state: &UIState, event_handler: &mut Events<GUIEvent>, game_board: &mut GameBoard) {
|
||||||
if !ui_state.frozen && ui_state.mouse_in_minefield {
|
if !ui_state.frozen {
|
||||||
|
if ui_state.mouse_in_minefield {
|
||||||
if is_mouse_button_pressed(MouseButton::Left) {
|
if is_mouse_button_pressed(MouseButton::Left) {
|
||||||
self.highlight = Highlight::Normal;
|
self.highlight = Highlight::Normal;
|
||||||
}
|
}
|
||||||
|
@ -38,18 +41,16 @@ impl Highlighter {
|
||||||
}
|
}
|
||||||
if is_mouse_button_released(MouseButton::Left) {
|
if is_mouse_button_released(MouseButton::Left) {
|
||||||
self.reset_highlight(ui_state, event_handler);
|
self.reset_highlight(ui_state, event_handler);
|
||||||
if !ui_state.frozen {
|
|
||||||
event_handler.add(GUIEvent::SetSmileyState(SmileyState::Chillin));
|
event_handler.add(GUIEvent::SetSmileyState(SmileyState::Chillin));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if is_mouse_button_released(MouseButton::Middle) {
|
if is_mouse_button_released(MouseButton::Middle) {
|
||||||
self.reset_highlight(ui_state, event_handler);
|
self.reset_highlight(ui_state, event_handler);
|
||||||
if !ui_state.frozen {
|
|
||||||
event_handler.add(GUIEvent::SetSmileyState(SmileyState::Chillin));
|
event_handler.add(GUIEvent::SetSmileyState(SmileyState::Chillin));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn check_reveal(&self, event_handler: &mut GUIEvents, interface: &UIState, game_board: &mut GameBoard) {
|
|
||||||
|
fn check_reveal(&self, event_handler: &mut Events<GUIEvent>, interface: &UIState, game_board: &mut GameBoard) {
|
||||||
let (x, y) = interface.cursor;
|
let (x, y) = interface.cursor;
|
||||||
if let Some(tile) = game_board.get_tile_mut(x, y) {
|
if let Some(tile) = game_board.get_tile_mut(x, y) {
|
||||||
let adjacent_mines = tile.adjacent;
|
let adjacent_mines = tile.adjacent;
|
||||||
|
@ -87,7 +88,7 @@ impl Highlighter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn highlight(&mut self, interface: &UIState, event_handler: &mut GUIEvents) {
|
pub fn highlight(&mut self, interface: &UIState, event_handler: &mut Events<GUIEvent>) {
|
||||||
if interface.frozen {
|
if interface.frozen {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -116,10 +117,10 @@ impl Highlighter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.move_highlight(&interface, event_handler);
|
self.move_highlight(interface, event_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_highlight(&mut self, interface: &UIState, event_handler: &mut GUIEvents) {
|
fn move_highlight(&mut self, interface: &UIState, event_handler: &mut Events<GUIEvent>) {
|
||||||
if let Some((old_x, old_y)) = self.cursor_old {
|
if let Some((old_x, old_y)) = self.cursor_old {
|
||||||
match self.highlight {
|
match self.highlight {
|
||||||
Highlight::None => (),
|
Highlight::None => (),
|
||||||
|
@ -165,7 +166,7 @@ impl Highlighter {
|
||||||
self.cursor_old = Some(interface.cursor);
|
self.cursor_old = Some(interface.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_highlight(&mut self, interface: &UIState, event_handler: &mut GUIEvents) {
|
fn reset_highlight(&mut self, interface: &UIState, event_handler: &mut Events<GUIEvent>) {
|
||||||
if let Some((x, y)) = self.cursor_old {
|
if let Some((x, y)) = self.cursor_old {
|
||||||
match self.highlight {
|
match self.highlight {
|
||||||
Highlight::None => (),
|
Highlight::None => (),
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
use crate::logic::game_board::ModifyMode;
|
use crate::{logic::game_board::ModifyMode, util::Events};
|
||||||
|
|
||||||
use super::{
|
use super::{seven_segment::draw_seven_segment_unscaled, texture_store::TextureStore, GUIEvent, Language, UIState};
|
||||||
texture_store::TextureStore,
|
|
||||||
ui_event::{GUIEvent, GUIEvents},
|
|
||||||
Language, UIState,
|
|
||||||
};
|
|
||||||
use macroquad::{
|
use macroquad::{
|
||||||
hash,
|
hash,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
ui::{root_ui, widgets, Skin, Ui},
|
ui::{root_ui, widgets, Skin, Ui},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const NEW_GAME_HEIGHT: f32 = 40f32;
|
||||||
|
const NEW_GAME_WIDTH: f32 = 250f32;
|
||||||
|
const BUTTON_MENU_WIDTH: f32 = 250f32;
|
||||||
|
const BUTTON_SIZE: f32 = 100f32;
|
||||||
|
const BUTTON_MENU_Y: f32 = 400f32;
|
||||||
|
const BUTTON_MENU_LABEL_HEIGHT: f32 = 20f32;
|
||||||
|
|
||||||
|
const MIN_MINEFIELD_WIDTH: usize = 5;
|
||||||
|
const MAX_MINEFIELD_WIDTH: usize = 100;
|
||||||
|
const MIN_MINEFIELD_HEIGHT: usize = 5;
|
||||||
|
const MAX_MINEFIELD_HEIGHT: usize = 100;
|
||||||
|
|
||||||
pub struct SettingsMenu {
|
pub struct SettingsMenu {
|
||||||
mines: usize,
|
mines: usize,
|
||||||
width: usize,
|
width: usize,
|
||||||
|
@ -32,7 +40,7 @@ impl SettingsMenu {
|
||||||
pub fn render(
|
pub fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
ui_state: &UIState,
|
ui_state: &UIState,
|
||||||
event_handler: &mut GUIEvents,
|
event_handler: &mut Events<GUIEvent>,
|
||||||
textures: &TextureStore,
|
textures: &TextureStore,
|
||||||
skin: &Skin,
|
skin: &Skin,
|
||||||
exit_button_skin: &Skin,
|
exit_button_skin: &Skin,
|
||||||
|
@ -43,46 +51,41 @@ impl SettingsMenu {
|
||||||
|
|
||||||
root_ui().window(hash!(), vec2(0., 0.), vec2(screen_width, screen_height), |ui| {
|
root_ui().window(hash!(), vec2(0., 0.), vec2(screen_width, screen_height), |ui| {
|
||||||
draw_rectangle(0f32, 0f32, screen_width, screen_height, background_color);
|
draw_rectangle(0f32, 0f32, screen_width, screen_height, background_color);
|
||||||
ui.push_skin(&exit_button_skin);
|
ui.push_skin(exit_button_skin);
|
||||||
if widgets::Button::new("").size(vec2(50.0, 50.0)).position(vec2(0f32, 0f32)).ui(ui) {
|
if widgets::Button::new("").size(vec2(50.0, 50.0)).position(vec2(0f32, 0f32)).ui(ui) {
|
||||||
event_handler.add(GUIEvent::CloseSettings)
|
event_handler.add(GUIEvent::CloseSettings)
|
||||||
}
|
}
|
||||||
ui.pop_skin();
|
ui.pop_skin();
|
||||||
ui.push_skin(&skin);
|
ui.push_skin(skin);
|
||||||
let half_screen_width = screen_width * 0.5;
|
let half_screen_width = screen_width * 0.5;
|
||||||
const MIN_MINEFIELD_WIDTH: usize = 5;
|
|
||||||
const MAX_MINEFIELD_WIDTH: usize = 100;
|
|
||||||
const MIN_MINEFIELD_HEIGHT: usize = 5;
|
|
||||||
const MAX_MINEFIELD_HEIGHT: usize = 100;
|
|
||||||
render_counter(
|
render_counter(
|
||||||
&mut self.width,
|
&mut self.width,
|
||||||
ui,
|
ui,
|
||||||
&textures,
|
textures,
|
||||||
vec2(half_screen_width, 100f32),
|
vec2(half_screen_width, 100f32),
|
||||||
String::from("Minefield Width"),
|
"Minefield Width",
|
||||||
MIN_MINEFIELD_WIDTH,
|
MIN_MINEFIELD_WIDTH,
|
||||||
MAX_MINEFIELD_WIDTH,
|
MAX_MINEFIELD_WIDTH,
|
||||||
);
|
);
|
||||||
render_counter(
|
render_counter(
|
||||||
&mut self.height,
|
&mut self.height,
|
||||||
ui,
|
ui,
|
||||||
&textures,
|
textures,
|
||||||
vec2(half_screen_width, 200f32),
|
vec2(half_screen_width, 200f32),
|
||||||
String::from("Minefield Height"),
|
"Minefield Height",
|
||||||
MIN_MINEFIELD_WIDTH,
|
|
||||||
MIN_MINEFIELD_HEIGHT,
|
MIN_MINEFIELD_HEIGHT,
|
||||||
|
MAX_MINEFIELD_HEIGHT,
|
||||||
);
|
);
|
||||||
render_counter(
|
render_counter(
|
||||||
&mut self.mines,
|
&mut self.mines,
|
||||||
ui,
|
ui,
|
||||||
&textures,
|
textures,
|
||||||
vec2(half_screen_width, 300f32),
|
vec2(half_screen_width, 300f32),
|
||||||
String::from("Mines"),
|
"Mines",
|
||||||
1,
|
1,
|
||||||
self.width * self.height - 10,
|
self.width * self.height - 10,
|
||||||
);
|
);
|
||||||
const NEW_GAME_HEIGHT: f32 = 40f32;
|
|
||||||
const NEW_GAME_WIDTH: f32 = 250f32;
|
|
||||||
if widgets::Button::new("New Game")
|
if widgets::Button::new("New Game")
|
||||||
.size(vec2(NEW_GAME_WIDTH, NEW_GAME_HEIGHT))
|
.size(vec2(NEW_GAME_WIDTH, NEW_GAME_HEIGHT))
|
||||||
.position(vec2((screen_width - NEW_GAME_WIDTH) * 0.5, 0.0))
|
.position(vec2((screen_width - NEW_GAME_WIDTH) * 0.5, 0.0))
|
||||||
|
@ -91,12 +94,9 @@ impl SettingsMenu {
|
||||||
event_handler.add(GUIEvent::CreateNewGame(self.width, self.height, self.mines));
|
event_handler.add(GUIEvent::CreateNewGame(self.width, self.height, self.mines));
|
||||||
event_handler.add(GUIEvent::CloseSettings);
|
event_handler.add(GUIEvent::CloseSettings);
|
||||||
}
|
}
|
||||||
const BUTTON_MENU_WIDTH: f32 = 250f32;
|
|
||||||
let language_button_x = (screen_width - BUTTON_MENU_WIDTH) * 0.5;
|
let language_button_x = (screen_width - BUTTON_MENU_WIDTH) * 0.5;
|
||||||
const BUTTON_SIZE: f32 = 100f32;
|
|
||||||
const BUTTON_MENU_Y: f32 = 400f32;
|
|
||||||
let question_button_x = (screen_width - BUTTON_MENU_WIDTH) * 0.5 + (BUTTON_MENU_WIDTH - BUTTON_SIZE);
|
let question_button_x = (screen_width - BUTTON_MENU_WIDTH) * 0.5 + (BUTTON_MENU_WIDTH - BUTTON_SIZE);
|
||||||
const BUTTON_MENU_LABEL_HEIGHT: f32 = 20f32;
|
|
||||||
widgets::Label::new("Language")
|
widgets::Label::new("Language")
|
||||||
.position(vec2(language_button_x, BUTTON_MENU_Y - BUTTON_MENU_LABEL_HEIGHT))
|
.position(vec2(language_button_x, BUTTON_MENU_Y - BUTTON_MENU_LABEL_HEIGHT))
|
||||||
.size(vec2(BUTTON_SIZE, BUTTON_MENU_LABEL_HEIGHT))
|
.size(vec2(BUTTON_SIZE, BUTTON_MENU_LABEL_HEIGHT))
|
||||||
|
@ -113,15 +113,13 @@ impl SettingsMenu {
|
||||||
{
|
{
|
||||||
event_handler.add(GUIEvent::SwitchLanguage(Language::Japanese));
|
event_handler.add(GUIEvent::SwitchLanguage(Language::Japanese));
|
||||||
}
|
}
|
||||||
} else {
|
} else if widgets::Button::new("Japanese")
|
||||||
if widgets::Button::new("Japanese")
|
|
||||||
.size(vec2(BUTTON_SIZE, BUTTON_SIZE))
|
.size(vec2(BUTTON_SIZE, BUTTON_SIZE))
|
||||||
.position(vec2(language_button_x, BUTTON_MENU_Y))
|
.position(vec2(language_button_x, BUTTON_MENU_Y))
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
{
|
{
|
||||||
event_handler.add(GUIEvent::SwitchLanguage(Language::English));
|
event_handler.add(GUIEvent::SwitchLanguage(Language::English));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if let ModifyMode::Question = self.board_modify_mode {
|
if let ModifyMode::Question = self.board_modify_mode {
|
||||||
if widgets::Button::new("ON")
|
if widgets::Button::new("ON")
|
||||||
.size(vec2(BUTTON_SIZE, BUTTON_SIZE))
|
.size(vec2(BUTTON_SIZE, BUTTON_SIZE))
|
||||||
|
@ -131,8 +129,7 @@ impl SettingsMenu {
|
||||||
self.board_modify_mode = ModifyMode::Flag;
|
self.board_modify_mode = ModifyMode::Flag;
|
||||||
event_handler.add(GUIEvent::SetQuestionMode(ModifyMode::Flag));
|
event_handler.add(GUIEvent::SetQuestionMode(ModifyMode::Flag));
|
||||||
}
|
}
|
||||||
} else {
|
} else if widgets::Button::new("OFF")
|
||||||
if widgets::Button::new("OFF")
|
|
||||||
.size(vec2(BUTTON_SIZE, BUTTON_SIZE))
|
.size(vec2(BUTTON_SIZE, BUTTON_SIZE))
|
||||||
.position(vec2(question_button_x, BUTTON_MENU_Y))
|
.position(vec2(question_button_x, BUTTON_MENU_Y))
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
|
@ -140,41 +137,27 @@ impl SettingsMenu {
|
||||||
self.board_modify_mode = ModifyMode::Question;
|
self.board_modify_mode = ModifyMode::Question;
|
||||||
event_handler.add(GUIEvent::SetQuestionMode(ModifyMode::Question));
|
event_handler.add(GUIEvent::SetQuestionMode(ModifyMode::Question));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_counter(
|
const COUNTER_DIGIT_WIDTH: f32 = 13f32 * 2.0;
|
||||||
count: &mut usize,
|
const COUNTER_DIGIT_HEIGHT: f32 = 23f32 * 2.0;
|
||||||
ui: &mut Ui,
|
const COUNTER_BUTTON_HEIGHT: f32 = 30f32;
|
||||||
textures: &TextureStore,
|
const COUNTER_BUTTON_MARGIN: f32 = 10f32;
|
||||||
position: Vec2,
|
const BUTTON_OFFSET_HEIGHT: f32 = (COUNTER_DIGIT_HEIGHT - COUNTER_BUTTON_HEIGHT) * 0.5;
|
||||||
title: String,
|
|
||||||
min: usize,
|
fn render_counter(count: &mut usize, ui: &mut Ui, textures: &TextureStore, position: Vec2, title: &str, min: usize, max: usize) {
|
||||||
max: usize,
|
|
||||||
) {
|
|
||||||
let digits: Vec<usize> = {
|
let digits: Vec<usize> = {
|
||||||
let digits = count.to_string();
|
let digits = count.to_string();
|
||||||
let digits = format!("{:0>3}", digits);
|
let digits = format!("{:0>3}", digits);
|
||||||
digits.chars().map(|i| (i.to_digit(10u32).unwrap_or(0)) as usize).collect()
|
digits.chars().map(|i| (i.to_digit(10u32).unwrap_or(0)) as usize).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
const COUNTER_DIGIT_WIDTH: f32 = 13f32 * 2.0;
|
|
||||||
const COUNTER_DIGIT_HEIGHT: f32 = 23f32 * 2.0;
|
|
||||||
const COUNTER_BUTTON_HEIGHT: f32 = 30f32;
|
|
||||||
const COUNTER_BUTTON_MARGIN: f32 = 10f32;
|
|
||||||
const BUTTON_OFFSET_HEIGHT: f32 = (COUNTER_DIGIT_HEIGHT - COUNTER_BUTTON_HEIGHT) * 0.5;
|
|
||||||
|
|
||||||
let counter_width = digits.len() as f32 * COUNTER_DIGIT_WIDTH;
|
let counter_width = digits.len() as f32 * COUNTER_DIGIT_WIDTH;
|
||||||
let position = position - vec2(counter_width * 0.5, 0.0);
|
let position = position - vec2(counter_width * 0.5, 0.0);
|
||||||
for (x, digit) in digits.iter().enumerate() {
|
|
||||||
let position = vec2(COUNTER_DIGIT_WIDTH * x as f32, 0f32) + position;
|
draw_seven_segment_unscaled(ui, textures, &digits, position.x as usize, position.y as usize);
|
||||||
widgets::Texture::new(textures.numbers[*digit])
|
|
||||||
.size(COUNTER_DIGIT_WIDTH, COUNTER_DIGIT_HEIGHT)
|
|
||||||
.position(position)
|
|
||||||
.ui(ui);
|
|
||||||
}
|
|
||||||
if widgets::Button::new("+")
|
if widgets::Button::new("+")
|
||||||
.size(vec2(COUNTER_BUTTON_HEIGHT, COUNTER_BUTTON_HEIGHT))
|
.size(vec2(COUNTER_BUTTON_HEIGHT, COUNTER_BUTTON_HEIGHT))
|
||||||
.position(position + vec2(counter_width + COUNTER_BUTTON_MARGIN, BUTTON_OFFSET_HEIGHT))
|
.position(position + vec2(counter_width + COUNTER_BUTTON_MARGIN, BUTTON_OFFSET_HEIGHT))
|
||||||
|
@ -185,12 +168,10 @@ fn render_counter(
|
||||||
if widgets::Button::new("-")
|
if widgets::Button::new("-")
|
||||||
.size(vec2(COUNTER_BUTTON_HEIGHT, COUNTER_BUTTON_HEIGHT))
|
.size(vec2(COUNTER_BUTTON_HEIGHT, COUNTER_BUTTON_HEIGHT))
|
||||||
.position(position - vec2(COUNTER_BUTTON_HEIGHT + COUNTER_BUTTON_MARGIN, -BUTTON_OFFSET_HEIGHT))
|
.position(position - vec2(COUNTER_BUTTON_HEIGHT + COUNTER_BUTTON_MARGIN, -BUTTON_OFFSET_HEIGHT))
|
||||||
.ui(ui)
|
.ui(ui) && *count > min
|
||||||
{
|
{
|
||||||
if *count > min {
|
|
||||||
*count -= 1;
|
*count -= 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if widgets::Button::new("++")
|
if widgets::Button::new("++")
|
||||||
.size(vec2(COUNTER_BUTTON_HEIGHT, COUNTER_BUTTON_HEIGHT))
|
.size(vec2(COUNTER_BUTTON_HEIGHT, COUNTER_BUTTON_HEIGHT))
|
||||||
.position(
|
.position(
|
||||||
|
|
31
src/gui/seven_segment.rs
Normal file
31
src/gui/seven_segment.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use macroquad::{
|
||||||
|
prelude::vec2,
|
||||||
|
ui::{widgets, Ui},
|
||||||
|
};
|
||||||
|
pub const WIDTH: usize = 13 * 2;
|
||||||
|
pub const HEIGHT: usize = 23 * 2;
|
||||||
|
|
||||||
|
use super::{texture_store::TextureStore, UIState};
|
||||||
|
|
||||||
|
pub fn draw_seven_segment(ui_state: &UIState, ui: &mut Ui, textures: &TextureStore, val: &[usize], x: usize, y: usize) {
|
||||||
|
for (n, digit) in val.iter().enumerate() {
|
||||||
|
let (scaled_width, scaled_height) = ui_state.pixel_screen_scale(WIDTH, HEIGHT);
|
||||||
|
let (pos_x, pos_y) = ui_state.pixel_screen_offset(n * WIDTH + x, y);
|
||||||
|
|
||||||
|
widgets::Texture::new(textures.numbers[*digit])
|
||||||
|
.size(scaled_width, scaled_height)
|
||||||
|
.position(vec2(pos_x, pos_y))
|
||||||
|
.ui(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_seven_segment_unscaled(ui: &mut Ui, textures: &TextureStore, val: &[usize], x: usize, y: usize) {
|
||||||
|
for (n, digit) in val.iter().enumerate() {
|
||||||
|
let (pos_x, pos_y) = ((n * WIDTH + x) as f32, y as f32);
|
||||||
|
|
||||||
|
widgets::Texture::new(textures.numbers[*digit])
|
||||||
|
.size(WIDTH as f32, HEIGHT as f32)
|
||||||
|
.position(vec2(pos_x, pos_y))
|
||||||
|
.ui(ui);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,20 +22,20 @@ impl Default for TextureStore {
|
||||||
impl TextureStore {
|
impl TextureStore {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
numbers: load_sprites(include_bytes!("../../assets/numbers.png"), [26, 46], 1, 10).expect("Could not load sprites"),
|
numbers: load_sprites(include_bytes!("../../assets/numbers.png"), (26, 46), 1, 10).expect("Could not load sprites"),
|
||||||
english_tiles: load_sprites(include_bytes!("../../assets/english_32x.png"), [32, 32], 2, 8)
|
english_tiles: load_sprites(include_bytes!("../../assets/english_32x.png"), (32, 32), 2, 8)
|
||||||
.expect("Could not load Tile Sprites"),
|
.expect("Could not load Tile Sprites"),
|
||||||
japanese_tiles: load_sprites(include_bytes!("../../assets/japanese_32x.png"), [32, 32], 2, 8)
|
japanese_tiles: load_sprites(include_bytes!("../../assets/japanese_32x.png"), (32, 32), 2, 8)
|
||||||
.expect("Could not load Tile Sprites"),
|
.expect("Could not load Tile Sprites"),
|
||||||
smilies: load_sprites(include_bytes!("../../assets/faces.png"), [48, 48], 1, 5).expect("Could not load face sprites"),
|
smilies: load_sprites(include_bytes!("../../assets/faces.png"), (48, 48), 1, 5).expect("Could not load face sprites"),
|
||||||
cog: Texture2D::from_file_with_format(include_bytes!("../../assets/cog.png"), Some(ImageFormat::Png)),
|
cog: Texture2D::from_file_with_format(include_bytes!("../../assets/cog.png"), Some(ImageFormat::Png)),
|
||||||
lang: Language::English,
|
lang: Language::English,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_tiles(&self) -> &Vec<Texture2D> {
|
pub fn get_tiles(&self) -> &Vec<Texture2D> {
|
||||||
return match self.lang {
|
match self.lang {
|
||||||
Language::English => &self.english_tiles,
|
Language::English => &self.english_tiles,
|
||||||
Language::Japanese => &self.japanese_tiles,
|
Language::Japanese => &self.japanese_tiles,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::logic::tile::{Tile, TileModifier, TileState};
|
use crate::logic::tile::{Tile, TileModifier, TileState};
|
||||||
|
|
||||||
|
#[repr(usize)]
|
||||||
pub enum TileIndex {
|
pub enum TileIndex {
|
||||||
Hidden,
|
Unknown,
|
||||||
Revealed,
|
Revealed,
|
||||||
Flag,
|
Flag,
|
||||||
Question,
|
Question,
|
||||||
|
@ -20,52 +21,46 @@ pub enum TileIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tile {
|
impl Tile {
|
||||||
pub fn render(self, show_all: bool) -> TileIndex {
|
pub fn render(self, game_over: bool) -> TileIndex {
|
||||||
if self.swept && self.state == TileState::Mine {
|
// Behold: the match statement from hell!
|
||||||
return TileIndex::Explosion;
|
match (
|
||||||
}
|
self.state,
|
||||||
if show_all {
|
self.modifier,
|
||||||
if let Some(modifier) = self.modifier {
|
self.adjacent,
|
||||||
if modifier == TileModifier::Flagged {
|
game_over,
|
||||||
if self.state == TileState::Mine {
|
self.swept,
|
||||||
return TileIndex::Flag;
|
self.highlighted,
|
||||||
} else {
|
) {
|
||||||
return TileIndex::FalseFlagMine;
|
// Has mine, clicked mine: BOOM!
|
||||||
}
|
(TileState::Mine, _, _, _, true, _) => TileIndex::Explosion,
|
||||||
}
|
// Has mine, has flag, and game is over: True Flag
|
||||||
}
|
(TileState::Mine, Some(TileModifier::Flagged), _, true, _, _) => TileIndex::Flag,
|
||||||
if self.state == TileState::Mine {
|
// Has flag, is not Mine, and game is over: False flag
|
||||||
return TileIndex::RevealedMine;
|
(TileState::Empty, Some(TileModifier::Flagged), _, true, _, _) => TileIndex::FalseFlagMine,
|
||||||
}
|
// Revealed mine after game is over
|
||||||
}
|
(TileState::Mine, _, _, true, _, _) => TileIndex::RevealedMine,
|
||||||
if self.swept {
|
// Revealed tiles with adjacent tile count
|
||||||
if self.state == TileState::Mine {
|
(TileState::Empty, _, 0, _, true, _) => TileIndex::Revealed,
|
||||||
TileIndex::Explosion
|
(TileState::Empty, _, 1, _, true, _) => TileIndex::One,
|
||||||
} else {
|
(TileState::Empty, _, 2, _, true, _) => TileIndex::Two,
|
||||||
match self.adjacent {
|
(TileState::Empty, _, 3, _, true, _) => TileIndex::Three,
|
||||||
0 => TileIndex::Revealed,
|
(TileState::Empty, _, 4, _, true, _) => TileIndex::Four,
|
||||||
1 => TileIndex::One,
|
(TileState::Empty, _, 5, _, true, _) => TileIndex::Five,
|
||||||
2 => TileIndex::Two,
|
(TileState::Empty, _, 6, _, true, _) => TileIndex::Six,
|
||||||
3 => TileIndex::Three,
|
(TileState::Empty, _, 7, _, true, _) => TileIndex::Seven,
|
||||||
4 => TileIndex::Four,
|
(TileState::Empty, _, 8, _, true, _) => TileIndex::Eight,
|
||||||
5 => TileIndex::Five,
|
// Flag modifier
|
||||||
6 => TileIndex::Six,
|
(_, Some(TileModifier::Flagged), _, _, _, _) => TileIndex::Flag,
|
||||||
7 => TileIndex::Seven,
|
// Question mark modifier
|
||||||
8 => TileIndex::Eight,
|
(_, Some(TileModifier::Unsure), _, _, _, _) => TileIndex::Question,
|
||||||
_ => TileIndex::RevealedQuestion,
|
// No modifier, not swept, but highlighted
|
||||||
}
|
(_, None, _, _, false, true) => TileIndex::Revealed,
|
||||||
}
|
// No modifier, Not swept, and not highlighted: Unknown tile
|
||||||
} else {
|
(_, None, _, _, false, false) => TileIndex::Unknown,
|
||||||
if let Some(modif) = self.modifier {
|
// unsigned 8 bit integer has too much range for the adjacent tiles count, creating an invalid state
|
||||||
match modif {
|
// from 9 onward. This last clause is to catch if somehow this invalid state occurs, and display
|
||||||
TileModifier::Flagged => TileIndex::Flag,
|
// the invalid tile in that case.
|
||||||
TileModifier::Unsure => TileIndex::Question,
|
(TileState::Empty, _, 9..=u8::MAX, _, true, _) => TileIndex::RevealedQuestion,
|
||||||
}
|
|
||||||
} else if self.highlighted {
|
|
||||||
TileIndex::Revealed
|
|
||||||
} else {
|
|
||||||
TileIndex::Hidden
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,11 @@ pub mod flag_counter;
|
||||||
pub mod smile;
|
pub mod smile;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
|
||||||
use crate::logic::Minesweeper;
|
use crate::{gui::GUIEvent, logic::Minesweeper, util::Events};
|
||||||
|
|
||||||
use self::{flag_counter::GUIFlagCounter, smile::GUISmile, timer::GUITimer};
|
use self::{flag_counter::GUIFlagCounter, smile::GUISmile, timer::GUITimer};
|
||||||
|
|
||||||
use super::{
|
use super::{texture_store::TextureStore, UIState};
|
||||||
texture_store::TextureStore,
|
|
||||||
ui_event::{GUIEvent, GUIEvents},
|
|
||||||
UIState,
|
|
||||||
};
|
|
||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GUITop {
|
pub struct GUITop {
|
||||||
|
@ -29,7 +25,7 @@ impl GUITop {
|
||||||
&mut self,
|
&mut self,
|
||||||
ui_state: &UIState,
|
ui_state: &UIState,
|
||||||
game_logic: &Minesweeper,
|
game_logic: &Minesweeper,
|
||||||
event_handler: &mut GUIEvents,
|
event_handler: &mut Events<GUIEvent>,
|
||||||
textures: &TextureStore,
|
textures: &TextureStore,
|
||||||
) {
|
) {
|
||||||
let background_color = Color::from_rgba(192, 192, 192, 255);
|
let background_color = Color::from_rgba(192, 192, 192, 255);
|
||||||
|
@ -58,7 +54,7 @@ impl GUITop {
|
||||||
const HEIGHT: usize = 35;
|
const HEIGHT: usize = 35;
|
||||||
let pos_y = (ui_state.top_offset - HEIGHT) / 2;
|
let pos_y = (ui_state.top_offset - HEIGHT) / 2;
|
||||||
let pos_x = (13 * 2 * 2 - WIDTH) / 2;
|
let pos_x = (13 * 2 * 2 - WIDTH) / 2;
|
||||||
let (scaled_width, scaled_height) = ui_state.pixel_screen_scale(WIDTH as usize, HEIGHT);
|
let (scaled_width, scaled_height) = ui_state.pixel_screen_scale(WIDTH, HEIGHT);
|
||||||
let (pos_x, pos_y) = ui_state.pixel_screen_offset(pos_x, pos_y);
|
let (pos_x, pos_y) = ui_state.pixel_screen_offset(pos_x, pos_y);
|
||||||
if widgets::Button::new(textures.cog)
|
if widgets::Button::new(textures.cog)
|
||||||
.size(vec2(scaled_width, scaled_height))
|
.size(vec2(scaled_width, scaled_height))
|
||||||
|
@ -73,9 +69,9 @@ impl GUITop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.timer.render(&ui_state, game_logic.get_time(), ui, &textures);
|
self.timer.render(ui_state, game_logic.get_time(), ui, textures);
|
||||||
self.smile.render(&ui_state, ui, event_handler, &textures);
|
self.smile.render(ui_state, ui, event_handler, textures);
|
||||||
self.flag_counter.render(&ui_state, game_logic.board.remaining_flags(), ui, &textures);
|
self.flag_counter.render(ui_state, game_logic.board.remaining_flags(), ui, textures);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use macroquad::{
|
use macroquad::ui::Ui;
|
||||||
prelude::*,
|
|
||||||
ui::{widgets, Ui},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::gui::texture_store::TextureStore;
|
use crate::gui::{
|
||||||
|
seven_segment::{self, draw_seven_segment},
|
||||||
|
texture_store::TextureStore,
|
||||||
|
};
|
||||||
|
|
||||||
use super::UIState;
|
use super::UIState;
|
||||||
|
|
||||||
|
@ -33,18 +33,13 @@ impl GUIFlagCounter {
|
||||||
self.old_count = remaining;
|
self.old_count = remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
let top = ui_state.top_offset;
|
draw_seven_segment(
|
||||||
const WIDTH: usize = 13 * 2;
|
ui_state,
|
||||||
const HEIGHT: usize = 23 * 2;
|
ui,
|
||||||
let (scaled_width, scaled_height) = ui_state.pixel_screen_scale(WIDTH, HEIGHT);
|
textures,
|
||||||
|
&self.digits,
|
||||||
// let length = self.digits.len() as f32;
|
seven_segment::WIDTH * 2,
|
||||||
for (x, digit) in self.digits.iter().enumerate() {
|
(ui_state.top_offset - seven_segment::HEIGHT) / 2,
|
||||||
let (pos_x, pos_y) = ui_state.pixel_screen_offset((x + 2) * WIDTH, (top - HEIGHT) / 2);
|
);
|
||||||
widgets::Texture::new(textures.numbers[*digit])
|
|
||||||
.size(scaled_width, scaled_height)
|
|
||||||
.position(vec2(pos_x, pos_y))
|
|
||||||
.ui(ui);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ use macroquad::{
|
||||||
ui::{widgets, Ui},
|
ui::{widgets, Ui},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::gui::{
|
use crate::{
|
||||||
texture_store::TextureStore,
|
gui::{texture_store::TextureStore, GUIEvent},
|
||||||
ui_event::{GUIEvent, GUIEvents},
|
util::Events,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::UIState;
|
use super::UIState;
|
||||||
|
@ -34,7 +34,7 @@ const WIDTH: usize = 70;
|
||||||
const HEIGHT: usize = 70;
|
const HEIGHT: usize = 70;
|
||||||
|
|
||||||
impl GUISmile {
|
impl GUISmile {
|
||||||
pub fn render(&mut self, ui_state: &UIState, ui: &mut Ui, event_handler: &mut GUIEvents, textures: &TextureStore) {
|
pub fn render(&mut self, ui_state: &UIState, ui: &mut Ui, event_handler: &mut Events<GUIEvent>, textures: &TextureStore) {
|
||||||
let top_height = ui_state.top_offset;
|
let top_height = ui_state.top_offset;
|
||||||
let top_width = ui_state.width * ui_state.tile_size;
|
let top_width = ui_state.width * ui_state.tile_size;
|
||||||
let pos_x = (top_width - HEIGHT) / 2;
|
let pos_x = (top_width - HEIGHT) / 2;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use macroquad::{
|
use macroquad::{prelude::*, ui::Ui};
|
||||||
prelude::*,
|
|
||||||
ui::{widgets, Ui},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::gui::texture_store::TextureStore;
|
use crate::gui::{
|
||||||
|
seven_segment::{self, draw_seven_segment},
|
||||||
|
texture_store::TextureStore,
|
||||||
|
};
|
||||||
|
|
||||||
use super::UIState;
|
use super::UIState;
|
||||||
|
|
||||||
|
@ -31,18 +31,15 @@ impl GUITimer {
|
||||||
let digits: Vec<usize> = time_1.chars().map(|i| (i.to_digit(10u32).unwrap_or(0)) as usize).collect();
|
let digits: Vec<usize> = time_1.chars().map(|i| (i.to_digit(10u32).unwrap_or(0)) as usize).collect();
|
||||||
self.digits = digits;
|
self.digits = digits;
|
||||||
}
|
}
|
||||||
let top = ui_state.top_offset;
|
|
||||||
const WIDTH: usize = 13 * 2;
|
|
||||||
const HEIGHT: usize = 23 * 2;
|
|
||||||
let (scaled_width, scaled_height) = ui_state.pixel_screen_scale(WIDTH as usize, HEIGHT);
|
|
||||||
let board_width = ui_state.width * ui_state.tile_size;
|
let board_width = ui_state.width * ui_state.tile_size;
|
||||||
let length = self.digits.len();
|
|
||||||
for (x, digit) in self.digits.iter().enumerate() {
|
draw_seven_segment(
|
||||||
let (pos_x, pos_y) = ui_state.pixel_screen_offset(WIDTH * x + board_width - WIDTH * (length + 2), (top - HEIGHT) / 2);
|
ui_state,
|
||||||
widgets::Texture::new(textures.numbers[*digit])
|
ui,
|
||||||
.size(scaled_width, scaled_height)
|
textures,
|
||||||
.position(vec2(pos_x, pos_y))
|
&self.digits,
|
||||||
.ui(ui);
|
board_width - seven_segment::WIDTH * (self.digits.len() + 2),
|
||||||
}
|
(ui_state.top_offset - seven_segment::HEIGHT) / 2,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
use crate::logic::game_board::ModifyMode;
|
|
||||||
|
|
||||||
use super::{top_menu::smile::SmileyState, Language};
|
|
||||||
|
|
||||||
pub enum GUIEvent {
|
|
||||||
ClickReset,
|
|
||||||
OpenSettings,
|
|
||||||
CloseSettings,
|
|
||||||
SwitchLanguage(Language),
|
|
||||||
ClickTile(usize, usize),
|
|
||||||
ModifyTile(usize, usize),
|
|
||||||
HighlightTile(usize, usize),
|
|
||||||
UnHighlightTile(usize, usize),
|
|
||||||
CreateNewGame(usize, usize, usize),
|
|
||||||
SetQuestionMode(ModifyMode),
|
|
||||||
SetSmileyState(SmileyState),
|
|
||||||
}
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct GUIEvents {
|
|
||||||
events: Vec<GUIEvent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GUIEvents {
|
|
||||||
pub fn add(&mut self, event: GUIEvent) {
|
|
||||||
self.events.push(event);
|
|
||||||
}
|
|
||||||
pub fn next(&mut self) -> Option<GUIEvent> {
|
|
||||||
if self.events.len() > 0 {
|
|
||||||
self.events.pop()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.events.clear();
|
|
||||||
}
|
|
||||||
}
|
|
25
src/logic.rs
25
src/logic.rs
|
@ -1,11 +1,10 @@
|
||||||
pub mod events;
|
// pub mod events;
|
||||||
pub mod game_board;
|
pub mod game_board;
|
||||||
pub mod tile;
|
pub mod tile;
|
||||||
mod timer;
|
mod timer;
|
||||||
use self::{
|
use crate::util::Events;
|
||||||
events::{Events, GameEvent},
|
|
||||||
timer::Timer,
|
use self::{tile::Tile, timer::Timer};
|
||||||
};
|
|
||||||
use game_board::GameBoard;
|
use game_board::GameBoard;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
@ -17,13 +16,25 @@ pub enum GameState {
|
||||||
GameOver,
|
GameOver,
|
||||||
Victory,
|
Victory,
|
||||||
}
|
}
|
||||||
|
pub enum GameEvent {
|
||||||
|
Lose(usize, usize, Tile),
|
||||||
|
RevealTile(usize, usize, Tile),
|
||||||
|
FlagTile(usize, usize, Tile),
|
||||||
|
QuestionTile(usize, usize, Tile),
|
||||||
|
SweepDone,
|
||||||
|
SweepBegin,
|
||||||
|
InitDone,
|
||||||
|
Win,
|
||||||
|
Reset,
|
||||||
|
GameEnd(GameBoard),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Minesweeper {
|
pub struct Minesweeper {
|
||||||
pub board: GameBoard,
|
pub board: GameBoard,
|
||||||
|
pub events: Events<GameEvent>,
|
||||||
pub state: GameState,
|
pub state: GameState,
|
||||||
timer: Timer,
|
timer: Timer,
|
||||||
pub events: Events,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Minesweeper {
|
impl Minesweeper {
|
||||||
|
@ -71,7 +82,7 @@ impl Minesweeper {
|
||||||
self.board.modify(x, y, &mut self.events)
|
self.board.modify(x, y, &mut self.events)
|
||||||
}
|
}
|
||||||
pub fn get_time(&self) -> Option<f64> {
|
pub fn get_time(&self) -> Option<f64> {
|
||||||
return self.timer.elapsed();
|
self.timer.elapsed()
|
||||||
}
|
}
|
||||||
pub fn highlight(&mut self, x: usize, y: usize) {
|
pub fn highlight(&mut self, x: usize, y: usize) {
|
||||||
if self.state == GameState::Playing || self.state == GameState::Empty {
|
if self.state == GameState::Playing || self.state == GameState::Empty {
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
use super::game_board::GameBoard;
|
|
||||||
use super::tile::Tile;
|
|
||||||
pub enum GameEvent {
|
|
||||||
Lose(usize, usize, Tile),
|
|
||||||
RevealTile(usize, usize, Tile),
|
|
||||||
FlagTile(usize, usize, Tile),
|
|
||||||
QuestionTile(usize, usize, Tile),
|
|
||||||
SweepDone,
|
|
||||||
SweepBegin,
|
|
||||||
InitDone,
|
|
||||||
Win,
|
|
||||||
Reset,
|
|
||||||
GameEnd(GameBoard),
|
|
||||||
}
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Events {
|
|
||||||
events: Vec<GameEvent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Events {
|
|
||||||
pub fn add(&mut self, event: GameEvent) {
|
|
||||||
self.events.push(event);
|
|
||||||
}
|
|
||||||
pub fn next(&mut self) -> Option<GameEvent> {
|
|
||||||
if self.events.len() > 0 {
|
|
||||||
self.events.pop()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.events.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -98,7 +98,7 @@ impl GameBoard {
|
||||||
self.mines as isize - self.flags as isize
|
self.mines as isize - self.flags as isize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modify(&mut self, x: usize, y: usize, event_handler: &mut Events) {
|
pub fn modify(&mut self, x: usize, y: usize, event_handler: &mut Events<GameEvent>) {
|
||||||
if let Some(&tile) = &self.get_tile(x, y) {
|
if let Some(&tile) = &self.get_tile(x, y) {
|
||||||
if tile.swept {
|
if tile.swept {
|
||||||
return;
|
return;
|
||||||
|
@ -109,11 +109,11 @@ impl GameBoard {
|
||||||
self.flags -= 1;
|
self.flags -= 1;
|
||||||
match self.modify_mode {
|
match self.modify_mode {
|
||||||
ModifyMode::Flag => {
|
ModifyMode::Flag => {
|
||||||
event_handler.add(GameEvent::FlagTile(x, y, tile.clone()));
|
event_handler.add(GameEvent::FlagTile(x, y, tile));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ModifyMode::Question => {
|
ModifyMode::Question => {
|
||||||
event_handler.add(GameEvent::QuestionTile(x, y, tile.clone()));
|
event_handler.add(GameEvent::QuestionTile(x, y, tile));
|
||||||
|
|
||||||
Some(TileModifier::Unsure)
|
Some(TileModifier::Unsure)
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ impl GameBoard {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.flags += 1;
|
self.flags += 1;
|
||||||
event_handler.add(GameEvent::FlagTile(x, y, tile.clone()));
|
event_handler.add(GameEvent::FlagTile(x, y, tile));
|
||||||
Some(TileModifier::Flagged)
|
Some(TileModifier::Flagged)
|
||||||
};
|
};
|
||||||
if let Some(tile) = self.get_tile_mut(x, y) {
|
if let Some(tile) = self.get_tile_mut(x, y) {
|
||||||
|
@ -132,12 +132,12 @@ impl GameBoard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sweep(&mut self, x: usize, y: usize, event_handler: &mut Events) -> Option<GameState> {
|
pub fn sweep(&mut self, x: usize, y: usize, event_handler: &mut Events<GameEvent>) -> Option<GameState> {
|
||||||
if let BoardState::Ungenerated = self.state {
|
if let BoardState::Ungenerated = self.state {
|
||||||
self.generate(x, y);
|
self.generate(x, y);
|
||||||
}
|
}
|
||||||
let &tile = &self.tiles[x][y];
|
let tile = self.tiles[x][y];
|
||||||
if let Some(_) = tile.modifier {
|
if tile.modifier.is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if tile.swept {
|
if tile.swept {
|
||||||
|
@ -145,10 +145,10 @@ impl GameBoard {
|
||||||
}
|
}
|
||||||
self.tiles[x][y].swept = true;
|
self.tiles[x][y].swept = true;
|
||||||
self.revealed_tiles += 1;
|
self.revealed_tiles += 1;
|
||||||
event_handler.add(GameEvent::RevealTile(x, y, self.tiles[x][y].clone()));
|
event_handler.add(GameEvent::RevealTile(x, y, self.tiles[x][y]));
|
||||||
|
|
||||||
if tile.state == TileState::Mine {
|
if tile.state == TileState::Mine {
|
||||||
event_handler.add(GameEvent::Lose(x, y, tile.clone()));
|
event_handler.add(GameEvent::Lose(x, y, tile));
|
||||||
|
|
||||||
event_handler.add(GameEvent::GameEnd(self.clone()));
|
event_handler.add(GameEvent::GameEnd(self.clone()));
|
||||||
return Some(GameState::GameOver);
|
return Some(GameState::GameOver);
|
||||||
|
@ -157,14 +157,15 @@ impl GameBoard {
|
||||||
|
|
||||||
let mut scan_list = VecDeque::from([(x, y)]);
|
let mut scan_list = VecDeque::from([(x, y)]);
|
||||||
let mut revealed: usize = 0;
|
let mut revealed: usize = 0;
|
||||||
while scan_list.len() > 0 {
|
while !scan_list.is_empty() {
|
||||||
for &scan_location in ADJACENT_WITHOUT_CENTER.iter() {
|
for &scan_location in ADJACENT_WITHOUT_CENTER.iter() {
|
||||||
if let Some(old_tile) = self.get_tile(scan_list[0].0, scan_list[0].1) {
|
if let Some((x, y)) = scan_list.front() {
|
||||||
|
if let Some(old_tile) = self.get_tile(*x, *y) {
|
||||||
if old_tile.adjacent > 0 {
|
if old_tile.adjacent > 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let x = scan_list[0].0 as isize + scan_location.0;
|
let x = *x as isize + scan_location.0;
|
||||||
let y = scan_list[0].1 as isize + scan_location.1;
|
let y = *y as isize + scan_location.1;
|
||||||
|
|
||||||
if x < 0 || y < 0 {
|
if x < 0 || y < 0 {
|
||||||
continue;
|
continue;
|
||||||
|
@ -178,7 +179,8 @@ impl GameBoard {
|
||||||
scan_list.push_back((x, y));
|
scan_list.push_back((x, y));
|
||||||
tile.swept = true;
|
tile.swept = true;
|
||||||
revealed += 1;
|
revealed += 1;
|
||||||
event_handler.add(GameEvent::RevealTile(x, y, tile.clone()));
|
event_handler.add(GameEvent::RevealTile(x, y, *tile));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,7 +226,7 @@ impl GameBoard {
|
||||||
let y = macroquad::rand::gen_range(0, height);
|
let y = macroquad::rand::gen_range(0, height);
|
||||||
let mut tile = &mut self.tiles[x][y];
|
let mut tile = &mut self.tiles[x][y];
|
||||||
|
|
||||||
if tile.state == TileState::Mine || tile.safe == true {
|
if tile.state == TileState::Mine || tile.safe {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ impl Tile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn highlight(&mut self) {
|
pub fn highlight(&mut self) {
|
||||||
if self.swept == false {
|
if !self.swept {
|
||||||
self.highlighted = true;
|
self.highlighted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,7 @@ impl Timer {
|
||||||
if let TimerState::Frozen = self.state {
|
if let TimerState::Frozen = self.state {
|
||||||
return Some(self.old);
|
return Some(self.old);
|
||||||
}
|
}
|
||||||
if let Some(time) = self.start_time {
|
self.start_time.map(|time| get_time() - time)
|
||||||
Some(get_time() - time)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
self.old = self.elapsed().unwrap_or(0f64);
|
self.old = self.elapsed().unwrap_or(0f64);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use gui::top_menu::smile::SmileyState;
|
use gui::top_menu::smile::SmileyState;
|
||||||
use gui::ui_event::*;
|
use gui::{GUIEvent, GameUI, UIState};
|
||||||
use gui::{GameUI, UIState};
|
use logic::{GameEvent, Minesweeper};
|
||||||
use logic::{events::GameEvent, Minesweeper};
|
|
||||||
use macroquad::{
|
use macroquad::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
ui::{root_ui, Skin},
|
ui::{root_ui, Skin},
|
||||||
|
@ -13,8 +12,8 @@ mod sprite_loader;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let width = (30 * 32) as i32;
|
let width = 30 * 32;
|
||||||
let height = (16 * 32) as i32 + 100;
|
let height = 16 * 32 + 100;
|
||||||
Window::from_config(
|
Window::from_config(
|
||||||
Conf {
|
Conf {
|
||||||
sample_count: 2,
|
sample_count: 2,
|
||||||
|
|
|
@ -2,13 +2,11 @@ use std::error::Error;
|
||||||
|
|
||||||
use image::{load_from_memory, EncodableLayout};
|
use image::{load_from_memory, EncodableLayout};
|
||||||
use macroquad::texture::{FilterMode, Texture2D};
|
use macroquad::texture::{FilterMode, Texture2D};
|
||||||
pub fn load_sprites(bytes: &[u8], tile_size: [usize; 2], rows: usize, columns: usize) -> Result<Vec<Texture2D>, Box<dyn Error>> {
|
pub fn load_sprites(bytes: &[u8], tile_size: (u32, u32), rows: usize, columns: usize) -> Result<Vec<Texture2D>, Box<dyn Error>> {
|
||||||
let sprite_sheet = load_from_memory(bytes)?.to_rgba8();
|
let sprite_sheet = load_from_memory(bytes)?.to_rgba8();
|
||||||
|
|
||||||
let mut sprite_list: Vec<Texture2D> = vec![];
|
let mut sprite_list: Vec<Texture2D> = vec![];
|
||||||
let [tile_width, tile_height] = tile_size;
|
let (tile_width, tile_height) = tile_size;
|
||||||
let tile_width = tile_width as u32;
|
|
||||||
let tile_height = tile_height as u32;
|
|
||||||
|
|
||||||
for i in 0..(rows * columns) {
|
for i in 0..(rows * columns) {
|
||||||
let x = (i % columns) as u32;
|
let x = (i % columns) as u32;
|
||||||
|
|
44
src/util.rs
44
src/util.rs
|
@ -1,4 +1,44 @@
|
||||||
|
#[rustfmt::skip]
|
||||||
pub const ADJACENT_WITH_CENTER: [(isize, isize); 9] =
|
pub const ADJACENT_WITH_CENTER: [(isize, isize); 9] =
|
||||||
[(-1, -1), (0, -1), (1, -1), (-1, 0), (0, 0), (1, 0), (-1, 1), (0, 1), (1, 1)];
|
[
|
||||||
|
(-1, -1), (0, -1), (1, -1),
|
||||||
|
(-1, 0), (0, 0), (1, 0),
|
||||||
|
(-1, 1), (0, 1), (1, 1)
|
||||||
|
];
|
||||||
|
|
||||||
pub const ADJACENT_WITHOUT_CENTER: [(isize, isize); 8] = [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)];
|
#[rustfmt::skip]
|
||||||
|
pub const ADJACENT_WITHOUT_CENTER: [(isize, isize); 8] =
|
||||||
|
[
|
||||||
|
(-1, -1), (0, -1), (1, -1),
|
||||||
|
(-1, 0), (1, 0),
|
||||||
|
(-1, 1), (0, 1), (1, 1)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Event Queue
|
||||||
|
pub struct Events<E> {
|
||||||
|
events: Vec<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> Events<E> {
|
||||||
|
pub fn add(&mut self, event: E) {
|
||||||
|
self.events.push(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> Option<E> {
|
||||||
|
if !self.events.is_empty() {
|
||||||
|
self.events.pop()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.events.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<E> Default for Events<E> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
events: Vec::<E>::with_capacity(10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue