diff --git a/src/actor.rs b/src/actor.rs new file mode 100644 index 0000000..8fdc4b9 --- /dev/null +++ b/src/actor.rs @@ -0,0 +1,30 @@ +use ggez::graphics; + +#[path = "board.rs"] +mod board; + +struct BoardPosnStruct { + x: u8, + y: u8, +} + +pub enum ActorPosn { + BoardPosn { + x: u8, + y: u8, + }, + FloatingPosn { + //This probably needs some `previous position on board' fields + x: f32, + y: f32, + held_x: f32, + held_y: f32, + }, +} + +pub struct Actor { + pub image: graphics::Image, + + pub posn: ActorPosn, + pub behaviour: u8, //index into the behaviours array +} diff --git a/src/apr.rs b/src/apr.rs new file mode 100644 index 0000000..8d3e78a --- /dev/null +++ b/src/apr.rs @@ -0,0 +1,153 @@ +use ggez::event::EventHandler; +use ggez::graphics::{self, Color, DrawMode, Mesh, MeshBuilder}; +use ggez::input::mouse::{self, MouseButton}; +use ggez::{Context, GameResult}; + +#[path = "actor.rs"] +mod actor; +#[path = "behaviour.rs"] +mod behaviour; +#[path = "board.rs"] +mod board; + +pub struct Apr { + board: board::Board, + grid: Mesh, + should_update_grid: bool, + + actors: Vec, + behaviours: Vec, +} + +pub fn make_grid(ctx: &mut Context, r: u8, c: u8, board: &[Vec]) -> GameResult { + let mut builder = MeshBuilder::new(); + + for row in 0..r { + for col in 0..c { + builder.rectangle( + DrawMode::fill(), + graphics::Rect { + x: board::START_X + (board::BOARD_SQUARE_SIZE * col as f32), + y: board::START_Y + (board::BOARD_SQUARE_SIZE * row as f32), + w: board::BOARD_SQUARE_SIZE, + h: board::BOARD_SQUARE_SIZE, + }, + match board[row as usize][col as usize] % 2 { + 0 => Color::WHITE, + 1 => Color::BLACK, + _ => Color::BLACK, //impossible + /* 2 => Color::RED, + 3 => Color::CYAN, + 4 => Color::YELLOW, + 5 => Color::GREEN, + 6 => Color::BLUE, + 7 => Color::MAGENTA, + _ => Color::BLACK,*/ + }, + )?; + } + } + + builder.build(ctx) +} + +impl Apr { + pub fn new(_ctx: &mut Context, r: u8, c: u8) -> GameResult { + // Load/create resources such as images here. + let board = board::make_board(r, c); + Ok(Apr { + grid: make_grid(_ctx, r, c, board.board.as_slice())?, + board: board, + should_update_grid: false, + + actors: vec![actor::Actor { + image: graphics::Image::new(_ctx, "/pawn.png")?, + posn: actor::ActorPosn::BoardPosn { x: 0, y: 0 }, + behaviour: 0, + }], + behaviours: vec![behaviour::Behaviour { + moves: vec![(0, 1)], + }], //the pawn can go ONE forwards + }) + } + + pub fn set_colour(&mut self, r: usize, c: usize, col: u8) { + //You should only modify self.board through this method. + self.board.board[r][c] = col; //Making a getter for self.board would sadly break borrowing in the same way that closest_square_coords does. + self.should_update_grid = true; //Similar solutions apply. + } +} + +impl EventHandler for Apr { + fn update(&mut self, ctx: &mut Context) -> GameResult<()> { + let posn = mouse::position(ctx); + for actor in self.actors.iter_mut() { + match actor.posn { + actor::ActorPosn::FloatingPosn { + x: _, + y: _, + held_x, + held_y, + } => { + if !mouse::button_pressed(ctx, MouseButton::Left) { + // no long clicking and dragging + let (x, y) = self.board.closest_square_coords(posn.x, posn.y); + actor.posn = actor::ActorPosn::BoardPosn { x, y }; + } else { + actor.posn = actor::ActorPosn::FloatingPosn { + x: posn.x + held_x, + y: posn.y + held_y, + held_x, + held_y, + }; + } + } + actor::ActorPosn::BoardPosn { x, y } => { + if mouse::button_pressed(ctx, MouseButton::Left) { + let (posx, posy) = + self.board.closest_square_coords(posn.x, posn.y); + if posx == x && posy == y { + let x = board::START_X + board::BOARD_SQUARE_SIZE * (x as f32); + let y = board::START_Y + board::BOARD_SQUARE_SIZE * (y as f32); + actor.posn = actor::ActorPosn::FloatingPosn { + x, + y, + held_x: x - posn.x, + held_y: y - posn.y, + }; + } + } + } + } + } + Ok(()) + } + + fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { + graphics::clear(ctx, Color::new(0.5, 0.5, 0.5, 1.0)); + let drawparams = graphics::DrawParam::new(); + + if self.should_update_grid { + self.grid = make_grid(ctx, self.board.row, self.board.col, self.board.board.as_slice())?; + self.should_update_grid = false; + } + graphics::draw(ctx, &self.grid, drawparams)?; + for actor in &self.actors { + match actor.posn { + actor::ActorPosn::FloatingPosn { x, y, .. } => { + graphics::draw(ctx, &actor.image, drawparams.dest([x, y]))? + } + actor::ActorPosn::BoardPosn { x, y } => graphics::draw( + ctx, + &actor.image, + drawparams.dest([ + board::START_X + board::BOARD_SQUARE_SIZE * (x as f32), + board::START_Y + board::BOARD_SQUARE_SIZE * (y as f32), + ]), + )?, + } + } + // Draw code here... + graphics::present(ctx) + } +} diff --git a/src/behaviour.rs b/src/behaviour.rs new file mode 100644 index 0000000..571af69 --- /dev/null +++ b/src/behaviour.rs @@ -0,0 +1,3 @@ +pub struct Behaviour { + pub moves: Vec<(u8, u8)>, //this is a placeholder +} diff --git a/src/board.rs b/src/board.rs new file mode 100644 index 0000000..03a9873 --- /dev/null +++ b/src/board.rs @@ -0,0 +1,47 @@ +pub const START_X: f32 = 20.0; +pub const START_Y: f32 = 20.0; +pub const BOARD_SQUARE_SIZE: f32 = 20.0; + +pub struct Board { + pub board: Vec>, + pub row: u8, + pub col: u8, +} + +pub fn make_board(r: u8, c: u8) -> Board { + let mut init: Vec> = vec![vec![0u8; r as usize]; c as usize]; + for (row, rowa) in init.iter_mut().enumerate() { + for (col, item) in rowa.iter_mut().enumerate() { + *item = (row + col) as u8 % 8; + } + } + Board { + board: init, + row: r, + col: c, + } +} + +impl Board { + pub fn closest_square_coords(&mut self, x: f32, y: f32) -> (u8, u8) { + //First, normalise to within the grid. + let mut x = x; + x = x.max(START_X); + x = x.min(START_X + (BOARD_SQUARE_SIZE * self.col as f32) - 1.0); + + let mut y = y; + y = y.max(START_Y); + y = y.min(START_Y + (BOARD_SQUARE_SIZE * self.row as f32) - 1.0); + + //Then, remove the start x and y + x -= START_X; + y -= START_Y; + + //Then, snap to board coords. + + let x = (x / BOARD_SQUARE_SIZE) as u8; + let y = (y / BOARD_SQUARE_SIZE) as u8; + + (x, y) + } +} diff --git a/src/main.rs b/src/main.rs index c697974..bece96b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,220 +1,10 @@ -use ggez::event::{self, EventHandler}; -use ggez::graphics::{self, Color, DrawMode, Mesh, MeshBuilder}; -use ggez::input::mouse::{self, MouseButton}; -use ggez::{Context, ContextBuilder, GameResult}; +use ggez::event; +use ggez::{ContextBuilder, GameResult}; use std::env; use std::path; -const START_X: f32 = 20.0; -const START_Y: f32 = 20.0; -const BOARD_SQUARE_SIZE: f32 = 20.0; - -struct Apr { - row: i32, - col: i32, - board: Vec>, - grid: Mesh, - should_update_grid: bool, - - actors: Vec, - behaviours: Vec, -} - -struct Behaviour { - moves: Vec<(u8, u8)>, //this is a placeholder -} - -enum ActorPosn { - Board { - x: u8, - y: u8, - }, - Floating { //This probably needs some `previous position on board' fields - x: f32, - y: f32, - held_x: f32, - held_y: f32, - }, -} - -use crate::ActorPosn::{Board, Floating}; - -struct Actor { - image: graphics::Image, - - posn: ActorPosn, - behaviour: u8, //index into the behaviours array -} - -fn make_grid(ctx: &mut Context, r: i32, c: i32, board: &[Vec]) -> GameResult { - let mut builder = MeshBuilder::new(); - - for row in 0..r { - for col in 0..c { - builder.rectangle( - DrawMode::fill(), - graphics::Rect { - x: START_X + (BOARD_SQUARE_SIZE * col as f32), - y: START_Y + (BOARD_SQUARE_SIZE * row as f32), - w: BOARD_SQUARE_SIZE, - h: BOARD_SQUARE_SIZE, - }, - match board[row as usize][col as usize] % 2 { - 0 => Color::WHITE, - 1 => Color::BLACK, - _ => Color::BLACK //impossible -/* 2 => Color::RED, - 3 => Color::CYAN, - 4 => Color::YELLOW, - 5 => Color::GREEN, - 6 => Color::BLUE, - 7 => Color::MAGENTA, - _ => Color::BLACK,*/ - }, - )?; - } - } - - builder.build(ctx) -} - -fn make_board(r: i32, c: i32) -> Vec> { - let mut init: Vec> = vec![vec![0u8; r as usize]; c as usize]; - for (row, rowa) in init.iter_mut().enumerate() { - for (col, item) in rowa.iter_mut().enumerate() { - *item = (row + col) as u8 % 8; - } - } - init -} - -//I have moved this function out of the impl block for borrow checking reasons. -//This should really only borrow self.col and self.row, but as a method it borrows everything. -//As a result, I couldn't use this while iterating through actors. -//Possible solution: Wrap `col' and `row' up as some kind of Dimensions struct, then make this a method on that struct. -//Would then only borrow the dimensions. -//http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/ - -fn closest_square_coords(x: f32, y: f32, col: i32, row: i32) -> (u8, u8) { - //First, normalise to within the grid. - let mut x = x; - x = x.max(START_X); - x = x.min(START_X + (BOARD_SQUARE_SIZE * col as f32) - 1.0); - - let mut y = y; - y = y.max(START_Y); - y = y.min(START_Y + (BOARD_SQUARE_SIZE * row as f32) - 1.0); - - //Then, remove the start x and y - x -= START_X; - y -= START_Y; - - //Then, snap to board coords. - - let x = (x / BOARD_SQUARE_SIZE) as u8; - let y = (y / BOARD_SQUARE_SIZE) as u8; - - (x, y) -} -impl Apr { - pub fn new(_ctx: &mut Context, r: i32, c: i32) -> GameResult { - // Load/create resources such as images here. - let board = make_board(r, c); - Ok(Apr { - row: r, - col: c, - grid: make_grid(_ctx, r, c, board.as_slice())?, - board, - should_update_grid: false, - - actors: vec![Actor { - image: graphics::Image::new(_ctx, "/pawn.png")?, - posn: Board { x: 0, y: 0 }, - behaviour: 0, - }], - behaviours: vec![Behaviour { - moves: vec![(0, 1)], - }], //the pawn can go ONE forwards - }) - } - - fn set_colour(&mut self, r: usize, c: usize, col: u8) { //You should only modify self.board through this method. - self.board[r][c] = col; //Making a getter for self.board would sadly break borrowing in the same way that closest_square_coords does. - self.should_update_grid = true; //Similar solutions apply. - } -} -impl EventHandler for Apr { - fn update(&mut self, ctx: &mut Context) -> GameResult<()> { - let posn = mouse::position(ctx); - for actor in self.actors.iter_mut() { - match actor.posn { - Floating { - x: _, - y: _, - held_x, - held_y, - } => { - if !mouse::button_pressed(ctx, MouseButton::Left) { - // no long clicking and dragging - let (x, y) = closest_square_coords(posn.x, posn.y, self.col, self.row); - actor.posn = Board { x, y }; - } else { - actor.posn = Floating { - x: posn.x + held_x, - y: posn.y + held_y, - held_x, - held_y, - }; - } - } - Board { x, y } => { - if mouse::button_pressed(ctx, MouseButton::Left) { - let (posx, posy) = - closest_square_coords(posn.x, posn.y, self.col, self.row); - if posx == x && posy == y { - let x = START_X + BOARD_SQUARE_SIZE * (x as f32); - let y = START_Y + BOARD_SQUARE_SIZE * (y as f32); - actor.posn = Floating { - x, - y, - held_x: x - posn.x, - held_y: y - posn.y, - }; - } - } - } - } - } - Ok(()) - } - - fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { - graphics::clear(ctx, Color::new(0.5, 0.5, 0.5, 1.0)); - let drawparams = graphics::DrawParam::new(); - - if self.should_update_grid { - self.grid = make_grid(ctx, self.row, self.col, self.board.as_slice())?; - self.should_update_grid = false; - } - graphics::draw(ctx, &self.grid, drawparams)?; - for actor in &self.actors { - match actor.posn { - Floating { x, y, .. } => graphics::draw(ctx, &actor.image, drawparams.dest([x, y]))?, - Board { x, y } => graphics::draw( - ctx, - &actor.image, - drawparams.dest([ - START_X + BOARD_SQUARE_SIZE * (x as f32), - START_Y + BOARD_SQUARE_SIZE * (y as f32), - ]), - )?, - } - } - // Draw code here... - graphics::present(ctx) - } -} +mod apr; fn main() -> GameResult<()> { let mut cb = ContextBuilder::new("apruebo", "s1m7u and e-dt"); @@ -232,7 +22,7 @@ fn main() -> GameResult<()> { // Create an instance of your event handler. // Usually, you should provide it with the Context object to // use when setting your game up. - let apr = Apr::new(&mut ctx, 8, 8)?; + let apr = apr::Apr::new(&mut ctx, 8, 8)?; // Run! event::run(ctx, event_loop, apr);