From 3cb08bf08edde5def079714a8529989324987dad Mon Sep 17 00:00:00 2001 From: cool-mist Date: Sat, 14 Mar 2026 00:41:33 +0530 Subject: [PATCH] Major refactor --- gui/src/game.rs | 93 ++++++-- gui/src/game/draw.rs | 177 +++++---------- gui/src/game/logic.rs | 357 ++++++++++++++++++------------- gui/src/main.rs | 7 +- gui/src/widgets.rs | 2 +- gui/src/widgets/board.rs | 46 ++-- gui/src/widgets/button.rs | 32 +-- gui/src/widgets/counter.rs | 33 ++- gui/src/widgets/id_text_input.rs | 2 +- gui/src/widgets/label.rs | 2 +- lib/src/board.rs | 2 +- 11 files changed, 413 insertions(+), 340 deletions(-) diff --git a/gui/src/game.rs b/gui/src/game.rs index 7ef6a52..5cb264c 100644 --- a/gui/src/game.rs +++ b/gui/src/game.rs @@ -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, - pieces_label: LabelWidget, - max_age_label: LabelWidget, - pieces_counter: CounterWidget, - max_age_counter: CounterWidget, - generate_btn: ButtonWidget, + widgets: HashMap, + + 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, 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, diff --git a/gui/src/game/draw.rs b/gui/src/game/draw.rs index 9a08fdb..f716158 100644 --- a/gui/src/game/draw.rs +++ b/gui/src/game/draw.rs @@ -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); } } diff --git a/gui/src/game/logic.rs b/gui/src/game/logic.rs index ad75cc1..d6b72fe 100644 --- a/gui/src/game/logic.rs +++ b/gui/src/game/logic.rs @@ -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, 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, diff --git a/gui/src/main.rs b/gui/src/main.rs index a0bedac..c34156f 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -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 } diff --git a/gui/src/widgets.rs b/gui/src/widgets.rs index 3fa3f6e..9be2d0e 100644 --- a/gui/src/widgets.rs +++ b/gui/src/widgets.rs @@ -18,7 +18,7 @@ use macroquad::{ }; use quad_snd::PlaySoundParams; -pub struct WidgetInput { +pub struct UserInteraction { pub mouse_pos: Circle, } diff --git a/gui/src/widgets/board.rs b/gui/src/widgets/board.rs index 26cc99e..2e23980 100644 --- a/gui/src/widgets/board.rs +++ b/gui/src/widgets/board.rs @@ -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, } 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), }; } diff --git a/gui/src/widgets/button.rs b/gui/src/widgets/button.rs index 2bb2031..63a6228 100644 --- a/gui/src/widgets/button.rs +++ b/gui/src/widgets/button.rs @@ -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) -> 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(), } } } diff --git a/gui/src/widgets/counter.rs b/gui/src/widgets/counter.rs index f52d938..d421a16 100644 --- a/gui/src/widgets/counter.rs +++ b/gui/src/widgets/counter.rs @@ -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; } diff --git a/gui/src/widgets/id_text_input.rs b/gui/src/widgets/id_text_input.rs index 1b06818..effa5e8 100644 --- a/gui/src/widgets/id_text_input.rs +++ b/gui/src/widgets/id_text_input.rs @@ -65,7 +65,7 @@ impl IdTextInput { ); } - pub fn handle_input(&self, _: &WidgetInput) -> IdTextInputInteraction { + pub fn handle_input(&self, _: &UserInteraction) -> IdTextInputInteraction { IdTextInputInteraction {} } } diff --git a/gui/src/widgets/label.rs b/gui/src/widgets/label.rs index 428e7cf..7223350 100644 --- a/gui/src/widgets/label.rs +++ b/gui/src/widgets/label.rs @@ -7,7 +7,7 @@ pub struct LabelWidget { rect: Rect, font_size: f32, text_dimensions: TextDimensions, - text: String, + pub text: String, } impl LabelWidget { diff --git a/lib/src/board.rs b/lib/src/board.rs index 78146e4..87aeff1 100644 --- a/lib/src/board.rs +++ b/lib/src/board.rs @@ -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,