mirror of
https://github.com/szymon-jozef/checkers.git
synced 2026-10-27 19:20:44 +01:00
221 lines
5.3 KiB
Rust
221 lines
5.3 KiB
Rust
use std::ops::{Index, IndexMut};
|
|
|
|
use macroquad::{
|
|
color::{BLACK, Color, GREEN, ORANGE, RED, WHITE},
|
|
input::{is_mouse_button_pressed, mouse_position},
|
|
math::{Circle, Rect, vec2},
|
|
miniquad::window::high_dpi,
|
|
shapes::{draw_circle, draw_circle_lines, draw_rectangle, draw_rectangle_ex},
|
|
};
|
|
use uuid::Uuid;
|
|
|
|
use crate::logic::{
|
|
board::{
|
|
board_view::BoardView,
|
|
field::Field,
|
|
pawn::{CapturePath, MovePath},
|
|
},
|
|
math::position::Position,
|
|
};
|
|
|
|
const MOVE_HIGHLITGHT_COLOR: Color = GREEN;
|
|
const CAPTURE_HIGHLITGHT_COLOR: Color = ORANGE;
|
|
|
|
#[derive(Default)]
|
|
struct GuiField {
|
|
rect: Rect,
|
|
field: Field,
|
|
pos: Position,
|
|
is_clickable: bool,
|
|
color: Color,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct GuiFieldVec {
|
|
gui_fields: Vec<GuiField>,
|
|
}
|
|
|
|
impl GuiFieldVec {
|
|
pub fn push(&mut self, input: GuiField) {
|
|
self.gui_fields.push(input);
|
|
}
|
|
|
|
pub fn clear(&mut self) {
|
|
self.gui_fields.clear();
|
|
}
|
|
|
|
pub fn size(&self) -> usize {
|
|
self.gui_fields.len()
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a GuiFieldVec {
|
|
type Item = &'a GuiField;
|
|
type IntoIter = std::slice::Iter<'a, GuiField>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
self.gui_fields.iter()
|
|
}
|
|
}
|
|
|
|
impl Index<Position> for GuiFieldVec {
|
|
type Output = GuiField;
|
|
|
|
fn index(&self, index: Position) -> &Self::Output {
|
|
self.gui_fields
|
|
.iter()
|
|
.find(|field| field.pos == index)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
impl IndexMut<Position> for GuiFieldVec {
|
|
fn index_mut(&mut self, index: Position) -> &mut Self::Output {
|
|
self.gui_fields
|
|
.iter_mut()
|
|
.find(|field| field.pos == index)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
pub struct Board {
|
|
rect: Rect,
|
|
|
|
board_view: BoardView,
|
|
fields: GuiFieldVec,
|
|
|
|
my_id: Uuid,
|
|
|
|
available_captures: Option<Vec<CapturePath>>,
|
|
available_moves: Option<Vec<MovePath>>,
|
|
}
|
|
|
|
impl Default for Board {
|
|
fn default() -> Self {
|
|
Self::new(Rect::default(), Uuid::default())
|
|
}
|
|
}
|
|
|
|
impl Board {
|
|
pub fn new(rect: Rect, player_id: Uuid) -> Self {
|
|
Self {
|
|
rect,
|
|
|
|
board_view: BoardView::default(),
|
|
fields: GuiFieldVec::default(),
|
|
|
|
my_id: player_id,
|
|
|
|
available_captures: None,
|
|
available_moves: None,
|
|
}
|
|
}
|
|
|
|
pub fn update_board_view(&mut self, board_view: BoardView) {
|
|
self.board_view = board_view;
|
|
}
|
|
|
|
pub fn update_available_captures(&mut self, captures: Vec<CapturePath>) {
|
|
self.available_captures = Some(captures);
|
|
self.available_moves = None;
|
|
}
|
|
|
|
pub fn update_available_moves(&mut self, moves: Vec<MovePath>) {
|
|
self.available_moves = Some(moves);
|
|
self.available_captures = None;
|
|
self.highlight_current_moves();
|
|
}
|
|
|
|
pub fn update_board_rect(&mut self, rect: Rect) -> bool {
|
|
if self.rect != rect {
|
|
self.rect = rect;
|
|
return true;
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn update_state(&mut self) {
|
|
self.fields.clear();
|
|
|
|
let field_size = self.rect.w / self.board_view.size as f32;
|
|
|
|
for field in &self.board_view {
|
|
let pos: Position = field.position;
|
|
|
|
let is_row_even: bool = pos.row % 2 == 0;
|
|
let is_column_even: bool = pos.column % 2 == 0;
|
|
let is_field_black: bool = is_row_even ^ is_column_even;
|
|
|
|
let abs_x = self.rect.x + pos.column as f32 * field_size;
|
|
let abs_y = self.rect.y + pos.row as f32 * field_size;
|
|
|
|
let rect = Rect {
|
|
x: abs_x,
|
|
y: abs_y,
|
|
w: field_size,
|
|
h: field_size,
|
|
};
|
|
|
|
self.fields.push(GuiField {
|
|
rect,
|
|
field: field.clone(),
|
|
is_clickable: false,
|
|
pos,
|
|
color: if is_field_black { BLACK } else { WHITE },
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn draw(&self) {
|
|
for field in &self.fields {
|
|
draw_rectangle(
|
|
field.rect.x,
|
|
field.rect.y,
|
|
field.rect.w,
|
|
field.rect.h,
|
|
field.color,
|
|
);
|
|
|
|
if field.is_clickable {
|
|
draw_rectangle(
|
|
field.rect.x,
|
|
field.rect.y,
|
|
field.rect.w,
|
|
field.rect.h,
|
|
GREEN,
|
|
);
|
|
}
|
|
|
|
if let Some(pawn) = &field.field.pawn {
|
|
let color = if pawn.owner == self.my_id { WHITE } else { RED }; // TODO! This should
|
|
// be customizable
|
|
|
|
let center = field.rect.center();
|
|
|
|
let r = field.rect.w * 0.5;
|
|
let thickness = 0.5;
|
|
|
|
draw_circle(center.x, center.y, r, color);
|
|
draw_circle_lines(center.x, center.y, r, thickness, BLACK);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn highlight_current_moves(&mut self) {
|
|
let mut from: Vec<Position> = vec![];
|
|
|
|
let Some(available_moves) = &self.available_moves else {
|
|
return;
|
|
};
|
|
|
|
for available_move in available_moves {
|
|
from.push(available_move.from);
|
|
}
|
|
|
|
for pos in from {
|
|
self.fields[pos].color = MOVE_HIGHLITGHT_COLOR;
|
|
}
|
|
}
|
|
}
|