Major refactor
This commit is contained in:
+73
-20
@@ -10,37 +10,90 @@ use sol_lib::generator::Puzzle;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Game {
|
||||
resources: Resources,
|
||||
// Game state
|
||||
settings: GameSettings,
|
||||
puzzle: Puzzle,
|
||||
show_rules: bool,
|
||||
game_mode: GameMode,
|
||||
|
||||
// Render state
|
||||
resources: Resources,
|
||||
|
||||
window_height: f32,
|
||||
window_width: f32,
|
||||
|
||||
puzzle: Puzzle,
|
||||
id_text_btn: ButtonWidget,
|
||||
reset_btn: ButtonWidget,
|
||||
next_btn: ButtonWidget,
|
||||
rules_font_size: f32, // TODO: remove
|
||||
|
||||
show_rules: bool,
|
||||
rules_font_size: f32,
|
||||
generate_btn: ButtonWidget,
|
||||
board: BoardWidget,
|
||||
|
||||
heading_text: String,
|
||||
heading: LabelWidget,
|
||||
|
||||
rules_btn_text: String,
|
||||
rules_btn: ButtonWidget,
|
||||
|
||||
game_mode: GameMode,
|
||||
game_mode_btns: HashMap<GameMode, ButtonWidget>,
|
||||
|
||||
pieces_label: LabelWidget,
|
||||
max_age_label: LabelWidget,
|
||||
pieces_counter: CounterWidget,
|
||||
max_age_counter: CounterWidget,
|
||||
generate_btn: ButtonWidget,
|
||||
widgets: HashMap<WidgetId, Widget>,
|
||||
|
||||
counter_pieces: WidgetId,
|
||||
counter_moves: WidgetId,
|
||||
|
||||
label_pieces: WidgetId,
|
||||
label_max_age: WidgetId,
|
||||
label_heading: WidgetId,
|
||||
|
||||
btn_rules: WidgetId,
|
||||
btn_reset: WidgetId,
|
||||
btn_next: WidgetId,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Hash, Default)]
|
||||
pub struct WidgetId(u64);
|
||||
|
||||
pub enum Widget {
|
||||
Label(LabelWidget),
|
||||
Counter(CounterWidget),
|
||||
Button(ButtonWidget),
|
||||
}
|
||||
|
||||
impl Widget {
|
||||
fn draw(&mut self, resources: &Resources) {
|
||||
match self {
|
||||
Widget::Label(label) => label.draw(&resources),
|
||||
Widget::Counter(counter) => counter.draw(&resources),
|
||||
Widget::Button(btn) => btn.draw(&resources),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! get_widget_fn {
|
||||
($name:ident, $ret:ty, $variant:path) => {
|
||||
fn $name(widgets: &mut HashMap<WidgetId, Widget>, id: WidgetId) -> &mut $ret {
|
||||
match widgets.get_mut(&id) {
|
||||
Some(w) => match w {
|
||||
$variant(v) => return v,
|
||||
_ => panic!("Widget with id {} not found", id.0),
|
||||
},
|
||||
None => panic!("Widget with id {} not found", id.0),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get_widget_fn!(label, LabelWidget, Widget::Label);
|
||||
get_widget_fn!(button, ButtonWidget, Widget::Button);
|
||||
get_widget_fn!(counter, CounterWidget, Widget::Counter);
|
||||
|
||||
macro_rules! widget_accessor {
|
||||
($name:ident, $fn:ident) => {
|
||||
macro_rules! $name {
|
||||
($this:expr, $widget_id:ident) => {
|
||||
game::$fn(&mut $this.widgets, $this.$widget_id)
|
||||
};
|
||||
}
|
||||
pub(crate) use $name;
|
||||
};
|
||||
}
|
||||
|
||||
widget_accessor!(w_button, button);
|
||||
widget_accessor!(w_label, label);
|
||||
widget_accessor!(w_counter, counter);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GameSettings {
|
||||
pub volume: f32,
|
||||
|
||||
+54
-123
@@ -1,9 +1,12 @@
|
||||
use super::{Game, constants};
|
||||
use crate::{game::GameMode, widgets::*};
|
||||
use crate::{
|
||||
game::{self, GameMode, w_button, w_counter, w_label},
|
||||
widgets::*,
|
||||
};
|
||||
use macroquad::{math, prelude::*};
|
||||
|
||||
impl Game {
|
||||
fn initialize_drawables(&mut self, new_width: f32, new_height: f32) {
|
||||
fn initialize_scene(&mut self, new_width: f32, new_height: f32) {
|
||||
self.window_height = new_height;
|
||||
self.window_width = new_width;
|
||||
|
||||
@@ -19,7 +22,7 @@ impl Game {
|
||||
let heading_font_size = constants::HEADING_FONT_SIZE_MULTIPLIER * min_dimension;
|
||||
let f = heading_font_size.floor() as u16;
|
||||
let heading_text_dims = measure_text(
|
||||
self.heading_text.as_str(),
|
||||
w_label!(self, label_heading).text.as_str(),
|
||||
Some(&self.resources.font()),
|
||||
f,
|
||||
1.0,
|
||||
@@ -32,8 +35,11 @@ impl Game {
|
||||
heading_text_dims.width,
|
||||
heading_text_dims.height,
|
||||
);
|
||||
self.heading
|
||||
.initialize_drawables(heading_rect, heading_font_size, &self.resources);
|
||||
w_label!(self, label_heading).initialize_drawables(
|
||||
heading_rect,
|
||||
heading_font_size,
|
||||
&self.resources,
|
||||
);
|
||||
|
||||
// Buttons
|
||||
let btn_h = constants::BUTTON_HEIGHT_MULTIPLIER * min_dimension;
|
||||
@@ -42,14 +48,14 @@ impl Game {
|
||||
// Bottom row
|
||||
let bottom_row_y =
|
||||
board_width + board_y + constants::BOTTOM_ROW_OFFSET_MULTIPLIER * square_width;
|
||||
self.rules_btn.initialize_drawables(Rect::new(
|
||||
w_button!(self, btn_rules).place(Rect::new(
|
||||
board_x + (square_width - btn_w) / 2.,
|
||||
bottom_row_y,
|
||||
btn_w,
|
||||
btn_h,
|
||||
));
|
||||
|
||||
self.reset_btn.initialize_drawables(Rect::new(
|
||||
w_button!(self, btn_reset).place(Rect::new(
|
||||
board_x + square_width + (square_width - btn_w) / 2.,
|
||||
bottom_row_y,
|
||||
btn_w,
|
||||
@@ -57,52 +63,42 @@ impl Game {
|
||||
));
|
||||
|
||||
let btn_next_x_offset = (board_width - square_width) + (square_width - btn_w) / 2.;
|
||||
self.next_btn.initialize_drawables(Rect::new(
|
||||
w_button!(self, btn_reset).place(Rect::new(
|
||||
board_x + btn_next_x_offset,
|
||||
bottom_row_y,
|
||||
btn_w,
|
||||
btn_h,
|
||||
));
|
||||
|
||||
let id_text_rect = Rect::new(
|
||||
board_x + 2. * square_width - btn_w,
|
||||
bottom_row_y,
|
||||
btn_w * 2.,
|
||||
btn_h,
|
||||
);
|
||||
self.id_text_btn.initialize_drawables(id_text_rect);
|
||||
|
||||
self.rules_font_size = heading_font_size;
|
||||
|
||||
// Right column
|
||||
let right_column_x =
|
||||
board_x + board_width + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width;
|
||||
self.get_game_mode_button(&GameMode::Easy)
|
||||
.initialize_drawables(Rect::new(
|
||||
right_column_x,
|
||||
board_y + (square_width - btn_h) / 2.,
|
||||
btn_w,
|
||||
btn_h,
|
||||
));
|
||||
self.get_game_mode_button(&GameMode::Easy).place(Rect::new(
|
||||
right_column_x,
|
||||
board_y + (square_width - btn_h) / 2.,
|
||||
btn_w,
|
||||
btn_h,
|
||||
));
|
||||
|
||||
self.get_game_mode_button(&GameMode::Medium)
|
||||
.initialize_drawables(Rect::new(
|
||||
.place(Rect::new(
|
||||
right_column_x,
|
||||
board_y + square_width + (square_width - btn_h) / 2.,
|
||||
btn_w,
|
||||
btn_h,
|
||||
));
|
||||
|
||||
self.get_game_mode_button(&GameMode::Hard)
|
||||
.initialize_drawables(Rect::new(
|
||||
right_column_x,
|
||||
board_y + 2. * square_width + (square_width - btn_h) / 2.,
|
||||
btn_w,
|
||||
btn_h,
|
||||
));
|
||||
self.get_game_mode_button(&GameMode::Hard).place(Rect::new(
|
||||
right_column_x,
|
||||
board_y + 2. * square_width + (square_width - btn_h) / 2.,
|
||||
btn_w,
|
||||
btn_h,
|
||||
));
|
||||
|
||||
self.get_game_mode_button(&GameMode::Custom)
|
||||
.initialize_drawables(Rect::new(
|
||||
.place(Rect::new(
|
||||
right_column_x,
|
||||
board_y + 3. * square_width + (square_width - btn_h) / 2.,
|
||||
btn_w,
|
||||
@@ -112,14 +108,14 @@ impl Game {
|
||||
// Right column 2
|
||||
let right_column_2_x =
|
||||
right_column_x + btn_w + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width;
|
||||
self.generate_btn.initialize_drawables(Rect::new(
|
||||
self.generate_btn.place(Rect::new(
|
||||
right_column_2_x,
|
||||
board_y + board_width - square_width + (square_width - btn_h) / 2.,
|
||||
2. * btn_w,
|
||||
btn_h,
|
||||
));
|
||||
|
||||
self.pieces_label.initialize_drawables(
|
||||
w_label!(self, label_pieces).initialize_drawables(
|
||||
Rect::new(
|
||||
right_column_2_x,
|
||||
board_y + (square_width + btn_h) / 2.,
|
||||
@@ -129,7 +125,8 @@ impl Game {
|
||||
0.30 * btn_h,
|
||||
&self.resources,
|
||||
);
|
||||
self.pieces_counter.initialize_drawables(
|
||||
|
||||
w_counter!(self, counter_pieces).initialize_drawables(
|
||||
Rect::new(
|
||||
right_column_2_x,
|
||||
board_y + square_width + (2. * square_width - 2. * btn_h) / 2.,
|
||||
@@ -142,7 +139,7 @@ impl Game {
|
||||
|
||||
// Right column 3
|
||||
let right_column_3_x = right_column_2_x + btn_w;
|
||||
self.max_age_label.initialize_drawables(
|
||||
w_label!(self, label_max_age).initialize_drawables(
|
||||
Rect::new(
|
||||
right_column_2_x + btn_w,
|
||||
board_y + (square_width + btn_h) / 2.,
|
||||
@@ -152,7 +149,7 @@ impl Game {
|
||||
0.30 * btn_h,
|
||||
&self.resources,
|
||||
);
|
||||
self.max_age_counter.initialize_drawables(
|
||||
w_counter!(self, counter_moves).initialize_drawables(
|
||||
Rect::new(
|
||||
right_column_3_x,
|
||||
board_y + square_width + (2. * square_width - 2. * btn_h) / 2.,
|
||||
@@ -167,16 +164,18 @@ impl Game {
|
||||
pub fn draw(&mut self) {
|
||||
self.update_window_size();
|
||||
|
||||
self.draw_heading();
|
||||
self.draw_board();
|
||||
self.draw_id_text_button();
|
||||
self.draw_buttons();
|
||||
self.draw_setting_controls();
|
||||
self.draw_debug();
|
||||
}
|
||||
|
||||
fn draw_heading(&self) {
|
||||
self.heading.draw(&self.resources);
|
||||
self.generate_btn.draw(&self.resources);
|
||||
for (_, btn) in &self.game_mode_btns {
|
||||
btn.draw(&self.resources);
|
||||
}
|
||||
|
||||
for (_, widget) in &mut self.widgets {
|
||||
widget.draw(&self.resources);
|
||||
}
|
||||
|
||||
self.draw_fps();
|
||||
}
|
||||
|
||||
fn draw_board(&mut self) {
|
||||
@@ -192,87 +191,19 @@ impl Game {
|
||||
self.game_mode_btns.get_mut(mode).unwrap()
|
||||
}
|
||||
|
||||
fn draw_game_mode_button(&mut self, mode: &GameMode, color: &UiColor, text: &str) {
|
||||
let btn = self.game_mode_btns.get_mut(mode).unwrap();
|
||||
btn.draw(text, color, &self.resources);
|
||||
}
|
||||
|
||||
fn draw_buttons(&mut self) {
|
||||
self.reset_btn.draw(
|
||||
constants::RESET_BUTTON_TEXT,
|
||||
&UiColor::Yellow,
|
||||
&self.resources,
|
||||
);
|
||||
|
||||
self.next_btn.draw(
|
||||
constants::NEXT_BUTTON_TEXT,
|
||||
&UiColor::Green,
|
||||
&self.resources,
|
||||
);
|
||||
|
||||
self.draw_game_mode_button(
|
||||
&GameMode::Easy,
|
||||
&UiColor::Yellow,
|
||||
constants::EASY_BUTTON_TEXT,
|
||||
);
|
||||
|
||||
self.draw_game_mode_button(
|
||||
&GameMode::Medium,
|
||||
&UiColor::Yellow,
|
||||
constants::MEDIUM_BUTTON_TEXT,
|
||||
);
|
||||
|
||||
self.draw_game_mode_button(
|
||||
&GameMode::Hard,
|
||||
&UiColor::Yellow,
|
||||
constants::HARD_BUTTON_TEXT,
|
||||
);
|
||||
|
||||
self.draw_game_mode_button(
|
||||
&GameMode::Custom,
|
||||
&UiColor::Blue,
|
||||
constants::CUSTOM_BUTTON_TEXT,
|
||||
);
|
||||
|
||||
self.generate_btn.draw(
|
||||
constants::GENERATE_BUTTON_TEXT,
|
||||
&UiColor::Pink,
|
||||
&self.resources,
|
||||
);
|
||||
|
||||
self.rules_btn
|
||||
.draw(&self.rules_btn_text, &UiColor::Brown, &self.resources);
|
||||
}
|
||||
|
||||
fn draw_setting_controls(&mut self) {
|
||||
self.pieces_label.draw(&self.resources);
|
||||
self.pieces_counter.draw(&self.resources);
|
||||
self.max_age_label.draw(&self.resources);
|
||||
self.max_age_counter.draw(&self.resources);
|
||||
}
|
||||
|
||||
fn draw_id_text_button(&self) {
|
||||
// self.id_text_btn
|
||||
// .draw(&self.puzzle.board.id, &UiColor::Yellow, &self.resources);
|
||||
}
|
||||
|
||||
fn draw_debug(&self) {
|
||||
fn draw_fps(&self) {
|
||||
if self.settings.debug {
|
||||
self.show_fps();
|
||||
let fps = get_fps();
|
||||
draw_text(
|
||||
&format!("FPS: {}", fps),
|
||||
10.0,
|
||||
screen_height() - 20.0,
|
||||
20.0,
|
||||
BLACK,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn show_fps(&self) {
|
||||
let fps = get_fps();
|
||||
draw_text(
|
||||
&format!("FPS: {}", fps),
|
||||
10.0,
|
||||
screen_height() - 20.0,
|
||||
20.0,
|
||||
BLACK,
|
||||
);
|
||||
}
|
||||
|
||||
fn update_window_size(&mut self) {
|
||||
let new_height = math::clamp(
|
||||
screen_height(),
|
||||
@@ -289,6 +220,6 @@ impl Game {
|
||||
return;
|
||||
}
|
||||
|
||||
self.initialize_drawables(new_width, new_height);
|
||||
self.initialize_scene(new_width, new_height);
|
||||
}
|
||||
}
|
||||
|
||||
+204
-153
@@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use super::{Game, GameMode, GameSettings, constants};
|
||||
use crate::{
|
||||
game::{self, Widget, WidgetId, w_button, w_counter},
|
||||
resources::{self, *},
|
||||
widgets::*,
|
||||
};
|
||||
@@ -12,7 +13,7 @@ use sol_lib::{
|
||||
};
|
||||
|
||||
impl Game {
|
||||
pub async fn initialize_state() -> Self {
|
||||
pub async fn new() -> Self {
|
||||
let resources = resources::init().await;
|
||||
let game_mode = GameMode::Medium;
|
||||
let settings = GameSettings {
|
||||
@@ -25,23 +26,83 @@ impl Game {
|
||||
let board = BoardWidget::initialize_state(puzzle.board.size, puzzle.board.clone());
|
||||
let mut game_mode_btns = HashMap::new();
|
||||
let game_mode = GameMode::Medium;
|
||||
game_mode_btns.insert(GameMode::Easy, ButtonWidget::initialize_state(true));
|
||||
game_mode_btns.insert(GameMode::Medium, ButtonWidget::initialize_state(true));
|
||||
game_mode_btns.insert(GameMode::Hard, ButtonWidget::initialize_state(true));
|
||||
game_mode_btns.insert(GameMode::Custom, ButtonWidget::initialize_state(true));
|
||||
let reset_btn = ButtonWidget::initialize_state(true);
|
||||
let next_btn = ButtonWidget::initialize_state(false);
|
||||
let rules_btn = ButtonWidget::initialize_state(true);
|
||||
let generate_btn = ButtonWidget::initialize_state(false);
|
||||
let id_text_btn = ButtonWidget::initialize_state(true);
|
||||
game_mode_btns.insert(
|
||||
GameMode::Easy,
|
||||
ButtonWidget::new(true, UiColor::Yellow, constants::EASY_BUTTON_TEXT),
|
||||
);
|
||||
game_mode_btns.insert(
|
||||
GameMode::Medium,
|
||||
ButtonWidget::new(true, UiColor::Yellow, constants::MEDIUM_BUTTON_TEXT),
|
||||
);
|
||||
game_mode_btns.insert(
|
||||
GameMode::Hard,
|
||||
ButtonWidget::new(true, UiColor::Yellow, constants::HARD_BUTTON_TEXT),
|
||||
);
|
||||
game_mode_btns.insert(
|
||||
GameMode::Custom,
|
||||
ButtonWidget::new(true, UiColor::Blue, constants::CUSTOM_BUTTON_TEXT),
|
||||
);
|
||||
let generate_btn = ButtonWidget::new(false, UiColor::Pink, constants::GENERATE_BUTTON_TEXT);
|
||||
let heading = LabelWidget::initialize_state(constants::HEADING_TEXT);
|
||||
let pieces_label = LabelWidget::initialize_state(constants::PIECES_LABEL_TEXT);
|
||||
let max_age_label = LabelWidget::initialize_state(constants::AGE_LABEL_TEXT);
|
||||
let show_rules = false;
|
||||
let pieces_counter = CounterWidget::initialize_state(2, 7, settings.num_pieces, false);
|
||||
let moves_counter = CounterWidget::initialize_state(1, 7, settings.max_moves_per_piece, false);
|
||||
game_mode_btns.get_mut(&game_mode).unwrap().is_active = false;
|
||||
|
||||
let mut widgets = HashMap::new();
|
||||
let label_pieces = add_widget(&mut widgets, Widget::Label(pieces_label));
|
||||
let label_max_age = add_widget(&mut widgets, Widget::Label(max_age_label));
|
||||
let label_heading = add_widget(&mut widgets, Widget::Label(heading));
|
||||
|
||||
let counter_pieces = add_widget(
|
||||
&mut widgets,
|
||||
Widget::Counter(CounterWidget::new(
|
||||
2,
|
||||
7,
|
||||
settings.num_pieces,
|
||||
false,
|
||||
UiColor::Yellow,
|
||||
)),
|
||||
);
|
||||
|
||||
let counter_moves = add_widget(
|
||||
&mut widgets,
|
||||
Widget::Counter(CounterWidget::new(
|
||||
1,
|
||||
7,
|
||||
settings.max_moves_per_piece,
|
||||
false,
|
||||
UiColor::Yellow,
|
||||
)),
|
||||
);
|
||||
|
||||
let btn_reset = add_widget(
|
||||
&mut widgets,
|
||||
Widget::Button(ButtonWidget::new(
|
||||
true,
|
||||
UiColor::Yellow,
|
||||
constants::RESET_BUTTON_TEXT,
|
||||
)),
|
||||
);
|
||||
|
||||
let btn_next = add_widget(
|
||||
&mut widgets,
|
||||
Widget::Button(ButtonWidget::new(
|
||||
false,
|
||||
UiColor::Green,
|
||||
constants::NEXT_BUTTON_TEXT,
|
||||
)),
|
||||
);
|
||||
|
||||
let btn_rules = add_widget(
|
||||
&mut widgets,
|
||||
Widget::Button(ButtonWidget::new(
|
||||
true,
|
||||
UiColor::Brown,
|
||||
constants::RULES_BUTTON_TEXT,
|
||||
)),
|
||||
);
|
||||
|
||||
Self {
|
||||
puzzle,
|
||||
board,
|
||||
@@ -49,187 +110,169 @@ impl Game {
|
||||
settings,
|
||||
game_mode,
|
||||
game_mode_btns,
|
||||
rules_btn_text: constants::RULES_BUTTON_TEXT.to_string(),
|
||||
reset_btn,
|
||||
next_btn,
|
||||
rules_btn,
|
||||
id_text_btn,
|
||||
btn_reset,
|
||||
btn_next,
|
||||
btn_rules,
|
||||
show_rules,
|
||||
heading_text: constants::HEADING_TEXT.to_string(),
|
||||
heading,
|
||||
pieces_counter,
|
||||
max_age_counter: moves_counter,
|
||||
widgets,
|
||||
counter_pieces,
|
||||
counter_moves,
|
||||
generate_btn,
|
||||
pieces_label,
|
||||
max_age_label,
|
||||
label_pieces,
|
||||
label_max_age,
|
||||
label_heading,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self) {
|
||||
pub fn handle_interaction(&mut self, interaction: GameInteraction) {
|
||||
match interaction {
|
||||
GameInteraction::NoInteraction => {}
|
||||
GameInteraction::Board(new_board_state) => match new_board_state {
|
||||
BoardState::Won => {
|
||||
w_button!(self, btn_next).is_active = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
GameInteraction::ButtonRelease(button_name) => match button_name {
|
||||
ButtonName::Reset => self.reset(),
|
||||
ButtonName::Next => self.next_puzzle(),
|
||||
ButtonName::Rules => {
|
||||
self.show_rules = !self.show_rules;
|
||||
w_button!(self, btn_rules).text = match self.show_rules {
|
||||
true => constants::RULES_BUTTON_ALT_TEXT.to_string(),
|
||||
false => constants::RULES_BUTTON_TEXT.to_string(),
|
||||
};
|
||||
}
|
||||
ButtonName::Generate => {
|
||||
if self.game_mode == GameMode::Custom {
|
||||
self.next_puzzle();
|
||||
}
|
||||
}
|
||||
},
|
||||
GameInteraction::GameMode(mode) => {
|
||||
self.game_mode = mode;
|
||||
for (mode, btn) in &mut self.game_mode_btns {
|
||||
if *mode == self.game_mode {
|
||||
btn.is_active = false;
|
||||
} else {
|
||||
btn.is_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
let is_custom_game_mode = self.game_mode == GameMode::Custom;
|
||||
self.generate_btn.is_active = is_custom_game_mode;
|
||||
w_counter!(self, counter_pieces).set_active(is_custom_game_mode);
|
||||
w_counter!(self, counter_moves).set_active(is_custom_game_mode);
|
||||
|
||||
// A change in game mode, immediately generate new puzzle
|
||||
if !is_custom_game_mode {
|
||||
self.next_puzzle();
|
||||
}
|
||||
}
|
||||
GameInteraction::Counter(counter_name, new_value) => match counter_name {
|
||||
CounterName::Pieces => {
|
||||
self.settings.num_pieces = new_value;
|
||||
}
|
||||
CounterName::Age => {
|
||||
self.settings.max_moves_per_piece = new_value;
|
||||
}
|
||||
},
|
||||
GameInteraction::ToggleDebug => self.settings.debug = !self.settings.debug,
|
||||
GameInteraction::HideRules => {
|
||||
if self.show_rules {
|
||||
play_sound_once(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
);
|
||||
self.show_rules = false;
|
||||
w_button!(self, btn_rules).text = constants::RULES_BUTTON_TEXT.to_string();
|
||||
}
|
||||
}
|
||||
GameInteraction::Quit => std::process::exit(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_user_input(&mut self) -> GameInteraction {
|
||||
let (mx, my) = mouse_position();
|
||||
let winput = WidgetInput {
|
||||
let winput = UserInteraction {
|
||||
mouse_pos: Circle::new(mx, my, 0.0),
|
||||
};
|
||||
|
||||
// CORE GAMEPLAY
|
||||
let (mouse_x, mouse_y) = mouse_position();
|
||||
let mouse_pos = Circle::new(mouse_x, mouse_y, 0.0);
|
||||
// WIDGETS
|
||||
let board_interaction = self
|
||||
.board
|
||||
.handle_input(mouse_pos, &self.resources, &self.settings);
|
||||
if board_interaction.interacted {
|
||||
let Some(board_state) = board_interaction.board_state else {
|
||||
return;
|
||||
};
|
||||
|
||||
match board_state {
|
||||
BoardState::Won => {
|
||||
self.next_btn.is_active = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return;
|
||||
.handle_input(&winput, &self.resources, &self.settings);
|
||||
if let Some(new_board_state) = board_interaction.new_board_state {
|
||||
return GameInteraction::Board(new_board_state);
|
||||
}
|
||||
|
||||
self.id_text_btn.handle_input(
|
||||
&self.resources.sound(&SoundKind::Click),
|
||||
if w_button!(self, btn_reset).handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
);
|
||||
|
||||
if self
|
||||
.reset_btn
|
||||
.handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
)
|
||||
.is_clicked
|
||||
{
|
||||
self.reset();
|
||||
return;
|
||||
) {
|
||||
return GameInteraction::ButtonRelease(ButtonName::Reset);
|
||||
}
|
||||
|
||||
if self
|
||||
.next_btn
|
||||
.handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
)
|
||||
.is_clicked
|
||||
{
|
||||
self.next_puzzle();
|
||||
return;
|
||||
if w_button!(self, btn_next).handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
) {
|
||||
return GameInteraction::ButtonRelease(ButtonName::Next);
|
||||
}
|
||||
|
||||
// PRESETS
|
||||
let mut game_mode_changed = false;
|
||||
for (mode, btn) in &mut self.game_mode_btns {
|
||||
if btn
|
||||
.handle_input(
|
||||
&self.resources.sound(&SoundKind::Mode),
|
||||
self.settings.volume,
|
||||
)
|
||||
.is_clicked
|
||||
{
|
||||
self.game_mode = *mode;
|
||||
game_mode_changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if game_mode_changed {
|
||||
for (mode, btn) in &mut self.game_mode_btns {
|
||||
if *mode == self.game_mode {
|
||||
btn.is_active = false;
|
||||
} else {
|
||||
btn.is_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
if self.game_mode != GameMode::Custom {
|
||||
self.generate_btn.is_active = false;
|
||||
self.pieces_counter.set_active(false);
|
||||
self.max_age_counter.set_active(false);
|
||||
self.next_puzzle();
|
||||
} else {
|
||||
self.generate_btn.is_active = true;
|
||||
self.pieces_counter.set_active(true);
|
||||
self.max_age_counter.set_active(true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if self
|
||||
.rules_btn
|
||||
.handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
if btn.handle_input(
|
||||
&self.resources.sound(&SoundKind::Mode),
|
||||
self.settings.volume,
|
||||
)
|
||||
.is_clicked
|
||||
{
|
||||
self.show_rules = !self.show_rules;
|
||||
self.rules_btn_text = match self.show_rules {
|
||||
true => constants::RULES_BUTTON_ALT_TEXT.to_string(),
|
||||
false => constants::RULES_BUTTON_TEXT.to_string(),
|
||||
};
|
||||
) {
|
||||
return GameInteraction::GameMode(*mode);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
if w_button!(self, btn_rules).handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
) {
|
||||
return GameInteraction::ButtonRelease(ButtonName::Rules);
|
||||
}
|
||||
|
||||
let pieces_counter_interaction =
|
||||
self.pieces_counter
|
||||
.handle_input(&winput, &self.resources, &self.settings);
|
||||
w_counter!(self, counter_pieces).handle_input(&winput, &self.resources, &self.settings);
|
||||
if pieces_counter_interaction.interacted {
|
||||
self.settings.num_pieces = pieces_counter_interaction.current;
|
||||
return;
|
||||
return GameInteraction::Counter(
|
||||
CounterName::Pieces,
|
||||
pieces_counter_interaction.current,
|
||||
);
|
||||
}
|
||||
|
||||
let moves_counter_interaction =
|
||||
self.max_age_counter
|
||||
.handle_input(&winput, &self.resources, &self.settings);
|
||||
if moves_counter_interaction.interacted {
|
||||
self.settings.max_moves_per_piece = moves_counter_interaction.current;
|
||||
return;
|
||||
let age_counter_interaction =
|
||||
w_counter!(self, counter_moves).handle_input(&winput, &self.resources, &self.settings);
|
||||
if age_counter_interaction.interacted {
|
||||
return GameInteraction::Counter(CounterName::Age, age_counter_interaction.current);
|
||||
}
|
||||
|
||||
if self
|
||||
.generate_btn
|
||||
.handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
)
|
||||
.is_clicked
|
||||
{
|
||||
if self.game_mode == GameMode::Custom {
|
||||
self.next_puzzle();
|
||||
}
|
||||
return;
|
||||
if self.generate_btn.handle_input(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
) {
|
||||
return GameInteraction::ButtonRelease(ButtonName::Generate);
|
||||
}
|
||||
|
||||
// KEY INPUTS
|
||||
if is_key_released(KeyCode::Escape) {
|
||||
if self.show_rules {
|
||||
play_sound_once(
|
||||
&self.resources.sound(&SoundKind::Button),
|
||||
self.settings.volume,
|
||||
);
|
||||
self.show_rules = false;
|
||||
self.rules_btn_text = constants::RULES_BUTTON_TEXT.to_string();
|
||||
}
|
||||
|
||||
return;
|
||||
return GameInteraction::HideRules;
|
||||
}
|
||||
|
||||
if is_key_released(KeyCode::D) {
|
||||
self.settings.debug = !self.settings.debug;
|
||||
return;
|
||||
return GameInteraction::ToggleDebug;
|
||||
}
|
||||
|
||||
if is_key_released(KeyCode::Q) {
|
||||
std::process::exit(0);
|
||||
return GameInteraction::Quit;
|
||||
}
|
||||
|
||||
return GameInteraction::NoInteraction;
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
@@ -241,7 +284,7 @@ impl Game {
|
||||
self.puzzle = Game::generate_puzzle(self.game_mode, &self.settings);
|
||||
}
|
||||
|
||||
self.next_btn.is_active = false;
|
||||
w_button!(self, btn_next).is_active = false;
|
||||
self.board.reset(&self.puzzle);
|
||||
}
|
||||
|
||||
@@ -278,6 +321,14 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_widget(widgets: &mut HashMap<WidgetId, Widget>, w: Widget) -> WidgetId {
|
||||
let id = rand::rand() as u64;
|
||||
let widget_id = WidgetId(id);
|
||||
widgets.insert(widget_id, w);
|
||||
|
||||
widget_id
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ResetOptions {
|
||||
create_new_puzzle: bool,
|
||||
|
||||
+5
-2
@@ -28,10 +28,13 @@ fn window_conf() -> Conf {
|
||||
async fn main() {
|
||||
rand::srand(date::now() as u64);
|
||||
let background_color = Color::from_rgba(196, 195, 208, 255);
|
||||
let mut game = Game::initialize_state().await;
|
||||
let mut game = Game::new().await;
|
||||
loop {
|
||||
clear_background(background_color);
|
||||
game.handle_input();
|
||||
|
||||
let interaction = game.handle_user_input();
|
||||
game.handle_interaction(interaction);
|
||||
|
||||
game.draw();
|
||||
next_frame().await
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ use macroquad::{
|
||||
};
|
||||
use quad_snd::PlaySoundParams;
|
||||
|
||||
pub struct WidgetInput {
|
||||
pub struct UserInteraction {
|
||||
pub mouse_pos: Circle,
|
||||
}
|
||||
|
||||
|
||||
+34
-12
@@ -2,7 +2,7 @@ use core::fmt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use crate::{
|
||||
game::{GameSettings, constants},
|
||||
game::{GameMode, GameSettings, constants},
|
||||
resources::{Resources, SoundKind},
|
||||
widgets::*,
|
||||
};
|
||||
@@ -22,9 +22,32 @@ pub struct BoardWidget {
|
||||
state: GameState,
|
||||
}
|
||||
|
||||
pub struct BoardInteraction<'a> {
|
||||
pub interacted: bool,
|
||||
pub board_state: Option<&'a BoardState>,
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ButtonName {
|
||||
Reset,
|
||||
Next,
|
||||
Rules,
|
||||
Generate,
|
||||
}
|
||||
|
||||
pub enum CounterName {
|
||||
Pieces,
|
||||
Age,
|
||||
}
|
||||
|
||||
pub enum GameInteraction {
|
||||
NoInteraction,
|
||||
Board(BoardState),
|
||||
ButtonRelease(ButtonName),
|
||||
GameMode(GameMode),
|
||||
Counter(CounterName, u32),
|
||||
ToggleDebug,
|
||||
HideRules,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub struct BoardInteraction {
|
||||
pub new_board_state: Option<BoardState>,
|
||||
}
|
||||
|
||||
impl BoardWidget {
|
||||
@@ -189,16 +212,16 @@ impl BoardWidget {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input<'a>(
|
||||
&'a mut self,
|
||||
mouse_pos: Circle,
|
||||
pub fn handle_input(
|
||||
&mut self,
|
||||
interaction: &UserInteraction,
|
||||
resources: &Resources,
|
||||
settings: &GameSettings,
|
||||
) -> BoardInteraction<'a> {
|
||||
) -> BoardInteraction {
|
||||
let mouse_pos = interaction.mouse_pos;
|
||||
if mouse_pos.overlaps_rect(&self.board_rect) == false {
|
||||
return BoardInteraction {
|
||||
interacted: false,
|
||||
board_state: None,
|
||||
new_board_state: None,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -239,8 +262,7 @@ impl BoardWidget {
|
||||
|
||||
let board_state = &self.current_board.game_state;
|
||||
return BoardInteraction {
|
||||
interacted: true,
|
||||
board_state: Some(board_state),
|
||||
new_board_state: Some(*board_state),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+17
-15
@@ -3,31 +3,31 @@ use macroquad::{audio::Sound, prelude::*};
|
||||
|
||||
pub struct ButtonWidget {
|
||||
pub is_active: bool,
|
||||
pub text: String,
|
||||
pub color: UiColor,
|
||||
is_down: bool,
|
||||
rect: Rect,
|
||||
shadow_width: f32,
|
||||
}
|
||||
|
||||
pub struct ButtonInteraction {
|
||||
pub is_clicked: bool,
|
||||
}
|
||||
|
||||
impl ButtonWidget {
|
||||
pub fn initialize_state(is_active: bool) -> Self {
|
||||
pub fn new(is_active: bool, color: UiColor, text: impl Into<String>) -> Self {
|
||||
Self {
|
||||
is_active,
|
||||
text: text.into(),
|
||||
color,
|
||||
shadow_width: 5.0,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize_drawables(&mut self, rect: Rect) {
|
||||
pub fn place(&mut self, rect: Rect) {
|
||||
self.rect = rect;
|
||||
self.shadow_width = 5.0;
|
||||
}
|
||||
|
||||
pub fn draw(&self, text: &str, color: &UiColor, resources: &Resources) {
|
||||
self.draw_button(color);
|
||||
self.draw_label(text, resources, color);
|
||||
pub fn draw(&self, resources: &Resources) {
|
||||
self.draw_button(&self.color);
|
||||
self.draw_label(&self.text, resources, &self.color);
|
||||
}
|
||||
|
||||
fn draw_button(&self, color: &UiColor) {
|
||||
@@ -105,10 +105,10 @@ impl ButtonWidget {
|
||||
button_pressed_correction
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self, click_sound: &Sound, volume: f32) -> ButtonInteraction {
|
||||
pub fn handle_input(&mut self, click_sound: &Sound, volume: f32) -> bool {
|
||||
if !self.is_active {
|
||||
self.is_down = false;
|
||||
return ButtonInteraction { is_clicked: false };
|
||||
return false;
|
||||
}
|
||||
|
||||
let (mx, my) = mouse_position();
|
||||
@@ -117,7 +117,7 @@ impl ButtonWidget {
|
||||
if is_mouse_button_pressed(MouseButton::Left) {
|
||||
if c.overlaps_rect(&self.rect) {
|
||||
self.is_down = true;
|
||||
return ButtonInteraction { is_clicked: false };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,13 +125,13 @@ impl ButtonWidget {
|
||||
if c.overlaps_rect(&self.rect) {
|
||||
play_sound_once(&click_sound, volume);
|
||||
self.is_down = false;
|
||||
return ButtonInteraction { is_clicked: true };
|
||||
return true;
|
||||
}
|
||||
|
||||
self.is_down = false;
|
||||
}
|
||||
|
||||
return ButtonInteraction { is_clicked: false };
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,8 @@ impl Default for ButtonWidget {
|
||||
is_down: Default::default(),
|
||||
rect: Default::default(),
|
||||
shadow_width: Default::default(),
|
||||
color: UiColor::Grey,
|
||||
text: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-11
@@ -26,12 +26,22 @@ pub struct CounterWidgetInteraction {
|
||||
}
|
||||
|
||||
impl CounterWidget {
|
||||
pub fn initialize_state(min: u32, max: u32, current: u32, is_active: bool) -> Self {
|
||||
pub fn new(
|
||||
min: u32,
|
||||
max: u32,
|
||||
current: u32,
|
||||
is_active: bool,
|
||||
color: UiColor,
|
||||
) -> Self {
|
||||
let increment = ButtonWidget::new(is_active, color, "+");
|
||||
let decrement = ButtonWidget::new(is_active, color, "-");
|
||||
Self {
|
||||
min,
|
||||
max,
|
||||
current,
|
||||
is_active,
|
||||
increment,
|
||||
decrement,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -59,12 +69,10 @@ impl CounterWidget {
|
||||
self.visual_rect = Rect::new(btn_x, visual_y, btn_w, btn_h);
|
||||
|
||||
let increment_rect = Rect::new(btn_x, increment_y, btn_w, btn_h);
|
||||
self.increment = ButtonWidget::initialize_state(self.is_active);
|
||||
self.increment.initialize_drawables(increment_rect);
|
||||
self.increment.place(increment_rect);
|
||||
|
||||
let decrement_rect = Rect::new(btn_x, decrement_y, btn_w, btn_h);
|
||||
self.decrement = ButtonWidget::initialize_state(self.is_active);
|
||||
self.decrement.initialize_drawables(decrement_rect);
|
||||
self.decrement.place(decrement_rect);
|
||||
|
||||
self.increment_dims = measure_text("+", Some(resources.font()), font_size as u16, 1.0);
|
||||
self.decrement_dims = measure_text("-", Some(resources.font()), font_size as u16, 1.0);
|
||||
@@ -75,7 +83,7 @@ impl CounterWidget {
|
||||
|
||||
pub fn handle_input(
|
||||
&mut self,
|
||||
_: &WidgetInput,
|
||||
_: &UserInteraction,
|
||||
resources: &Resources,
|
||||
settings: &GameSettings,
|
||||
) -> CounterWidgetInteraction {
|
||||
@@ -83,7 +91,6 @@ impl CounterWidget {
|
||||
if self
|
||||
.increment
|
||||
.handle_input(resources.sound(&SoundKind::Click), settings.volume)
|
||||
.is_clicked
|
||||
{
|
||||
if self.current < self.max {
|
||||
self.current += 1;
|
||||
@@ -94,7 +101,6 @@ impl CounterWidget {
|
||||
if self
|
||||
.decrement
|
||||
.handle_input(resources.sound(&SoundKind::Click), settings.volume)
|
||||
.is_clicked
|
||||
{
|
||||
if self.current > self.min {
|
||||
self.current -= 1;
|
||||
@@ -109,8 +115,8 @@ impl CounterWidget {
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, resources: &Resources) {
|
||||
self.increment.draw("+", &UiColor::Yellow, resources);
|
||||
self.decrement.draw("-", &UiColor::Yellow, resources);
|
||||
self.increment.draw(resources);
|
||||
self.decrement.draw(resources);
|
||||
|
||||
let font = resources.font();
|
||||
let mut min_dim = self.visual_rect.w.min(self.visual_rect.h);
|
||||
@@ -120,7 +126,12 @@ impl CounterWidget {
|
||||
break;
|
||||
}
|
||||
|
||||
let dims = measure_text(&self.current.to_string(), Some(&font), font_size as u16, 1.0);
|
||||
let dims = measure_text(
|
||||
&self.current.to_string(),
|
||||
Some(&font),
|
||||
font_size as u16,
|
||||
1.0,
|
||||
);
|
||||
min_dim = dims.width.min(dims.height);
|
||||
font_size -= 1;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ impl IdTextInput {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn handle_input(&self, _: &WidgetInput) -> IdTextInputInteraction {
|
||||
pub fn handle_input(&self, _: &UserInteraction) -> IdTextInputInteraction {
|
||||
IdTextInputInteraction {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ pub struct LabelWidget {
|
||||
rect: Rect,
|
||||
font_size: f32,
|
||||
text_dimensions: TextDimensions,
|
||||
text: String,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl LabelWidget {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ pub struct Board {
|
||||
max_moves_per_piece: u32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum BoardState {
|
||||
NotStarted,
|
||||
InProgress,
|
||||
|
||||
Reference in New Issue
Block a user