From 3168f8e85760519db4706237c0256aa6897f1478 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Fri, 29 Jan 2021 23:17:10 -0500 Subject: [PATCH 01/59] multiple colors in singleplayer --- src/game.rs | 128 +++++++++++++++++++++++++++++++++------------- src/game/board.rs | 20 +++++--- src/game/tile.rs | 7 ++- 3 files changed, 111 insertions(+), 44 deletions(-) diff --git a/src/game.rs b/src/game.rs index 4176334..9110a5f 100644 --- a/src/game.rs +++ b/src/game.rs @@ -205,17 +205,18 @@ impl Game { } else { vec_gamepad_id_map_to_player = Vec::with_capacity(1); } + // for 1 player we have 3 sprite batches for player pieces because we have different color pieces let mut vec_batch_player_piece: Vec = - Vec::with_capacity(game_options.num_players as usize); + Vec::with_capacity(std::cmp::max(game_options.num_players as usize, 3)); let mut vec_batch_next_piece: Vec = - Vec::with_capacity(game_options.num_players as usize); - for player in 0..game_options.num_players { + Vec::with_capacity(std::cmp::max(game_options.num_players as usize, 3)); + for player in 0..std::cmp::max(game_options.num_players as usize, 3) { vec_next_piece.push(NextPiece::new(Shapes::None)); vec_batch_player_piece.push(spritebatch::SpriteBatch::new( - TileGraphic::new_player(ctx, player).image, + TileGraphic::new_player(ctx, player as u8).image, )); vec_batch_next_piece.push(spritebatch::SpriteBatch::new( - TileGraphic::new_player(ctx, player).image, + TileGraphic::new_player(ctx, player as u8).image, )); } let mut game_info_text = Text::new( @@ -650,32 +651,69 @@ impl Game { for x in 0..self.board.width { for y in 0..self.board.height { if !self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].empty { - let player = self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize] - [x as usize] - .player; let player_tile = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); - self.vec_batch_player_piece[player as usize].add(player_tile); + if self.num_players > 1 { + let player = self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize] + [x as usize] + .player; + self.vec_batch_player_piece[player as usize].add(player_tile); + } else { + if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::J || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::S { + self.vec_batch_player_piece[0].add(player_tile); + } else if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::L || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::Z { + self.vec_batch_player_piece[1].add(player_tile); + } else if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::I || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::O || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::T { + self.vec_batch_player_piece[2].add(player_tile); + } + } } } } // next pieces + let mut color_number_singleplayer = 2; + let next_piece = self.vec_next_piece[0].shape; + if next_piece == Shapes::J || next_piece == Shapes::S { + color_number_singleplayer = 0; + } else if next_piece == Shapes::L || next_piece == Shapes::Z { + color_number_singleplayer = 1; + } for player in &mut self.vec_players { if player.redraw_next_piece_flag { - player.redraw_next_piece_flag = false; // if we need to redraw, clear the next piece sprite batch and rebuild it - self.vec_batch_next_piece[player.player_num as usize].clear(); - for x in 0..4 { - for y in 0..2 { - if self.vec_next_piece[player.player_num as usize].matrix[y][x] { - let next_tile = graphics::DrawParam::new().dest(Point2::new( - x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - )); - self.vec_batch_next_piece[player.player_num as usize] - .add(next_tile); + player.redraw_next_piece_flag = false; + if self.num_players > 1 { + self.vec_batch_next_piece[player.player_num as usize].clear(); + for x in 0u8..4u8 { + for y in 0u8..2u8 { + if self.vec_next_piece[player.player_num as usize].matrix[y as usize][x as usize] { + let next_tile = graphics::DrawParam::new().dest(Point2::new( + x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + )); + self.vec_batch_next_piece[player.player_num as usize] + .add(next_tile); + } + } + } + } else { + println!("clearing next piece spritebatches"); + for x in 0..3 { + self.vec_batch_next_piece[x].clear(); + } + for x in 0u8..4u8 { + for y in 0u8..2u8 { + if self.vec_next_piece[player.player_num as usize].matrix[y as usize][x as usize] { + println!("adding next piece tile at position x = {}, y = {}", x, y); + let next_tile = graphics::DrawParam::new().dest(Point2::new( + x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + )); + self.vec_batch_next_piece[color_number_singleplayer] + .add(next_tile); + } } } } @@ -703,7 +741,7 @@ impl Game { ) .unwrap(); // player tiles - for player in 0..self.num_players { + for player in 0..std::cmp::max(self.num_players, 3) { graphics::draw( ctx, &self.vec_batch_player_piece[player as usize], @@ -718,20 +756,38 @@ impl Game { } // next piece tiles for player in self.vec_players.iter() { - graphics::draw( - ctx, - &self.vec_batch_next_piece[player.player_num as usize], - DrawParam::new() - .dest(Point2::new( - board_top_left_corner - + (player.spawn_column - 2) as f32 - * scaled_tile_size - * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - (NON_BOARD_SPACE_U - BOARD_NEXT_PIECE_SPACING) as f32 * self.tile_size, - )) - .scale(Vector2::new(scaled_tile_size, scaled_tile_size)), - ) - .unwrap(); + if self.num_players > 1 { + graphics::draw( + ctx, + &self.vec_batch_next_piece[player.player_num as usize], + DrawParam::new() + .dest(Point2::new( + board_top_left_corner + + (player.spawn_column - 2) as f32 + * scaled_tile_size + * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + (NON_BOARD_SPACE_U - BOARD_NEXT_PIECE_SPACING) as f32 * self.tile_size, + )) + .scale(Vector2::new(scaled_tile_size, scaled_tile_size)), + ) + .unwrap(); + } else { + let spawn_column = player.spawn_column; + graphics::draw( + ctx, + &self.vec_batch_next_piece[color_number_singleplayer], + DrawParam::new() + .dest(Point2::new( + board_top_left_corner + + (spawn_column - 2) as f32 + * scaled_tile_size + * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + (NON_BOARD_SPACE_U - BOARD_NEXT_PIECE_SPACING) as f32 * self.tile_size, + )) + .scale(Vector2::new(scaled_tile_size, scaled_tile_size)), + ) + .unwrap(); + } } // score text; TODO: perhaps make a separate function for something based on the bottom, or just figure out how to do this better so we don't divide out by the window_height self.draw_text( @@ -742,7 +798,7 @@ impl Game { ); // clear player sprite batches - for player in 0..self.num_players { + for player in 0..std::cmp::max(self.num_players, 3) { self.vec_batch_player_piece[player as usize].clear(); } } diff --git a/src/game/board.rs b/src/game/board.rs index d2f2676..21e2a8e 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -45,7 +45,8 @@ impl Board { .take(4) { if position != &(0xffu8, 0xffu8) { - self.matrix[position.0 as usize][position.1 as usize] = Tile::new_empty(); + self.matrix[position.0 as usize][position.1 as usize].empty = true; + self.matrix[position.0 as usize][position.1 as usize].active = false; } else { println!("[!] tried to emptify piece that contained position (0xffu8, 0xffu8)"); } @@ -59,8 +60,7 @@ impl Board { .take(4) { if position != &(0xffu8, 0xffu8) { - self.matrix[position.0 as usize][position.1 as usize] = - Tile::new(false, true, player); + self.matrix[position.0 as usize][position.1 as usize] = Tile::new(false, true, player, self.vec_active_piece[player as usize].shape); } else { println!("[!] tried to playerify piece that contained position (0xffu8, 0xffu8)"); } @@ -90,6 +90,11 @@ impl Board { } self.vec_active_piece[player as usize] = new_piece; self.vec_active_piece[player as usize].positions = spawn_positions; + // initialize the tile logic for the newly spawned piece + for position in spawn_positions.iter().take(4) { + self.matrix[position.0 as usize][position.1 as usize] = Tile::new(false, true, player, spawn_piece_shape); + } + (false, false) } @@ -189,7 +194,8 @@ impl Board { .iter() .take(4) { - self.matrix[position.0 as usize][position.1 as usize] = Tile::new(false, false, player); + self.matrix[position.0 as usize][position.1 as usize].empty = false; + self.matrix[position.0 as usize][position.1 as usize].active = false; } let mut y_vals: Vec = vec![self.vec_active_piece[player as usize].positions[0].0]; @@ -383,7 +389,7 @@ mod tests { for y in (board_height + BOARD_HEIGHT_BUFFER_U - 8)..board_height + BOARD_HEIGHT_BUFFER_U { - board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8); + board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } } @@ -426,7 +432,7 @@ mod tests { for y in (board_height + BOARD_HEIGHT_BUFFER_U - 4)..board_height + BOARD_HEIGHT_BUFFER_U { - board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8); + board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } } @@ -478,7 +484,7 @@ mod tests { for y in (board_height + BOARD_HEIGHT_BUFFER_U - 8)..board_height + BOARD_HEIGHT_BUFFER_U { - board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8); + board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } } diff --git a/src/game/tile.rs b/src/game/tile.rs index 604155e..4d35ea2 100644 --- a/src/game/tile.rs +++ b/src/game/tile.rs @@ -1,5 +1,7 @@ use ggez::{graphics, Context}; +use crate::game::Shapes; + pub const NUM_PIXEL_ROWS_PER_TILEGRAPHIC: u16 = 8u16; const DARK_GRAY: (u8, u8, u8, u8) = (20u8, 20u8, 20u8, 0xffu8); @@ -51,14 +53,16 @@ pub struct Tile { pub empty: bool, pub active: bool, pub player: u8, + pub shape: Shapes, } impl Tile { - pub fn new(empty: bool, active: bool, player: u8) -> Self { + pub fn new(empty: bool, active: bool, player: u8, shape: Shapes) -> Self { Self { empty, active, player, + shape, } } @@ -67,6 +71,7 @@ impl Tile { empty: true, active: false, player: 0xffu8, + shape: Shapes::None, } } } From cf109e2e17336305569e738308567cfe6c82c4ea Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 00:53:06 -0500 Subject: [PATCH 02/59] just some prep --- src/game.rs | 29 +++++++++++++++++++++----- src/game/board.rs | 52 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/game.rs b/src/game.rs index 9110a5f..97cff83 100644 --- a/src/game.rs +++ b/src/game.rs @@ -27,6 +27,8 @@ use crate::menu::MenuGameOptions; const BOARD_HEIGHT: u8 = 20u8; +const ROTATRIS_BOARD_SIDE_LENGTH: u8 = 13u8; + pub const CLEAR_DELAY: i8 = 30i8; pub const SCORE_SINGLE_BASE: u8 = 40u8; @@ -69,6 +71,17 @@ pub const INITIAL_HANG_FRAMES: u8 = 180; pub const DETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.5; pub const UNDETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.2; +pub enum Modes { + Classic, + Rotatris, +} + +pub enum InvisibilityLevels { + None, + Near, + All, +} + // this struct is for the Menu class so that it can return what game options to start the game with pub struct GameOptions { pub num_players: u8, @@ -140,7 +153,15 @@ pub struct Game { impl Game { pub fn new(ctx: &mut Context, game_options: &GameOptions) -> Game { - let board_width = 6 + 4 * game_options.num_players; + let mode = Modes::Rotatris; + let board_width = match mode { + Modes::Classic => 6 + 4 * game_options.num_players, + Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, + }; + let board_height = match mode { + Modes::Classic => BOARD_HEIGHT, + Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, + }; let mut vec_players: Vec = Vec::with_capacity(game_options.num_players as usize); // spawn columns // first half, not including middle player if there's an odd number of players @@ -179,7 +200,7 @@ impl Game { let mut batch_empty_tile = spritebatch::SpriteBatch::new(TileGraphic::new_empty(ctx).image); // the emtpy tile batch will be constant once the game starts with the player tile batches drawing on top of it, so just set that up here for x in 0..board_width { - for y in 0..BOARD_HEIGHT as usize { + for y in 0..board_height as usize { // empty tiles let empty_tile = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, @@ -261,7 +282,7 @@ impl Game { ); Self { - board: Board::new(board_width, BOARD_HEIGHT, game_options.num_players), + board: Board::new(board_width, board_height, game_options.num_players), num_players: game_options.num_players, vec_players, vec_next_piece, @@ -699,14 +720,12 @@ impl Game { } } } else { - println!("clearing next piece spritebatches"); for x in 0..3 { self.vec_batch_next_piece[x].clear(); } for x in 0u8..4u8 { for y in 0u8..2u8 { if self.vec_next_piece[player.player_num as usize].matrix[y as usize][x as usize] { - println!("adding next piece tile at position x = {}, y = {}", x, y); let next_tile = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, diff --git a/src/game/board.rs b/src/game/board.rs index 21e2a8e..4761e56 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -3,11 +3,35 @@ use crate::game::tile::Tile; use crate::game::{ CLEAR_DELAY, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, SCORE_TRIPLE_BASE, }; +use crate::game::Modes; // this constant is for the two unseen columns above the board so that when an I piece is rotated // right after spawning, the two tiles that go above the board are kept track of pub const BOARD_HEIGHT_BUFFER_U: u8 = 2; +// abstract the board and the possible gamemodes into one struct +pub struct BoardHandler { + pub board: Board, + pub rotatris: Option, +} + +impl BoardHandler { + pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: Modes) -> Self { + let rotatris = match mode { + Modes::Rotatris => Some(Rotatris::new()), + _ => None, + }; + Self { + board: Board::new(board_width, board_height, num_players), + rotatris, + } + } +} + +// example Board coordinates system (2 width, 2 height) +// [(0, 0)][(0, 1)] +// [(1, 0)][(1, 1)] + pub struct Board { pub width: u8, pub height: u8, @@ -16,10 +40,6 @@ pub struct Board { vec_full_lines: Vec, } -// example Board coordinates system (2 width, 2 height) -// [(0, 0)][(0, 1)] -// [(1, 0)][(1, 1)] - impl Board { pub fn new(board_width: u8, board_height: u8, num_players: u8) -> Self { let mut vec_active_piece: Vec = Vec::with_capacity(num_players as usize); @@ -366,7 +386,29 @@ impl FullLine { } } -// do `cargo test --release` because Rust doesn't like underflow, but that's how the board width works :( +// other modes +pub struct Rotatris { + pub board_rotation: u8, +} + +impl Rotatris { + fn new() -> Self { + Self { + board_rotation: 0, + } + } + + pub fn attempt_rotate_board(board: &Board, rotate_direction: Movement) { + if rotate_direction != Movement::RotateCcw && rotate_direction != Movement::RotateCw { + println!("[!] A non-rotation Movement was passed to function `attempt_rotate_board`"); + panic!(); + } + + + } +} + + #[cfg(test)] mod tests { use super::*; From 002a4a699965d2d4f61f63e513803b98d58384c6 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 02:24:49 -0500 Subject: [PATCH 03/59] uhhh ship it --- src/game.rs | 75 +++++++++++++++++++++++++++++++------------- src/game/board.rs | 80 ++++++++++++++++++++++++++++++++--------------- src/game/piece.rs | 60 +++++++++++++++++------------------ 3 files changed, 136 insertions(+), 79 deletions(-) diff --git a/src/game.rs b/src/game.rs index 97cff83..d804521 100644 --- a/src/game.rs +++ b/src/game.rs @@ -19,8 +19,7 @@ mod piece; use crate::game::piece::{Movement, NextPiece, Shapes}; mod board; -use crate::game::board::Board; -use crate::game::board::BOARD_HEIGHT_BUFFER_U; +use crate::game::board::BoardHandler; use crate::inputs::KeyboardControlScheme; use crate::menu::MenuGameOptions; @@ -128,7 +127,7 @@ impl From<&MenuGameOptions> for GameOptions { pub struct Game { // GAME STUFF // logic (mostly) - board: Board, + bh: BoardHandler, num_players: u8, vec_players: Vec, vec_next_piece: Vec, @@ -139,6 +138,8 @@ pub struct Game { num_cleared_lines: u16, score: u64, pause_flag: (bool, bool), + rotate_board_cw: (bool, bool), + rotate_board_ccw: (bool, bool), game_over_flag: bool, game_over_delay: i8, // drawing @@ -282,7 +283,7 @@ impl Game { ); Self { - board: Board::new(board_width, board_height, game_options.num_players), + bh: BoardHandler::new(board_width, board_height, game_options.num_players, mode), num_players: game_options.num_players, vec_players, vec_next_piece, @@ -293,6 +294,8 @@ impl Game { num_cleared_lines: 0u16, score: 0u64, pause_flag: (false, false), + rotate_board_cw: (false, false), + rotate_board_ccw: (false, false), game_over_flag: false, game_over_delay: GAME_OVER_DELAY, tile_size: 0f32, @@ -315,6 +318,8 @@ impl Game { return ProgramState::Menu; } player.input.was_just_pressed_setfalse(); + self.rotate_board_cw.1 = false; + self.rotate_board_ccw.1 = false; } } else { self.game_over_delay -= 1; @@ -341,6 +346,8 @@ impl Game { if player.input.keydown_start.1 { self.pause_flag = (false, false); player.input.was_just_pressed_setfalse(); + self.rotate_board_cw.1 = false; + self.rotate_board_ccw.1 = false; } } } @@ -348,9 +355,11 @@ impl Game { // GAME LOGIC for player in &mut self.vec_players { if !player.spawn_piece_flag - && self.board.vec_active_piece[player.player_num as usize].shape == Shapes::None + && self.bh.board.vec_active_piece[player.player_num as usize].shape == Shapes::None { player.input.was_just_pressed_setfalse(); + self.rotate_board_cw.1 = false; + self.rotate_board_ccw.1 = false; continue; } @@ -358,7 +367,7 @@ impl Game { if player.spawn_piece_flag { if player.spawn_delay <= 0 { // (blocked, blocked by some !active tile); if .1, game over sequence, if .0 and !.1, only blocked by other players, wait until they move, then carry on - let blocked: (bool, bool) = self.board.attempt_piece_spawn( + let blocked: (bool, bool) = self.bh.board.attempt_piece_spawn( player.player_num, player.spawn_column, player.next_piece_shape, @@ -369,7 +378,7 @@ impl Game { } continue; } else { - self.board.playerify_piece(player.player_num); + self.bh.board.playerify_piece(player.player_num); player.spawn_delay = SPAWN_DELAY; player.spawn_piece_flag = false; // set das_countdown to the smaller das value if input left or right is pressed as the piece spawns in @@ -385,7 +394,7 @@ impl Game { } } let random_shape = Shapes::from(rand % 7); - if self.board.vec_active_piece[player.player_num as usize].shape + if self.bh.board.vec_active_piece[player.player_num as usize].shape != random_shape { player.next_piece_shape = random_shape; @@ -409,11 +418,20 @@ impl Game { continue; } + if self.rotate_board_cw.1 { + self.bh.rotatris_attempt_rotate_board(Movement::RotateCw); + } + + if self.rotate_board_ccw.1 { + self.bh.rotatris_attempt_rotate_board(Movement::RotateCw); + } + // piece movement // LEFT / RIGHT if player.input.keydown_left.1 { // if it didn't move on the initial input, set waiting_to_shift to true player.waiting_to_shift = !self + .bh .board .attempt_piece_movement(Movement::Left, player.player_num) .0; @@ -422,6 +440,7 @@ impl Game { if player.input.keydown_right.1 { // if it didn't move on the initial input, set waiting_to_shift to true player.waiting_to_shift = !self + .bh .board .attempt_piece_movement(Movement::Right, player.player_num) .0; @@ -433,6 +452,7 @@ impl Game { } if player.das_countdown == 0 || player.waiting_to_shift { if self + .bh .board .attempt_piece_movement(Movement::Left, player.player_num) .0 @@ -451,6 +471,7 @@ impl Game { } if player.das_countdown == 0 || player.waiting_to_shift { if self + .bh .board .attempt_piece_movement(Movement::Right, player.player_num) .0 @@ -465,11 +486,11 @@ impl Game { } // CW / CCW if player.input.keydown_rotate_cw.1 { - self.board + self.bh.board .attempt_piece_movement(Movement::RotateCw, player.player_num); } if player.input.keydown_rotate_ccw.1 { - self.board + self.bh.board .attempt_piece_movement(Movement::RotateCcw, player.player_num); } // DOWN @@ -479,10 +500,11 @@ impl Game { || player.fall_countdown == 0 { let (moved_flag, caused_full_line_flag): (bool, bool) = self + .bh .board .attempt_piece_movement(Movement::Down, player.player_num); // if the piece got locked, piece.shape gets set to Shapes::None, so set the spawn piece flag - if self.board.vec_active_piece[player.player_num as usize].shape == Shapes::None + if self.bh.board.vec_active_piece[player.player_num as usize].shape == Shapes::None { player.spawn_piece_flag = true; player.fall_countdown = if self.level < 30 { @@ -514,14 +536,18 @@ impl Game { if player.input.keydown_start.1 { self.pause_flag = (true, true); player.input.was_just_pressed_setfalse(); + self.rotate_board_cw.1 = false; + self.rotate_board_ccw.1 = false; } // update controls (always do after all player player input for each player) player.input.was_just_pressed_setfalse(); + self.rotate_board_cw.1 = false; + self.rotate_board_ccw.1 = false; } // attempt to line clear (go through the vector of FullLine's and decrement clear_delay if > 0, clear and return (lines_cleared, score) for <= 0) - let (returned_lines, returned_score) = self.board.attempt_clear_lines(self.level); + let (returned_lines, returned_score) = self.bh.board.attempt_clear_lines(self.level); if returned_lines > 0 { self.num_cleared_lines += returned_lines as u16; self.game_info_text.fragments_mut()[1].text = @@ -555,6 +581,11 @@ impl Game { return; } } + if keycode == KeyCode::Z { + self.rotate_board_ccw = (true, true); + } else if keycode == KeyCode::X { + self.rotate_board_cw = (true, true); + } } } @@ -646,8 +677,8 @@ impl Game { let (window_width, window_height) = graphics::size(ctx); self.tile_size = TileGraphic::get_size( ctx, - self.board.width, - self.board.height + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D, + self.bh.board.width, + self.bh.board.height + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D, ); if self.game_over_flag && self.game_over_delay == 0 { // DRAW GAME OVER @@ -669,24 +700,24 @@ impl Game { } else { // DRAW GAME // add each non-empty tile to the correct SpriteBatch - for x in 0..self.board.width { - for y in 0..self.board.height { - if !self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].empty { + for x in 0..self.bh.board.width { + for y in 0..self.bh.board.height { + if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].empty { let player_tile = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); if self.num_players > 1 { - let player = self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize] + let player = self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] [x as usize] .player; self.vec_batch_player_piece[player as usize].add(player_tile); } else { - if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::J || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::S { + if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::J || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::S { self.vec_batch_player_piece[0].add(player_tile); - } else if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::L || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::Z { + } else if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::L || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::Z { self.vec_batch_player_piece[1].add(player_tile); - } else if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::I || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::O || self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].shape == Shapes::T { + } else if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::I || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::O || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::T { self.vec_batch_player_piece[2].add(player_tile); } } @@ -745,7 +776,7 @@ impl Game { let board_top_left_corner = window_width / 2.0 - (scaled_tile_size * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32 - * self.board.width as f32 + * self.bh.board.width as f32 / 2.0); // empty tiles graphics::draw( diff --git a/src/game/board.rs b/src/game/board.rs index 4761e56..0f72367 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -5,10 +5,6 @@ use crate::game::{ }; use crate::game::Modes; -// this constant is for the two unseen columns above the board so that when an I piece is rotated -// right after spawning, the two tiles that go above the board are kept track of -pub const BOARD_HEIGHT_BUFFER_U: u8 = 2; - // abstract the board and the possible gamemodes into one struct pub struct BoardHandler { pub board: Board, @@ -17,15 +13,52 @@ pub struct BoardHandler { impl BoardHandler { pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: Modes) -> Self { - let rotatris = match mode { - Modes::Rotatris => Some(Rotatris::new()), - _ => None, + let (board_height_buffer, piece_spawn_row, rotatris) = match mode { + Modes::Rotatris => (0, (board_height - 1) / 2, Some(Rotatris::new())), + Modes::Classic => (2, 0, None), }; Self { - board: Board::new(board_width, board_height, num_players), + board: Board::new(board_width, board_height, board_height_buffer, piece_spawn_row, num_players), rotatris, } } + + // return bool is if rotate was successful + pub fn rotatris_attempt_rotate_board(&mut self, rotate_direction: Movement) -> bool { + // subtract 1 for easier math in the case of an even board side length + let center: u8 = self.board.width / 2; + let rem_more_one: u8 = (self.board.width + 1) % 2; + let mut new_positions: [(u8, u8); 4] = [(0u8, 0u8); 4]; + match rotate_direction { + Movement::RotateCw => { + for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { + new_positions[index] = (center + (position.1 - center), position.0); + } + }, + Movement::RotateCcw => { + for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { + new_positions[index] = (position.1, center + (position.0 - center)); + } + }, + _ => { + println!("[!] Sent some non-rotation Movement to `rotatris_attempt_rotate_board()`, a method of `BoardHandler`"); + return false; + } + } + + // check validity of new positions + for position in new_positions.iter().take(4) { + if !self.board.matrix[position.0 as usize][position.1 as usize].empty && !self.board.matrix[position.0 as usize][position.1 as usize].active { + return false; + } + } + + self.board.emptify_piece(0); + self.board.vec_active_piece[0].positions = new_positions; + self.board.playerify_piece(0); + + true + } } // example Board coordinates system (2 width, 2 height) @@ -35,13 +68,15 @@ impl BoardHandler { pub struct Board { pub width: u8, pub height: u8, + pub height_buffer: u8, + pub spawn_row: u8, pub matrix: Vec>, pub vec_active_piece: Vec, vec_full_lines: Vec, } impl Board { - pub fn new(board_width: u8, board_height: u8, num_players: u8) -> Self { + pub fn new(board_width: u8, board_height: u8, board_height_buffer: u8, spawn_row: u8, num_players: u8) -> Self { let mut vec_active_piece: Vec = Vec::with_capacity(num_players as usize); for _ in 0..num_players { vec_active_piece.push(Piece::new(Shapes::None)); @@ -49,9 +84,11 @@ impl Board { Self { width: board_width, height: board_height, + height_buffer: board_height_buffer, + spawn_row, matrix: vec![ vec![Tile::new_empty(); board_width as usize]; - (board_height + BOARD_HEIGHT_BUFFER_U) as usize + (board_height + board_height_buffer) as usize ], vec_active_piece, vec_full_lines: vec![], @@ -95,7 +132,7 @@ impl Board { spawn_piece_shape: Shapes, ) -> (bool, bool) { let new_piece = Piece::new(spawn_piece_shape); - let spawn_positions = new_piece.spawn_pos(spawn_col); + let spawn_positions = new_piece.spawn_pos(spawn_col, self.spawn_row, self.height_buffer); let mut blocked_flag: bool = false; for position in spawn_positions.iter().take(4) { if !self.matrix[position.0 as usize][position.1 as usize].empty { @@ -126,7 +163,7 @@ impl Board { let new_positions = self.vec_active_piece[player as usize].piece_pos(movement); for position in new_positions.iter().take(4) { // due to integer underflow (u8 board width and u8 board height), we must only check the positive side of x and y positions - if position.0 >= self.height + BOARD_HEIGHT_BUFFER_U { + if position.0 >= self.height + self.height_buffer { cant_move_flag = true; break; } @@ -194,7 +231,7 @@ impl Board { .take(4) { // we just want to know if moving down by 1 will run the piece into the bottom of the board or an inactive tile - if position.0 as usize + 1 >= (self.height + BOARD_HEIGHT_BUFFER_U) as usize { + if position.0 as usize + 1 >= (self.height + self.height_buffer) as usize { return true; } if !self.matrix[position.0 as usize + 1][position.1 as usize].active @@ -388,7 +425,7 @@ impl FullLine { // other modes pub struct Rotatris { - pub board_rotation: u8, + pub board_rotation: u8, // 0, 1, 2, 3: 0, 90, 180, 270; CW } impl Rotatris { @@ -397,15 +434,6 @@ impl Rotatris { board_rotation: 0, } } - - pub fn attempt_rotate_board(board: &Board, rotate_direction: Movement) { - if rotate_direction != Movement::RotateCcw && rotate_direction != Movement::RotateCw { - println!("[!] A non-rotation Movement was passed to function `attempt_rotate_board`"); - panic!(); - } - - - } } @@ -429,7 +457,7 @@ mod tests { for x in 0..4 { for y in - (board_height + BOARD_HEIGHT_BUFFER_U - 8)..board_height + BOARD_HEIGHT_BUFFER_U + (board_height + self.height_buffer - 8)..board_height + self.height_buffer { board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } @@ -472,7 +500,7 @@ mod tests { for x in 0..board_width - 2 { for y in - (board_height + BOARD_HEIGHT_BUFFER_U - 4)..board_height + BOARD_HEIGHT_BUFFER_U + (board_height + self.height_buffer - 4)..board_height + self.height_buffer { board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } @@ -524,7 +552,7 @@ mod tests { for x in 0..board_width - 1 { for y in - (board_height + BOARD_HEIGHT_BUFFER_U - 8)..board_height + BOARD_HEIGHT_BUFFER_U + (board_height + self.height_buffer - 8)..board_height + self.height_buffer { board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } diff --git a/src/game/piece.rs b/src/game/piece.rs index c7ca06d..40694ae 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -1,5 +1,3 @@ -use crate::game::board::BOARD_HEIGHT_BUFFER_U; - #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] pub enum Shapes { @@ -173,7 +171,7 @@ impl Piece { } } - pub fn spawn_pos(&self, spawn_column: u8) -> [(u8, u8); 4] { + pub fn spawn_pos(&self, spawn_column: u8, spawn_row: u8, board_height_buffer: u8) -> [(u8, u8); 4] { match self.shape { Shapes::None => { println!("[!] tried to spawn a piece with shape type Shapes::None"); @@ -181,58 +179,58 @@ impl Piece { } Shapes::I => { [ - (BOARD_HEIGHT_BUFFER_U, spawn_column - 2), // [-][-][-][-] | [-][-][0][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][-][-][-] | [-][-][1][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column), // [0][1][2][3] | [-][-][2][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column + 1), // [-][-][-][-] | [-][-][3][-] + (spawn_row + board_height_buffer, spawn_column - 2), // [-][-][-][-] | [-][-][0][-] + (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] | [-][-][1][-] + (spawn_row + board_height_buffer, spawn_column), // [0][1][2][3] | [-][-][2][-] + (spawn_row + board_height_buffer, spawn_column + 1), // [-][-][-][-] | [-][-][3][-] ] } Shapes::O => { [ - (BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][-][-][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][-][-][-] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][0][1][-] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][2][3][-] + (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] + (spawn_row + 1 + board_height_buffer, spawn_column - 1), // [-][0][1][-] + (spawn_row + 1 + board_height_buffer, spawn_column), // [-][2][3][-] ] } Shapes::T => { [ - (BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] | [-][-][-][-] | [-][-][-][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][-][-][-] | [-][-][0][-] | [-][-][3][-] | [-][-][2][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column + 1), // [-][0][1][2] | [-][3][1][-] | [-][2][1][0] | [-][-][1][3] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][-][3][-] | [-][-][2][-] | [-][-][-][-] | [-][-][0][-] + (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] | [-][-][-][-] | [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] | [-][-][0][-] | [-][-][3][-] | [-][-][2][-] + (spawn_row + board_height_buffer, spawn_column + 1), // [-][0][1][2] | [-][3][1][-] | [-][2][1][0] | [-][-][1][3] + (spawn_row + 1 + board_height_buffer, spawn_column), // [-][-][3][-] | [-][-][2][-] | [-][-][-][-] | [-][-][0][-] ] } Shapes::J => { [ - (BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] | [-][-][-][-] | [-][-][-][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][-][-][-] | [-][-][0][-] | [-][3][-][-] | [-][-][2][3] - (BOARD_HEIGHT_BUFFER_U, spawn_column + 1), // [-][0][1][2] | [-][-][1][-] | [-][2][1][0] | [-][-][1][-] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column + 1), // [-][-][-][3] | [-][3][2][-] | [-][-][-][-] | [-][-][0][-] + (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] | [-][-][-][-] | [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] | [-][-][0][-] | [-][3][-][-] | [-][-][2][3] + (spawn_row + board_height_buffer, spawn_column + 1), // [-][0][1][2] | [-][-][1][-] | [-][2][1][0] | [-][-][1][-] + (spawn_row + 1 + board_height_buffer, spawn_column + 1), // [-][-][-][3] | [-][3][2][-] | [-][-][-][-] | [-][-][0][-] ] } Shapes::L => { [ - (BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] | [-][-][-][-] | [-][-][-][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][-][-][-] | [-][3][0][-] | [-][-][-][3] | [-][-][2][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column + 1), // [-][0][1][2] | [-][-][1][-] | [-][2][1][0] | [-][-][1][-] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][3][-][-] | [-][-][2][-] | [-][-][-][-] | [-][-][0][3] + (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] | [-][-][-][-] | [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] | [-][3][0][-] | [-][-][-][3] | [-][-][2][-] + (spawn_row + board_height_buffer, spawn_column + 1), // [-][0][1][2] | [-][-][1][-] | [-][2][1][0] | [-][-][1][-] + (spawn_row + 1 + board_height_buffer, spawn_column - 1), // [-][3][-][-] | [-][-][2][-] | [-][-][-][-] | [-][-][0][3] ] } Shapes::S => { [ - (BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][-][-][-] | [-][-][-][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column + 1), // [-][-][-][-] | [-][-][1][-] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][-][0][1] | [-][-][0][3] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][2][3][-] | [-][-][-][2] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] | [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column + 1), // [-][-][-][-] | [-][-][1][-] + (spawn_row + 1 + board_height_buffer, spawn_column - 1), // [-][-][0][1] | [-][-][0][3] + (spawn_row + 1 + board_height_buffer, spawn_column), // [-][2][3][-] | [-][-][-][2] ] } Shapes::Z => { [ - (BOARD_HEIGHT_BUFFER_U, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] - (BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][-][-][-] | [-][-][-][3] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column), // [-][0][1][-] | [-][-][1][2] - (1 + BOARD_HEIGHT_BUFFER_U, spawn_column + 1), // [-][-][2][3] | [-][-][0][-] + (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] | [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] | [-][-][-][3] + (spawn_row + 1 + board_height_buffer, spawn_column), // [-][0][1][-] | [-][-][1][2] + (spawn_row + 1 + board_height_buffer, spawn_column + 1), // [-][-][2][3] | [-][-][0][-] ] } } From 1ac1e1eee62207eb732308cb97fc62656d4984a4 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 12:24:45 -0500 Subject: [PATCH 04/59] fixed it for odd board side length --- src/game/board.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/board.rs b/src/game/board.rs index 0f72367..84c6a20 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -32,12 +32,12 @@ impl BoardHandler { match rotate_direction { Movement::RotateCw => { for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { - new_positions[index] = (center + (position.1 - center), position.0); + new_positions[index] = (position.1, center * 2 - position.0); } }, Movement::RotateCcw => { for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { - new_positions[index] = (position.1, center + (position.0 - center)); + new_positions[index] = (center * 2 - position.1, position.0); } }, _ => { From 95355ec682088d67216aebc07912cfb0bf5d51ba Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 14:51:13 -0500 Subject: [PATCH 05/59] added proper gravity and locking --- src/game.rs | 29 ++++++++++++++-------- src/game/board.rs | 62 ++++++++++++++++++++++++++++++++++++----------- src/game/piece.rs | 35 ++++++++++++++++++-------- 3 files changed, 92 insertions(+), 34 deletions(-) diff --git a/src/game.rs b/src/game.rs index d804521..d0e97db 100644 --- a/src/game.rs +++ b/src/game.rs @@ -26,7 +26,7 @@ use crate::menu::MenuGameOptions; const BOARD_HEIGHT: u8 = 20u8; -const ROTATRIS_BOARD_SIDE_LENGTH: u8 = 13u8; +const ROTATRIS_BOARD_SIDE_LENGTH: u8 = 16u8; pub const CLEAR_DELAY: i8 = 30i8; @@ -140,6 +140,7 @@ pub struct Game { pause_flag: (bool, bool), rotate_board_cw: (bool, bool), rotate_board_ccw: (bool, bool), + gravity_direction: Movement, game_over_flag: bool, game_over_delay: i8, // drawing @@ -296,6 +297,7 @@ impl Game { pause_flag: (false, false), rotate_board_cw: (false, false), rotate_board_ccw: (false, false), + gravity_direction: Movement::Down, game_over_flag: false, game_over_delay: GAME_OVER_DELAY, tile_size: 0f32, @@ -418,13 +420,20 @@ impl Game { continue; } + // rotatris + // BOARD ROTATION if self.rotate_board_cw.1 { - self.bh.rotatris_attempt_rotate_board(Movement::RotateCw); + if self.bh.rotatris_attempt_rotate_board(Movement::RotateCw) { + self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 1) % 4); + } } if self.rotate_board_ccw.1 { - self.bh.rotatris_attempt_rotate_board(Movement::RotateCw); + if self.bh.rotatris_attempt_rotate_board(Movement::RotateCcw) { + self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 3) % 4); + } } + // rotatris end // piece movement // LEFT / RIGHT @@ -433,7 +442,7 @@ impl Game { player.waiting_to_shift = !self .bh .board - .attempt_piece_movement(Movement::Left, player.player_num) + .attempt_piece_movement(Movement::from((Movement::Left as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) .0; player.das_countdown = DAS_THRESHOLD_BIG; } @@ -442,7 +451,7 @@ impl Game { player.waiting_to_shift = !self .bh .board - .attempt_piece_movement(Movement::Right, player.player_num) + .attempt_piece_movement(Movement::from((Movement::Right as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) .0; player.das_countdown = DAS_THRESHOLD_BIG; } @@ -454,7 +463,7 @@ impl Game { if self .bh .board - .attempt_piece_movement(Movement::Left, player.player_num) + .attempt_piece_movement(Movement::from((Movement::Left as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) .0 { player.das_countdown = @@ -473,7 +482,7 @@ impl Game { if self .bh .board - .attempt_piece_movement(Movement::Right, player.player_num) + .attempt_piece_movement(Movement::from((Movement::Right as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) .0 { player.das_countdown = @@ -487,11 +496,11 @@ impl Game { // CW / CCW if player.input.keydown_rotate_cw.1 { self.bh.board - .attempt_piece_movement(Movement::RotateCw, player.player_num); + .attempt_piece_movement(Movement::RotateCw, player.player_num, self.gravity_direction); } if player.input.keydown_rotate_ccw.1 { self.bh.board - .attempt_piece_movement(Movement::RotateCcw, player.player_num); + .attempt_piece_movement(Movement::RotateCcw, player.player_num, self.gravity_direction); } // DOWN // down is interesting because every time the downwards position is false we have to check if it's running into the bottom or an inactive tile so we know if we should lock it @@ -502,7 +511,7 @@ impl Game { let (moved_flag, caused_full_line_flag): (bool, bool) = self .bh .board - .attempt_piece_movement(Movement::Down, player.player_num); + .attempt_piece_movement(Movement::from((Movement::Down as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction); // if the piece got locked, piece.shape gets set to Shapes::None, so set the spawn piece flag if self.bh.board.vec_active_piece[player.player_num as usize].shape == Shapes::None { diff --git a/src/game/board.rs b/src/game/board.rs index 84c6a20..fb3542c 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -25,19 +25,18 @@ impl BoardHandler { // return bool is if rotate was successful pub fn rotatris_attempt_rotate_board(&mut self, rotate_direction: Movement) -> bool { - // subtract 1 for easier math in the case of an even board side length let center: u8 = self.board.width / 2; - let rem_more_one: u8 = (self.board.width + 1) % 2; + let is_center_even: u8 = (self.board.width + 1) % 2; let mut new_positions: [(u8, u8); 4] = [(0u8, 0u8); 4]; match rotate_direction { Movement::RotateCw => { for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { - new_positions[index] = (position.1, center * 2 - position.0); + new_positions[index] = (position.1, center * 2 - position.0 - is_center_even); } }, Movement::RotateCcw => { for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { - new_positions[index] = (center * 2 - position.1, position.0); + new_positions[index] = (center * 2 - position.1 - is_center_even, position.0); } }, _ => { @@ -157,7 +156,7 @@ impl Board { // returns (bool, bool) based on (if piece moved successfully, if (piece is locked && filled some line)) // sets the shape of the piece to Shapes::None if it locks - pub fn attempt_piece_movement(&mut self, movement: Movement, player: u8) -> (bool, bool) { + pub fn attempt_piece_movement(&mut self, movement: Movement, player: u8, current_gravity: Movement) -> (bool, bool) { let mut cant_move_flag = false; // determine if it can move let new_positions = self.vec_active_piece[player as usize].piece_pos(movement); @@ -182,7 +181,7 @@ impl Board { } if cant_move_flag { - if movement == Movement::Down && self.should_lock(player) { + if movement == current_gravity && self.should_lock(player, current_gravity) { // lock piece and push any full lines to vec_full_lines self.vec_active_piece[player as usize].shape = Shapes::None; let mut is_full_line = false; @@ -224,20 +223,55 @@ impl Board { (true, false) } - fn should_lock(&self, player: u8) -> bool { + fn should_lock(&self, player: u8, current_gravity: Movement) -> bool { for position in self.vec_active_piece[player as usize] .positions .iter() .take(4) { // we just want to know if moving down by 1 will run the piece into the bottom of the board or an inactive tile - if position.0 as usize + 1 >= (self.height + self.height_buffer) as usize { - return true; - } - if !self.matrix[position.0 as usize + 1][position.1 as usize].active - && !self.matrix[position.0 as usize + 1][position.1 as usize].empty - { - return true; + match current_gravity { + Movement::Down => { + if position.0 as usize + 1 >= (self.height + self.height_buffer) as usize { + return true; + } + if !self.matrix[position.0 as usize + 1][position.1 as usize].active + && !self.matrix[position.0 as usize + 1][position.1 as usize].empty + { + return true; + } + }, + Movement::Left => { + if position.1 <= 0 { + return true; + } + if !self.matrix[position.0 as usize][position.1 as usize - 1].active + && !self.matrix[position.0 as usize][position.1 as usize - 1].empty + { + return true; + } + }, + Movement::Up => { + if position.0 <= 0 { + return true; + } + if !self.matrix[position.0 as usize - 1][position.1 as usize].active + && !self.matrix[position.0 as usize - 1][position.1 as usize].empty + { + return true; + } + }, + Movement::Right => { + if position.1 >= self.width - 1 { + return true; + } + if !self.matrix[position.0 as usize][position.1 as usize + 1].active + && !self.matrix[position.0 as usize][position.1 as usize + 1].empty + { + return true; + } + }, + _ => panic!("[!] Error: current gravity is {}", current_gravity as u8), } } diff --git a/src/game/piece.rs b/src/game/piece.rs index 40694ae..c0bffde 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -29,13 +29,28 @@ impl From for Shapes { #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] pub enum Movement { - None, + Down, Left, + Up, Right, - Down, RotateCw, RotateCcw, - // Up, + None, +} + +impl From for Movement { + fn from(value: u8) -> Movement { + match value { + 0 => Movement::Down, + 1 => Movement::Left, + 2 => Movement::Up, + 3 => Movement::Right, + 4 => Movement::RotateCw, + 5 => Movement::RotateCcw, + 6 => Movement::None, + _ => panic!("[!] Unknown Movement value: {}", value), + } + } } pub struct Piece { @@ -262,13 +277,13 @@ impl Piece { (self.positions[2].0 + 1, self.positions[2].1), (self.positions[3].0 + 1, self.positions[3].1), ] - // } else if r#move == Movement::Up { - // return [ - // (self.positions[0].0 - 1, self.positions[0].1), - // (self.positions[1].0 - 1, self.positions[1].1), - // (self.positions[2].0 - 1, self.positions[2].1), - // (self.positions[3].0 - 1, self.positions[3].1), - // ]; + } else if r#move == Movement::Up { + return [ + (self.positions[0].0 - 1, self.positions[0].1), + (self.positions[1].0 - 1, self.positions[1].1), + (self.positions[2].0 - 1, self.positions[2].1), + (self.positions[3].0 - 1, self.positions[3].1), + ]; } else { // T, L, J if self.num_rotations == 4 { From a45d2f401f5340fb64cf0c749f26a25951c3af5f Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 16:06:17 -0500 Subject: [PATCH 06/59] basically functional --- src/game.rs | 4 +-- src/game/board.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++ src/game/tile.rs | 2 +- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/game.rs b/src/game.rs index d0e97db..c140068 100644 --- a/src/game.rs +++ b/src/game.rs @@ -28,7 +28,7 @@ const BOARD_HEIGHT: u8 = 20u8; const ROTATRIS_BOARD_SIDE_LENGTH: u8 = 16u8; -pub const CLEAR_DELAY: i8 = 30i8; +pub const CLEAR_DELAY: i8 = 0i8; pub const SCORE_SINGLE_BASE: u8 = 40u8; pub const SCORE_DOUBLE_BASE: u8 = 100u8; @@ -556,7 +556,7 @@ impl Game { } // attempt to line clear (go through the vector of FullLine's and decrement clear_delay if > 0, clear and return (lines_cleared, score) for <= 0) - let (returned_lines, returned_score) = self.bh.board.attempt_clear_lines(self.level); + let (returned_lines, returned_score) = self.bh.attempt_clear_lines(self.level); if returned_lines > 0 { self.num_cleared_lines += returned_lines as u16; self.game_info_text.fragments_mut()[1].text = diff --git a/src/game/board.rs b/src/game/board.rs index fb3542c..c672019 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -7,6 +7,7 @@ use crate::game::Modes; // abstract the board and the possible gamemodes into one struct pub struct BoardHandler { + pub mode: Modes, pub board: Board, pub rotatris: Option, } @@ -18,6 +19,7 @@ impl BoardHandler { Modes::Classic => (2, 0, None), }; Self { + mode, board: Board::new(board_width, board_height, board_height_buffer, piece_spawn_row, num_players), rotatris, } @@ -58,6 +60,69 @@ impl BoardHandler { true } + + pub fn attempt_clear_lines(&mut self, level: u8) -> (u8, u32) { + match self.mode { + Modes::Classic => self.board.attempt_clear_lines(level), + Modes::Rotatris => self.rotatris_attempt_clear_rings(level), + } + } + + pub fn rotatris_attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { + let mut num_cleared_rings = 0; + let mut score_from_cleared_rings = 0; + let num_rings_to_check = self.board.width / 2 - 4; + + // go from inner rings to outer rings checking if any ring is full, avoiding the middle 4 rings + for z in (0..num_rings_to_check).rev() { + if self.rotatris_check_single_ring(z) { + num_cleared_rings += 1; + // clear and pull inner stuff out + for j in (z + 1)..num_rings_to_check { + self.rotatris_pull_single_ring_out(j); + } + } + } + + (num_cleared_rings, score_from_cleared_rings) + } + + fn rotatris_check_single_ring(&mut self, z: u8) -> bool { + let board_side_length = self.board.width; + for a in [z, board_side_length - z - 1].into_iter() { + for b in z..(board_side_length - z) { + if b >= z && b <= board_side_length - z { + if self.board.matrix[*a as usize][b as usize].empty || self.board.matrix[*a as usize][b as usize].active || self.board.matrix[b as usize][*a as usize].empty || self.board.matrix[b as usize][*a as usize].active { + return false; + } + } + } + } + + true + } + + fn rotatris_pull_single_ring_out(&mut self, j: u8) { + let j = j as usize; + let k = self.board.width as usize - j - 1; + + // sides + for a in j..=k { + // top + self.board.matrix[j - 1][a] = self.board.matrix[j][a]; + // left + self.board.matrix[a][j - 1] = self.board.matrix[a][j]; + // down + self.board.matrix[k + 1][a] = self.board.matrix[k][a]; + // right + self.board.matrix[a][k + 1] = self.board.matrix[a][k]; + } + // corners + self.board.matrix[j - 1][j - 1] = self.board.matrix[j][j]; + self.board.matrix[j - 1][k + 1] = self.board.matrix[j][k]; + self.board.matrix[k + 1][j - 1] = self.board.matrix[k][j]; + self.board.matrix[k + 1][k + 1] = self.board.matrix[k][k]; + } } // example Board coordinates system (2 width, 2 height) diff --git a/src/game/tile.rs b/src/game/tile.rs index 4d35ea2..43a829b 100644 --- a/src/game/tile.rs +++ b/src/game/tile.rs @@ -48,7 +48,7 @@ const PLAYER_RGBA: [(u8, u8, u8, u8); NUM_PLAYERCOLORS as usize] = [ const BASE_PLAYER_COLOR: (u8, u8, u8, u8) = (20u8, 80u8, 150u8, 0xffu8); -#[derive(Clone)] +#[derive(Copy, Clone)] pub struct Tile { pub empty: bool, pub active: bool, From a19bfbb96800706dcba8e74df3d93379531651ca Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 16:09:06 -0500 Subject: [PATCH 07/59] cargo fmt --- src/game.rs | 127 +++++++++++++++++++++++++++++++++++++--------- src/game/board.rs | 88 +++++++++++++++++++++----------- src/game/piece.rs | 9 +++- 3 files changed, 168 insertions(+), 56 deletions(-) diff --git a/src/game.rs b/src/game.rs index c140068..99f6fad 100644 --- a/src/game.rs +++ b/src/game.rs @@ -357,7 +357,8 @@ impl Game { // GAME LOGIC for player in &mut self.vec_players { if !player.spawn_piece_flag - && self.bh.board.vec_active_piece[player.player_num as usize].shape == Shapes::None + && self.bh.board.vec_active_piece[player.player_num as usize].shape + == Shapes::None { player.input.was_just_pressed_setfalse(); self.rotate_board_cw.1 = false; @@ -424,13 +425,15 @@ impl Game { // BOARD ROTATION if self.rotate_board_cw.1 { if self.bh.rotatris_attempt_rotate_board(Movement::RotateCw) { - self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 1) % 4); + self.gravity_direction = + Movement::from(((self.gravity_direction as u8) + 1) % 4); } } if self.rotate_board_ccw.1 { if self.bh.rotatris_attempt_rotate_board(Movement::RotateCcw) { - self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 3) % 4); + self.gravity_direction = + Movement::from(((self.gravity_direction as u8) + 3) % 4); } } // rotatris end @@ -442,7 +445,13 @@ impl Game { player.waiting_to_shift = !self .bh .board - .attempt_piece_movement(Movement::from((Movement::Left as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) + .attempt_piece_movement( + Movement::from( + (Movement::Left as u8 + self.gravity_direction as u8) % 4, + ), + player.player_num, + self.gravity_direction, + ) .0; player.das_countdown = DAS_THRESHOLD_BIG; } @@ -451,7 +460,13 @@ impl Game { player.waiting_to_shift = !self .bh .board - .attempt_piece_movement(Movement::from((Movement::Right as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) + .attempt_piece_movement( + Movement::from( + (Movement::Right as u8 + self.gravity_direction as u8) % 4, + ), + player.player_num, + self.gravity_direction, + ) .0; player.das_countdown = DAS_THRESHOLD_BIG; } @@ -463,7 +478,13 @@ impl Game { if self .bh .board - .attempt_piece_movement(Movement::from((Movement::Left as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) + .attempt_piece_movement( + Movement::from( + (Movement::Left as u8 + self.gravity_direction as u8) % 4, + ), + player.player_num, + self.gravity_direction, + ) .0 { player.das_countdown = @@ -482,7 +503,13 @@ impl Game { if self .bh .board - .attempt_piece_movement(Movement::from((Movement::Right as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction) + .attempt_piece_movement( + Movement::from( + (Movement::Right as u8 + self.gravity_direction as u8) % 4, + ), + player.player_num, + self.gravity_direction, + ) .0 { player.das_countdown = @@ -495,12 +522,18 @@ impl Game { } // CW / CCW if player.input.keydown_rotate_cw.1 { - self.bh.board - .attempt_piece_movement(Movement::RotateCw, player.player_num, self.gravity_direction); + self.bh.board.attempt_piece_movement( + Movement::RotateCw, + player.player_num, + self.gravity_direction, + ); } if player.input.keydown_rotate_ccw.1 { - self.bh.board - .attempt_piece_movement(Movement::RotateCcw, player.player_num, self.gravity_direction); + self.bh.board.attempt_piece_movement( + Movement::RotateCcw, + player.player_num, + self.gravity_direction, + ); } // DOWN // down is interesting because every time the downwards position is false we have to check if it's running into the bottom or an inactive tile so we know if we should lock it @@ -508,12 +541,17 @@ impl Game { || (player.input.keydown_down.0 && player.force_fall_countdown == 0) || player.fall_countdown == 0 { - let (moved_flag, caused_full_line_flag): (bool, bool) = self - .bh - .board - .attempt_piece_movement(Movement::from((Movement::Down as u8 + self.gravity_direction as u8) % 4), player.player_num, self.gravity_direction); + let (moved_flag, caused_full_line_flag): (bool, bool) = + self.bh.board.attempt_piece_movement( + Movement::from( + (Movement::Down as u8 + self.gravity_direction as u8) % 4, + ), + player.player_num, + self.gravity_direction, + ); // if the piece got locked, piece.shape gets set to Shapes::None, so set the spawn piece flag - if self.bh.board.vec_active_piece[player.player_num as usize].shape == Shapes::None + if self.bh.board.vec_active_piece[player.player_num as usize].shape + == Shapes::None { player.spawn_piece_flag = true; player.fall_countdown = if self.level < 30 { @@ -711,22 +749,55 @@ impl Game { // add each non-empty tile to the correct SpriteBatch for x in 0..self.bh.board.width { for y in 0..self.bh.board.height { - if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].empty { + if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize] + .empty + { let player_tile = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); if self.num_players > 1 { - let player = self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] + let player = self.bh.board.matrix + [(y + self.bh.board.height_buffer) as usize] [x as usize] .player; self.vec_batch_player_piece[player as usize].add(player_tile); } else { - if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::J || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::S { + if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] + [x as usize] + .shape + == Shapes::J + || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] + [x as usize] + .shape + == Shapes::S + { self.vec_batch_player_piece[0].add(player_tile); - } else if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::L || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::Z { + } else if self.bh.board.matrix + [(y + self.bh.board.height_buffer) as usize] + [x as usize] + .shape + == Shapes::L + || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] + [x as usize] + .shape + == Shapes::Z + { self.vec_batch_player_piece[1].add(player_tile); - } else if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::I || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::O || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].shape == Shapes::T { + } else if self.bh.board.matrix + [(y + self.bh.board.height_buffer) as usize] + [x as usize] + .shape + == Shapes::I + || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] + [x as usize] + .shape + == Shapes::O + || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] + [x as usize] + .shape + == Shapes::T + { self.vec_batch_player_piece[2].add(player_tile); } } @@ -749,7 +820,9 @@ impl Game { self.vec_batch_next_piece[player.player_num as usize].clear(); for x in 0u8..4u8 { for y in 0u8..2u8 { - if self.vec_next_piece[player.player_num as usize].matrix[y as usize][x as usize] { + if self.vec_next_piece[player.player_num as usize].matrix + [y as usize][x as usize] + { let next_tile = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, @@ -765,7 +838,9 @@ impl Game { } for x in 0u8..4u8 { for y in 0u8..2u8 { - if self.vec_next_piece[player.player_num as usize].matrix[y as usize][x as usize] { + if self.vec_next_piece[player.player_num as usize].matrix + [y as usize][x as usize] + { let next_tile = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, @@ -825,7 +900,8 @@ impl Game { + (player.spawn_column - 2) as f32 * scaled_tile_size * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - (NON_BOARD_SPACE_U - BOARD_NEXT_PIECE_SPACING) as f32 * self.tile_size, + (NON_BOARD_SPACE_U - BOARD_NEXT_PIECE_SPACING) as f32 + * self.tile_size, )) .scale(Vector2::new(scaled_tile_size, scaled_tile_size)), ) @@ -841,7 +917,8 @@ impl Game { + (spawn_column - 2) as f32 * scaled_tile_size * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - (NON_BOARD_SPACE_U - BOARD_NEXT_PIECE_SPACING) as f32 * self.tile_size, + (NON_BOARD_SPACE_U - BOARD_NEXT_PIECE_SPACING) as f32 + * self.tile_size, )) .scale(Vector2::new(scaled_tile_size, scaled_tile_size)), ) diff --git a/src/game/board.rs b/src/game/board.rs index c672019..be26f4a 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -1,9 +1,9 @@ use crate::game::piece::{Movement, Piece, Shapes}; use crate::game::tile::Tile; +use crate::game::Modes; use crate::game::{ CLEAR_DELAY, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, SCORE_TRIPLE_BASE, }; -use crate::game::Modes; // abstract the board and the possible gamemodes into one struct pub struct BoardHandler { @@ -20,7 +20,13 @@ impl BoardHandler { }; Self { mode, - board: Board::new(board_width, board_height, board_height_buffer, piece_spawn_row, num_players), + board: Board::new( + board_width, + board_height, + board_height_buffer, + piece_spawn_row, + num_players, + ), rotatris, } } @@ -32,15 +38,25 @@ impl BoardHandler { let mut new_positions: [(u8, u8); 4] = [(0u8, 0u8); 4]; match rotate_direction { Movement::RotateCw => { - for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { + for (index, position) in self.board.vec_active_piece[0] + .positions + .iter() + .take(4) + .enumerate() + { new_positions[index] = (position.1, center * 2 - position.0 - is_center_even); } - }, + } Movement::RotateCcw => { - for (index, position) in self.board.vec_active_piece[0].positions.iter().take(4).enumerate() { + for (index, position) in self.board.vec_active_piece[0] + .positions + .iter() + .take(4) + .enumerate() + { new_positions[index] = (center * 2 - position.1 - is_center_even, position.0); } - }, + } _ => { println!("[!] Sent some non-rotation Movement to `rotatris_attempt_rotate_board()`, a method of `BoardHandler`"); return false; @@ -49,7 +65,9 @@ impl BoardHandler { // check validity of new positions for position in new_positions.iter().take(4) { - if !self.board.matrix[position.0 as usize][position.1 as usize].empty && !self.board.matrix[position.0 as usize][position.1 as usize].active { + if !self.board.matrix[position.0 as usize][position.1 as usize].empty + && !self.board.matrix[position.0 as usize][position.1 as usize].active + { return false; } } @@ -92,7 +110,11 @@ impl BoardHandler { for a in [z, board_side_length - z - 1].into_iter() { for b in z..(board_side_length - z) { if b >= z && b <= board_side_length - z { - if self.board.matrix[*a as usize][b as usize].empty || self.board.matrix[*a as usize][b as usize].active || self.board.matrix[b as usize][*a as usize].empty || self.board.matrix[b as usize][*a as usize].active { + if self.board.matrix[*a as usize][b as usize].empty + || self.board.matrix[*a as usize][b as usize].active + || self.board.matrix[b as usize][*a as usize].empty + || self.board.matrix[b as usize][*a as usize].active + { return false; } } @@ -140,7 +162,13 @@ pub struct Board { } impl Board { - pub fn new(board_width: u8, board_height: u8, board_height_buffer: u8, spawn_row: u8, num_players: u8) -> Self { + pub fn new( + board_width: u8, + board_height: u8, + board_height_buffer: u8, + spawn_row: u8, + num_players: u8, + ) -> Self { let mut vec_active_piece: Vec = Vec::with_capacity(num_players as usize); for _ in 0..num_players { vec_active_piece.push(Piece::new(Shapes::None)); @@ -181,7 +209,12 @@ impl Board { .take(4) { if position != &(0xffu8, 0xffu8) { - self.matrix[position.0 as usize][position.1 as usize] = Tile::new(false, true, player, self.vec_active_piece[player as usize].shape); + self.matrix[position.0 as usize][position.1 as usize] = Tile::new( + false, + true, + player, + self.vec_active_piece[player as usize].shape, + ); } else { println!("[!] tried to playerify piece that contained position (0xffu8, 0xffu8)"); } @@ -213,7 +246,8 @@ impl Board { self.vec_active_piece[player as usize].positions = spawn_positions; // initialize the tile logic for the newly spawned piece for position in spawn_positions.iter().take(4) { - self.matrix[position.0 as usize][position.1 as usize] = Tile::new(false, true, player, spawn_piece_shape); + self.matrix[position.0 as usize][position.1 as usize] = + Tile::new(false, true, player, spawn_piece_shape); } (false, false) @@ -221,7 +255,12 @@ impl Board { // returns (bool, bool) based on (if piece moved successfully, if (piece is locked && filled some line)) // sets the shape of the piece to Shapes::None if it locks - pub fn attempt_piece_movement(&mut self, movement: Movement, player: u8, current_gravity: Movement) -> (bool, bool) { + pub fn attempt_piece_movement( + &mut self, + movement: Movement, + player: u8, + current_gravity: Movement, + ) -> (bool, bool) { let mut cant_move_flag = false; // determine if it can move let new_positions = self.vec_active_piece[player as usize].piece_pos(movement); @@ -305,7 +344,7 @@ impl Board { { return true; } - }, + } Movement::Left => { if position.1 <= 0 { return true; @@ -315,7 +354,7 @@ impl Board { { return true; } - }, + } Movement::Up => { if position.0 <= 0 { return true; @@ -325,7 +364,7 @@ impl Board { { return true; } - }, + } Movement::Right => { if position.1 >= self.width - 1 { return true; @@ -335,7 +374,7 @@ impl Board { { return true; } - }, + } _ => panic!("[!] Error: current gravity is {}", current_gravity as u8), } } @@ -529,13 +568,10 @@ pub struct Rotatris { impl Rotatris { fn new() -> Self { - Self { - board_rotation: 0, - } + Self { board_rotation: 0 } } } - #[cfg(test)] mod tests { use super::*; @@ -555,9 +591,7 @@ mod tests { let mut board = Board::new(board_width, board_height, num_players); for x in 0..4 { - for y in - (board_height + self.height_buffer - 8)..board_height + self.height_buffer - { + for y in (board_height + self.height_buffer - 8)..board_height + self.height_buffer { board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } } @@ -598,9 +632,7 @@ mod tests { let mut num_cleared_lines: u16 = 0; for x in 0..board_width - 2 { - for y in - (board_height + self.height_buffer - 4)..board_height + self.height_buffer - { + for y in (board_height + self.height_buffer - 4)..board_height + self.height_buffer { board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } } @@ -650,9 +682,7 @@ mod tests { let mut num_cleared_lines: u16 = 0; for x in 0..board_width - 1 { - for y in - (board_height + self.height_buffer - 8)..board_height + self.height_buffer - { + for y in (board_height + self.height_buffer - 8)..board_height + self.height_buffer { board.matrix[y as usize][x as usize] = Tile::new(false, false, 0u8, Shapes::I); } } diff --git a/src/game/piece.rs b/src/game/piece.rs index c0bffde..5b9cf29 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -186,7 +186,12 @@ impl Piece { } } - pub fn spawn_pos(&self, spawn_column: u8, spawn_row: u8, board_height_buffer: u8) -> [(u8, u8); 4] { + pub fn spawn_pos( + &self, + spawn_column: u8, + spawn_row: u8, + board_height_buffer: u8, + ) -> [(u8, u8); 4] { match self.shape { Shapes::None => { println!("[!] tried to spawn a piece with shape type Shapes::None"); @@ -196,7 +201,7 @@ impl Piece { [ (spawn_row + board_height_buffer, spawn_column - 2), // [-][-][-][-] | [-][-][0][-] (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] | [-][-][1][-] - (spawn_row + board_height_buffer, spawn_column), // [0][1][2][3] | [-][-][2][-] + (spawn_row + board_height_buffer, spawn_column), // [0][1][2][3] | [-][-][2][-] (spawn_row + board_height_buffer, spawn_column + 1), // [-][-][-][-] | [-][-][3][-] ] } From 14b06d51dd3a38ab37a76a4719646a0064412eb9 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 16:53:32 -0500 Subject: [PATCH 08/59] board rotates now, not piece, visually --- src/game.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/game.rs b/src/game.rs index 99f6fad..89b8f72 100644 --- a/src/game.rs +++ b/src/game.rs @@ -749,12 +749,23 @@ impl Game { // add each non-empty tile to the correct SpriteBatch for x in 0..self.bh.board.width { for y in 0..self.bh.board.height { - if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize] - .empty + // actually go through and add tiles to a spritebatch + if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].empty { + // account for the gravity direction in how to draw it (rotatris) + let center = self.bh.board.width / 2; + let is_center_even: u8 = (center + 1) % 2; + let (y_draw_pos, x_draw_pos) = match self.gravity_direction { + Movement::Down => (y, x), + Movement::Left => (center * 2 - x - is_center_even, y), + Movement::Up => (center * 2 - y - is_center_even, center * 2 - x - is_center_even), + Movement::Right => (x, center * 2 - y - is_center_even), + _ => panic!("[!] Error: self.gravity_direction is {}", self.gravity_direction as u8), + }; + // create the proper DrawParam and add to the spritebatch let player_tile = graphics::DrawParam::new().dest(Point2::new( - x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + x_draw_pos as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + y_draw_pos as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); if self.num_players > 1 { let player = self.bh.board.matrix From 9bd660c2aac018e9835adce713863348f36e3511 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 30 Jan 2021 21:10:30 -0500 Subject: [PATCH 09/59] proper spawning, spacing, board size --- src/game.rs | 22 +++++----- src/game/board.rs | 32 +++++++++++--- src/game/piece.rs | 105 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 136 insertions(+), 23 deletions(-) diff --git a/src/game.rs b/src/game.rs index 89b8f72..1969566 100644 --- a/src/game.rs +++ b/src/game.rs @@ -26,7 +26,7 @@ use crate::menu::MenuGameOptions; const BOARD_HEIGHT: u8 = 20u8; -const ROTATRIS_BOARD_SIDE_LENGTH: u8 = 16u8; +const ROTATRIS_BOARD_SIDE_LENGTH: u8 = 20u8; pub const CLEAR_DELAY: i8 = 0i8; @@ -75,12 +75,6 @@ pub enum Modes { Rotatris, } -pub enum InvisibilityLevels { - None, - Near, - All, -} - // this struct is for the Menu class so that it can return what game options to start the game with pub struct GameOptions { pub num_players: u8, @@ -374,6 +368,7 @@ impl Game { player.player_num, player.spawn_column, player.next_piece_shape, + self.gravity_direction, ); if blocked.0 { if blocked.1 { @@ -750,7 +745,8 @@ impl Game { for x in 0..self.bh.board.width { for y in 0..self.bh.board.height { // actually go through and add tiles to a spritebatch - if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize].empty + if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize] + .empty { // account for the gravity direction in how to draw it (rotatris) let center = self.bh.board.width / 2; @@ -758,9 +754,15 @@ impl Game { let (y_draw_pos, x_draw_pos) = match self.gravity_direction { Movement::Down => (y, x), Movement::Left => (center * 2 - x - is_center_even, y), - Movement::Up => (center * 2 - y - is_center_even, center * 2 - x - is_center_even), + Movement::Up => ( + center * 2 - y - is_center_even, + center * 2 - x - is_center_even, + ), Movement::Right => (x, center * 2 - y - is_center_even), - _ => panic!("[!] Error: self.gravity_direction is {}", self.gravity_direction as u8), + _ => panic!( + "[!] Error: self.gravity_direction is {}", + self.gravity_direction as u8 + ), }; // create the proper DrawParam and add to the spritebatch let player_tile = graphics::DrawParam::new().dest(Point2::new( diff --git a/src/game/board.rs b/src/game/board.rs index be26f4a..28e5179 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -173,15 +173,31 @@ impl Board { for _ in 0..num_players { vec_active_piece.push(Piece::new(Shapes::None)); } + let mut matrix = vec![ + vec![Tile::new_empty(); board_width as usize]; + (board_height + board_height_buffer) as usize + ]; + + // DEBUG + for a in 0..4 { + for b in 0..board_width as usize { + matrix[a][b] = Tile::new(false, false, 0, Shapes::I); + matrix[b][a] = Tile::new(false, false, 0, Shapes::I); + matrix[board_width as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); + matrix[b][board_width as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); + } + } + matrix[board_width as usize / 2][board_height as usize - 1] = Tile::new_empty(); + matrix[board_width as usize / 2][board_height as usize - 2] = Tile::new_empty(); + matrix[board_width as usize / 2][board_height as usize - 3] = Tile::new_empty(); + matrix[board_width as usize / 2][board_height as usize - 4] = Tile::new_empty(); + // DEBUG END Self { width: board_width, height: board_height, height_buffer: board_height_buffer, spawn_row, - matrix: vec![ - vec![Tile::new_empty(); board_width as usize]; - (board_height + board_height_buffer) as usize - ], + matrix, vec_active_piece, vec_full_lines: vec![], } @@ -227,9 +243,15 @@ impl Board { player: u8, spawn_col: u8, spawn_piece_shape: Shapes, + current_gravity: Movement, ) -> (bool, bool) { let new_piece = Piece::new(spawn_piece_shape); - let spawn_positions = new_piece.spawn_pos(spawn_col, self.spawn_row, self.height_buffer); + let spawn_positions = new_piece.spawn_pos( + spawn_col, + self.spawn_row, + self.height_buffer, + current_gravity, + ); let mut blocked_flag: bool = false; for position in spawn_positions.iter().take(4) { if !self.matrix[position.0 as usize][position.1 as usize].empty { diff --git a/src/game/piece.rs b/src/game/piece.rs index 5b9cf29..80ecb45 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -35,6 +35,7 @@ pub enum Movement { Right, RotateCw, RotateCcw, + DoubleRotate, None, } @@ -47,12 +48,14 @@ impl From for Movement { 3 => Movement::Right, 4 => Movement::RotateCw, 5 => Movement::RotateCcw, - 6 => Movement::None, + 6 => Movement::DoubleRotate, + 7 => Movement::None, _ => panic!("[!] Unknown Movement value: {}", value), } } } +#[derive(Copy, Clone)] pub struct Piece { pub shape: Shapes, pub positions: [(u8, u8); 4], @@ -191,8 +194,10 @@ impl Piece { spawn_column: u8, spawn_row: u8, board_height_buffer: u8, + current_gravity: Movement, ) -> [(u8, u8); 4] { - match self.shape { + let mut piece_copy = Self::new(self.shape); + piece_copy.positions = match piece_copy.shape { Shapes::None => { println!("[!] tried to spawn a piece with shape type Shapes::None"); [(0xff, 0xff); 4] @@ -201,14 +206,14 @@ impl Piece { [ (spawn_row + board_height_buffer, spawn_column - 2), // [-][-][-][-] | [-][-][0][-] (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] | [-][-][1][-] - (spawn_row + board_height_buffer, spawn_column), // [0][1][2][3] | [-][-][2][-] + (spawn_row + board_height_buffer, spawn_column), // [0][1][2][3] | [-][-][2][-] (spawn_row + board_height_buffer, spawn_column + 1), // [-][-][-][-] | [-][-][3][-] ] } Shapes::O => { [ (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] - (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] (spawn_row + 1 + board_height_buffer, spawn_column - 1), // [-][0][1][-] (spawn_row + 1 + board_height_buffer, spawn_column), // [-][2][3][-] ] @@ -253,6 +258,31 @@ impl Piece { (spawn_row + 1 + board_height_buffer, spawn_column + 1), // [-][-][2][3] | [-][-][0][-] ] } + }; + if piece_copy.shape == Shapes::None { + panic!("[!] Error: `Piece::spawn_pos()` called with shape: Shapes::None"); + } else if piece_copy.shape == Shapes::O { + piece_copy.positions + } else { + match current_gravity { + Movement::Down => piece_copy.positions, + Movement::Left => { + piece_copy.positions = piece_copy.rotate(true); + piece_copy.piece_pos(Movement::Right) + } + Movement::Up => { + piece_copy.positions = piece_copy.double_rotate(); + piece_copy.piece_pos(Movement::Down) + } + Movement::Right => { + piece_copy.positions = piece_copy.rotate(false); + piece_copy.piece_pos(Movement::Down) + } + _ => panic!( + "[!] Error: current_gravity is invalid: {} (in `Piece::spawn_pos()`)", + current_gravity as u8 + ), + } } } @@ -294,16 +324,22 @@ impl Piece { if self.num_rotations == 4 { if r#move == Movement::RotateCw { self.rotate(true) - } else { + } else if r#move == Movement::RotateCcw { self.rotate(false) + } else { + self.double_rotate() } // I, S, Z } else if self.num_rotations == 2 { // the I piece is special in that it starts with rotation: 1 so that it lines up with S and Z - if self.rotation == 0 { - self.rotate(false) + if r#move != Movement::DoubleRotate { + if self.rotation == 0 { + self.rotate(false) + } else { + self.rotate(true) + } } else { - self.rotate(true) + self.positions } } else if self.num_rotations == 1 { self.positions @@ -378,6 +414,59 @@ impl Piece { ] } } + + fn double_rotate(&self) -> [(u8, u8); 4] { + if self.pivot > 3 { + println!("[!] tried to rotate piece with pivot fields {}", self.pivot); + return self.positions; + } + let pivot = self.pivot; + let positions = [ + ( + self.positions[self.pivot as usize].0 + + (self.positions[0].1 - self.positions[self.pivot as usize].1), + self.positions[self.pivot as usize].1 + + (self.positions[self.pivot as usize].0 - self.positions[0].0), + ), + ( + self.positions[self.pivot as usize].0 + + (self.positions[1].1 - self.positions[self.pivot as usize].1), + self.positions[self.pivot as usize].1 + + (self.positions[self.pivot as usize].0 - self.positions[1].0), + ), + ( + self.positions[self.pivot as usize].0 + + (self.positions[2].1 - self.positions[self.pivot as usize].1), + self.positions[self.pivot as usize].1 + + (self.positions[self.pivot as usize].0 - self.positions[2].0), + ), + ( + self.positions[self.pivot as usize].0 + + (self.positions[3].1 - self.positions[self.pivot as usize].1), + self.positions[self.pivot as usize].1 + + (self.positions[self.pivot as usize].0 - self.positions[3].0), + ), + ]; + + [ + ( + positions[pivot as usize].0 + (positions[0].1 - positions[pivot as usize].1), + positions[pivot as usize].1 + (positions[pivot as usize].0 - positions[0].0), + ), + ( + positions[pivot as usize].0 + (positions[1].1 - positions[pivot as usize].1), + positions[pivot as usize].1 + (positions[pivot as usize].0 - positions[1].0), + ), + ( + positions[pivot as usize].0 + (positions[2].1 - positions[pivot as usize].1), + positions[pivot as usize].1 + (positions[pivot as usize].0 - positions[2].0), + ), + ( + positions[pivot as usize].0 + (positions[3].1 - positions[pivot as usize].1), + positions[pivot as usize].1 + (positions[pivot as usize].0 - positions[3].0), + ), + ] + } } #[derive(PartialEq, Eq)] From 3380b996b65d022ee7f82b3b5a985523ecf2cdef Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sun, 31 Jan 2021 13:47:59 -0500 Subject: [PATCH 10/59] actually useful changes for rotatris --- src/game/board.rs | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/game/board.rs b/src/game/board.rs index 28e5179..8e93eb5 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -89,19 +89,37 @@ impl BoardHandler { pub fn rotatris_attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { let mut num_cleared_rings = 0; let mut score_from_cleared_rings = 0; - let num_rings_to_check = self.board.width / 2 - 4; + let num_rings_to_check = self.board.width / 2 - 3; // go from inner rings to outer rings checking if any ring is full, avoiding the middle 4 rings for z in (0..num_rings_to_check).rev() { if self.rotatris_check_single_ring(z) { num_cleared_rings += 1; // clear and pull inner stuff out - for j in (z + 1)..num_rings_to_check { + for j in (z + 1)..(self.board.width / 2) { self.rotatris_pull_single_ring_out(j); } } } + score_from_cleared_rings += match num_cleared_rings { + 0 => return (0, 0), + 1 => SCORE_SINGLE_BASE as u32 * (level as u32 + 1), + 2 => SCORE_DOUBLE_BASE as u32 * (level as u32 + 1), + 3 => SCORE_TRIPLE_BASE as u32 * (level as u32 + 1), + 4 => SCORE_QUADRUPLE_BASE as u32 * (level as u32 + 1), + _ => { + println!("[!] player was attributed a number of rings too large maybe, what the heck? num_cleared_rings: {}", num_cleared_rings); + 0u32 + } + }; + + if num_cleared_rings > 0 { + for z in num_rings_to_check..(self.board.width + 1 / 2) { + self.rotatris_emptify_single_ring(z); + } + } + (num_cleared_rings, score_from_cleared_rings) } @@ -145,6 +163,20 @@ impl BoardHandler { self.board.matrix[k + 1][j - 1] = self.board.matrix[k][j]; self.board.matrix[k + 1][k + 1] = self.board.matrix[k][k]; } + + fn rotatris_emptify_single_ring(&mut self, z: u8) { + let board_side_length = self.board.width; + for a in [z, board_side_length - z - 1].into_iter() { + for b in z..(board_side_length - z) { + if b >= z && b <= board_side_length - z { + self.board.matrix[*a as usize][b as usize].empty = true; + self.board.matrix[*a as usize][b as usize].active = false; + self.board.matrix[b as usize][*a as usize].empty = true; + self.board.matrix[b as usize][*a as usize].active = false; + } + } + } + } } // example Board coordinates system (2 width, 2 height) @@ -187,6 +219,7 @@ impl Board { matrix[b][board_width as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); } } + matrix[9][9] = Tile::new(false, false, 0, Shapes::I); matrix[board_width as usize / 2][board_height as usize - 1] = Tile::new_empty(); matrix[board_width as usize / 2][board_height as usize - 2] = Tile::new_empty(); matrix[board_width as usize / 2][board_height as usize - 3] = Tile::new_empty(); From 0a7c6c956ae8fabb08e98a6abc54f8347b1c616e Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Mon, 22 Mar 2021 22:04:00 -0400 Subject: [PATCH 11/59] oops, now conflicts resolved --- src/game.rs | 13 --- src/game/board.rs | 246 +++++++++++++++++++++++----------------------- 2 files changed, 123 insertions(+), 136 deletions(-) diff --git a/src/game.rs b/src/game.rs index e8bf4d0..b828156 100644 --- a/src/game.rs +++ b/src/game.rs @@ -749,7 +749,6 @@ impl Game { self.draw_text(ctx, &self.pause_text, 0.4, &(window_width, window_height)); } else { // DRAW GAME -<<<<<<< HEAD // add each non-empty tile to the correct SpriteBatch for x in 0..self.bh.board.width { for y in 0..self.bh.board.height { @@ -774,20 +773,10 @@ impl Game { ), }; // create the proper DrawParam and add to the spritebatch -======= - // add each non-empty tile to the correct SpriteBatch and add highlights to active pieces - for x in 0..self.board.width { - for y in 0..self.board.height { - if !self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize].empty { - let player = self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize] - [x as usize] - .player; ->>>>>>> 64d404ebd4ff5e15e394839d9da9c787e0381db6 let player_tile = graphics::DrawParam::new().dest(Point2::new( x_draw_pos as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y_draw_pos as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); -<<<<<<< HEAD if self.num_players > 1 { let player = self.bh.board.matrix [(y + self.bh.board.height_buffer) as usize] @@ -832,7 +821,6 @@ impl Game { { self.vec_batch_player_piece[2].add(player_tile); } -======= self.vec_batch_player_piece[player as usize].add(player_tile); // highlight if active if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize] @@ -897,7 +885,6 @@ impl Game { > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) { break; ->>>>>>> 64d404ebd4ff5e15e394839d9da9c787e0381db6 } } } diff --git a/src/game/board.rs b/src/game/board.rs index 921a0c0..34a3408 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -15,88 +15,22 @@ pub struct BoardHandler { impl BoardHandler { pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: Modes) -> Self { let (board_height_buffer, piece_spawn_row, rotatris) = match mode { - Modes::Rotatris => (0, (board_height - 1) / 2, Some(BoardRotatris::new())), + Modes::Rotatris => (0, (board_height - 1) / 2, Some(BoardRotatris::new(board_width, board_width / 2, num_players))), Modes::Classic => (2, 0, None), }; Self { mode, - board: BoardClassic::new( - board_width, - board_height, - board_height_buffer, - piece_spawn_row, - num_players, - ), + classic, rotatris, } } pub fn attempt_clear_lines(&mut self, level: u8) -> (u8, u32) { match self.mode { - Modes::Classic => self.board.attempt_clear_lines(level), - Modes::Rotatris => self.rotatris_attempt_clear_rings(level), + Modes::Classic => self.classic.expect("[!] BoardHandler has wrong option").attempt_clear_lines(level), + Modes::Rotatris => self.rotatris.expect("[!] BoardHandler has wrong option").rotatris_attempt_clear_rings(level), } } - - pub fn rotatris_attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { - let mut num_cleared_rings = 0; - let mut score_from_cleared_rings = 0; - let num_rings_to_check = self.board.width / 2 - 4; - - // go from inner rings to outer rings checking if any ring is full, avoiding the middle 4 rings - for z in (0..num_rings_to_check).rev() { - if self.rotatris_check_single_ring(z) { - num_cleared_rings += 1; - // clear and pull inner stuff out - for j in (z + 1)..num_rings_to_check { - self.rotatris_pull_single_ring_out(j); - } - } - } - - (num_cleared_rings, score_from_cleared_rings) - } - - fn rotatris_check_single_ring(&mut self, z: u8) -> bool { - let board_side_length = self.board.width; - for a in [z, board_side_length - z - 1].into_iter() { - for b in z..(board_side_length - z) { - if b >= z && b <= board_side_length - z { - if self.board.matrix[*a as usize][b as usize].empty - || self.board.matrix[*a as usize][b as usize].active - || self.board.matrix[b as usize][*a as usize].empty - || self.board.matrix[b as usize][*a as usize].active - { - return false; - } - } - } - } - - true - } - - fn rotatris_pull_single_ring_out(&mut self, j: u8) { - let j = j as usize; - let k = self.board.width as usize - j - 1; - - // sides - for a in j..=k { - // top - self.board.matrix[j - 1][a] = self.board.matrix[j][a]; - // left - self.board.matrix[a][j - 1] = self.board.matrix[a][j]; - // down - self.board.matrix[k + 1][a] = self.board.matrix[k][a]; - // right - self.board.matrix[a][k + 1] = self.board.matrix[a][k]; - } - // corners - self.board.matrix[j - 1][j - 1] = self.board.matrix[j][j]; - self.board.matrix[j - 1][k + 1] = self.board.matrix[j][k]; - self.board.matrix[k + 1][j - 1] = self.board.matrix[k][j]; - self.board.matrix[k + 1][k + 1] = self.board.matrix[k][k]; - } } // example Board coordinates system (2 width, 2 height) @@ -126,34 +60,12 @@ impl BoardClassic { vec_active_piece.push(Piece::new(Shapes::None)); } let mut matrix = vec![ - vec![Tile::new_empty(); board_width as usize]; + vec![Tile::default(); board_width as usize]; (board_height + board_height_buffer) as usize ]; - // DEBUG - for a in 0..4 { - for b in 0..board_width as usize { - matrix[a][b] = Tile::new(false, false, 0, Shapes::I); - matrix[b][a] = Tile::new(false, false, 0, Shapes::I); - matrix[board_width as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); - matrix[b][board_width as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); - } - } - matrix[board_width as usize / 2][board_height as usize - 1] = Tile::new_empty(); - matrix[board_width as usize / 2][board_height as usize - 2] = Tile::new_empty(); - matrix[board_width as usize / 2][board_height as usize - 3] = Tile::new_empty(); - matrix[board_width as usize / 2][board_height as usize - 4] = Tile::new_empty(); - // DEBUG END - Self { - width: board_width, - height: board_height, - height_buffer: board_height_buffer, - spawn_row, - matrix, - ]; - // DEBUG TILES ADDED - // let mut matrix = vec![vec![Tile::new_empty(); board_width as usize]; (board_height + BOARD_HEIGHT_BUFFER_U) as usize]; + // let mut matrix = vec![vec![Tile::default(); board_width as usize]; (board_height + BOARD_HEIGHT_BUFFER_U) as usize]; // for x in 0..(board_width - 1) { // for y in (board_height + BOARD_HEIGHT_BUFFER_U - 8)..(board_height + BOARD_HEIGHT_BUFFER_U) { // matrix[y as usize][x as usize] = Tile::new(false, false, 0u8); @@ -163,6 +75,8 @@ impl BoardClassic { Self { width: board_width, height: board_height, + height_buffer: board_height_buffer, + spawn_row: board_height_buffer, matrix, vec_active_piece, vec_full_lines: vec![], @@ -560,9 +474,10 @@ impl FullLine { // other modes pub struct BoardRotatris { pub board_rotation: u8, // 0, 1, 2, 3: 0, 90, 180, 270; CW - pub side_length: u8, + pub board_side_length: u8, pub spawn_row: u8, pub matrix: Vec>, + pub vec_active_piece: Vec, } impl BoardRotatris { @@ -576,49 +491,41 @@ impl BoardRotatris { vec_active_piece.push(Piece::new(Shapes::None)); } let mut matrix = vec![ - vec![Tile::new_empty(); board_side_length as usize]; + vec![Tile::default(); board_side_length as usize]; board_side_length as usize ]; // DEBUG for a in 0..4 { - for b in 0..board_width as usize { + for b in 0..board_side_length as usize { matrix[a][b] = Tile::new(false, false, 0, Shapes::I); matrix[b][a] = Tile::new(false, false, 0, Shapes::I); - matrix[board_width as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); - matrix[b][board_width as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); + matrix[board_side_length as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); + matrix[b][board_side_length as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); } } - matrix[board_width as usize / 2][board_height as usize - 1] = Tile::new_empty(); - matrix[board_width as usize / 2][board_height as usize - 2] = Tile::new_empty(); - matrix[board_width as usize / 2][board_height as usize - 3] = Tile::new_empty(); - matrix[board_width as usize / 2][board_height as usize - 4] = Tile::new_empty(); - // DEBUG END - Self { - width: board_width, - height: board_height, - height_buffer: board_height_buffer, - spawn_row, - matrix, - ]; + matrix[board_side_length as usize / 2][board_side_length as usize - 1] = Tile::default(); + matrix[board_side_length as usize / 2][board_side_length as usize - 2] = Tile::default(); + matrix[board_side_length as usize / 2][board_side_length as usize - 3] = Tile::default(); + matrix[board_side_length as usize / 2][board_side_length as usize - 4] = Tile::default(); Self { - width: board_width, - height: board_height, + board_rotation: 0, + board_side_length, + spawn_row, matrix, vec_active_piece, - vec_full_lines: vec![], } } // return bool is if rotate was successful pub fn rotatris_attempt_rotate_board(&mut self, rotate_direction: Movement) -> bool { - let center: u8 = self.board.width / 2; - let is_center_even: u8 = (self.board.width + 1) % 2; + let center: u8 = self.board_side_length / 2; + let is_center_even: u8 = (self.board_side_length + 1) % 2; let mut new_positions: [(u8, u8); 4] = [(0u8, 0u8); 4]; match rotate_direction { Movement::RotateCw => { - for (index, position) in self.board.vec_active_piece[0] + for (index, position) in self.vec_active_piece[0] .positions .iter() .take(4) @@ -628,7 +535,7 @@ impl BoardRotatris { } } Movement::RotateCcw => { - for (index, position) in self.board.vec_active_piece[0] + for (index, position) in self.vec_active_piece[0] .positions .iter() .take(4) @@ -645,19 +552,112 @@ impl BoardRotatris { // check validity of new positions for position in new_positions.iter().take(4) { - if !self.board.matrix[position.0 as usize][position.1 as usize].empty - && !self.board.matrix[position.0 as usize][position.1 as usize].active + if !self.matrix[position.0 as usize][position.1 as usize].empty + && !self.matrix[position.0 as usize][position.1 as usize].active { return false; } } - self.board.emptify_piece(0); - self.board.vec_active_piece[0].positions = new_positions; - self.board.playerify_piece(0); + self.emptify_piece(0); + self.vec_active_piece[0].positions = new_positions; + self.playerify_piece(0); + + true + } + + pub fn playerify_piece(&mut self, player: u8) { + for position in self.vec_active_piece[player as usize] + .positions + .iter() + .take(4) + { + if position != &(0xffu8, 0xffu8) { + self.matrix[position.0 as usize][position.1 as usize] = Tile::new( + false, + true, + player, + self.vec_active_piece[player as usize].shape, + ); + } else { + println!("[!] tried to playerify piece that contained position (0xffu8, 0xffu8)"); + } + } + } + + fn emptify_piece(&mut self, player: u8) { + for position in self.vec_active_piece[player as usize] + .positions + .iter() + .take(4) + { + if position != &(0xffu8, 0xffu8) { + self.matrix[position.0 as usize][position.1 as usize].empty = true; + self.matrix[position.0 as usize][position.1 as usize].active = false; + } else { + println!("[!] tried to emptify piece that contained position (0xffu8, 0xffu8)"); + } + } + } + + pub fn rotatris_attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { + let mut num_cleared_rings = 0; + let mut score_from_cleared_rings = 0; + let num_rings_to_check = self.board_side_length / 2 - 4; + + // go from inner rings to outer rings checking if any ring is full, avoiding the middle 4 rings + for z in (0..num_rings_to_check).rev() { + if self.rotatris_check_single_ring(z) { + num_cleared_rings += 1; + // clear and pull inner stuff out + for j in (z + 1)..num_rings_to_check { + self.rotatris_pull_single_ring_out(j); + } + } + } + + (num_cleared_rings, score_from_cleared_rings) + } + + fn rotatris_check_single_ring(&mut self, z: u8) -> bool { + for a in [z, self.board_side_length - z - 1].into_iter() { + for b in z..(self.board_side_length - z) { + if b >= z && b <= self.board_side_length - z { + if self.matrix[*a as usize][b as usize].empty + || self.matrix[*a as usize][b as usize].active + || self.matrix[b as usize][*a as usize].empty + || self.matrix[b as usize][*a as usize].active + { + return false; + } + } + } + } true } + + fn rotatris_pull_single_ring_out(&mut self, j: u8) { + let j = j as usize; + let k = self.board_side_length as usize - j - 1; + + // sides + for a in j..=k { + // top + self.matrix[j - 1][a] = self.matrix[j][a]; + // left + self.matrix[a][j - 1] = self.matrix[a][j]; + // down + self.matrix[k + 1][a] = self.matrix[k][a]; + // right + self.matrix[a][k + 1] = self.matrix[a][k]; + } + // corners + self.matrix[j - 1][j - 1] = self.matrix[j][j]; + self.matrix[j - 1][k + 1] = self.matrix[j][k]; + self.matrix[k + 1][j - 1] = self.matrix[k][j]; + self.matrix[k + 1][k + 1] = self.matrix[k][k]; + } } From db59d2fe8c55419a57400511c69418490b958e11 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 23 Mar 2021 00:08:17 -0400 Subject: [PATCH 12/59] yay only 18 errors left --- src/game.rs | 199 ++++++++++-------------- src/game/board.rs | 388 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 417 insertions(+), 170 deletions(-) diff --git a/src/game.rs b/src/game.rs index b828156..0947a07 100644 --- a/src/game.rs +++ b/src/game.rs @@ -427,14 +427,14 @@ impl Game { // rotatris // BOARD ROTATION if self.rotate_board_cw.1 { - if self.bh.rotatris_attempt_rotate_board(Movement::RotateCw) { + if self.bh.attempt_rotate_board(Movement::RotateCw) { self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 1) % 4); } } if self.rotate_board_ccw.1 { - if self.bh.rotatris_attempt_rotate_board(Movement::RotateCcw) { + if self.bh.attempt_rotate_board(Movement::RotateCcw) { self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 3) % 4); } @@ -447,13 +447,11 @@ impl Game { // if it didn't move on the initial input, set waiting_to_shift to true player.waiting_to_shift = !self .bh - .board .attempt_piece_movement( Movement::from( (Movement::Left as u8 + self.gravity_direction as u8) % 4, ), player.player_num, - self.gravity_direction, ) .0; player.das_countdown = DAS_THRESHOLD_BIG; @@ -462,13 +460,11 @@ impl Game { // if it didn't move on the initial input, set waiting_to_shift to true player.waiting_to_shift = !self .bh - .board .attempt_piece_movement( Movement::from( (Movement::Right as u8 + self.gravity_direction as u8) % 4, ), player.player_num, - self.gravity_direction, ) .0; player.das_countdown = DAS_THRESHOLD_BIG; @@ -480,13 +476,11 @@ impl Game { if player.das_countdown == 0 || player.waiting_to_shift { if self .bh - .board .attempt_piece_movement( Movement::from( (Movement::Left as u8 + self.gravity_direction as u8) % 4, ), player.player_num, - self.gravity_direction, ) .0 { @@ -505,13 +499,11 @@ impl Game { if player.das_countdown == 0 || player.waiting_to_shift { if self .bh - .board .attempt_piece_movement( Movement::from( (Movement::Right as u8 + self.gravity_direction as u8) % 4, ), player.player_num, - self.gravity_direction, ) .0 { @@ -525,18 +517,12 @@ impl Game { } // CW / CCW if player.input.keydown_rotate_cw.1 { - self.bh.board.attempt_piece_movement( - Movement::RotateCw, - player.player_num, - self.gravity_direction, - ); + self.bh + .attempt_piece_movement(Movement::RotateCw, player.player_num); } if player.input.keydown_rotate_ccw.1 { - self.bh.board.attempt_piece_movement( - Movement::RotateCcw, - player.player_num, - self.gravity_direction, - ); + self.bh + .attempt_piece_movement(Movement::RotateCcw, player.player_num); } // DOWN // down is interesting because every time the downwards position is false we have to check if it's running into the bottom or an inactive tile so we know if we should lock it @@ -545,17 +531,14 @@ impl Game { || player.fall_countdown == 0 { let (moved_flag, caused_full_line_flag): (bool, bool) = - self.bh.board.attempt_piece_movement( + self.bh.attempt_piece_movement( Movement::from( (Movement::Down as u8 + self.gravity_direction as u8) % 4, ), player.player_num, - self.gravity_direction, ); // if the piece got locked, piece.shape gets set to Shapes::None, so set the spawn piece flag - if self.bh.board.vec_active_piece[player.player_num as usize].shape - == Shapes::None - { + if self.bh.get_shape_from_player(player.player_num) == Shapes::None { player.spawn_piece_flag = true; player.fall_countdown = if self.level < 30 { FALL_DELAY_VALUES[self.level as usize] @@ -723,12 +706,16 @@ impl Game { // then when we actually draw the board, we scale it to the appropriate size and place the top left corner of the board at the appropriate place; // there's a sprite batch for each players' tiles and one more for the empty tiles, which is constant, and the player tiles are drawn after so they are on top pub fn draw(&mut self, ctx: &mut Context) { + // constants used throughout draw + let height_buffer = self.bh.get_height_buffer(); + + // start doing drawing stuff graphics::clear(ctx, graphics::BLACK); let (window_width, window_height) = graphics::size(ctx); self.tile_size = TileGraphic::get_size( ctx, - self.bh.board.width, - self.bh.board.height + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D, + self.bh.get_width(), + self.bh.get_height() + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D, ); if self.game_over_flag && self.game_over_delay == 0 { // DRAW GAME OVER @@ -750,14 +737,12 @@ impl Game { } else { // DRAW GAME // add each non-empty tile to the correct SpriteBatch - for x in 0..self.bh.board.width { - for y in 0..self.bh.board.height { + for x in 0..self.bh.get_width() { + for y in 0..self.bh.get_height() { // actually go through and add tiles to a spritebatch - if !self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize][x as usize] - .empty - { + if !self.bh.get_empty_from_pos(y + height_buffer, x) { // account for the gravity direction in how to draw it (rotatris) - let center = self.bh.board.width / 2; + let center = self.bh.get_width() / 2; let is_center_even: u8 = (center + 1) % 2; let (y_draw_pos, x_draw_pos) = match self.gravity_direction { Movement::Down => (y, x), @@ -778,113 +763,85 @@ impl Game { y_draw_pos as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); if self.num_players > 1 { - let player = self.bh.board.matrix - [(y + self.bh.board.height_buffer) as usize] - [x as usize] - .player; + let player = self.bh.get_player_from_pos(y + height_buffer, x); self.vec_batch_player_piece[player as usize].add(player_tile); } else { - if self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] - [x as usize] - .shape - == Shapes::J - || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] - [x as usize] - .shape - == Shapes::S - { + let shape: Shapes = self.bh.get_shape_from_pos(y + height_buffer, x); + if shape == Shapes::J || shape == Shapes::S { self.vec_batch_player_piece[0].add(player_tile); - } else if self.bh.board.matrix - [(y + self.bh.board.height_buffer) as usize] - [x as usize] - .shape - == Shapes::L - || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] - [x as usize] - .shape - == Shapes::Z - { + } else if shape == Shapes::L || shape == Shapes::Z { self.vec_batch_player_piece[1].add(player_tile); - } else if self.bh.board.matrix - [(y + self.bh.board.height_buffer) as usize] - [x as usize] - .shape - == Shapes::I - || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] - [x as usize] - .shape - == Shapes::O - || self.bh.board.matrix[(y + self.bh.board.height_buffer) as usize] - [x as usize] - .shape - == Shapes::T + } else if shape == Shapes::I || shape == Shapes::O || shape == Shapes::T { self.vec_batch_player_piece[2].add(player_tile); } + } self.vec_batch_player_piece[player as usize].add(player_tile); // highlight if active - if self.board.matrix[(y + BOARD_HEIGHT_BUFFER_U) as usize][x as usize] - .active - { + if self.bh.get_active_from_pos(y + BOARD_HEIGHT_BUFFER_U, x) { self.batch_highlight_active_tile.add(player_tile); } } } } // line clear highlights - for full_line in self.board.vec_full_lines.iter() { - if full_line.lines_cleared_together < 4 { - // standard clear animation - let y = (full_line.row - BOARD_HEIGHT_BUFFER_U) as usize; - let board_max_index_remainder_2 = (self.board.width - 1) % 2; - // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 - for x in (self.board.width / 2)..self.board.width { - let highlight_pos_right = graphics::DrawParam::new().dest(Point2::new( - x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - )); - let highlight_pos_left = graphics::DrawParam::new().dest(Point2::new( - (self.board.width as f32 - (x + board_max_index_remainder_2) as f32) - * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - )); + if let Some(classic) = self.bh.classic { + for full_line in classic.vec_full_lines.iter() { + if full_line.lines_cleared_together < 4 { + // standard clear animation + let y = (full_line.row - BOARD_HEIGHT_BUFFER_U) as usize; + let board_max_index_remainder_2 = (self.bh.get_width() - 1) % 2; + // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 + for x in (self.bh.get_width() / 2)..self.bh.get_width() { + let highlight_pos_right = graphics::DrawParam::new().dest(Point2::new( + x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + )); + let highlight_pos_left = graphics::DrawParam::new().dest(Point2::new( + (self.bh.get_width() as f32 + - (x + board_max_index_remainder_2) as f32) + * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + )); - self.batch_highlight_clearing_standard_tile - .add(highlight_pos_right); - self.batch_highlight_clearing_standard_tile - .add(highlight_pos_left); + self.batch_highlight_clearing_standard_tile + .add(highlight_pos_right); + self.batch_highlight_clearing_standard_tile + .add(highlight_pos_left); - if ((x as f32) / (self.board.width as f32) - 0.5) * 2.0 - > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) - { - break; + if ((x as f32) / (self.bh.get_width() as f32) - 0.5) * 2.0 + > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) + { + break; + } } - } - } else { - // tetrisnt clear animation - let y = (full_line.row - BOARD_HEIGHT_BUFFER_U) as usize; - let board_max_index_remainder_2 = (self.board.width - 1) % 2; - // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 - for x in (self.board.width / 2)..self.board.width { - let highlight_pos_right = graphics::DrawParam::new().dest(Point2::new( - x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - )); - let highlight_pos_left = graphics::DrawParam::new().dest(Point2::new( - (self.board.width as f32 - (x + board_max_index_remainder_2) as f32) - * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, - )); + } else { + // tetrisnt clear animation + let y = (full_line.row - BOARD_HEIGHT_BUFFER_U) as usize; + let board_max_index_remainder_2 = (self.bh.get_width() - 1) % 2; + // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 + for x in (self.bh.get_width() / 2)..self.bh.get_width() { + let highlight_pos_right = graphics::DrawParam::new().dest(Point2::new( + x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + )); + let highlight_pos_left = graphics::DrawParam::new().dest(Point2::new( + (self.bh.get_width() as f32 + - (x + board_max_index_remainder_2) as f32) + * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, + )); - self.batch_highlight_clearing_tetrisnt_tile - .add(highlight_pos_right); - self.batch_highlight_clearing_tetrisnt_tile - .add(highlight_pos_left); + self.batch_highlight_clearing_tetrisnt_tile + .add(highlight_pos_right); + self.batch_highlight_clearing_tetrisnt_tile + .add(highlight_pos_left); - if ((x as f32) / (self.board.width as f32) - 0.5) * 2.0 - > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) - { - break; + if ((x as f32) / (self.bh.get_width() as f32) - 0.5) * 2.0 + > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) + { + break; + } } } } @@ -945,7 +902,7 @@ impl Game { let board_top_left_corner = window_width / 2.0 - (scaled_tile_size * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32 - * self.bh.board.width as f32 + * self.bh.get_width() as f32 / 2.0); // empty tiles graphics::draw( diff --git a/src/game/board.rs b/src/game/board.rs index cd0ceaa..42857c7 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -5,6 +5,28 @@ use crate::game::{ CLEAR_DELAY, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, SCORE_TRIPLE_BASE, }; +static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong option"; + +#[repr(u8)] +pub enum Gravity { + Down, + Left, + Up, + Right, +} + +impl From for Gravity { + fn from(value: u8) -> Gravity { + match value { + 0 => Gravity::Down, + 1 => Gravity::Left, + 2 => Gravity::Up, + 3 => Gravity::Right, + _ => panic!("Unknown Gravity value: {}", value), + } + } +} + // abstract the board and the possible gamemodes into one struct pub struct BoardHandler { pub mode: Modes, @@ -15,7 +37,15 @@ pub struct BoardHandler { impl BoardHandler { pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: Modes) -> Self { let (board_height_buffer, piece_spawn_row, rotatris) = match mode { - Modes::Rotatris => (0, (board_height - 1) / 2, Some(BoardRotatris::new(board_width, board_width / 2, num_players))), + Modes::Rotatris => ( + 0, + (board_height - 1) / 2, + Some(BoardRotatris::new( + board_width, + board_width / 2, + num_players, + )), + ), Modes::Classic => (2, 0, None), }; Self { @@ -25,10 +55,117 @@ impl BoardHandler { } } + // get... + pub fn get_width(&mut self) -> u8 { + match self.mode { + Modes::Classic => self.classic.expect(BH_WRONG_MODE).width, + Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_side_length, + } + } + + pub fn get_height(&mut self) -> u8 { + match self.mode { + Modes::Classic => self.classic.expect(BH_WRONG_MODE).height, + Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_side_length, + } + } + + pub fn get_height_buffer(&mut self) -> u8 { + match self.mode { + Modes::Classic => self.classic.expect(BH_WRONG_MODE).height_buffer, + Modes::Rotatris => 0u8, + } + } + + pub fn get_shape_from_pos(&mut self, y: u8, x: u8) -> Shapes { + match self.mode { + Modes::Classic => { + self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape + } + Modes::Rotatris => { + self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape + } + } + } + + pub fn get_active_from_pos(&mut self, y: u8, x: u8) -> bool { + match self.mode { + Modes::Classic => { + self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active + } + Modes::Rotatris => { + self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active + } + } + } + + pub fn get_empty_from_pos(&mut self, y: u8, x: u8) -> bool { + match self.mode { + Modes::Classic => { + self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty + } + Modes::Rotatris => { + self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty + } + } + } + + pub fn get_player_from_pos(&mut self, y: u8, x: u8) -> u8 { + match self.mode { + Modes::Classic => { + self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player + } + Modes::Rotatris => { + self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player + } + } + } + + pub fn get_shape_from_player(&mut self, player: u8) -> Shapes { + match self.mode { + Modes::Classic => { + self.classic.expect(BH_WRONG_MODE).vec_active_piece[player as usize].shape + } + Modes::Rotatris => { + self.rotatris.expect(BH_WRONG_MODE).vec_active_piece[player as usize].shape + } + } + } + + // board logic functions pub fn attempt_clear_lines(&mut self, level: u8) -> (u8, u32) { match self.mode { - Modes::Classic => self.classic.expect("[!] BoardHandler has wrong option").attempt_clear_lines(level), - Modes::Rotatris => self.rotatris.expect("[!] BoardHandler has wrong option").rotatris_attempt_clear_rings(level), + Modes::Classic => self + .classic + .expect(BH_WRONG_MODE) + .attempt_clear_lines(level), + Modes::Rotatris => self + .rotatris + .expect(BH_WRONG_MODE) + .rotatris_attempt_clear_rings(level), + } + } + + pub fn attempt_piece_movement(&mut self, m: Movement, p: u8) -> (bool, bool) { + match self.mode { + Modes::Classic => self + .classic + .expect(BH_WRONG_MODE) + .attempt_piece_movement(m, p), + Modes::Rotatris => self + .rotatris + .expect(BH_WRONG_MODE) + .attempt_piece_movement(m, p), + } + } + + pub fn attempt_rotate_board(&mut self, rd: Movement) -> bool { + match self.mode { + Modes::Classic => { + println!("[!] `attempt_rotate_board` called but mode is Modes::Classic"); + false; + } + Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).attempt_rotate_board(rd), } } } @@ -157,12 +294,7 @@ impl BoardClassic { // returns (bool, bool) based on (if piece moved successfully, if (piece is locked && filled some line)) // sets the shape of the piece to Shapes::None if it locks - pub fn attempt_piece_movement( - &mut self, - movement: Movement, - player: u8, - current_gravity: Movement, - ) -> (bool, bool) { + pub fn attempt_piece_movement(&mut self, movement: Movement, player: u8) -> (bool, bool) { let mut cant_move_flag = false; // determine if it can move let new_positions = self.vec_active_piece[player as usize].piece_pos(movement); @@ -473,7 +605,7 @@ impl FullLine { // other modes pub struct BoardRotatris { - pub board_rotation: u8, // 0, 1, 2, 3: 0, 90, 180, 270; CW + pub gravity: Gravity, pub board_side_length: u8, pub spawn_row: u8, pub matrix: Vec>, @@ -481,27 +613,23 @@ pub struct BoardRotatris { } impl BoardRotatris { - pub fn new( - board_side_length: u8, - spawn_row: u8, - num_players: u8, - ) -> Self { + pub fn new(board_side_length: u8, spawn_row: u8, num_players: u8) -> Self { let mut vec_active_piece: Vec = Vec::with_capacity(num_players as usize); for _ in 0..num_players { vec_active_piece.push(Piece::new(Shapes::None)); } - let mut matrix = vec![ - vec![Tile::default(); board_side_length as usize]; - board_side_length as usize - ]; + let mut matrix = + vec![vec![Tile::default(); board_side_length as usize]; board_side_length as usize]; // DEBUG for a in 0..4 { for b in 0..board_side_length as usize { matrix[a][b] = Tile::new(false, false, 0, Shapes::I); matrix[b][a] = Tile::new(false, false, 0, Shapes::I); - matrix[board_side_length as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); - matrix[b][board_side_length as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); + matrix[board_side_length as usize - a - 1][b] = + Tile::new(false, false, 0, Shapes::I); + matrix[b][board_side_length as usize - a - 1] = + Tile::new(false, false, 0, Shapes::I); } } matrix[board_side_length as usize / 2][board_side_length as usize - 1] = Tile::default(); @@ -510,7 +638,7 @@ impl BoardRotatris { matrix[board_side_length as usize / 2][board_side_length as usize - 4] = Tile::default(); Self { - board_rotation: 0, + gravity: Gravity::Down, board_side_length, spawn_row, matrix, @@ -519,7 +647,7 @@ impl BoardRotatris { } // return bool is if rotate was successful - pub fn rotatris_attempt_rotate_board(&mut self, rotate_direction: Movement) -> bool { + pub fn attempt_rotate_board(&mut self, rotate_direction: Movement) -> bool { let center: u8 = self.board_side_length / 2; let is_center_even: u8 = (self.board_side_length + 1) % 2; let mut new_positions: [(u8, u8); 4] = [(0u8, 0u8); 4]; @@ -545,7 +673,7 @@ impl BoardRotatris { } } _ => { - println!("[!] Sent some non-rotation Movement to `rotatris_attempt_rotate_board()`, a method of `BoardHandler`"); + println!("[!] Sent some non-rotation Movement to `attempt_rotate_board()`, a method of `BoardHandler`"); return false; } } @@ -563,6 +691,10 @@ impl BoardRotatris { self.vec_active_piece[0].positions = new_positions; self.playerify_piece(0); + self.gravity = Gravity::from( + (self.gravity as u8 + if rotate_direction == RotateCw { 1 } else { 3 }) % 4, + ); + true } @@ -585,6 +717,184 @@ impl BoardRotatris { } } + // returns (bool, bool) based on (if piece moved successfully, if (piece is locked && filled some line)) + // sets the shape of the piece to Shapes::None if it locks + pub fn attempt_piece_movement(&mut self, movement: Movement, player: u8) -> (bool, bool) { + let mut cant_move_flag = false; + // determine if it can move + let new_positions = self.vec_active_piece[player as usize].piece_pos(movement); + for position in new_positions.iter().take(4) { + // due to integer underflow (u8 board width and u8 board height), we must only check the positive side of x and y positions + if position.0 >= self.board_side_length + self.board_side_length { + cant_move_flag = true; + break; + } + if position.1 >= self.board_side_length { + cant_move_flag = true; + break; + } + // make sure the position is empty or is part of the piece being moved + if !self.matrix[position.0 as usize][position.1 as usize].empty + && !(self.matrix[position.0 as usize][position.1 as usize].active + && self.matrix[position.0 as usize][position.1 as usize].player == player) + { + cant_move_flag = true; + break; + } + } + + if cant_move_flag { + // TODO: fix this awful code + if movement as u8 == self.gravity as u8 && self.should_lock(player) { + // lock piece and push any full lines to vec_full_lines + self.vec_active_piece[player as usize].shape = Shapes::None; + + let mut does_full_ring_exist: bool = false; + for ring in &self.lock_piece(player) { + if self.rotatris_check_single_ring(*ring) { + does_full_ring_exist = true; + } + } + + return (false, does_full_ring_exist); + } + + return (false, false); + } + + // move it + self.emptify_piece(player); + self.vec_active_piece[player as usize].positions = new_positions; + self.playerify_piece(player); + + // update self.piece.rotation if it was a rotate + if movement == Movement::RotateCw { + self.vec_active_piece[player as usize].rotation = + (self.vec_active_piece[player as usize].rotation + 1) + % self.vec_active_piece[player as usize].num_rotations; + } + if movement == Movement::RotateCcw { + self.vec_active_piece[player as usize].rotation = + (self.vec_active_piece[player as usize].rotation + + self.vec_active_piece[player as usize].num_rotations + - 1) + % self.vec_active_piece[player as usize].num_rotations; + } + + (true, false) + } + + // returns ring(s) of the locked piece to test if it filled a line + fn lock_piece(&mut self, player: u8) -> Vec { + for position in self.vec_active_piece[player as usize] + .positions + .iter() + .take(4) + { + self.matrix[position.0 as usize][position.1 as usize].empty = false; + self.matrix[position.0 as usize][position.1 as usize].active = false; + } + + let rings_with_repeat: [u8; 4] = [ + find_ring_from_pos( + self.vec_active_piece[player as usize].positions[0].0, + self.vec_active_piece[player as usize].positions[0].1, + ), + find_ring_from_pos( + self.vec_active_piece[player as usize].positions[1].0, + self.vec_active_piece[player as usize].positions[1].1, + ), + find_ring_from_pos( + self.vec_active_piece[player as usize].positions[2].0, + self.vec_active_piece[player as usize].positions[2].1, + ), + find_ring_from_pos( + self.vec_active_piece[player as usize].positions[3].0, + self.vec_active_piece[player as usize].positions[3].1, + ), + ]; + + let mut rings: Vec = vec![rings_with_repeat[0]]; + if rings_with_repeat[0] != rings_with_repeat[1] { + rings.push(rings_with_repeat[1]); + } + if rings_with_repeat[0] != rings_with_repeat[2] + && rings_with_repeat[1] != rings_with_repeat[2] + { + rings.push(rings_with_repeat[2]); + } + if rings_with_repeat[0] != rings_with_repeat[3] + && rings_with_repeat[1] != rings_with_repeat[3] + && rings_with_repeat[2] != rings_with_repeat[3] + { + rings.push(rings_with_repeat[3]); + } + + rings + } + + fn find_ring_from_pos(&self, y: u8, x: u8) -> u8 { + std::cmp::min( + std::cmp::min(x, self.board_side_length - x - 1), + std::cmp::min(y, self.board_side_length - y - 1), + ) + } + + fn should_lock(&self, player: u8) -> bool { + for position in self.vec_active_piece[player as usize] + .positions + .iter() + .take(4) + { + // we just want to know if moving down by 1 will run the piece into the bottom of the board or an inactive tile + match self.gravity { + Gravity::Down => { + if position.0 as usize + 1 >= self.board_side_length as usize { + return true; + } + if !self.matrix[position.0 as usize + 1][position.1 as usize].active + && !self.matrix[position.0 as usize + 1][position.1 as usize].empty + { + return true; + } + } + Gravity::Left => { + if position.1 <= 0 { + return true; + } + if !self.matrix[position.0 as usize][position.1 as usize - 1].active + && !self.matrix[position.0 as usize][position.1 as usize - 1].empty + { + return true; + } + } + Gravity::Up => { + if position.0 <= 0 { + return true; + } + if !self.matrix[position.0 as usize - 1][position.1 as usize].active + && !self.matrix[position.0 as usize - 1][position.1 as usize].empty + { + return true; + } + } + Gravity::Right => { + if position.1 >= self.board_side_length - 1 { + return true; + } + if !self.matrix[position.0 as usize][position.1 as usize + 1].active + && !self.matrix[position.0 as usize][position.1 as usize + 1].empty + { + return true; + } + } + _ => panic!("[!] Error: current gravity is {}", current_gravity as u8), + } + } + + false + } + fn emptify_piece(&mut self, player: u8) { for position in self.vec_active_piece[player as usize] .positions @@ -620,9 +930,11 @@ impl BoardRotatris { } fn rotatris_check_single_ring(&mut self, z: u8) -> bool { - for a in [z, self.board_side_length - z - 1].into_iter() { - for b in z..(self.board_side_length - z) { - if b >= z && b <= self.board_side_length - z { + let min = std::cmp::min(z, self.board_side_length - z); + let max = std::cmp::max(z, self.board_side_length - z); + for a in [min, max - 1].into_iter() { + for b in min..max { + if b >= min && b <= max { if self.matrix[*a as usize][b as usize].empty || self.matrix[*a as usize][b as usize].active || self.matrix[b as usize][*a as usize].empty @@ -673,28 +985,6 @@ impl BoardRotatris { } } - - - - - - - - - - - - - - - - - - - - - - #[cfg(test)] mod tests { use super::*; From 136f67218ced012361c983780221a1e027066840 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 23 Mar 2021 21:55:56 -0400 Subject: [PATCH 13/59] got to another layer of errors --- src/game.rs | 19 ++-- src/game/board.rs | 268 +++++++++++++++++++++++++++------------------- src/game/piece.rs | 12 ++- 3 files changed, 170 insertions(+), 129 deletions(-) diff --git a/src/game.rs b/src/game.rs index 0947a07..3860188 100644 --- a/src/game.rs +++ b/src/game.rs @@ -359,8 +359,7 @@ impl Game { // GAME LOGIC for player in &mut self.vec_players { if !player.spawn_piece_flag - && self.bh.board.vec_active_piece[player.player_num as usize].shape - == Shapes::None + && self.bh.get_shape_from_player(player.player_num) == Shapes::None { player.input.was_just_pressed_setfalse(); self.rotate_board_cw.1 = false; @@ -372,11 +371,10 @@ impl Game { if player.spawn_piece_flag { if player.spawn_delay <= 0 { // (blocked, blocked by some !active tile); if .1, game over sequence, if .0 and !.1, only blocked by other players, wait until they move, then carry on - let blocked: (bool, bool) = self.bh.board.attempt_piece_spawn( + let blocked: (bool, bool) = self.bh.attempt_piece_spawn( player.player_num, player.spawn_column, player.next_piece_shape, - self.gravity_direction, ); if blocked.0 { if blocked.1 { @@ -384,7 +382,7 @@ impl Game { } continue; } else { - self.bh.board.playerify_piece(player.player_num); + self.bh.playerify_piece(player.player_num); player.spawn_delay = SPAWN_DELAY; player.spawn_piece_flag = false; // set das_countdown to the smaller das value if input left or right is pressed as the piece spawns in @@ -400,9 +398,7 @@ impl Game { } } let random_shape = Shapes::from(rand % 7); - if self.bh.board.vec_active_piece[player.player_num as usize].shape - != random_shape - { + if self.bh.get_shape_from_player(player.player_num) != random_shape { player.next_piece_shape = random_shape; } else { let mut rand: u8; @@ -776,9 +772,8 @@ impl Game { self.vec_batch_player_piece[2].add(player_tile); } } - self.vec_batch_player_piece[player as usize].add(player_tile); // highlight if active - if self.bh.get_active_from_pos(y + BOARD_HEIGHT_BUFFER_U, x) { + if self.bh.get_active_from_pos(y + height_buffer, x) { self.batch_highlight_active_tile.add(player_tile); } } @@ -789,7 +784,7 @@ impl Game { for full_line in classic.vec_full_lines.iter() { if full_line.lines_cleared_together < 4 { // standard clear animation - let y = (full_line.row - BOARD_HEIGHT_BUFFER_U) as usize; + let y = (full_line.row - height_buffer) as usize; let board_max_index_remainder_2 = (self.bh.get_width() - 1) % 2; // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 for x in (self.bh.get_width() / 2)..self.bh.get_width() { @@ -817,7 +812,7 @@ impl Game { } } else { // tetrisnt clear animation - let y = (full_line.row - BOARD_HEIGHT_BUFFER_U) as usize; + let y = (full_line.row - height_buffer) as usize; let board_max_index_remainder_2 = (self.bh.get_width() - 1) % 2; // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 for x in (self.bh.get_width() / 2)..self.bh.get_width() { diff --git a/src/game/board.rs b/src/game/board.rs index 42857c7..8c3082f 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -8,6 +8,7 @@ use crate::game::{ static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong option"; #[repr(u8)] +#[derive(Copy, Clone)] pub enum Gravity { Down, Left, @@ -36,18 +37,27 @@ pub struct BoardHandler { impl BoardHandler { pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: Modes) -> Self { - let (board_height_buffer, piece_spawn_row, rotatris) = match mode { - Modes::Rotatris => ( - 0, - (board_height - 1) / 2, - Some(BoardRotatris::new( + // determine some rules based on gamemode + let (board_height_buffer, spawn_row) = match mode { + Modes::Rotatris => (0, (board_height - 1) / 2), + Modes::Classic => (2, 0), + }; + let mut classic: Option = None; + let mut rotatris: Option = None; + match mode { + Modes::Rotatris => { + rotatris = Some(BoardRotatris::new(board_width, spawn_row, num_players)) + } + Modes::Classic => { + classic = Some(BoardClassic::new( board_width, - board_width / 2, + board_height, + board_height_buffer, + spawn_row, num_players, - )), - ), - Modes::Classic => (2, 0, None), - }; + )) + } + } Self { mode, classic, @@ -59,14 +69,14 @@ impl BoardHandler { pub fn get_width(&mut self) -> u8 { match self.mode { Modes::Classic => self.classic.expect(BH_WRONG_MODE).width, - Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_side_length, + Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_size, } } pub fn get_height(&mut self) -> u8 { match self.mode { Modes::Classic => self.classic.expect(BH_WRONG_MODE).height, - Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_side_length, + Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_size, } } @@ -77,17 +87,6 @@ impl BoardHandler { } } - pub fn get_shape_from_pos(&mut self, y: u8, x: u8) -> Shapes { - match self.mode { - Modes::Classic => { - self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape - } - Modes::Rotatris => { - self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape - } - } - } - pub fn get_active_from_pos(&mut self, y: u8, x: u8) -> bool { match self.mode { Modes::Classic => { @@ -121,6 +120,17 @@ impl BoardHandler { } } + pub fn get_shape_from_pos(&mut self, y: u8, x: u8) -> Shapes { + match self.mode { + Modes::Classic => { + self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape + } + Modes::Rotatris => { + self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape + } + } + } + pub fn get_shape_from_player(&mut self, player: u8) -> Shapes { match self.mode { Modes::Classic => { @@ -133,6 +143,26 @@ impl BoardHandler { } // board logic functions + pub fn attempt_piece_spawn( + &mut self, + player: u8, + spawn_col: u8, + spawn_piece_shape: Shapes, + ) -> (bool, bool) { + match self.mode { + Modes::Classic => self.classic.expect(BH_WRONG_MODE).attempt_piece_spawn( + player, + spawn_col, + spawn_piece_shape, + ), + Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).attempt_piece_spawn( + player, + spawn_col, + spawn_piece_shape, + ), + } + } + pub fn attempt_clear_lines(&mut self, level: u8) -> (u8, u32) { match self.mode { Modes::Classic => self @@ -163,11 +193,18 @@ impl BoardHandler { match self.mode { Modes::Classic => { println!("[!] `attempt_rotate_board` called but mode is Modes::Classic"); - false; + false } Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).attempt_rotate_board(rd), } } + + pub fn playerify_piece(&mut self, player: u8) { + match self.mode { + Modes::Classic => self.classic.expect(BH_WRONG_MODE).playerify_piece(player), + Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).playerify_piece(player), + } + } } // example Board coordinates system (2 width, 2 height) @@ -196,7 +233,7 @@ impl BoardClassic { for _ in 0..num_players { vec_active_piece.push(Piece::new(Shapes::None)); } - let mut matrix = vec![ + let matrix = vec![ vec![Tile::default(); board_width as usize]; (board_height + board_height_buffer) as usize ]; @@ -260,15 +297,10 @@ impl BoardClassic { player: u8, spawn_col: u8, spawn_piece_shape: Shapes, - current_gravity: Movement, ) -> (bool, bool) { let new_piece = Piece::new(spawn_piece_shape); - let spawn_positions = new_piece.spawn_pos( - spawn_col, - self.spawn_row, - self.height_buffer, - current_gravity, - ); + let spawn_positions = + new_piece.spawn_pos(spawn_col, self.spawn_row, self.height_buffer, Gravity::Down); let mut blocked_flag: bool = false; for position in spawn_positions.iter().take(4) { if !self.matrix[position.0 as usize][position.1 as usize].empty { @@ -319,7 +351,7 @@ impl BoardClassic { } if cant_move_flag { - if movement == current_gravity && self.should_lock(player, current_gravity) { + if movement == Movement::Down && self.should_lock(player) { // lock piece and push any full lines to vec_full_lines self.vec_active_piece[player as usize].shape = Shapes::None; let mut full_line_rows: Vec = Vec::with_capacity(4); @@ -367,55 +399,20 @@ impl BoardClassic { (true, false) } - fn should_lock(&self, player: u8, current_gravity: Movement) -> bool { + fn should_lock(&self, player: u8) -> bool { for position in self.vec_active_piece[player as usize] .positions .iter() .take(4) { // we just want to know if moving down by 1 will run the piece into the bottom of the board or an inactive tile - match current_gravity { - Movement::Down => { - if position.0 as usize + 1 >= (self.height + self.height_buffer) as usize { - return true; - } - if !self.matrix[position.0 as usize + 1][position.1 as usize].active - && !self.matrix[position.0 as usize + 1][position.1 as usize].empty - { - return true; - } - } - Movement::Left => { - if position.1 <= 0 { - return true; - } - if !self.matrix[position.0 as usize][position.1 as usize - 1].active - && !self.matrix[position.0 as usize][position.1 as usize - 1].empty - { - return true; - } - } - Movement::Up => { - if position.0 <= 0 { - return true; - } - if !self.matrix[position.0 as usize - 1][position.1 as usize].active - && !self.matrix[position.0 as usize - 1][position.1 as usize].empty - { - return true; - } - } - Movement::Right => { - if position.1 >= self.width - 1 { - return true; - } - if !self.matrix[position.0 as usize][position.1 as usize + 1].active - && !self.matrix[position.0 as usize][position.1 as usize + 1].empty - { - return true; - } - } - _ => panic!("[!] Error: current gravity is {}", current_gravity as u8), + if position.0 as usize + 1 >= (self.height + self.height_buffer) as usize { + return true; + } + if !self.matrix[position.0 as usize + 1][position.1 as usize].active + && !self.matrix[position.0 as usize + 1][position.1 as usize].empty + { + return true; } } @@ -603,43 +600,40 @@ impl FullLine { } } -// other modes +// rotatris pub struct BoardRotatris { pub gravity: Gravity, - pub board_side_length: u8, + pub board_size: u8, pub spawn_row: u8, pub matrix: Vec>, pub vec_active_piece: Vec, } impl BoardRotatris { - pub fn new(board_side_length: u8, spawn_row: u8, num_players: u8) -> Self { + pub fn new(board_size: u8, spawn_row: u8, num_players: u8) -> Self { let mut vec_active_piece: Vec = Vec::with_capacity(num_players as usize); for _ in 0..num_players { vec_active_piece.push(Piece::new(Shapes::None)); } - let mut matrix = - vec![vec![Tile::default(); board_side_length as usize]; board_side_length as usize]; + let mut matrix = vec![vec![Tile::default(); board_size as usize]; board_size as usize]; // DEBUG for a in 0..4 { - for b in 0..board_side_length as usize { + for b in 0..board_size as usize { matrix[a][b] = Tile::new(false, false, 0, Shapes::I); matrix[b][a] = Tile::new(false, false, 0, Shapes::I); - matrix[board_side_length as usize - a - 1][b] = - Tile::new(false, false, 0, Shapes::I); - matrix[b][board_side_length as usize - a - 1] = - Tile::new(false, false, 0, Shapes::I); + matrix[board_size as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); + matrix[b][board_size as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); } } - matrix[board_side_length as usize / 2][board_side_length as usize - 1] = Tile::default(); - matrix[board_side_length as usize / 2][board_side_length as usize - 2] = Tile::default(); - matrix[board_side_length as usize / 2][board_side_length as usize - 3] = Tile::default(); - matrix[board_side_length as usize / 2][board_side_length as usize - 4] = Tile::default(); + matrix[board_size as usize / 2][board_size as usize - 1] = Tile::default(); + matrix[board_size as usize / 2][board_size as usize - 2] = Tile::default(); + matrix[board_size as usize / 2][board_size as usize - 3] = Tile::default(); + matrix[board_size as usize / 2][board_size as usize - 4] = Tile::default(); Self { gravity: Gravity::Down, - board_side_length, + board_size, spawn_row, matrix, vec_active_piece, @@ -648,8 +642,8 @@ impl BoardRotatris { // return bool is if rotate was successful pub fn attempt_rotate_board(&mut self, rotate_direction: Movement) -> bool { - let center: u8 = self.board_side_length / 2; - let is_center_even: u8 = (self.board_side_length + 1) % 2; + let center: u8 = self.board_size / 2; + let is_center_even: u8 = (self.board_size + 1) % 2; let mut new_positions: [(u8, u8); 4] = [(0u8, 0u8); 4]; match rotate_direction { Movement::RotateCw => { @@ -692,7 +686,13 @@ impl BoardRotatris { self.playerify_piece(0); self.gravity = Gravity::from( - (self.gravity as u8 + if rotate_direction == RotateCw { 1 } else { 3 }) % 4, + (self.gravity as u8 + + if rotate_direction == Movement::RotateCw { + 3 + } else { + 1 + }) + % 4, ); true @@ -725,11 +725,11 @@ impl BoardRotatris { let new_positions = self.vec_active_piece[player as usize].piece_pos(movement); for position in new_positions.iter().take(4) { // due to integer underflow (u8 board width and u8 board height), we must only check the positive side of x and y positions - if position.0 >= self.board_side_length + self.board_side_length { + if position.0 >= self.board_size + self.board_size { cant_move_flag = true; break; } - if position.1 >= self.board_side_length { + if position.1 >= self.board_size { cant_move_flag = true; break; } @@ -784,6 +784,39 @@ impl BoardRotatris { (true, false) } + // returns (bool, bool) based on (blocked, blocked by some !active tile) + pub fn attempt_piece_spawn( + &mut self, + player: u8, + spawn_col: u8, + spawn_piece_shape: Shapes, + ) -> (bool, bool) { + let new_piece = Piece::new(spawn_piece_shape); + let spawn_positions = + new_piece.spawn_pos(spawn_col, self.spawn_row, self.board_size / 2, self.gravity); + let mut blocked_flag: bool = false; + for position in spawn_positions.iter().take(4) { + if !self.matrix[position.0 as usize][position.1 as usize].empty { + if !self.matrix[position.0 as usize][position.1 as usize].active { + return (true, true); + } + blocked_flag = true; + } + } + if blocked_flag { + return (true, false); + } + self.vec_active_piece[player as usize] = new_piece; + self.vec_active_piece[player as usize].positions = spawn_positions; + // initialize the tile logic for the newly spawned piece + for position in spawn_positions.iter().take(4) { + self.matrix[position.0 as usize][position.1 as usize] = + Tile::new(false, true, player, spawn_piece_shape); + } + + (false, false) + } + // returns ring(s) of the locked piece to test if it filled a line fn lock_piece(&mut self, player: u8) -> Vec { for position in self.vec_active_piece[player as usize] @@ -796,19 +829,19 @@ impl BoardRotatris { } let rings_with_repeat: [u8; 4] = [ - find_ring_from_pos( + self.find_ring_from_pos( self.vec_active_piece[player as usize].positions[0].0, self.vec_active_piece[player as usize].positions[0].1, ), - find_ring_from_pos( + self.find_ring_from_pos( self.vec_active_piece[player as usize].positions[1].0, self.vec_active_piece[player as usize].positions[1].1, ), - find_ring_from_pos( + self.find_ring_from_pos( self.vec_active_piece[player as usize].positions[2].0, self.vec_active_piece[player as usize].positions[2].1, ), - find_ring_from_pos( + self.find_ring_from_pos( self.vec_active_piece[player as usize].positions[3].0, self.vec_active_piece[player as usize].positions[3].1, ), @@ -835,8 +868,8 @@ impl BoardRotatris { fn find_ring_from_pos(&self, y: u8, x: u8) -> u8 { std::cmp::min( - std::cmp::min(x, self.board_side_length - x - 1), - std::cmp::min(y, self.board_side_length - y - 1), + std::cmp::min(x, self.board_size - x - 1), + std::cmp::min(y, self.board_size - y - 1), ) } @@ -849,7 +882,7 @@ impl BoardRotatris { // we just want to know if moving down by 1 will run the piece into the bottom of the board or an inactive tile match self.gravity { Gravity::Down => { - if position.0 as usize + 1 >= self.board_side_length as usize { + if position.0 as usize + 1 >= self.board_size as usize { return true; } if !self.matrix[position.0 as usize + 1][position.1 as usize].active @@ -879,7 +912,7 @@ impl BoardRotatris { } } Gravity::Right => { - if position.1 >= self.board_side_length - 1 { + if position.1 >= self.board_size - 1 { return true; } if !self.matrix[position.0 as usize][position.1 as usize + 1].active @@ -888,7 +921,7 @@ impl BoardRotatris { return true; } } - _ => panic!("[!] Error: current gravity is {}", current_gravity as u8), + _ => panic!("[!] Error: current gravity is {}", self.gravity as u8), } } @@ -913,7 +946,7 @@ impl BoardRotatris { pub fn rotatris_attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { let mut num_cleared_rings = 0; let mut score_from_cleared_rings = 0; - let num_rings_to_check = self.board_side_length / 2 - 4; + let num_rings_to_check = self.board_size / 2 - 4; // go from inner rings to outer rings checking if any ring is full, avoiding the middle 4 rings for z in (0..num_rings_to_check).rev() { @@ -926,12 +959,23 @@ impl BoardRotatris { } } + score_from_cleared_rings += match num_cleared_rings { + 1 => SCORE_SINGLE_BASE as u32 * (level as u32 + 1), + 2 => SCORE_DOUBLE_BASE as u32 * (level as u32 + 1), + 3 => SCORE_TRIPLE_BASE as u32 * (level as u32 + 1), + 4 => SCORE_QUADRUPLE_BASE as u32 * (level as u32 + 1), + _ => { + println!("[!] player was attributed a number of lines too large maybe, what the heck? lines_player_cleared: {}", num_cleared_rings); + 0u32 + } + }; + (num_cleared_rings, score_from_cleared_rings) } fn rotatris_check_single_ring(&mut self, z: u8) -> bool { - let min = std::cmp::min(z, self.board_side_length - z); - let max = std::cmp::max(z, self.board_side_length - z); + let min = std::cmp::min(z, self.board_size - z); + let max = std::cmp::max(z, self.board_size - z); for a in [min, max - 1].into_iter() { for b in min..max { if b >= min && b <= max { @@ -951,7 +995,7 @@ impl BoardRotatris { fn rotatris_pull_single_ring_out(&mut self, j: u8) { let j = j as usize; - let k = self.board_side_length as usize - j - 1; + let k = self.board_size as usize - j - 1; // sides for a in j..=k { @@ -972,9 +1016,9 @@ impl BoardRotatris { } fn rotatris_emptify_single_ring(&mut self, z: u8) { - for a in [z, self.board_side_length - z - 1].into_iter() { - for b in z..(self.board_side_length - z) { - if b >= z && b <= self.board_side_length - z { + for a in [z, self.board_size - z - 1].into_iter() { + for b in z..(self.board_size - z) { + if b >= z && b <= self.board_size - z { self.matrix[*a as usize][b as usize].empty = true; self.matrix[*a as usize][b as usize].active = false; self.matrix[b as usize][*a as usize].empty = true; diff --git a/src/game/piece.rs b/src/game/piece.rs index 80ecb45..a10930e 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -1,3 +1,5 @@ +use crate::game::board::Gravity; + #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] pub enum Shapes { @@ -194,7 +196,7 @@ impl Piece { spawn_column: u8, spawn_row: u8, board_height_buffer: u8, - current_gravity: Movement, + current_gravity: Gravity, ) -> [(u8, u8); 4] { let mut piece_copy = Self::new(self.shape); piece_copy.positions = match piece_copy.shape { @@ -265,16 +267,16 @@ impl Piece { piece_copy.positions } else { match current_gravity { - Movement::Down => piece_copy.positions, - Movement::Left => { + Gravity::Down => piece_copy.positions, + Gravity::Left => { piece_copy.positions = piece_copy.rotate(true); piece_copy.piece_pos(Movement::Right) } - Movement::Up => { + Gravity::Up => { piece_copy.positions = piece_copy.double_rotate(); piece_copy.piece_pos(Movement::Down) } - Movement::Right => { + Gravity::Right => { piece_copy.positions = piece_copy.rotate(false); piece_copy.piece_pos(Movement::Down) } From 026a995a44f7959b8260f6ef6d2d2d0aca7f2317 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 23 Mar 2021 21:58:14 -0400 Subject: [PATCH 14/59] yet another layer of errors --- src/game.rs | 2 +- src/game/board.rs | 76 ++++++++++++++++++++++++++++++----------------- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/game.rs b/src/game.rs index 3860188..e50bf6b 100644 --- a/src/game.rs +++ b/src/game.rs @@ -780,7 +780,7 @@ impl Game { } } // line clear highlights - if let Some(classic) = self.bh.classic { + if let Some(classic) = &self.bh.classic { for full_line in classic.vec_full_lines.iter() { if full_line.lines_cleared_together < 4 { // standard clear animation diff --git a/src/game/board.rs b/src/game/board.rs index 8c3082f..8a00ddd 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -68,21 +68,21 @@ impl BoardHandler { // get... pub fn get_width(&mut self) -> u8 { match self.mode { - Modes::Classic => self.classic.expect(BH_WRONG_MODE).width, - Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_size, + Modes::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).width, + Modes::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, } } pub fn get_height(&mut self) -> u8 { match self.mode { - Modes::Classic => self.classic.expect(BH_WRONG_MODE).height, - Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).board_size, + Modes::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height, + Modes::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, } } pub fn get_height_buffer(&mut self) -> u8 { match self.mode { - Modes::Classic => self.classic.expect(BH_WRONG_MODE).height_buffer, + Modes::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height_buffer, Modes::Rotatris => 0u8, } } @@ -90,10 +90,10 @@ impl BoardHandler { pub fn get_active_from_pos(&mut self, y: u8, x: u8) -> bool { match self.mode { Modes::Classic => { - self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active + self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active } Modes::Rotatris => { - self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active + self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active } } } @@ -101,10 +101,10 @@ impl BoardHandler { pub fn get_empty_from_pos(&mut self, y: u8, x: u8) -> bool { match self.mode { Modes::Classic => { - self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty + self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty } Modes::Rotatris => { - self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty + self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty } } } @@ -112,10 +112,10 @@ impl BoardHandler { pub fn get_player_from_pos(&mut self, y: u8, x: u8) -> u8 { match self.mode { Modes::Classic => { - self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player + self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player } Modes::Rotatris => { - self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player + self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player } } } @@ -123,10 +123,10 @@ impl BoardHandler { pub fn get_shape_from_pos(&mut self, y: u8, x: u8) -> Shapes { match self.mode { Modes::Classic => { - self.classic.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape + self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape } Modes::Rotatris => { - self.rotatris.expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape + self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape } } } @@ -134,10 +134,14 @@ impl BoardHandler { pub fn get_shape_from_player(&mut self, player: u8) -> Shapes { match self.mode { Modes::Classic => { - self.classic.expect(BH_WRONG_MODE).vec_active_piece[player as usize].shape + self.classic.as_mut().expect(BH_WRONG_MODE).vec_active_piece[player as usize].shape } Modes::Rotatris => { - self.rotatris.expect(BH_WRONG_MODE).vec_active_piece[player as usize].shape + self.rotatris + .as_mut() + .expect(BH_WRONG_MODE) + .vec_active_piece[player as usize] + .shape } } } @@ -150,16 +154,16 @@ impl BoardHandler { spawn_piece_shape: Shapes, ) -> (bool, bool) { match self.mode { - Modes::Classic => self.classic.expect(BH_WRONG_MODE).attempt_piece_spawn( - player, - spawn_col, - spawn_piece_shape, - ), - Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).attempt_piece_spawn( - player, - spawn_col, - spawn_piece_shape, - ), + Modes::Classic => self + .classic + .as_mut() + .expect(BH_WRONG_MODE) + .attempt_piece_spawn(player, spawn_col, spawn_piece_shape), + Modes::Rotatris => self + .rotatris + .as_mut() + .expect(BH_WRONG_MODE) + .attempt_piece_spawn(player, spawn_col, spawn_piece_shape), } } @@ -167,10 +171,12 @@ impl BoardHandler { match self.mode { Modes::Classic => self .classic + .as_mut() .expect(BH_WRONG_MODE) .attempt_clear_lines(level), Modes::Rotatris => self .rotatris + .as_mut() .expect(BH_WRONG_MODE) .rotatris_attempt_clear_rings(level), } @@ -180,10 +186,12 @@ impl BoardHandler { match self.mode { Modes::Classic => self .classic + .as_mut() .expect(BH_WRONG_MODE) .attempt_piece_movement(m, p), Modes::Rotatris => self .rotatris + .as_mut() .expect(BH_WRONG_MODE) .attempt_piece_movement(m, p), } @@ -195,14 +203,26 @@ impl BoardHandler { println!("[!] `attempt_rotate_board` called but mode is Modes::Classic"); false } - Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).attempt_rotate_board(rd), + Modes::Rotatris => self + .rotatris + .as_mut() + .expect(BH_WRONG_MODE) + .attempt_rotate_board(rd), } } pub fn playerify_piece(&mut self, player: u8) { match self.mode { - Modes::Classic => self.classic.expect(BH_WRONG_MODE).playerify_piece(player), - Modes::Rotatris => self.rotatris.expect(BH_WRONG_MODE).playerify_piece(player), + Modes::Classic => self + .classic + .as_mut() + .expect(BH_WRONG_MODE) + .playerify_piece(player), + Modes::Rotatris => self + .rotatris + .as_mut() + .expect(BH_WRONG_MODE) + .playerify_piece(player), } } } From 9c18d2e6b95b258c01bdc06206090ee4364aaefb Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 23 Mar 2021 22:23:28 -0400 Subject: [PATCH 15/59] it works! kinda --- src/game.rs | 40 +++++++++++++++++----------------------- src/game/board.rs | 45 +++++++++++++++++++++++++-------------------- src/game/piece.rs | 4 ---- 3 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/game.rs b/src/game.rs index e50bf6b..7080e50 100644 --- a/src/game.rs +++ b/src/game.rs @@ -576,7 +576,7 @@ impl Game { } // attempt to line clear (go through the vector of FullLine's and decrement clear_delay if > 0, clear and return (lines_cleared, score) for <= 0) - let (returned_lines, returned_score) = self.bh.attempt_clear_lines(self.level); + let (returned_lines, returned_score) = self.bh.attempt_clear(self.level); if returned_lines > 0 { self.num_cleared_lines += returned_lines as u16; self.game_info_text.fragments_mut()[1].text = @@ -704,15 +704,14 @@ impl Game { pub fn draw(&mut self, ctx: &mut Context) { // constants used throughout draw let height_buffer = self.bh.get_height_buffer(); + let width = self.bh.get_width(); + let height = self.bh.get_height(); // start doing drawing stuff graphics::clear(ctx, graphics::BLACK); let (window_width, window_height) = graphics::size(ctx); - self.tile_size = TileGraphic::get_size( - ctx, - self.bh.get_width(), - self.bh.get_height() + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D, - ); + self.tile_size = + TileGraphic::get_size(ctx, width, height + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D); if self.game_over_flag && self.game_over_delay == 0 { // DRAW GAME OVER self.draw_text( @@ -733,12 +732,12 @@ impl Game { } else { // DRAW GAME // add each non-empty tile to the correct SpriteBatch - for x in 0..self.bh.get_width() { - for y in 0..self.bh.get_height() { + for x in 0..width { + for y in 0..height { // actually go through and add tiles to a spritebatch if !self.bh.get_empty_from_pos(y + height_buffer, x) { // account for the gravity direction in how to draw it (rotatris) - let center = self.bh.get_width() / 2; + let center = width / 2; let is_center_even: u8 = (center + 1) % 2; let (y_draw_pos, x_draw_pos) = match self.gravity_direction { Movement::Down => (y, x), @@ -785,16 +784,15 @@ impl Game { if full_line.lines_cleared_together < 4 { // standard clear animation let y = (full_line.row - height_buffer) as usize; - let board_max_index_remainder_2 = (self.bh.get_width() - 1) % 2; + let board_max_index_remainder_2 = (width - 1) % 2; // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 - for x in (self.bh.get_width() / 2)..self.bh.get_width() { + for x in (width / 2)..width { let highlight_pos_right = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); let highlight_pos_left = graphics::DrawParam::new().dest(Point2::new( - (self.bh.get_width() as f32 - - (x + board_max_index_remainder_2) as f32) + (width as f32 - (x + board_max_index_remainder_2) as f32) * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); @@ -804,7 +802,7 @@ impl Game { self.batch_highlight_clearing_standard_tile .add(highlight_pos_left); - if ((x as f32) / (self.bh.get_width() as f32) - 0.5) * 2.0 + if ((x as f32) / (width as f32) - 0.5) * 2.0 > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) { break; @@ -813,16 +811,15 @@ impl Game { } else { // tetrisnt clear animation let y = (full_line.row - height_buffer) as usize; - let board_max_index_remainder_2 = (self.bh.get_width() - 1) % 2; + let board_max_index_remainder_2 = (width - 1) % 2; // go from the middle to the outside and reach the end right before full_line.clear_delay reaches 0 - for x in (self.bh.get_width() / 2)..self.bh.get_width() { + for x in (width / 2)..width { let highlight_pos_right = graphics::DrawParam::new().dest(Point2::new( x as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); let highlight_pos_left = graphics::DrawParam::new().dest(Point2::new( - (self.bh.get_width() as f32 - - (x + board_max_index_remainder_2) as f32) + (width as f32 - (x + board_max_index_remainder_2) as f32) * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, y as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32, )); @@ -832,7 +829,7 @@ impl Game { self.batch_highlight_clearing_tetrisnt_tile .add(highlight_pos_left); - if ((x as f32) / (self.bh.get_width() as f32) - 0.5) * 2.0 + if ((x as f32) / (width as f32) - 0.5) * 2.0 > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) { break; @@ -895,10 +892,7 @@ impl Game { // draw each SpriteBatch let board_top_left_corner = window_width / 2.0 - - (scaled_tile_size - * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32 - * self.bh.get_width() as f32 - / 2.0); + - (scaled_tile_size * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32 * width as f32 / 2.0); // empty tiles graphics::draw( ctx, diff --git a/src/game/board.rs b/src/game/board.rs index 8a00ddd..b702329 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -167,7 +167,7 @@ impl BoardHandler { } } - pub fn attempt_clear_lines(&mut self, level: u8) -> (u8, u32) { + pub fn attempt_clear(&mut self, level: u8) -> (u8, u32) { match self.mode { Modes::Classic => self .classic @@ -178,7 +178,7 @@ impl BoardHandler { .rotatris .as_mut() .expect(BH_WRONG_MODE) - .rotatris_attempt_clear_rings(level), + .attempt_clear_rings(level), } } @@ -270,7 +270,7 @@ impl BoardClassic { width: board_width, height: board_height, height_buffer: board_height_buffer, - spawn_row: board_height_buffer, + spawn_row, matrix, vec_active_piece, vec_full_lines: vec![], @@ -812,8 +812,7 @@ impl BoardRotatris { spawn_piece_shape: Shapes, ) -> (bool, bool) { let new_piece = Piece::new(spawn_piece_shape); - let spawn_positions = - new_piece.spawn_pos(spawn_col, self.spawn_row, self.board_size / 2, self.gravity); + let spawn_positions = new_piece.spawn_pos(spawn_col, self.spawn_row, 0, self.gravity); let mut blocked_flag: bool = false; for position in spawn_positions.iter().take(4) { if !self.matrix[position.0 as usize][position.1 as usize].empty { @@ -941,7 +940,6 @@ impl BoardRotatris { return true; } } - _ => panic!("[!] Error: current gravity is {}", self.gravity as u8), } } @@ -963,10 +961,10 @@ impl BoardRotatris { } } - pub fn rotatris_attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { + pub fn attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { let mut num_cleared_rings = 0; let mut score_from_cleared_rings = 0; - let num_rings_to_check = self.board_size / 2 - 4; + let num_rings_to_check = self.board_size / 2 - 2; // go from inner rings to outer rings checking if any ring is full, avoiding the middle 4 rings for z in (0..num_rings_to_check).rev() { @@ -979,16 +977,23 @@ impl BoardRotatris { } } - score_from_cleared_rings += match num_cleared_rings { - 1 => SCORE_SINGLE_BASE as u32 * (level as u32 + 1), - 2 => SCORE_DOUBLE_BASE as u32 * (level as u32 + 1), - 3 => SCORE_TRIPLE_BASE as u32 * (level as u32 + 1), - 4 => SCORE_QUADRUPLE_BASE as u32 * (level as u32 + 1), - _ => { - println!("[!] player was attributed a number of lines too large maybe, what the heck? lines_player_cleared: {}", num_cleared_rings); - 0u32 + if num_cleared_rings > 0 { + // get rid of all tiles in the middle area + for z in num_rings_to_check..=((self.board_size - 1) / 2) { + self.emptify_single_ring(z); } - }; + + score_from_cleared_rings += match num_cleared_rings { + 1 => SCORE_SINGLE_BASE as u32 * (level as u32 + 1), + 2 => SCORE_DOUBLE_BASE as u32 * (level as u32 + 1), + 3 => SCORE_TRIPLE_BASE as u32 * (level as u32 + 1), + 4 => SCORE_QUADRUPLE_BASE as u32 * (level as u32 + 1), + _ => { + println!("[!] player was attributed a number of rings too large maybe, what the heck? num_cleared_rings: {}", num_cleared_rings); + 0u32 + } + }; + } (num_cleared_rings, score_from_cleared_rings) } @@ -996,7 +1001,7 @@ impl BoardRotatris { fn rotatris_check_single_ring(&mut self, z: u8) -> bool { let min = std::cmp::min(z, self.board_size - z); let max = std::cmp::max(z, self.board_size - z); - for a in [min, max - 1].into_iter() { + for a in [min, max - 1].iter() { for b in min..max { if b >= min && b <= max { if self.matrix[*a as usize][b as usize].empty @@ -1035,8 +1040,8 @@ impl BoardRotatris { self.matrix[k + 1][k + 1] = self.matrix[k][k]; } - fn rotatris_emptify_single_ring(&mut self, z: u8) { - for a in [z, self.board_size - z - 1].into_iter() { + fn emptify_single_ring(&mut self, z: u8) { + for a in [z, self.board_size - z - 1].iter() { for b in z..(self.board_size - z) { if b >= z && b <= self.board_size - z { self.matrix[*a as usize][b as usize].empty = true; diff --git a/src/game/piece.rs b/src/game/piece.rs index a10930e..90a1b5b 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -280,10 +280,6 @@ impl Piece { piece_copy.positions = piece_copy.rotate(false); piece_copy.piece_pos(Movement::Down) } - _ => panic!( - "[!] Error: current_gravity is invalid: {} (in `Piece::spawn_pos()`)", - current_gravity as u8 - ), } } } From 150c123b8d4639943e60c25b5f58b788c539c394 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 23 Mar 2021 22:57:09 -0400 Subject: [PATCH 16/59] working tetrisnt gameplay? --- src/game/board.rs | 53 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/game/board.rs b/src/game/board.rs index b702329..cfe8c73 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -8,7 +8,7 @@ use crate::game::{ static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong option"; #[repr(u8)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq, PartialEq)] pub enum Gravity { Down, Left, @@ -28,6 +28,18 @@ impl From for Gravity { } } +impl From for Gravity { + fn from(value: Movement) -> Gravity { + match value { + Movement::Down => Gravity::Down, + Movement::Left => Gravity::Left, + Movement::Up => Gravity::Up, + Movement::Right => Gravity::Right, + _ => panic!("Unknown Gravity value: {}", value as u8), + } + } +} + // abstract the board and the possible gamemodes into one struct pub struct BoardHandler { pub mode: Modes, @@ -638,18 +650,18 @@ impl BoardRotatris { let mut matrix = vec![vec![Tile::default(); board_size as usize]; board_size as usize]; // DEBUG - for a in 0..4 { - for b in 0..board_size as usize { - matrix[a][b] = Tile::new(false, false, 0, Shapes::I); - matrix[b][a] = Tile::new(false, false, 0, Shapes::I); - matrix[board_size as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); - matrix[b][board_size as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); - } - } - matrix[board_size as usize / 2][board_size as usize - 1] = Tile::default(); - matrix[board_size as usize / 2][board_size as usize - 2] = Tile::default(); - matrix[board_size as usize / 2][board_size as usize - 3] = Tile::default(); - matrix[board_size as usize / 2][board_size as usize - 4] = Tile::default(); + // for a in 0..4 { + // for b in 0..board_size as usize { + // matrix[a][b] = Tile::new(false, false, 0, Shapes::I); + // matrix[b][a] = Tile::new(false, false, 0, Shapes::I); + // matrix[board_size as usize - a - 1][b] = Tile::new(false, false, 0, Shapes::I); + // matrix[b][board_size as usize - a - 1] = Tile::new(false, false, 0, Shapes::I); + // } + // } + // matrix[board_size as usize / 2][board_size as usize - 1] = Tile::default(); + // matrix[board_size as usize / 2][board_size as usize - 2] = Tile::default(); + // matrix[board_size as usize / 2][board_size as usize - 3] = Tile::default(); + // matrix[board_size as usize / 2][board_size as usize - 4] = Tile::default(); Self { gravity: Gravity::Down, @@ -708,9 +720,9 @@ impl BoardRotatris { self.gravity = Gravity::from( (self.gravity as u8 + if rotate_direction == Movement::RotateCw { - 3 - } else { 1 + } else { + 3 }) % 4, ); @@ -745,7 +757,7 @@ impl BoardRotatris { let new_positions = self.vec_active_piece[player as usize].piece_pos(movement); for position in new_positions.iter().take(4) { // due to integer underflow (u8 board width and u8 board height), we must only check the positive side of x and y positions - if position.0 >= self.board_size + self.board_size { + if position.0 >= self.board_size { cant_move_flag = true; break; } @@ -764,8 +776,10 @@ impl BoardRotatris { } if cant_move_flag { + println!("can't move {} == {} && should_lock(player)?", Gravity::from(movement) as u8 , self.gravity as u8); // TODO: fix this awful code - if movement as u8 == self.gravity as u8 && self.should_lock(player) { + if Gravity::from(movement) == self.gravity && self.should_lock(player) { + println!("hey we should be locking now"); // lock piece and push any full lines to vec_full_lines self.vec_active_piece[player as usize].shape = Shapes::None; @@ -898,9 +912,11 @@ impl BoardRotatris { .iter() .take(4) { + println!("gravity: {}", self.gravity as u8); // we just want to know if moving down by 1 will run the piece into the bottom of the board or an inactive tile match self.gravity { Gravity::Down => { + println!("position.0: {}; self.board_size: {}", position.0, self.board_size); if position.0 as usize + 1 >= self.board_size as usize { return true; } @@ -970,14 +986,17 @@ impl BoardRotatris { for z in (0..num_rings_to_check).rev() { if self.rotatris_check_single_ring(z) { num_cleared_rings += 1; + println!("ring {} is full", z); // clear and pull inner stuff out for j in (z + 1)..num_rings_to_check { self.rotatris_pull_single_ring_out(j); } + } else { } } if num_cleared_rings > 0 { + println!("and we're out with a line clear: {}", num_cleared_rings); // get rid of all tiles in the middle area for z in num_rings_to_check..=((self.board_size - 1) / 2) { self.emptify_single_ring(z); From 57fa5184dc89cd56c7b2ae330c777a3024e8e8dd Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 23 Mar 2021 23:06:14 -0400 Subject: [PATCH 17/59] rm debug printlnsteam --help --- src/game/board.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/game/board.rs b/src/game/board.rs index cfe8c73..b173100 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -776,10 +776,8 @@ impl BoardRotatris { } if cant_move_flag { - println!("can't move {} == {} && should_lock(player)?", Gravity::from(movement) as u8 , self.gravity as u8); // TODO: fix this awful code if Gravity::from(movement) == self.gravity && self.should_lock(player) { - println!("hey we should be locking now"); // lock piece and push any full lines to vec_full_lines self.vec_active_piece[player as usize].shape = Shapes::None; @@ -912,11 +910,9 @@ impl BoardRotatris { .iter() .take(4) { - println!("gravity: {}", self.gravity as u8); // we just want to know if moving down by 1 will run the piece into the bottom of the board or an inactive tile match self.gravity { Gravity::Down => { - println!("position.0: {}; self.board_size: {}", position.0, self.board_size); if position.0 as usize + 1 >= self.board_size as usize { return true; } @@ -986,7 +982,6 @@ impl BoardRotatris { for z in (0..num_rings_to_check).rev() { if self.rotatris_check_single_ring(z) { num_cleared_rings += 1; - println!("ring {} is full", z); // clear and pull inner stuff out for j in (z + 1)..num_rings_to_check { self.rotatris_pull_single_ring_out(j); @@ -996,7 +991,6 @@ impl BoardRotatris { } if num_cleared_rings > 0 { - println!("and we're out with a line clear: {}", num_cleared_rings); // get rid of all tiles in the middle area for z in num_rings_to_check..=((self.board_size - 1) / 2) { self.emptify_single_ring(z); From 8eea2b73c8a8273f36ebb531101612e0cbc8dffb Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Wed, 24 Mar 2021 21:59:15 -0400 Subject: [PATCH 18/59] fix bug that crashes on unable-to-rotate --- src/game/board.rs | 8 ++++++-- src/game/piece.rs | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/game/board.rs b/src/game/board.rs index b173100..07e2f37 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -14,6 +14,7 @@ pub enum Gravity { Left, Up, Right, + Invalid, } impl From for Gravity { @@ -23,7 +24,7 @@ impl From for Gravity { 1 => Gravity::Left, 2 => Gravity::Up, 3 => Gravity::Right, - _ => panic!("Unknown Gravity value: {}", value), + _ => panic!("[!] Unknown Gravity value: {}", value), } } } @@ -35,7 +36,7 @@ impl From for Gravity { Movement::Left => Gravity::Left, Movement::Up => Gravity::Up, Movement::Right => Gravity::Right, - _ => panic!("Unknown Gravity value: {}", value as u8), + _ => Gravity::Invalid, } } } @@ -952,6 +953,9 @@ impl BoardRotatris { return true; } } + Gravity::Invalid => { + panic!("[!] Gravity::Invalid attempted to be used in `should_lock()`") + } } } diff --git a/src/game/piece.rs b/src/game/piece.rs index 90a1b5b..ff869a8 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -280,6 +280,7 @@ impl Piece { piece_copy.positions = piece_copy.rotate(false); piece_copy.piece_pos(Movement::Down) } + Gravity::Invalid => panic!("[!] Gravity::Invalid passed into `spawn_pos()`"), } } } From cb1fd50f0d6f64970cf4830360ab10152c7105c8 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Wed, 24 Mar 2021 22:30:36 -0400 Subject: [PATCH 19/59] fix classic mode lines clearing instantly --- src/game.rs | 10 +++++----- src/game/board.rs | 15 ++++++++++----- src/game/piece.rs | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/game.rs b/src/game.rs index 7080e50..a3cdd35 100644 --- a/src/game.rs +++ b/src/game.rs @@ -28,7 +28,7 @@ const BOARD_HEIGHT: u8 = 20u8; const ROTATRIS_BOARD_SIDE_LENGTH: u8 = 20u8; -pub const CLEAR_DELAY: i8 = 0i8; +pub const CLEAR_DELAY_CLASSIC: i8 = 30i8; pub const SCORE_SINGLE_BASE: u8 = 40u8; pub const SCORE_DOUBLE_BASE: u8 = 100u8; @@ -152,7 +152,7 @@ pub struct Game { impl Game { pub fn new(ctx: &mut Context, game_options: &GameOptions) -> Game { - let mode = Modes::Rotatris; + let mode = Modes::Classic; let board_width = match mode { Modes::Classic => 6 + 4 * game_options.num_players, Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, @@ -544,7 +544,7 @@ impl Game { player.force_fall_countdown = FORCE_FALL_DELAY; // add more spawn delay if locking the piece caused a line clear if caused_full_line_flag { - player.spawn_delay += CLEAR_DELAY as i16; + player.spawn_delay += CLEAR_DELAY_CLASSIC as i16; } } if moved_flag { @@ -803,7 +803,7 @@ impl Game { .add(highlight_pos_left); if ((x as f32) / (width as f32) - 0.5) * 2.0 - > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) + > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY_CLASSIC as f32) { break; } @@ -830,7 +830,7 @@ impl Game { .add(highlight_pos_left); if ((x as f32) / (width as f32) - 0.5) * 2.0 - > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY as f32) + > 1.0 - (full_line.clear_delay as f32 / CLEAR_DELAY_CLASSIC as f32) { break; } diff --git a/src/game/board.rs b/src/game/board.rs index 07e2f37..a2aa982 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -2,7 +2,8 @@ use crate::game::piece::{Movement, Piece, Shapes}; use crate::game::tile::Tile; use crate::game::Modes; use crate::game::{ - CLEAR_DELAY, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, SCORE_TRIPLE_BASE, + CLEAR_DELAY_CLASSIC, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, + SCORE_TRIPLE_BASE, }; static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong option"; @@ -522,6 +523,10 @@ impl BoardClassic { // not much to see here return (0, 0); } + println!( + "clearing lines now size: {}", + vec_clearing_now_indices.len() + ); // for the return value, we need to know how many lines are being cleared (the first return in the tuple) // and we need to know the amount of points from the lines clearing right now (the second in the tuple) @@ -627,7 +632,7 @@ impl FullLine { row, lines_cleared_together, player, - clear_delay: CLEAR_DELAY, + clear_delay: CLEAR_DELAY_CLASSIC, remove_flag: false, } } @@ -1112,7 +1117,7 @@ mod tests { } // now to clear 2 Tetrises on the same frame and see what happens - for _ in 0..=CLEAR_DELAY { + for _ in 0..=CLEAR_DELAY_CLASSIC { let (returned_lines, returned_score) = board.attempt_clear_lines(0); if returned_lines > 0 { num_cleared_lines += returned_lines as u16; @@ -1159,7 +1164,7 @@ mod tests { } // now clear and see what happens - for _ in 0..=CLEAR_DELAY { + for _ in 0..=CLEAR_DELAY_CLASSIC { let (returned_lines, returned_score) = board.attempt_clear_lines(0); if returned_lines > 0 { num_cleared_lines += returned_lines as u16; @@ -1210,7 +1215,7 @@ mod tests { } // now to clear 2 Tetrises one frame apart and see what happens - for _ in 0..=CLEAR_DELAY { + for _ in 0..=CLEAR_DELAY_CLASSIC { let (returned_lines, returned_score) = board.attempt_clear_lines(0); if returned_lines > 0 { num_cleared_lines += returned_lines as u16; diff --git a/src/game/piece.rs b/src/game/piece.rs index ff869a8..a676683 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -215,7 +215,7 @@ impl Piece { Shapes::O => { [ (spawn_row + board_height_buffer, spawn_column - 1), // [-][-][-][-] - (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] + (spawn_row + board_height_buffer, spawn_column), // [-][-][-][-] (spawn_row + 1 + board_height_buffer, spawn_column - 1), // [-][0][1][-] (spawn_row + 1 + board_height_buffer, spawn_column), // [-][2][3][-] ] From 10b8ff7540f53c50af3c5739ae558e62335ca116 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Wed, 24 Mar 2021 22:43:48 -0400 Subject: [PATCH 20/59] different fall delay values for the different modes --- src/game.rs | 13 +++++++++---- src/game/board.rs | 11 +++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/game.rs b/src/game.rs index a3cdd35..45f5a97 100644 --- a/src/game.rs +++ b/src/game.rs @@ -50,10 +50,15 @@ const LITTLE_TEXT_SCALE: f32 = 20.0; // for each level (as the index), the number of frames it takes for a piece to move down one row (everything after 29 is also 0) // it's actually 1 less than the number of frames it takes the piece to fall because the game logic works out better that way -const FALL_DELAY_VALUES: [u8; 30] = [ +const FALL_DELAY_VALUES_CLASSIC: [u8; 30] = [ 47, 42, 37, 32, 27, 22, 17, 12, 7, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, ]; +// rotatris is much harder, so slower fall rates than classic +const FALL_DELAY_VALUES_ROTATRIS: [u8; 30] = [ + 60, 50, 40, 35, 30, 25, 20, 15, 10, 7, 6, 5, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, +]; // number of frames between downward movements when holding down pub const FORCE_FALL_DELAY: u8 = 2; @@ -152,7 +157,7 @@ pub struct Game { impl Game { pub fn new(ctx: &mut Context, game_options: &GameOptions) -> Game { - let mode = Modes::Classic; + let mode = Modes::Rotatris; let board_width = match mode { Modes::Classic => 6 + 4 * game_options.num_players, Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, @@ -537,7 +542,7 @@ impl Game { if self.bh.get_shape_from_player(player.player_num) == Shapes::None { player.spawn_piece_flag = true; player.fall_countdown = if self.level < 30 { - FALL_DELAY_VALUES[self.level as usize] + self.bh.get_fall_delay_from_level(self.level) } else { 0 }; @@ -549,7 +554,7 @@ impl Game { } if moved_flag { player.fall_countdown = if self.level < 30 { - FALL_DELAY_VALUES[self.level as usize] + self.bh.get_fall_delay_from_level(self.level) } else { 0 }; diff --git a/src/game/board.rs b/src/game/board.rs index a2aa982..31605da 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -2,8 +2,8 @@ use crate::game::piece::{Movement, Piece, Shapes}; use crate::game::tile::Tile; use crate::game::Modes; use crate::game::{ - CLEAR_DELAY_CLASSIC, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, - SCORE_TRIPLE_BASE, + CLEAR_DELAY_CLASSIC, FALL_DELAY_VALUES_CLASSIC, FALL_DELAY_VALUES_ROTATRIS, SCORE_DOUBLE_BASE, + SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, SCORE_TRIPLE_BASE, }; static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong option"; @@ -160,6 +160,13 @@ impl BoardHandler { } } + pub fn get_fall_delay_from_level(&mut self, level: u8) -> u8 { + match self.mode { + Modes::Classic => FALL_DELAY_VALUES_CLASSIC[level as usize], + Modes::Rotatris => FALL_DELAY_VALUES_ROTATRIS[level as usize], + } + } + // board logic functions pub fn attempt_piece_spawn( &mut self, From 1d823d79f7a4f5165c5f40c48ea55a4862f80776 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Wed, 24 Mar 2021 22:48:16 -0400 Subject: [PATCH 21/59] changed rotatris to have "Rings: ..." instead of Lines --- src/game.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/game.rs b/src/game.rs index 45f5a97..3e1021a 100644 --- a/src/game.rs +++ b/src/game.rs @@ -157,7 +157,7 @@ pub struct Game { impl Game { pub fn new(ctx: &mut Context, game_options: &GameOptions) -> Game { - let mode = Modes::Rotatris; + let mode = Modes::Classic; let board_width = match mode { Modes::Classic => 6 + 4 * game_options.num_players, Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, @@ -244,11 +244,18 @@ impl Game { TileGraphic::new_player(ctx, player as u8).image, )); } - let mut game_info_text = Text::new( - TextFragment::new("Lines: ") - .color(graphics::WHITE) - .scale(Scale::uniform(LITTLE_TEXT_SCALE)), - ); + let mut game_info_text = match mode { + Modes::Classic => Text::new( + TextFragment::new("Lines: ") + .color(graphics::WHITE) + .scale(Scale::uniform(LITTLE_TEXT_SCALE)), + ), + Modes::Rotatris => Text::new( + TextFragment::new("Rings: ") + .color(graphics::WHITE) + .scale(Scale::uniform(LITTLE_TEXT_SCALE)), + ), + }; game_info_text.add( TextFragment::new("000") .color(graphics::WHITE) From e50c1489c2389797ff4e8b59fc360ef10e9c7875 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 3 Apr 2021 20:51:27 -0400 Subject: [PATCH 22/59] start menu rewrite --- .cargo/config.toml | 2 + src/game/board.rs | 4 -- src/menu.rs | 98 ++++++++++++++++++++++++++++++++++-------- src/menu/choosemode.rs | 0 4 files changed, 82 insertions(+), 22 deletions(-) create mode 100644 src/menu/choosemode.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index ceabfd2..1f9d572 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,5 @@ [alias] optwin = "build -Z unstable-options --profile opt --target x86_64-pc-windows-gnu" relwin = "build --release --target x86_64-pc-windows-gnu" +relpi = "build --release --target armv7-unknown-linux-gnueabihf" + diff --git a/src/game/board.rs b/src/game/board.rs index 31605da..f8b6891 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -530,10 +530,6 @@ impl BoardClassic { // not much to see here return (0, 0); } - println!( - "clearing lines now size: {}", - vec_clearing_now_indices.len() - ); // for the return value, we need to know how many lines are being cleared (the first return in the tuple) // and we need to know the amount of points from the lines clearing right now (the second in the tuple) diff --git a/src/menu.rs b/src/menu.rs index 088a493..32f5dc7 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,5 +1,5 @@ use ggez::event::KeyCode; -use ggez::graphics::{self, Color}; +use ggez::graphics::{self, Color, Text, TextFragment}; use ggez::Context; use crate::control::ProgramState; @@ -22,6 +22,61 @@ mod start; use inputconfig::InputConfigMenu; use start::StartMenu; +pub enum MenuItemSize { + Small, + Normal, + Big, +} + +#[derive(Eq, PartialEq)] +pub enum MenuItemValueType { + None, + NumPlayers, + StartingLevel, +} + +#[derive(Eq, PartialEq)] +pub enum MenuItemTrigger { + None, + StartGame, + SubMenu1, + SubMenu2, + Back, +} + +pub struct MenuItem { + text: Text, + value_type: MenuItemValueType, + trigger: MenuItemTrigger, +} + +impl MenuItem { + fn new(item_start_str: String, value_type: MenuItemValueType, value: usize, trigger: MenuItemTrigger) -> Self { + let mut text = Text::new( + TextFragment::new(item_start_str) + .color(graphics::BLACK) + ); + if value_type != MenuItemValueType::None { + text.add( + TextFragment::new(format!(" {}", value)) + .color(graphics::BLACK) + ); + } + Self { + text, + value_type, + trigger, + } + } + + fn color(&mut self, new_color: Color) { + self.text.fragments_mut()[0].color = Some(new_color); + if self.value_type != MenuItemValueType::None { + self.text.fragments_mut()[1].color = Some(new_color); + } + } +} + pub struct MenuGameOptions { pub num_players: u8, pub starting_level: u8, @@ -132,23 +187,30 @@ impl Menu { pub fn update(&mut self) -> Option<(ProgramState, MenuGameOptions)> { match self.state { MenuState::Start => { - let ret_bools: (bool, bool) = self.start_menu.update(&self.input); - if ret_bools.0 { - if self.ensure_enough_controls() { - return Some(( - ProgramState::Game, - MenuGameOptions::new( - self.start_menu.num_players, - self.start_menu.starting_level, - self.input_config_menu.arr_split_controls, - ), - )); - } else { - self.start_menu.not_enough_controls_flag = true; - } - } else if ret_bools.1 { - // we are entering the InputConfig menu - self.state = MenuState::InputConfig; + let trigger: MenuItemTrigger = self.start_menu.update(&self.input); + match trigger { + MenuItemTrigger::StartGame => { + if self.ensure_enough_controls() { + return Some(( + ProgramState::Game, + MenuGameOptions::new( + self.start_menu.num_players, + self.start_menu.starting_level, + self.input_config_menu.arr_split_controls, + ), + )); + } else { + self.start_menu.not_enough_controls_flag = true; + } + }, + MenuItemTrigger::SubMenu1 => { + // InputConfig menu + self.state = MenuState::InputConfig; + }, + MenuItemTrigger::Back => { + println!("[!] what? 1"); + }, + _ => println!("[!] Wrong menu?"), } } MenuState::InputConfig => { diff --git a/src/menu/choosemode.rs b/src/menu/choosemode.rs new file mode 100644 index 0000000..e69de29 From 58e0792e1434f4e734dc073cd508033a5457f9ae Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sun, 4 Apr 2021 21:56:51 -0400 Subject: [PATCH 23/59] about to PURGE EVERYTHING --- src/menu.rs | 30 +++++++++++++++--------------- src/menu/start.rs | 11 +++++++---- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/menu.rs b/src/menu.rs index 32f5dc7..94e19d4 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -51,16 +51,15 @@ pub struct MenuItem { } impl MenuItem { - fn new(item_start_str: String, value_type: MenuItemValueType, value: usize, trigger: MenuItemTrigger) -> Self { - let mut text = Text::new( - TextFragment::new(item_start_str) - .color(graphics::BLACK) - ); + fn new( + item_start_str: String, + value_type: MenuItemValueType, + value: usize, + trigger: MenuItemTrigger, + ) -> Self { + let mut text = Text::new(TextFragment::new(item_start_str).color(graphics::BLACK)); if value_type != MenuItemValueType::None { - text.add( - TextFragment::new(format!(" {}", value)) - .color(graphics::BLACK) - ); + text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); } Self { text, @@ -69,10 +68,10 @@ impl MenuItem { } } - fn color(&mut self, new_color: Color) { - self.text.fragments_mut()[0].color = Some(new_color); + fn color(&mut self, new_color: &Color) { + self.text.fragments_mut()[0].color = Some(*new_color); if self.value_type != MenuItemValueType::None { - self.text.fragments_mut()[1].color = Some(new_color); + self.text.fragments_mut()[1].color = Some(*new_color); } } } @@ -202,14 +201,15 @@ impl Menu { } else { self.start_menu.not_enough_controls_flag = true; } - }, + } MenuItemTrigger::SubMenu1 => { // InputConfig menu self.state = MenuState::InputConfig; - }, + } MenuItemTrigger::Back => { println!("[!] what? 1"); - }, + } + MenuItemTrigger::None => {} _ => println!("[!] Wrong menu?"), } } diff --git a/src/menu/start.rs b/src/menu/start.rs index aef004b..bf692dd 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -4,6 +4,7 @@ use ggez::nalgebra::Point2; use ggez::Context; use crate::inputs::Input; +use crate::menu::{MenuItem, MenuItemTrigger}; use crate::menu::MAX_NUM_PLAYERS; use crate::menu::MAX_STARTING_LEVEL; @@ -30,6 +31,7 @@ pub struct StartMenu { pub starting_level: u8, pub not_enough_controls_flag: bool, // drawing + // vec_menu_items: Vec, start_text: Text, not_enough_controls_text: Text, num_players_text: Text, @@ -84,7 +86,7 @@ impl StartMenu { } } - pub fn update(&mut self, input: &Input) -> (bool, bool) { + pub fn update(&mut self, input: &Input) -> MenuItemTrigger { if input.keydown_right.1 { self.inc_or_dec_selection(true); } @@ -111,13 +113,14 @@ impl StartMenu { if input.keydown_start.1 && self.selection == StartMenuOption::Controls as u8 { self.not_enough_controls_flag = false; - return (false, true); + return MenuItemTrigger::SubMenu1; } if input.keydown_start.1 && self.selection == StartMenuOption::Start as u8 { - return (true, false); + return MenuItemTrigger::StartGame; } - (false, false) + + MenuItemTrigger::None } fn set_select(&mut self, select_flag: bool) { From f37f603177ba1e0fdcb181891650378f60710770 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sun, 4 Apr 2021 23:45:09 -0400 Subject: [PATCH 24/59] refactor start-menu code --- src/menu.rs | 91 +++++++++++++----- src/menu/start.rs | 228 ++++++++++++++-------------------------------- 2 files changed, 138 insertions(+), 181 deletions(-) diff --git a/src/menu.rs b/src/menu.rs index 94e19d4..f89d290 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -22,12 +22,6 @@ mod start; use inputconfig::InputConfigMenu; use start::StartMenu; -pub enum MenuItemSize { - Small, - Normal, - Big, -} - #[derive(Eq, PartialEq)] pub enum MenuItemValueType { None, @@ -35,7 +29,7 @@ pub enum MenuItemValueType { StartingLevel, } -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Copy, Clone)] pub enum MenuItemTrigger { None, StartGame, @@ -45,33 +39,82 @@ pub enum MenuItemTrigger { } pub struct MenuItem { - text: Text, - value_type: MenuItemValueType, - trigger: MenuItemTrigger, + pub text: Text, + pub value_type: MenuItemValueType, + max_value: u8, + pub value: u8, + value_show_increase: u8, + pub trigger: MenuItemTrigger, + selected: bool, } impl MenuItem { - fn new( - item_start_str: String, + pub fn new( + item_start_str: &str, value_type: MenuItemValueType, - value: usize, + value: u8, trigger: MenuItemTrigger, ) -> Self { let mut text = Text::new(TextFragment::new(item_start_str).color(graphics::BLACK)); - if value_type != MenuItemValueType::None { - text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); + let mut max_value = 0; + let mut value_show_increase = 0; + match value_type { + MenuItemValueType::None => {} + MenuItemValueType::NumPlayers => { + max_value = MAX_NUM_PLAYERS; + value_show_increase = 1; + text.add( + TextFragment::new(format!(" {}", value + value_show_increase)) + .color(graphics::BLACK), + ); + } + MenuItemValueType::StartingLevel => { + max_value = MAX_STARTING_LEVEL; + text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); + } } Self { text, value_type, + max_value, + value, + value_show_increase, trigger, + selected: false, } } - fn color(&mut self, new_color: &Color) { - self.text.fragments_mut()[0].color = Some(*new_color); + pub fn set_select(&mut self, select: bool) { + self.selected = select; + self.text.fragments_mut()[0].color = Some(if select { + SELECT_GREEN + } else { + graphics::BLACK + }); + if self.value_type != MenuItemValueType::None { + self.text.fragments_mut()[1].color = Some(if select { + SELECT_GREEN + } else { + graphics::BLACK + }); + self.text.fragments_mut()[1].text = if select { + format!(" <{}>", self.value + self.value_show_increase) + } else { + format!(" {}", self.value + self.value_show_increase) + }; + } + } + + pub fn inc_or_dec(&mut self, inc: bool) { if self.value_type != MenuItemValueType::None { - self.text.fragments_mut()[1].color = Some(*new_color); + self.value = if inc { + (self.value + 1) % self.max_value + } else { + (self.value - 1 + self.max_value) % self.max_value + }; + // assume it's selected because it's being incremented/decremented + self.text.fragments_mut()[1].text = + format!(" <{}>", self.value + self.value_show_increase); } } } @@ -160,7 +203,6 @@ impl Menu { input: Input::new(), state: MenuState::Start, start_menu: StartMenu::new( - window_dimensions, menu_game_options.num_players, menu_game_options.starting_level, ), @@ -174,7 +216,7 @@ impl Menu { Self { input: Input::new(), state: MenuState::Start, - start_menu: StartMenu::new(window_dimensions, 1, 0), + start_menu: StartMenu::new(1, 0), input_config_menu: InputConfigMenu::new( window_dimensions, [(None, false); MAX_NUM_PLAYERS as usize], @@ -190,11 +232,13 @@ impl Menu { match trigger { MenuItemTrigger::StartGame => { if self.ensure_enough_controls() { + let (num_players, starting_level) = + self.start_menu.find_important_values(); return Some(( ProgramState::Game, MenuGameOptions::new( - self.start_menu.num_players, - self.start_menu.starting_level, + num_players, + starting_level, self.input_config_menu.arr_split_controls, ), )); @@ -204,6 +248,7 @@ impl Menu { } MenuItemTrigger::SubMenu1 => { // InputConfig menu + self.start_menu.not_enough_controls_flag = false; self.state = MenuState::InputConfig; } MenuItemTrigger::Back => { @@ -231,7 +276,7 @@ impl Menu { ctrls_count += 1; } } - ctrls_count >= self.start_menu.num_players + ctrls_count >= self.start_menu.find_important_values().0 } pub fn key_down_event(&mut self, keycode: KeyCode, _repeat: bool) { diff --git a/src/menu/start.rs b/src/menu/start.rs index bf692dd..493b6ee 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -1,212 +1,109 @@ use ggez::graphics::{self, DrawParam}; -use ggez::graphics::{Scale, Text, TextFragment}; +use ggez::graphics::{Text, TextFragment}; use ggez::nalgebra::Point2; use ggez::Context; use crate::inputs::Input; -use crate::menu::{MenuItem, MenuItemTrigger}; - -use crate::menu::MAX_NUM_PLAYERS; -use crate::menu::MAX_STARTING_LEVEL; - -use crate::menu::SUB_TEXT_SCALE_DOWN; -use crate::menu::TEXT_SCALE_DOWN; +use crate::menu::{MenuItem, MenuItemTrigger, MenuItemValueType}; use crate::menu::HELP_RED; -use crate::menu::SELECT_GREEN; - -const NUM_STARTMENUOPTION_TEXT_ENTRIES: u8 = 4; -#[repr(u8)] -enum StartMenuOption { - Start, - NumPlayers, - StartingLevel, - Controls, -} pub struct StartMenu { // logic - selection: u8, - pub num_players: u8, - pub starting_level: u8, + selection: usize, pub not_enough_controls_flag: bool, // drawing - // vec_menu_items: Vec, - start_text: Text, + vec_menu_items: Vec, not_enough_controls_text: Text, - num_players_text: Text, - starting_level_text: Text, - controls_text: Text, } impl StartMenu { - pub fn new(window_dimensions: (f32, f32), num_players: u8, starting_level: u8) -> Self { - let mut num_players_text = Text::new( - TextFragment::new("Number of Players: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ); - num_players_text.add( - TextFragment::new(format!("{}", num_players)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ); - let mut starting_level_text = Text::new( - TextFragment::new("Starting Level: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ); - starting_level_text.add( - TextFragment::new(format!("{}", starting_level)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ); - Self { - selection: StartMenuOption::Start as u8, - num_players, + pub fn new(num_players: u8, starting_level: u8) -> Self { + let mut vec_menu_items: Vec = Vec::with_capacity(4); + vec_menu_items.push(MenuItem::new( + "Start", + MenuItemValueType::None, + 0, + MenuItemTrigger::StartGame, + )); + vec_menu_items.push(MenuItem::new( + "Number of Players: ", + MenuItemValueType::NumPlayers, + num_players - 1, + MenuItemTrigger::StartGame, + )); + vec_menu_items.push(MenuItem::new( + "Starting Level: ", + MenuItemValueType::StartingLevel, starting_level, + MenuItemTrigger::StartGame, + )); + vec_menu_items.push(MenuItem::new( + "Controls", + MenuItemValueType::None, + 0, + MenuItemTrigger::SubMenu1, + )); + vec_menu_items[0].set_select(true); + Self { + // logic + selection: 0, not_enough_controls_flag: false, - start_text: Text::new( - TextFragment::new("Start") - .color(SELECT_GREEN) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ), + vec_menu_items, + // drawing not_enough_controls_text: Text::new( - TextFragment::new("[!] Not enough controls setup to start") - .color(HELP_RED) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ), - num_players_text, - starting_level_text, - controls_text: Text::new( - TextFragment::new("Controls") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), + TextFragment::new("[!] Not enough Controls Setup to Start").color(HELP_RED), ), } } pub fn update(&mut self, input: &Input) -> MenuItemTrigger { if input.keydown_right.1 { - self.inc_or_dec_selection(true); + self.vec_menu_items[self.selection].inc_or_dec(true); } if input.keydown_left.1 { - self.inc_or_dec_selection(false); + self.vec_menu_items[self.selection].inc_or_dec(false); } if input.keydown_down.1 { - self.set_select(false); - self.selection = (self.selection + 1) % NUM_STARTMENUOPTION_TEXT_ENTRIES; - self.set_select(true); + self.vec_menu_items[self.selection].set_select(false); + self.selection = (self.selection + 1) % self.vec_menu_items.len(); + self.vec_menu_items[self.selection].set_select(true); } if input.keydown_up.1 { - self.set_select(false); + self.vec_menu_items[self.selection].set_select(false); self.selection = if self.selection == 0 { - NUM_STARTMENUOPTION_TEXT_ENTRIES - 1 + self.vec_menu_items.len() - 1 } else { self.selection - 1 }; - self.set_select(true); + self.vec_menu_items[self.selection].set_select(true); } - if input.keydown_start.1 && self.selection == StartMenuOption::Controls as u8 { - self.not_enough_controls_flag = false; - return MenuItemTrigger::SubMenu1; - } - - if input.keydown_start.1 && self.selection == StartMenuOption::Start as u8 { - return MenuItemTrigger::StartGame; + if input.keydown_start.1 { + return self.vec_menu_items[self.selection].trigger; } MenuItemTrigger::None } - fn set_select(&mut self, select_flag: bool) { - match self.selection { - x if x == StartMenuOption::Start as u8 => { - if select_flag { - self.start_text.fragments_mut()[0].color = Some(SELECT_GREEN); - } else { - self.start_text.fragments_mut()[0].color = Some(graphics::BLACK); - } - } - x if x == StartMenuOption::NumPlayers as u8 => { - if select_flag { - self.num_players_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.num_players_text.fragments_mut()[1].color = Some(SELECT_GREEN); - self.num_players_text.fragments_mut()[1].text = - format!("<{}>", self.num_players); - } else { - self.num_players_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.num_players_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.num_players_text.fragments_mut()[1].text = - format!(" {}", self.num_players); - } - } - x if x == StartMenuOption::StartingLevel as u8 => { - if select_flag { - self.starting_level_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.starting_level_text.fragments_mut()[1].color = Some(SELECT_GREEN); - self.starting_level_text.fragments_mut()[1].text = - format!("<{}>", self.starting_level); - } else { - self.starting_level_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.starting_level_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.starting_level_text.fragments_mut()[1].text = - format!(" {}", self.starting_level); - } - } - x if x == StartMenuOption::Controls as u8 => { - if select_flag { - self.controls_text.fragments_mut()[0].color = Some(SELECT_GREEN); - } else { - self.controls_text.fragments_mut()[0].color = Some(graphics::BLACK); - } - } - _ => println!("[!] main_menu_option didn't find match"), - } - } - - fn inc_or_dec_selection(&mut self, inc_flag: bool) { - // the if/else here only includes StartMenuOptions that have a value that can be modified - if self.selection == StartMenuOption::NumPlayers as u8 { - // special case (index by 1 because we can't have 0 players) - if inc_flag { - self.num_players = self.num_players % MAX_NUM_PLAYERS + 1; - } else { - self.num_players = if self.num_players == 1 { - MAX_NUM_PLAYERS - } else { - self.num_players - 1 - }; - } - self.num_players_text.fragments_mut()[1].text = format!("<{}>", self.num_players); - } else if self.selection == StartMenuOption::StartingLevel as u8 { - if inc_flag { - self.starting_level = (self.starting_level + 1) % (MAX_STARTING_LEVEL + 1); - } else { - self.starting_level = if self.starting_level == 0 { - MAX_STARTING_LEVEL - } else { - self.starting_level - 1 - }; - } - self.starting_level_text.fragments_mut()[1].text = format!("<{}>", self.starting_level); - } - } - pub fn draw(&mut self, ctx: &mut Context) { let window_dimensions = graphics::size(ctx); + let num_menu_items_to_draw = self.vec_menu_items.len(); if self.not_enough_controls_flag { self.draw_text(ctx, &self.not_enough_controls_text, 0.1, &window_dimensions); } - self.draw_text(ctx, &self.start_text, 0.2, &window_dimensions); - self.draw_text(ctx, &self.num_players_text, 0.4, &window_dimensions); - self.draw_text(ctx, &self.starting_level_text, 0.6, &window_dimensions); - self.draw_text(ctx, &self.controls_text, 0.8, &window_dimensions); + for (index, item) in self.vec_menu_items.iter().enumerate() { + self.draw_text( + ctx, + &item.text, + (1.0 * (index + 1) as f32) / (num_menu_items_to_draw + 1) as f32, + &window_dimensions, + ); + } } fn draw_text( @@ -227,4 +124,19 @@ impl StartMenu { ) .unwrap(); } + + // searches through self.vec_menu_items and gets the important values; returns (num_players, starting_level) + pub fn find_important_values(&self) -> (u8, u8) { + let mut num_players = 0; + let mut starting_level = 0; + for item in self.vec_menu_items.iter() { + match item.value_type { + MenuItemValueType::None => {} + MenuItemValueType::NumPlayers => num_players = item.value + 1, + MenuItemValueType::StartingLevel => starting_level = item.value, + } + } + + (num_players, starting_level) + } } From 2d771323d45aedfabef541b153dbed136dc88a11 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Mon, 5 Apr 2021 21:39:32 -0400 Subject: [PATCH 25/59] text scale like before --- src/menu.rs | 25 ++++++++++++++++++++----- src/menu/start.rs | 12 +++++++++++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/menu.rs b/src/menu.rs index f89d290..4356586 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,5 +1,5 @@ use ggez::event::KeyCode; -use ggez::graphics::{self, Color, Text, TextFragment}; +use ggez::graphics::{self, Color, Scale, Text, TextFragment}; use ggez::Context; use crate::control::ProgramState; @@ -44,6 +44,7 @@ pub struct MenuItem { max_value: u8, pub value: u8, value_show_increase: u8, + text_scale_down: f32, pub trigger: MenuItemTrigger, selected: bool, } @@ -53,9 +54,16 @@ impl MenuItem { item_start_str: &str, value_type: MenuItemValueType, value: u8, + window_height: f32, + text_scale_down: f32, trigger: MenuItemTrigger, ) -> Self { - let mut text = Text::new(TextFragment::new(item_start_str).color(graphics::BLACK)); + println!("window height: {:?}", window_height); + let mut text = Text::new( + TextFragment::new(item_start_str) + .color(graphics::BLACK) + .scale(Scale::uniform(window_height / text_scale_down)), + ); let mut max_value = 0; let mut value_show_increase = 0; match value_type { @@ -65,12 +73,17 @@ impl MenuItem { value_show_increase = 1; text.add( TextFragment::new(format!(" {}", value + value_show_increase)) - .color(graphics::BLACK), + .color(graphics::BLACK) + .scale(Scale::uniform(window_height / text_scale_down)), ); } MenuItemValueType::StartingLevel => { max_value = MAX_STARTING_LEVEL; - text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); + text.add( + TextFragment::new(format!(" {}", value)) + .color(graphics::BLACK) + .scale(Scale::uniform(window_height / text_scale_down)), + ); } } Self { @@ -79,6 +92,7 @@ impl MenuItem { max_value, value, value_show_increase, + text_scale_down, trigger, selected: false, } @@ -203,6 +217,7 @@ impl Menu { input: Input::new(), state: MenuState::Start, start_menu: StartMenu::new( + window_dimensions, menu_game_options.num_players, menu_game_options.starting_level, ), @@ -216,7 +231,7 @@ impl Menu { Self { input: Input::new(), state: MenuState::Start, - start_menu: StartMenu::new(1, 0), + start_menu: StartMenu::new(window_dimensions, 1, 0), input_config_menu: InputConfigMenu::new( window_dimensions, [(None, false); MAX_NUM_PLAYERS as usize], diff --git a/src/menu/start.rs b/src/menu/start.rs index 493b6ee..af4ef55 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -6,6 +6,8 @@ use ggez::Context; use crate::inputs::Input; use crate::menu::{MenuItem, MenuItemTrigger, MenuItemValueType}; +use crate::menu::TEXT_SCALE_DOWN; + use crate::menu::HELP_RED; pub struct StartMenu { @@ -18,30 +20,38 @@ pub struct StartMenu { } impl StartMenu { - pub fn new(num_players: u8, starting_level: u8) -> Self { + pub fn new(window_dimensions: (f32, f32), num_players: u8, starting_level: u8) -> Self { let mut vec_menu_items: Vec = Vec::with_capacity(4); vec_menu_items.push(MenuItem::new( "Start", MenuItemValueType::None, 0, + window_dimensions.1, + TEXT_SCALE_DOWN, MenuItemTrigger::StartGame, )); vec_menu_items.push(MenuItem::new( "Number of Players: ", MenuItemValueType::NumPlayers, num_players - 1, + window_dimensions.1, + TEXT_SCALE_DOWN, MenuItemTrigger::StartGame, )); vec_menu_items.push(MenuItem::new( "Starting Level: ", MenuItemValueType::StartingLevel, starting_level, + window_dimensions.1, + TEXT_SCALE_DOWN, MenuItemTrigger::StartGame, )); vec_menu_items.push(MenuItem::new( "Controls", MenuItemValueType::None, 0, + window_dimensions.1, + TEXT_SCALE_DOWN, MenuItemTrigger::SubMenu1, )); vec_menu_items[0].set_select(true); From 34fc9e813f9fc6e04ea6de87b7a224aa9d4618c9 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Mon, 5 Apr 2021 22:59:56 -0400 Subject: [PATCH 26/59] implemented proper resizing of text when window resizes for start menu --- src/control.rs | 14 ++++++++++++++ src/menu.rs | 36 ++++++++++++++++++++++-------------- src/menu/start.rs | 30 ++++++++++++++++++------------ 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/control.rs b/src/control.rs index b471c3e..3d82a1f 100644 --- a/src/control.rs +++ b/src/control.rs @@ -181,6 +181,20 @@ impl EventHandler for Control { fn resize_event(&mut self, ctx: &mut Context, width: f32, height: f32) { let new_rect = graphics::Rect::new(0.0, 0.0, width, height); graphics::set_screen_coordinates(ctx, new_rect).unwrap(); + + match self.state { + ProgramState::Menu => self + .menu + .as_mut() + .expect("[!] control.state == ProgramState::Menu but control.menu == None") + .resize_event(width, height), + ProgramState::Game => {} + // self + // .game + // .as_mut() + // .expect("[!] control.state == ProgramState::Game but control.game == None") + // .resize_event(width, height), + }; } fn focus_event(&mut self, _ctx: &mut Context, gained: bool) { diff --git a/src/menu.rs b/src/menu.rs index 4356586..c019c86 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,5 +1,5 @@ use ggez::event::KeyCode; -use ggez::graphics::{self, Color, Scale, Text, TextFragment}; +use ggez::graphics::{self, Color, Font, Scale, Text, TextFragment}; use ggez::Context; use crate::control::ProgramState; @@ -58,12 +58,7 @@ impl MenuItem { text_scale_down: f32, trigger: MenuItemTrigger, ) -> Self { - println!("window height: {:?}", window_height); - let mut text = Text::new( - TextFragment::new(item_start_str) - .color(graphics::BLACK) - .scale(Scale::uniform(window_height / text_scale_down)), - ); + let mut text = Text::new(TextFragment::new(item_start_str).color(graphics::BLACK)); let mut max_value = 0; let mut value_show_increase = 0; match value_type { @@ -73,19 +68,18 @@ impl MenuItem { value_show_increase = 1; text.add( TextFragment::new(format!(" {}", value + value_show_increase)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_height / text_scale_down)), + .color(graphics::BLACK), ); } MenuItemValueType::StartingLevel => { max_value = MAX_STARTING_LEVEL; - text.add( - TextFragment::new(format!(" {}", value)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_height / text_scale_down)), - ); + text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); } } + text.set_font( + Font::default(), + Scale::uniform(window_height / text_scale_down), + ); Self { text, value_type, @@ -131,6 +125,13 @@ impl MenuItem { format!(" <{}>", self.value + self.value_show_increase); } } + + pub fn resize(&mut self, window_height: f32) { + self.text.set_font( + Font::default(), + Scale::uniform(window_height / self.text_scale_down), + ); + } } pub struct MenuGameOptions { @@ -358,4 +359,11 @@ impl Menu { MenuState::InputConfig => self.input_config_menu.draw(ctx), } } + + pub fn resize_event(&mut self, width: f32, height: f32) { + match self.state { + MenuState::Start => self.start_menu.resize_event(height), + MenuState::InputConfig => {} + } + } } diff --git a/src/menu/start.rs b/src/menu/start.rs index af4ef55..ef96cbe 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -99,6 +99,21 @@ impl StartMenu { MenuItemTrigger::None } + // searches through self.vec_menu_items and gets the important values; returns (num_players, starting_level) + pub fn find_important_values(&self) -> (u8, u8) { + let mut num_players = 0; + let mut starting_level = 0; + for item in self.vec_menu_items.iter() { + match item.value_type { + MenuItemValueType::None => {} + MenuItemValueType::NumPlayers => num_players = item.value + 1, + MenuItemValueType::StartingLevel => starting_level = item.value, + } + } + + (num_players, starting_level) + } + pub fn draw(&mut self, ctx: &mut Context) { let window_dimensions = graphics::size(ctx); let num_menu_items_to_draw = self.vec_menu_items.len(); @@ -135,18 +150,9 @@ impl StartMenu { .unwrap(); } - // searches through self.vec_menu_items and gets the important values; returns (num_players, starting_level) - pub fn find_important_values(&self) -> (u8, u8) { - let mut num_players = 0; - let mut starting_level = 0; - for item in self.vec_menu_items.iter() { - match item.value_type { - MenuItemValueType::None => {} - MenuItemValueType::NumPlayers => num_players = item.value + 1, - MenuItemValueType::StartingLevel => starting_level = item.value, - } + pub fn resize_event(&mut self, height: f32) { + for item in self.vec_menu_items.iter_mut() { + item.resize(height); } - - (num_players, starting_level) } } From 09efdbe969d3200695f65de699d71d022b36f4e4 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Mon, 5 Apr 2021 23:08:19 -0400 Subject: [PATCH 27/59] fix some warnings --- src/control.rs | 2 +- src/game/board.rs | 2 +- src/menu.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/control.rs b/src/control.rs index 3d82a1f..6c7f2a3 100644 --- a/src/control.rs +++ b/src/control.rs @@ -187,7 +187,7 @@ impl EventHandler for Control { .menu .as_mut() .expect("[!] control.state == ProgramState::Menu but control.menu == None") - .resize_event(width, height), + .resize_event(height), ProgramState::Game => {} // self // .game diff --git a/src/game/board.rs b/src/game/board.rs index f8b6891..fe49af8 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -656,7 +656,7 @@ impl BoardRotatris { for _ in 0..num_players { vec_active_piece.push(Piece::new(Shapes::None)); } - let mut matrix = vec![vec![Tile::default(); board_size as usize]; board_size as usize]; + let matrix = vec![vec![Tile::default(); board_size as usize]; board_size as usize]; // DEBUG // for a in 0..4 { diff --git a/src/menu.rs b/src/menu.rs index c019c86..d32d913 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -360,7 +360,7 @@ impl Menu { } } - pub fn resize_event(&mut self, width: f32, height: f32) { + pub fn resize_event(&mut self, height: f32) { match self.state { MenuState::Start => self.start_menu.resize_event(height), MenuState::InputConfig => {} From eaff5551ca2d88731f3d1334206f9c78fba87a05 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 6 Apr 2021 21:33:56 -0400 Subject: [PATCH 28/59] use resize_event to redetermine tile size --- src/control.rs | 52 +++++++++++++++++++++++------------------------- src/game.rs | 20 ++++++++++++++++--- src/game/tile.rs | 11 +++++++--- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/control.rs b/src/control.rs index 6c7f2a3..7346e88 100644 --- a/src/control.rs +++ b/src/control.rs @@ -7,6 +7,11 @@ use ggez::{Context, GameResult}; use crate::game::{Game, GameOptions}; use crate::menu::{Menu, MenuGameOptions}; +static STATE_MENU_BUT_MENU_NONE: &str = + "[!] control.state == ProgramState::Menu but control.menu == None"; +static STATE_GAME_BUT_GAME_NONE: &str = + "[!] control.state == ProgramState::Game but control.game == None"; + #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] pub enum ProgramState { @@ -61,11 +66,8 @@ impl EventHandler for Control { match self.state { ProgramState::Menu => { // update the menu and get the state with GameOptions if ProgramState is changing - if let Some(state_and_gameoptions) = self - .menu - .as_mut() - .expect("[!] control.state == ProgramState::Menu but control.menu == None") - .update() + if let Some(state_and_gameoptions) = + self.menu.as_mut().expect(STATE_MENU_BUT_MENU_NONE).update() { self.menu = None; self.game_options = Some(state_and_gameoptions.1); @@ -74,11 +76,8 @@ impl EventHandler for Control { } ProgramState::Game => { // update the game and get the state that the program should be in - let state_returned = self - .game - .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") - .update(); + let state_returned = + self.game.as_mut().expect(STATE_GAME_BUT_GAME_NONE).update(); // should we change states? if self.state != state_returned { self.game = None; @@ -102,12 +101,12 @@ impl EventHandler for Control { ProgramState::Menu => self .menu .as_mut() - .expect("[!] control.state == ProgramState::Menu but control.menu == None") + .expect(STATE_MENU_BUT_MENU_NONE) .key_down_event(keycode, repeat), ProgramState::Game => self .game .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") + .expect(STATE_GAME_BUT_GAME_NONE) .key_down_event(keycode, repeat), }; } @@ -117,12 +116,12 @@ impl EventHandler for Control { ProgramState::Menu => self .menu .as_mut() - .expect("[!] control.state == ProgramState::Menu but control.menu == None") + .expect(STATE_MENU_BUT_MENU_NONE) .key_up_event(keycode), ProgramState::Game => self .game .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") + .expect(STATE_GAME_BUT_GAME_NONE) .key_up_event(keycode), }; } @@ -133,7 +132,7 @@ impl EventHandler for Control { ProgramState::Game => self .game .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") + .expect(STATE_GAME_BUT_GAME_NONE) .gamepad_button_down_event(btn, id), }; } @@ -144,7 +143,7 @@ impl EventHandler for Control { ProgramState::Game => self .game .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") + .expect(STATE_GAME_BUT_GAME_NONE) .gamepad_button_up_event(btn, id), }; } @@ -155,7 +154,7 @@ impl EventHandler for Control { ProgramState::Game => self .game .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") + .expect(STATE_GAME_BUT_GAME_NONE) .gamepad_axis_event(axis, value, id), } } @@ -165,12 +164,12 @@ impl EventHandler for Control { ProgramState::Menu => self .menu .as_mut() - .expect("[!] control.state == ProgramState::Menu but control.menu == None") + .expect(STATE_MENU_BUT_MENU_NONE) .draw(ctx), ProgramState::Game => self .game .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") + .expect(STATE_GAME_BUT_GAME_NONE) .draw(ctx), }; @@ -186,14 +185,13 @@ impl EventHandler for Control { ProgramState::Menu => self .menu .as_mut() - .expect("[!] control.state == ProgramState::Menu but control.menu == None") + .expect(STATE_MENU_BUT_MENU_NONE) .resize_event(height), - ProgramState::Game => {} - // self - // .game - // .as_mut() - // .expect("[!] control.state == ProgramState::Game but control.game == None") - // .resize_event(width, height), + ProgramState::Game => self + .game + .as_mut() + .expect(STATE_GAME_BUT_GAME_NONE) + .resize_event(width, height), }; } @@ -201,7 +199,7 @@ impl EventHandler for Control { if self.state == ProgramState::Game { self.game .as_mut() - .expect("[!] control.state == ProgramState::Game but control.game == None") + .expect(STATE_GAME_BUT_GAME_NONE) .focus_event(gained); } } diff --git a/src/game.rs b/src/game.rs index 3e1021a..968f367 100644 --- a/src/game.rs +++ b/src/game.rs @@ -292,6 +292,8 @@ impl Game { .scale(Scale::uniform(LITTLE_TEXT_SCALE * 2.0)), ); + let (window_width, window_height) = graphics::size(ctx); + Self { bh: BoardHandler::new(board_width, board_height, game_options.num_players, mode), num_players: game_options.num_players, @@ -309,7 +311,12 @@ impl Game { gravity_direction: Movement::Down, game_over_flag: false, game_over_delay: GAME_OVER_DELAY, - tile_size: 0f32, + tile_size: TileGraphic::get_size( + window_width, + window_height, + board_width, + board_height + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D, + ), batch_empty_tile, batch_highlight_active_tile: spritebatch::SpriteBatch::new( TileGraphic::new_active_highlight(ctx).image, @@ -722,8 +729,6 @@ impl Game { // start doing drawing stuff graphics::clear(ctx, graphics::BLACK); let (window_width, window_height) = graphics::size(ctx); - self.tile_size = - TileGraphic::get_size(ctx, width, height + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D); if self.game_over_flag && self.game_over_delay == 0 { // DRAW GAME OVER self.draw_text( @@ -1044,6 +1049,15 @@ impl Game { .unwrap(); } + pub fn resize_event(&mut self, width: f32, height: f32) { + self.tile_size = TileGraphic::get_size( + width, + height, + self.bh.get_width(), + self.bh.get_height() + NON_BOARD_SPACE_U + NON_BOARD_SPACE_D, + ); + } + pub fn focus_event(&mut self, gained: bool) { if !gained { self.pause_flag = (true, true); diff --git a/src/game/tile.rs b/src/game/tile.rs index 395cd40..bbbf5e8 100644 --- a/src/game/tile.rs +++ b/src/game/tile.rs @@ -561,10 +561,15 @@ impl TileGraphic { } } - pub fn get_size(ctx: &mut Context, board_width: u8, board_height: u8) -> f32 { + pub fn get_size( + window_width: f32, + window_height: f32, + board_width: u8, + board_height: u8, + ) -> f32 { std::cmp::min( - graphics::size(ctx).1 as u32 / board_height as u32, - graphics::size(ctx).0 as u32 / board_width as u32, + window_height as u32 / board_height as u32, + window_width as u32 / board_width as u32, ) as f32 } From 055c8efb6e2272709a3a29eee956f57d06d59971 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 6 Apr 2021 23:08:52 -0400 Subject: [PATCH 29/59] start inputconfig refactor; only 25 errors, yay! --- src/menu.rs | 49 ++- src/menu/inputconfig.rs | 689 ++++++++++++++++++++++++---------------- src/menu/start.rs | 4 + 3 files changed, 458 insertions(+), 284 deletions(-) diff --git a/src/menu.rs b/src/menu.rs index d32d913..736fd59 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -27,6 +27,8 @@ pub enum MenuItemValueType { None, NumPlayers, StartingLevel, + PlayerNum, + KeyCode, } #[derive(Eq, PartialEq, Copy, Clone)] @@ -36,6 +38,12 @@ pub enum MenuItemTrigger { SubMenu1, SubMenu2, Back, + SubSelection, + KeyLeft, + KeyRight, + KeyDown, + KeyRotateCw, + KeyRotateCcw, } pub struct MenuItem { @@ -43,6 +51,7 @@ pub struct MenuItem { pub value_type: MenuItemValueType, max_value: u8, pub value: u8, + pub keycode: Option, value_show_increase: u8, text_scale_down: f32, pub trigger: MenuItemTrigger, @@ -54,6 +63,7 @@ impl MenuItem { item_start_str: &str, value_type: MenuItemValueType, value: u8, + keycode: Option, window_height: f32, text_scale_down: f32, trigger: MenuItemTrigger, @@ -75,6 +85,17 @@ impl MenuItem { max_value = MAX_STARTING_LEVEL; text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); } + MenuItemValueType::StartingLevel => { + max_value = MAX_NUM_PLAYERS; + value_show_increase = 1; + text.add( + TextFragment::new(format!(" {}", value + value_show_increase)) + .color(graphics::BLACK), + ); + } + MenuItemValueType::KeyCode => { + text.add(TextFragment::new(format!("{:?}", keycode)).color(graphics::BLACK)); + } } text.set_font( Font::default(), @@ -85,6 +106,7 @@ impl MenuItem { value_type, max_value, value, + keycode, value_show_increase, text_scale_down, trigger, @@ -105,16 +127,24 @@ impl MenuItem { } else { graphics::BLACK }); - self.text.fragments_mut()[1].text = if select { - format!(" <{}>", self.value + self.value_show_increase) - } else { - format!(" {}", self.value + self.value_show_increase) - }; + if self.value_type == MenuItemValueType::NumPlayers + || self.value_type == MenuItemValueType::StartingLevel + || self.value_type == MenuItemValueType::PlayerNum + { + self.text.fragments_mut()[1].text = if select { + format!("<{}>", self.value + self.value_show_increase) + } else { + format!(" {}", self.value + self.value_show_increase) + }; + } } } pub fn inc_or_dec(&mut self, inc: bool) { - if self.value_type != MenuItemValueType::None { + if self.value_type == MenuItemValueType::NumPlayers + || self.value_type == MenuItemValueType::StartingLevel + || self.value_type == MenuItemValueType::PlayerNum + { self.value = if inc { (self.value + 1) % self.max_value } else { @@ -122,10 +152,15 @@ impl MenuItem { }; // assume it's selected because it's being incremented/decremented self.text.fragments_mut()[1].text = - format!(" <{}>", self.value + self.value_show_increase); + format!("<{}>", self.value + self.value_show_increase); } } + pub fn set_keycode(&mut self, keycode: Option) { + self.keycode = keycode; + self.text.fragments_mut()[1].text = format!("{:?}", keycode); + } + pub fn resize(&mut self, window_height: f32) { self.text.set_font( Font::default(), diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index c9f2bfc..2e51f02 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -6,6 +6,8 @@ use ggez::Context; use crate::inputs::{Input, KeyboardControlScheme}; +use crate::menu::{MenuItem, MenuItemTrigger, MenuItemValueType}; + use crate::menu::MAX_NUM_PLAYERS; use crate::menu::SUB_TEXT_SCALE_DOWN; @@ -16,23 +18,23 @@ use crate::menu::HELP_RED; use crate::menu::LIGHT_GRAY; use crate::menu::SELECT_GREEN; -const NUM_INPUTCONFIGMENUOPTION_TEXT_ENTRIES: u8 = 2; -#[repr(u8)] -enum InputConfigMenuOption { - Back, - PlayerInput, -} +// const NUM_INPUTCONFIGMENUOPTION_TEXT_ENTRIES: u8 = 2; +// #[repr(u8)] +// enum InputConfigMenuOption { +// Back, +// PlayerInput, +// } // currently `Start` is, for keyboards, always `ESC`, and alternate controls are only for Left/Right/Down for controllers, so don't include that in the number of entries for keyboards -const NUM_INPUTCONFIGMENUSUBOPTIONKEYBOARD_TEXT_ENTRIES: u8 = 5; -#[repr(u8)] -enum InputConfigMenuSubOptionKeyboard { - Left, - Right, - Down, - RotateCw, - RotateCcw, -} +// const NUM_INPUTCONFIGMENUSUBOPTIONKEYBOARD_TEXT_ENTRIES: u8 = 5; +// #[repr(u8)] +// enum InputConfigMenuSubOptionKeyboard { +// Left, +// Right, +// Down, +// RotateCw, +// RotateCcw, +// } pub struct InputConfigMenu { // logic @@ -54,18 +56,20 @@ pub struct InputConfigMenu { bool, ); MAX_NUM_PLAYERS as usize], // text - back_text: Text, - player_num_text: Text, + vec_menu_items_text: Vec, + // back_text: Text, + // player_num_text: Text, // subtext + vec_menu_items_keycode: Vec, input_uninitialized_text: Text, keycode_conflict_text: Text, is_gamepad_text: Text, - k_left_text: Text, - k_right_text: Text, - k_down_text: Text, - k_rotate_cw_text: Text, - k_rotate_ccw_text: Text, - k_start_text: Text, + // k_left_text: Text, + // k_right_text: Text, + // k_down_text: Text, + // k_rotate_cw_text: Text, + // k_rotate_ccw_text: Text, + // k_start_text: Text, } impl InputConfigMenu { @@ -95,99 +99,208 @@ impl InputConfigMenu { } arr_split_controls[idx].1 = ctrls.1; } - let mut player_num_text = Text::new( - TextFragment::new("Player Number: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ); - player_num_text.add( - TextFragment::new(" 1") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ); - let mut k_left_text = Text::new( - TextFragment::new("Left: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - let mut k_right_text = Text::new( - TextFragment::new("Right: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - let mut k_down_text = Text::new( - TextFragment::new("Down: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - let mut k_rotate_cw_text = Text::new( - TextFragment::new("RotateCw: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - let mut k_rotate_ccw_text = Text::new( - TextFragment::new("RotateCcw: ") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - let k_start_text = Text::new( - TextFragment::new("Start/Pause: Esc") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + let mut vec_menu_items_text: Vec = Vec::with_capacity(2); + vec_menu_items_text.push(MenuItem::new( + "back", + MenuItemValueType::None, + 0, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::Back, + )); + vec_menu_items_text.push(MenuItem::new( + "Player Number: ", + MenuItemValueType::PlayerNum, + 0, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::SubSelection, + )); + vec_menu_items_text[0].set_select(true); + // let mut player_num_text = Text::new( + // TextFragment::new("Player Number: ") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), + // ); + // player_num_text.add( + // TextFragment::new(" 1") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), + // ); + let first_player_previous_controls: ( + Option, + Option, + Option, + Option, + Option, ); if let Some(ctrls) = last_used_arr_controls[0].0 { - k_left_text.add( - TextFragment::new(format!("{:?}", ctrls.left)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_right_text.add( - TextFragment::new(format!("{:?}", ctrls.right)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_down_text.add( - TextFragment::new(format!("{:?}", ctrls.down)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_rotate_cw_text.add( - TextFragment::new(format!("{:?}", ctrls.rotate_cw)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_rotate_ccw_text.add( - TextFragment::new(format!("{:?}", ctrls.rotate_ccw)) - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // k_left_text.add( + // TextFragment::new(format!("{:?}", ctrls.left)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_right_text.add( + // TextFragment::new(format!("{:?}", ctrls.right)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_down_text.add( + // TextFragment::new(format!("{:?}", ctrls.down)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_rotate_cw_text.add( + // TextFragment::new(format!("{:?}", ctrls.rotate_cw)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_rotate_ccw_text.add( + // TextFragment::new(format!("{:?}", ctrls.rotate_ccw)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + first_player_previous_controls = ( + Some(ctrls.left), + Some(ctrls.right), + Some(ctrls.down), + Some(ctrls.rotate_cw), + Some(ctrls.rotate_ccw), ); } else { - k_left_text.add( - TextFragment::new("None") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_right_text.add( - TextFragment::new("None") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_down_text.add( - TextFragment::new("None") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_rotate_cw_text.add( - TextFragment::new("None") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); - k_rotate_ccw_text.add( - TextFragment::new("None") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ); + first_player_previous_controls = (None, None, None, None, None); } + let mut vec_menu_items_keycode: Vec = Vec::with_capacity(6); + vec_menu_items_keycode.push(MenuItem::new( + "Left: ", + MenuItemValueType::KeyCode, + 0, + first_player_previous_controls.0, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::KeyLeft, + )); + vec_menu_items_keycode.push(MenuItem::new( + "Right: ", + MenuItemValueType::KeyCode, + 0, + first_player_previous_controls.1, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::KeyRight, + )); + vec_menu_items_keycode.push(MenuItem::new( + "Down: ", + MenuItemValueType::KeyCode, + 0, + first_player_previous_controls.2, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::KeyDown, + )); + vec_menu_items_keycode.push(MenuItem::new( + "RotateCw: ", + MenuItemValueType::KeyCode, + 0, + first_player_previous_controls.3, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::KeyRotateCw, + )); + vec_menu_items_keycode.push(MenuItem::new( + "RotateCcw: ", + MenuItemValueType::KeyCode, + 0, + first_player_previous_controls.4, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::KeyRotateCcw, + )); + // let mut k_left_text = Text::new( + // TextFragment::new("Left: ") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // let mut k_right_text = Text::new( + // TextFragment::new("Right: ") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // let mut k_down_text = Text::new( + // TextFragment::new("Down: ") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // let mut k_rotate_cw_text = Text::new( + // TextFragment::new("RotateCw: ") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // let mut k_rotate_ccw_text = Text::new( + // TextFragment::new("RotateCcw: ") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // let k_start_text = Text::new( + // TextFragment::new("Start/Pause: Esc") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // if let Some(ctrls) = last_used_arr_controls[0].0 { + // k_left_text.add( + // TextFragment::new(format!("{:?}", ctrls.left)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_right_text.add( + // TextFragment::new(format!("{:?}", ctrls.right)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_down_text.add( + // TextFragment::new(format!("{:?}", ctrls.down)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_rotate_cw_text.add( + // TextFragment::new(format!("{:?}", ctrls.rotate_cw)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_rotate_ccw_text.add( + // TextFragment::new(format!("{:?}", ctrls.rotate_ccw)) + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // } else { + // k_left_text.add( + // TextFragment::new("None") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_right_text.add( + // TextFragment::new("None") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_down_text.add( + // TextFragment::new("None") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_rotate_cw_text.add( + // TextFragment::new("None") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // k_rotate_ccw_text.add( + // TextFragment::new("None") + // .color(graphics::BLACK) + // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + // ); + // } Self { selection: 0, player_num: 0, @@ -198,13 +311,15 @@ impl InputConfigMenu { keycode_conflict_flag: false, arr_split_controls, // text - back_text: Text::new( - TextFragment::new("Back") - .color(SELECT_GREEN) - .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - ), - player_num_text, + vec_menu_items_text, + // back_text: Text::new( + // TextFragment::new("Back") + // .color(SELECT_GREEN) + // .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), + // ), + // player_num_text, // subtext + vec_menu_items_keycode, input_uninitialized_text: Text::new( TextFragment::new("No Controls\nKeyboard: Space/Enter\nGamepad: 'G'") .color(HELP_RED) @@ -220,12 +335,12 @@ impl InputConfigMenu { .color(graphics::BLACK) .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), ), - k_left_text, - k_right_text, - k_down_text, - k_rotate_cw_text, - k_rotate_ccw_text, - k_start_text, + // k_left_text, + // k_right_text, + // k_down_text, + // k_rotate_cw_text, + // k_rotate_ccw_text, + // k_start_text, } } @@ -428,162 +543,174 @@ impl InputConfigMenu { } } - fn set_select(&mut self, select_flag: bool) { - if !self.sub_selection_keyboard_flag { - match self.selection { - x if x == InputConfigMenuOption::Back as u8 => { - if select_flag { - self.back_text.fragments_mut()[0].color = Some(SELECT_GREEN); - } else { - self.back_text.fragments_mut()[0].color = Some(graphics::BLACK); - } - } - x if x == InputConfigMenuOption::PlayerInput as u8 => { - if select_flag { - self.player_num_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.player_num_text.fragments_mut()[1].color = Some(SELECT_GREEN); - self.player_num_text.fragments_mut()[1].text = - format!("<{}>", self.player_num + 1); - } else { - self.player_num_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.player_num_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.player_num_text.fragments_mut()[1].text = - format!(" {}", self.player_num + 1); - } - } - _ => println!("[!] input_config_menu_option didn't find match"), - } - } else if self.sub_selection_keyboard_flag { - match self.sub_selection_keyboard { - x if x == InputConfigMenuSubOptionKeyboard::Left as u8 => { - if select_flag { - self.k_left_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.k_left_text.fragments_mut()[1].color = Some(SELECT_GREEN); - } else { - self.k_left_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.k_left_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.k_left_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - self.most_recently_pressed_key = None; - } - } - x if x == InputConfigMenuSubOptionKeyboard::Right as u8 => { - if select_flag { - self.k_right_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.k_right_text.fragments_mut()[1].color = Some(SELECT_GREEN); - } else { - self.k_right_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.k_right_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.k_right_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - self.most_recently_pressed_key = None; - } - } - x if x == InputConfigMenuSubOptionKeyboard::Down as u8 => { - if select_flag { - self.k_down_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.k_down_text.fragments_mut()[1].color = Some(SELECT_GREEN); - } else { - self.k_down_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.k_down_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.k_down_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - self.most_recently_pressed_key = None; - } - } - x if x == InputConfigMenuSubOptionKeyboard::RotateCw as u8 => { - if select_flag { - self.k_rotate_cw_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.k_rotate_cw_text.fragments_mut()[1].color = Some(SELECT_GREEN); - } else { - self.k_rotate_cw_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.k_rotate_cw_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.k_rotate_cw_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - self.most_recently_pressed_key = None; - } - } - x if x == InputConfigMenuSubOptionKeyboard::RotateCcw as u8 => { - if select_flag { - self.k_rotate_ccw_text.fragments_mut()[0].color = Some(SELECT_GREEN); - self.k_rotate_ccw_text.fragments_mut()[1].color = Some(SELECT_GREEN); - } else { - self.k_rotate_ccw_text.fragments_mut()[0].color = Some(graphics::BLACK); - self.k_rotate_ccw_text.fragments_mut()[1].color = Some(graphics::BLACK); - self.k_rotate_ccw_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - self.most_recently_pressed_key = None; - } - } - _ => println!("[!] input_config_menu_option didn't find match"), - } - } - } + // fn set_select(&mut self, select_flag: bool) { + // TODO - fn inc_or_dec_selection(&mut self, inc_flag: bool) { - if !self.sub_selection_keyboard_flag - && self.selection == InputConfigMenuOption::PlayerInput as u8 - { - if inc_flag { - self.player_num = (self.player_num + 1) % MAX_NUM_PLAYERS; - } else { - self.player_num = if self.player_num == 0 { - MAX_NUM_PLAYERS - 1 - } else { - self.player_num - 1 - }; - } - // display player_num + 1 because index by 1 to users - self.player_num_text.fragments_mut()[1].text = format!("<{}>", self.player_num + 1); - self.update_sub_text_strings(); - } - } + // if !self.sub_selection_keyboard_flag { + // match self.selection { + // x if x == InputConfigMenuOption::Back as u8 => { + // if select_flag { + // self.back_text.fragments_mut()[0].color = Some(SELECT_GREEN); + // } else { + // self.back_text.fragments_mut()[0].color = Some(graphics::BLACK); + // } + // } + // x if x == InputConfigMenuOption::PlayerInput as u8 => { + // if select_flag { + // self.player_num_text.fragments_mut()[0].color = Some(SELECT_GREEN); + // self.player_num_text.fragments_mut()[1].color = Some(SELECT_GREEN); + // self.player_num_text.fragments_mut()[1].text = + // format!("<{}>", self.player_num + 1); + // } else { + // self.player_num_text.fragments_mut()[0].color = Some(graphics::BLACK); + // self.player_num_text.fragments_mut()[1].color = Some(graphics::BLACK); + // self.player_num_text.fragments_mut()[1].text = + // format!(" {}", self.player_num + 1); + // } + // } + // _ => println!("[!] input_config_menu_option didn't find match"), + // } + // } else if self.sub_selection_keyboard_flag { + // match self.sub_selection_keyboard { + // x if x == InputConfigMenuSubOptionKeyboard::Left as u8 => { + // if select_flag { + // self.k_left_text.fragments_mut()[0].color = Some(SELECT_GREEN); + // self.k_left_text.fragments_mut()[1].color = Some(SELECT_GREEN); + // } else { + // self.k_left_text.fragments_mut()[0].color = Some(graphics::BLACK); + // self.k_left_text.fragments_mut()[1].color = Some(graphics::BLACK); + // self.k_left_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); + // self.most_recently_pressed_key = None; + // } + // } + // x if x == InputConfigMenuSubOptionKeyboard::Right as u8 => { + // if select_flag { + // self.k_right_text.fragments_mut()[0].color = Some(SELECT_GREEN); + // self.k_right_text.fragments_mut()[1].color = Some(SELECT_GREEN); + // } else { + // self.k_right_text.fragments_mut()[0].color = Some(graphics::BLACK); + // self.k_right_text.fragments_mut()[1].color = Some(graphics::BLACK); + // self.k_right_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); + // self.most_recently_pressed_key = None; + // } + // } + // x if x == InputConfigMenuSubOptionKeyboard::Down as u8 => { + // if select_flag { + // self.k_down_text.fragments_mut()[0].color = Some(SELECT_GREEN); + // self.k_down_text.fragments_mut()[1].color = Some(SELECT_GREEN); + // } else { + // self.k_down_text.fragments_mut()[0].color = Some(graphics::BLACK); + // self.k_down_text.fragments_mut()[1].color = Some(graphics::BLACK); + // self.k_down_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); + // self.most_recently_pressed_key = None; + // } + // } + // x if x == InputConfigMenuSubOptionKeyboard::RotateCw as u8 => { + // if select_flag { + // self.k_rotate_cw_text.fragments_mut()[0].color = Some(SELECT_GREEN); + // self.k_rotate_cw_text.fragments_mut()[1].color = Some(SELECT_GREEN); + // } else { + // self.k_rotate_cw_text.fragments_mut()[0].color = Some(graphics::BLACK); + // self.k_rotate_cw_text.fragments_mut()[1].color = Some(graphics::BLACK); + // self.k_rotate_cw_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); + // self.most_recently_pressed_key = None; + // } + // } + // x if x == InputConfigMenuSubOptionKeyboard::RotateCcw as u8 => { + // if select_flag { + // self.k_rotate_ccw_text.fragments_mut()[0].color = Some(SELECT_GREEN); + // self.k_rotate_ccw_text.fragments_mut()[1].color = Some(SELECT_GREEN); + // } else { + // self.k_rotate_ccw_text.fragments_mut()[0].color = Some(graphics::BLACK); + // self.k_rotate_ccw_text.fragments_mut()[1].color = Some(graphics::BLACK); + // self.k_rotate_ccw_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); + // self.most_recently_pressed_key = None; + // } + // } + // _ => println!("[!] input_config_menu_option didn't find match"), + // } + // } + // } + + // fn inc_or_dec_selection(&mut self, inc_flag: bool) { + // if !self.sub_selection_keyboard_flag + // && self.selection == InputConfigMenuOption::PlayerInput as u8 + // { + // if inc_flag { + // self.player_num = (self.player_num + 1) % MAX_NUM_PLAYERS; + // } else { + // self.player_num = if self.player_num == 0 { + // MAX_NUM_PLAYERS - 1 + // } else { + // self.player_num - 1 + // }; + // } + // // display player_num + 1 because index by 1 to users + // self.player_num_text.fragments_mut()[1].text = format!("<{}>", self.player_num + 1); + // self.update_sub_text_strings(); + // } + // } fn update_sub_text_strings(&mut self) { - if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { - match ctrls.0 { - Some(keycode) => { - self.k_left_text.fragments_mut()[1].text = format!("{:?}", keycode); - } - None => { - self.k_left_text.fragments_mut()[1].text = "None".to_string(); - } - } - match ctrls.1 { - Some(keycode) => { - self.k_right_text.fragments_mut()[1].text = format!("{:?}", keycode); - } - None => { - self.k_right_text.fragments_mut()[1].text = "None".to_string(); - } - } - match ctrls.2 { - Some(keycode) => { - self.k_down_text.fragments_mut()[1].text = format!("{:?}", keycode); - } - None => { - self.k_down_text.fragments_mut()[1].text = "None".to_string(); - } - } - match ctrls.3 { - Some(keycode) => { - self.k_rotate_cw_text.fragments_mut()[1].text = format!("{:?}", keycode); - } - None => { - self.k_rotate_cw_text.fragments_mut()[1].text = "None".to_string(); - } - } - match ctrls.4 { - Some(keycode) => { - self.k_rotate_ccw_text.fragments_mut()[1].text = format!("{:?}", keycode); - } - None => { - self.k_rotate_ccw_text.fragments_mut()[1].text = "None".to_string(); - } - } - } + // TODO + + // if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { + // match ctrls.0 { + // Some(keycode) => { + // self.k_left_text.fragments_mut()[1].text = format!("{:?}", keycode); + // } + // None => { + // self.k_left_text.fragments_mut()[1].text = "None".to_string(); + // } + // } + // match ctrls.1 { + // Some(keycode) => { + // self.k_right_text.fragments_mut()[1].text = format!("{:?}", keycode); + // } + // None => { + // self.k_right_text.fragments_mut()[1].text = "None".to_string(); + // } + // } + // match ctrls.2 { + // Some(keycode) => { + // self.k_down_text.fragments_mut()[1].text = format!("{:?}", keycode); + // } + // None => { + // self.k_down_text.fragments_mut()[1].text = "None".to_string(); + // } + // } + // match ctrls.3 { + // Some(keycode) => { + // self.k_rotate_cw_text.fragments_mut()[1].text = format!("{:?}", keycode); + // } + // None => { + // self.k_rotate_cw_text.fragments_mut()[1].text = "None".to_string(); + // } + // } + // match ctrls.4 { + // Some(keycode) => { + // self.k_rotate_ccw_text.fragments_mut()[1].text = format!("{:?}", keycode); + // } + // None => { + // self.k_rotate_ccw_text.fragments_mut()[1].text = "None".to_string(); + // } + // } + // } } pub fn draw(&mut self, ctx: &mut Context) { let window_dimensions = graphics::size(ctx); - self.draw_text(ctx, &self.back_text, 0.1, &window_dimensions); - self.draw_text(ctx, &self.player_num_text, 0.3, &window_dimensions); + for (index, item) in self.vec_menu_items_text.iter().enumerate() { + self.draw_text( + ctx, + &item.text, + 0.1 + 0.2 * index as f32, + &window_dimensions, + ); + } + // self.draw_text(ctx, &self.back_text, 0.1, &window_dimensions); + // self.draw_text(ctx, &self.player_num_text, 0.3, &window_dimensions); // display nothing special on InputConfigMenuOption::Back, so just draw the extra stuff when it's not on InputConfigMenuOption::Back // and then later determine which of the other InputConfigMenuOption's it is for the specifics @@ -630,12 +757,20 @@ impl InputConfigMenu { } if (self.arr_split_controls[self.player_num as usize].0).is_some() { - self.draw_text(ctx, &self.k_left_text, 0.5, &window_dimensions); - self.draw_text(ctx, &self.k_right_text, 0.55, &window_dimensions); - self.draw_text(ctx, &self.k_down_text, 0.6, &window_dimensions); - self.draw_text(ctx, &self.k_rotate_cw_text, 0.65, &window_dimensions); - self.draw_text(ctx, &self.k_rotate_ccw_text, 0.7, &window_dimensions); - self.draw_text(ctx, &self.k_start_text, 0.75, &window_dimensions); + for (index, item) in self.vec_menu_items_keycode.iter().enumerate() { + self.draw_text( + ctx, + &item.text, + 0.5 + 0.05 * index as f32, + &window_dimensions, + ); + } + // self.draw_text(ctx, &self.k_left_text, 0.5, &window_dimensions); + // self.draw_text(ctx, &self.k_right_text, 0.55, &window_dimensions); + // self.draw_text(ctx, &self.k_down_text, 0.6, &window_dimensions); + // self.draw_text(ctx, &self.k_rotate_cw_text, 0.65, &window_dimensions); + // self.draw_text(ctx, &self.k_rotate_ccw_text, 0.7, &window_dimensions); + // self.draw_text(ctx, &self.k_start_text, 0.75, &window_dimensions); } else if self.arr_split_controls[self.player_num as usize].1 { self.draw_text(ctx, &self.is_gamepad_text, 0.63, &window_dimensions); } else { diff --git a/src/menu/start.rs b/src/menu/start.rs index ef96cbe..897784f 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -26,6 +26,7 @@ impl StartMenu { "Start", MenuItemValueType::None, 0, + None, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::StartGame, @@ -34,6 +35,7 @@ impl StartMenu { "Number of Players: ", MenuItemValueType::NumPlayers, num_players - 1, + None, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::StartGame, @@ -42,6 +44,7 @@ impl StartMenu { "Starting Level: ", MenuItemValueType::StartingLevel, starting_level, + None, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::StartGame, @@ -50,6 +53,7 @@ impl StartMenu { "Controls", MenuItemValueType::None, 0, + None, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::SubMenu1, From 4514769c01b5eee2387623d9871fd4934e6d78fe Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Fri, 9 Apr 2021 22:50:33 -0400 Subject: [PATCH 30/59] builds, doesn't function perfect --- src/menu.rs | 2 +- src/menu/inputconfig.rs | 186 +++++++++++++++------------------------- src/menu/start.rs | 2 +- 3 files changed, 72 insertions(+), 118 deletions(-) diff --git a/src/menu.rs b/src/menu.rs index 736fd59..1e5c1eb 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -85,7 +85,7 @@ impl MenuItem { max_value = MAX_STARTING_LEVEL; text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); } - MenuItemValueType::StartingLevel => { + MenuItemValueType::PlayerNum => { max_value = MAX_NUM_PLAYERS; value_show_increase = 1; text.add( diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 2e51f02..f3e6635 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -18,6 +18,9 @@ use crate::menu::HELP_RED; use crate::menu::LIGHT_GRAY; use crate::menu::SELECT_GREEN; +static KEY_UNEXPECTEDLY_NONE: &str = + "[!] KeyCode of most recently pressed key is unexpectedly None"; + // const NUM_INPUTCONFIGMENUOPTION_TEXT_ENTRIES: u8 = 2; // #[repr(u8)] // enum InputConfigMenuOption { @@ -38,9 +41,9 @@ use crate::menu::SELECT_GREEN; pub struct InputConfigMenu { // logic - selection: u8, + selection: usize, player_num: u8, - sub_selection_keyboard: u8, + sub_selection_keyboard: usize, sub_selection_keyboard_flag: bool, pub most_recently_pressed_key: Option, vec_used_keycode: Vec, @@ -101,7 +104,7 @@ impl InputConfigMenu { } let mut vec_menu_items_text: Vec = Vec::with_capacity(2); vec_menu_items_text.push(MenuItem::new( - "back", + "Back", MenuItemValueType::None, 0, None, @@ -345,35 +348,36 @@ impl InputConfigMenu { } pub fn update(&mut self, input: &Input) -> bool { - if input.keydown_right.1 && !self.sub_selection_keyboard_flag { - self.inc_or_dec_selection(true); - } + if !self.sub_selection_keyboard_flag { + if input.keydown_right.1 { + self.vec_menu_items_text[self.selection].inc_or_dec(true); + } - if input.keydown_left.1 && !self.sub_selection_keyboard_flag { - self.inc_or_dec_selection(false); - } + if input.keydown_left.1 { + self.vec_menu_items_text[self.selection].inc_or_dec(false); + } - if !self.sub_selection_keyboard_flag { if input.keydown_down.1 { - self.set_select(false); - self.selection = (self.selection + 1) % NUM_INPUTCONFIGMENUOPTION_TEXT_ENTRIES; - self.set_select(true); + self.vec_menu_items_text[self.selection].set_select(false); + self.selection = (self.selection + 1) % self.vec_menu_items_text.len(); + self.vec_menu_items_text[self.selection].set_select(false); self.most_recently_pressed_key = None; } if input.keydown_up.1 { - self.set_select(false); + self.vec_menu_items_text[self.selection].set_select(false); self.selection = if self.selection == 0 { - NUM_INPUTCONFIGMENUOPTION_TEXT_ENTRIES - 1 + self.vec_menu_items_text.len() - 1 } else { self.selection - 1 }; - self.set_select(true); + self.vec_menu_items_text[self.selection].set_select(true); self.most_recently_pressed_key = None; } + // special case, set player's controls to gamepad ('G' was pressed) if input.keydown_rotate_cw.1 - && self.selection == InputConfigMenuOption::PlayerInput as u8 + && self.vec_menu_items_text[self.selection].trigger == MenuItemTrigger::SubSelection { self.arr_split_controls[self.player_num as usize].1 = true; if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { @@ -382,15 +386,14 @@ impl InputConfigMenu { } } - if input.keydown_rotate_ccw.1 { - return true; - } - + // 'Space' or 'Return' was pressed if input.keydown_start.1 { - if self.selection == InputConfigMenuOption::Back as u8 { + if self.vec_menu_items_text[self.selection].trigger == MenuItemTrigger::Back { self.sub_selection_keyboard = 0; return true; - } else if self.selection == InputConfigMenuOption::PlayerInput as u8 { + } else if self.vec_menu_items_text[self.selection].trigger + == MenuItemTrigger::SubSelection + { self.most_recently_pressed_key = None; if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { self.remove_from_used_keycodes(&ctrls); @@ -401,30 +404,34 @@ impl InputConfigMenu { Some((None, None, None, None, None)); self.update_sub_text_strings(); self.sub_selection_keyboard_flag = true; - self.set_select(true); + self.vec_menu_items_text[self.selection].set_select(true); } } - // remove input stuff from selection if we are on PlayerInput and Escape is pressed - if self.selection == InputConfigMenuOption::PlayerInput as u8 - && input.keydown_rotate_ccw.1 - { - if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { - self.remove_from_used_keycodes(&ctrls); - self.arr_split_controls[self.player_num as usize].0 = None; + // 'Escape' was pressed + if input.keydown_rotate_ccw.1 { + if self.vec_menu_items_text[self.selection].trigger == MenuItemTrigger::SubSelection + { + // remove input stuff from selection if we are on the SubSelection option + if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { + self.remove_from_used_keycodes(&ctrls); + self.arr_split_controls[self.player_num as usize].0 = None; + } + self.arr_split_controls[self.player_num as usize].1 = false; + self.most_recently_pressed_key = None; + } else { + return true; } - self.arr_split_controls[self.player_num as usize].1 = false; - self.most_recently_pressed_key = None; } } else if self.sub_selection_keyboard_flag && self.most_recently_pressed_key.is_some() { - // first check if the KeyCode is Escape, and if it is, just delete the layout entry and go out of the subselection section + // first check if the KeyCode is 'Escape', and if it is, just delete the layout entry and go out of the subselection section // second check if the KeyCode was already used. If it was, set the error message flag to true if input.keydown_rotate_ccw.1 { - self.set_select(false); + self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(false); self.keycode_conflict_flag = false; self.sub_selection_keyboard = 0; self.sub_selection_keyboard_flag = false; - // the user was in the middle of creating keyboard controls when they hit Escape, so pop however many KeyCode's off vec_used_keycode as the user set up + // the user was in the middle of creating keyboard controls when they hit 'Escape', so pop however many KeyCode's off vec_used_keycode that the user set up if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { if (ctrls.3).is_some() { for _ in 1..=4 { @@ -444,54 +451,46 @@ impl InputConfigMenu { } self.arr_split_controls[self.player_num as usize].0 = None; self.most_recently_pressed_key = None; - } else if self.vec_used_keycode.contains( - &self - .most_recently_pressed_key - .expect("[!] KeyCode of most recently pressed key is unexpectedly None"), - ) { + } else if self + .vec_used_keycode + .contains(&self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)) + { + // user tried to press a key that is currently assigned self.keycode_conflict_flag = true; } else { + // no conflict, enter KeyCode of key pressed self.keycode_conflict_flag = false; match (self.arr_split_controls[self.player_num as usize].0).as_mut() { - Some(mut ctrls) => match self.sub_selection_keyboard { - x if x == InputConfigMenuSubOptionKeyboard::Left as u8 => { + Some(mut ctrls) => match self.vec_menu_items_keycode + [self.sub_selection_keyboard] + .trigger + { + MenuItemTrigger::KeyLeft => { ctrls.0 = self.most_recently_pressed_key; self.vec_used_keycode - .push(self.most_recently_pressed_key.expect( - "[!] KeyCode of most recently pressed key is unexpectedly None", - )); + .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); } - x if x == InputConfigMenuSubOptionKeyboard::Right as u8 => { + MenuItemTrigger::KeyRight => { ctrls.1 = self.most_recently_pressed_key; self.vec_used_keycode - .push(self.most_recently_pressed_key.expect( - "[!] KeyCode of most recently pressed key is unexpectedly None", - )); + .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); } - x if x == InputConfigMenuSubOptionKeyboard::Down as u8 => { + MenuItemTrigger::KeyDown => { ctrls.2 = self.most_recently_pressed_key; self.vec_used_keycode - .push(self.most_recently_pressed_key.expect( - "[!] KeyCode of most recently pressed key is unexpectedly None", - )); + .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); } - x if x == InputConfigMenuSubOptionKeyboard::RotateCw as u8 => { + MenuItemTrigger::KeyRotateCw => { ctrls.3 = self.most_recently_pressed_key; self.vec_used_keycode - .push(self.most_recently_pressed_key.expect( - "[!] KeyCode of most recently pressed key is unexpectedly None", - )); + .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); } - x if x == InputConfigMenuSubOptionKeyboard::RotateCcw as u8 => { + MenuItemTrigger::KeyRotateCcw => { ctrls.4 = self.most_recently_pressed_key; self.vec_used_keycode - .push(self.most_recently_pressed_key.expect( - "[!] KeyCode of most recently pressed key is unexpectedly None", - )); + .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); } - _ => println!( - "[!] couldn't get correct tuple index to set most recently pressed key" - ), + _ => println!("[!] weird MenuItemTrigger in vec_menu_items_keycode item"), }, None => { println!( @@ -500,12 +499,10 @@ impl InputConfigMenu { ); } } - self.set_select(false); - if self.sub_selection_keyboard - < NUM_INPUTCONFIGMENUSUBOPTIONKEYBOARD_TEXT_ENTRIES as u8 - 1 - { + self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(false); + if self.sub_selection_keyboard < self.vec_menu_items_keycode.len() - 1 { self.sub_selection_keyboard += 1; - self.set_select(true); + self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(true); } else { self.sub_selection_keyboard = 0; self.sub_selection_keyboard_flag = false; @@ -536,7 +533,7 @@ impl InputConfigMenu { self.vec_used_keycode.remove(used_key_idx - items_removed); items_removed += 1; // we only need to get rid of NUM_INPUTCONFIGMENUSUBOPTIONKEYBOARD_TEXT_ENTRIES - if items_removed >= NUM_INPUTCONFIGMENUSUBOPTIONKEYBOARD_TEXT_ENTRIES as usize { + if items_removed >= self.vec_menu_items_keycode.len() { return; } } @@ -653,49 +650,6 @@ impl InputConfigMenu { fn update_sub_text_strings(&mut self) { // TODO - - // if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { - // match ctrls.0 { - // Some(keycode) => { - // self.k_left_text.fragments_mut()[1].text = format!("{:?}", keycode); - // } - // None => { - // self.k_left_text.fragments_mut()[1].text = "None".to_string(); - // } - // } - // match ctrls.1 { - // Some(keycode) => { - // self.k_right_text.fragments_mut()[1].text = format!("{:?}", keycode); - // } - // None => { - // self.k_right_text.fragments_mut()[1].text = "None".to_string(); - // } - // } - // match ctrls.2 { - // Some(keycode) => { - // self.k_down_text.fragments_mut()[1].text = format!("{:?}", keycode); - // } - // None => { - // self.k_down_text.fragments_mut()[1].text = "None".to_string(); - // } - // } - // match ctrls.3 { - // Some(keycode) => { - // self.k_rotate_cw_text.fragments_mut()[1].text = format!("{:?}", keycode); - // } - // None => { - // self.k_rotate_cw_text.fragments_mut()[1].text = "None".to_string(); - // } - // } - // match ctrls.4 { - // Some(keycode) => { - // self.k_rotate_ccw_text.fragments_mut()[1].text = format!("{:?}", keycode); - // } - // None => { - // self.k_rotate_ccw_text.fragments_mut()[1].text = "None".to_string(); - // } - // } - // } } pub fn draw(&mut self, ctx: &mut Context) { @@ -714,7 +668,7 @@ impl InputConfigMenu { // display nothing special on InputConfigMenuOption::Back, so just draw the extra stuff when it's not on InputConfigMenuOption::Back // and then later determine which of the other InputConfigMenuOption's it is for the specifics - if self.selection != InputConfigMenuOption::Back as u8 { + if self.vec_menu_items_text[self.selection].trigger != MenuItemTrigger::Back { // draw a rectangle containing the subtexts for choosing controls // with a color based on whether or not the user is editing controls let editing_indicator_rectangle: graphics::Mesh; @@ -751,7 +705,7 @@ impl InputConfigMenu { } graphics::draw(ctx, &editing_indicator_rectangle, (Point2::new(0.0, 0.0),)).unwrap(); - if self.selection == InputConfigMenuOption::PlayerInput as u8 { + if self.vec_menu_items_text[self.selection].trigger != MenuItemTrigger::SubSelection { if self.keycode_conflict_flag { self.draw_text(ctx, &self.keycode_conflict_text, 0.43, &window_dimensions); } diff --git a/src/menu/start.rs b/src/menu/start.rs index 897784f..82d38c5 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -109,9 +109,9 @@ impl StartMenu { let mut starting_level = 0; for item in self.vec_menu_items.iter() { match item.value_type { - MenuItemValueType::None => {} MenuItemValueType::NumPlayers => num_players = item.value + 1, MenuItemValueType::StartingLevel => starting_level = item.value, + _ => {} } } From f9f46f84f595dc01dd2e3db2761b6c6befed413c Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Fri, 9 Apr 2021 23:05:10 -0400 Subject: [PATCH 31/59] rm comments, tiny fix --- src/menu/inputconfig.rs | 319 ++++------------------------------------ 1 file changed, 26 insertions(+), 293 deletions(-) diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index f3e6635..4bf7252 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -21,24 +21,6 @@ use crate::menu::SELECT_GREEN; static KEY_UNEXPECTEDLY_NONE: &str = "[!] KeyCode of most recently pressed key is unexpectedly None"; -// const NUM_INPUTCONFIGMENUOPTION_TEXT_ENTRIES: u8 = 2; -// #[repr(u8)] -// enum InputConfigMenuOption { -// Back, -// PlayerInput, -// } - -// currently `Start` is, for keyboards, always `ESC`, and alternate controls are only for Left/Right/Down for controllers, so don't include that in the number of entries for keyboards -// const NUM_INPUTCONFIGMENUSUBOPTIONKEYBOARD_TEXT_ENTRIES: u8 = 5; -// #[repr(u8)] -// enum InputConfigMenuSubOptionKeyboard { -// Left, -// Right, -// Down, -// RotateCw, -// RotateCcw, -// } - pub struct InputConfigMenu { // logic selection: usize, @@ -59,20 +41,12 @@ pub struct InputConfigMenu { bool, ); MAX_NUM_PLAYERS as usize], // text - vec_menu_items_text: Vec, - // back_text: Text, - // player_num_text: Text, + vec_menu_items_main: Vec, // subtext vec_menu_items_keycode: Vec, input_uninitialized_text: Text, keycode_conflict_text: Text, is_gamepad_text: Text, - // k_left_text: Text, - // k_right_text: Text, - // k_down_text: Text, - // k_rotate_cw_text: Text, - // k_rotate_ccw_text: Text, - // k_start_text: Text, } impl InputConfigMenu { @@ -102,8 +76,9 @@ impl InputConfigMenu { } arr_split_controls[idx].1 = ctrls.1; } - let mut vec_menu_items_text: Vec = Vec::with_capacity(2); - vec_menu_items_text.push(MenuItem::new( + // main MenuItems + let mut vec_menu_items_main: Vec = Vec::with_capacity(2); + vec_menu_items_main.push(MenuItem::new( "Back", MenuItemValueType::None, 0, @@ -112,7 +87,7 @@ impl InputConfigMenu { TEXT_SCALE_DOWN, MenuItemTrigger::Back, )); - vec_menu_items_text.push(MenuItem::new( + vec_menu_items_main.push(MenuItem::new( "Player Number: ", MenuItemValueType::PlayerNum, 0, @@ -121,17 +96,9 @@ impl InputConfigMenu { TEXT_SCALE_DOWN, MenuItemTrigger::SubSelection, )); - vec_menu_items_text[0].set_select(true); - // let mut player_num_text = Text::new( - // TextFragment::new("Player Number: ") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - // ); - // player_num_text.add( - // TextFragment::new(" 1") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - // ); + vec_menu_items_main[0].set_select(true); + + // keycode MenuItems let first_player_previous_controls: ( Option, Option, @@ -140,31 +107,6 @@ impl InputConfigMenu { Option, ); if let Some(ctrls) = last_used_arr_controls[0].0 { - // k_left_text.add( - // TextFragment::new(format!("{:?}", ctrls.left)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_right_text.add( - // TextFragment::new(format!("{:?}", ctrls.right)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_down_text.add( - // TextFragment::new(format!("{:?}", ctrls.down)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_rotate_cw_text.add( - // TextFragment::new(format!("{:?}", ctrls.rotate_cw)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_rotate_ccw_text.add( - // TextFragment::new(format!("{:?}", ctrls.rotate_ccw)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); first_player_previous_controls = ( Some(ctrls.left), Some(ctrls.right), @@ -221,89 +163,6 @@ impl InputConfigMenu { TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCcw, )); - // let mut k_left_text = Text::new( - // TextFragment::new("Left: ") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // let mut k_right_text = Text::new( - // TextFragment::new("Right: ") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // let mut k_down_text = Text::new( - // TextFragment::new("Down: ") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // let mut k_rotate_cw_text = Text::new( - // TextFragment::new("RotateCw: ") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // let mut k_rotate_ccw_text = Text::new( - // TextFragment::new("RotateCcw: ") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // let k_start_text = Text::new( - // TextFragment::new("Start/Pause: Esc") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // if let Some(ctrls) = last_used_arr_controls[0].0 { - // k_left_text.add( - // TextFragment::new(format!("{:?}", ctrls.left)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_right_text.add( - // TextFragment::new(format!("{:?}", ctrls.right)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_down_text.add( - // TextFragment::new(format!("{:?}", ctrls.down)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_rotate_cw_text.add( - // TextFragment::new(format!("{:?}", ctrls.rotate_cw)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_rotate_ccw_text.add( - // TextFragment::new(format!("{:?}", ctrls.rotate_ccw)) - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // } else { - // k_left_text.add( - // TextFragment::new("None") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_right_text.add( - // TextFragment::new("None") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_down_text.add( - // TextFragment::new("None") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_rotate_cw_text.add( - // TextFragment::new("None") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // k_rotate_ccw_text.add( - // TextFragment::new("None") - // .color(graphics::BLACK) - // .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - // ); - // } Self { selection: 0, player_num: 0, @@ -314,13 +173,7 @@ impl InputConfigMenu { keycode_conflict_flag: false, arr_split_controls, // text - vec_menu_items_text, - // back_text: Text::new( - // TextFragment::new("Back") - // .color(SELECT_GREEN) - // .scale(Scale::uniform(window_dimensions.1 / TEXT_SCALE_DOWN)), - // ), - // player_num_text, + vec_menu_items_main, // subtext vec_menu_items_keycode, input_uninitialized_text: Text::new( @@ -338,46 +191,40 @@ impl InputConfigMenu { .color(graphics::BLACK) .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), ), - // k_left_text, - // k_right_text, - // k_down_text, - // k_rotate_cw_text, - // k_rotate_ccw_text, - // k_start_text, } } pub fn update(&mut self, input: &Input) -> bool { if !self.sub_selection_keyboard_flag { if input.keydown_right.1 { - self.vec_menu_items_text[self.selection].inc_or_dec(true); + self.vec_menu_items_main[self.selection].inc_or_dec(true); } if input.keydown_left.1 { - self.vec_menu_items_text[self.selection].inc_or_dec(false); + self.vec_menu_items_main[self.selection].inc_or_dec(false); } if input.keydown_down.1 { - self.vec_menu_items_text[self.selection].set_select(false); - self.selection = (self.selection + 1) % self.vec_menu_items_text.len(); - self.vec_menu_items_text[self.selection].set_select(false); + self.vec_menu_items_main[self.selection].set_select(false); + self.selection = (self.selection + 1) % self.vec_menu_items_main.len(); + self.vec_menu_items_main[self.selection].set_select(true); self.most_recently_pressed_key = None; } if input.keydown_up.1 { - self.vec_menu_items_text[self.selection].set_select(false); + self.vec_menu_items_main[self.selection].set_select(false); self.selection = if self.selection == 0 { - self.vec_menu_items_text.len() - 1 + self.vec_menu_items_main.len() - 1 } else { self.selection - 1 }; - self.vec_menu_items_text[self.selection].set_select(true); + self.vec_menu_items_main[self.selection].set_select(true); self.most_recently_pressed_key = None; } // special case, set player's controls to gamepad ('G' was pressed) if input.keydown_rotate_cw.1 - && self.vec_menu_items_text[self.selection].trigger == MenuItemTrigger::SubSelection + && self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { self.arr_split_controls[self.player_num as usize].1 = true; if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { @@ -388,10 +235,10 @@ impl InputConfigMenu { // 'Space' or 'Return' was pressed if input.keydown_start.1 { - if self.vec_menu_items_text[self.selection].trigger == MenuItemTrigger::Back { + if self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::Back { self.sub_selection_keyboard = 0; return true; - } else if self.vec_menu_items_text[self.selection].trigger + } else if self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { self.most_recently_pressed_key = None; @@ -404,13 +251,13 @@ impl InputConfigMenu { Some((None, None, None, None, None)); self.update_sub_text_strings(); self.sub_selection_keyboard_flag = true; - self.vec_menu_items_text[self.selection].set_select(true); + self.vec_menu_items_main[self.selection].set_select(true); } } // 'Escape' was pressed if input.keydown_rotate_ccw.1 { - if self.vec_menu_items_text[self.selection].trigger == MenuItemTrigger::SubSelection + if self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { // remove input stuff from selection if we are on the SubSelection option if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { @@ -532,7 +379,7 @@ impl InputConfigMenu { { self.vec_used_keycode.remove(used_key_idx - items_removed); items_removed += 1; - // we only need to get rid of NUM_INPUTCONFIGMENUSUBOPTIONKEYBOARD_TEXT_ENTRIES + // we only need to get rid of self.vec_menu_items_keycode.len() if items_removed >= self.vec_menu_items_keycode.len() { return; } @@ -540,114 +387,6 @@ impl InputConfigMenu { } } - // fn set_select(&mut self, select_flag: bool) { - // TODO - - // if !self.sub_selection_keyboard_flag { - // match self.selection { - // x if x == InputConfigMenuOption::Back as u8 => { - // if select_flag { - // self.back_text.fragments_mut()[0].color = Some(SELECT_GREEN); - // } else { - // self.back_text.fragments_mut()[0].color = Some(graphics::BLACK); - // } - // } - // x if x == InputConfigMenuOption::PlayerInput as u8 => { - // if select_flag { - // self.player_num_text.fragments_mut()[0].color = Some(SELECT_GREEN); - // self.player_num_text.fragments_mut()[1].color = Some(SELECT_GREEN); - // self.player_num_text.fragments_mut()[1].text = - // format!("<{}>", self.player_num + 1); - // } else { - // self.player_num_text.fragments_mut()[0].color = Some(graphics::BLACK); - // self.player_num_text.fragments_mut()[1].color = Some(graphics::BLACK); - // self.player_num_text.fragments_mut()[1].text = - // format!(" {}", self.player_num + 1); - // } - // } - // _ => println!("[!] input_config_menu_option didn't find match"), - // } - // } else if self.sub_selection_keyboard_flag { - // match self.sub_selection_keyboard { - // x if x == InputConfigMenuSubOptionKeyboard::Left as u8 => { - // if select_flag { - // self.k_left_text.fragments_mut()[0].color = Some(SELECT_GREEN); - // self.k_left_text.fragments_mut()[1].color = Some(SELECT_GREEN); - // } else { - // self.k_left_text.fragments_mut()[0].color = Some(graphics::BLACK); - // self.k_left_text.fragments_mut()[1].color = Some(graphics::BLACK); - // self.k_left_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - // self.most_recently_pressed_key = None; - // } - // } - // x if x == InputConfigMenuSubOptionKeyboard::Right as u8 => { - // if select_flag { - // self.k_right_text.fragments_mut()[0].color = Some(SELECT_GREEN); - // self.k_right_text.fragments_mut()[1].color = Some(SELECT_GREEN); - // } else { - // self.k_right_text.fragments_mut()[0].color = Some(graphics::BLACK); - // self.k_right_text.fragments_mut()[1].color = Some(graphics::BLACK); - // self.k_right_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - // self.most_recently_pressed_key = None; - // } - // } - // x if x == InputConfigMenuSubOptionKeyboard::Down as u8 => { - // if select_flag { - // self.k_down_text.fragments_mut()[0].color = Some(SELECT_GREEN); - // self.k_down_text.fragments_mut()[1].color = Some(SELECT_GREEN); - // } else { - // self.k_down_text.fragments_mut()[0].color = Some(graphics::BLACK); - // self.k_down_text.fragments_mut()[1].color = Some(graphics::BLACK); - // self.k_down_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - // self.most_recently_pressed_key = None; - // } - // } - // x if x == InputConfigMenuSubOptionKeyboard::RotateCw as u8 => { - // if select_flag { - // self.k_rotate_cw_text.fragments_mut()[0].color = Some(SELECT_GREEN); - // self.k_rotate_cw_text.fragments_mut()[1].color = Some(SELECT_GREEN); - // } else { - // self.k_rotate_cw_text.fragments_mut()[0].color = Some(graphics::BLACK); - // self.k_rotate_cw_text.fragments_mut()[1].color = Some(graphics::BLACK); - // self.k_rotate_cw_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - // self.most_recently_pressed_key = None; - // } - // } - // x if x == InputConfigMenuSubOptionKeyboard::RotateCcw as u8 => { - // if select_flag { - // self.k_rotate_ccw_text.fragments_mut()[0].color = Some(SELECT_GREEN); - // self.k_rotate_ccw_text.fragments_mut()[1].color = Some(SELECT_GREEN); - // } else { - // self.k_rotate_ccw_text.fragments_mut()[0].color = Some(graphics::BLACK); - // self.k_rotate_ccw_text.fragments_mut()[1].color = Some(graphics::BLACK); - // self.k_rotate_ccw_text.fragments_mut()[1].text = format!(" {:?}", self.most_recently_pressed_key.expect("[!] was setting keycode text, but most_recently_pressed_key == None")); - // self.most_recently_pressed_key = None; - // } - // } - // _ => println!("[!] input_config_menu_option didn't find match"), - // } - // } - // } - - // fn inc_or_dec_selection(&mut self, inc_flag: bool) { - // if !self.sub_selection_keyboard_flag - // && self.selection == InputConfigMenuOption::PlayerInput as u8 - // { - // if inc_flag { - // self.player_num = (self.player_num + 1) % MAX_NUM_PLAYERS; - // } else { - // self.player_num = if self.player_num == 0 { - // MAX_NUM_PLAYERS - 1 - // } else { - // self.player_num - 1 - // }; - // } - // // display player_num + 1 because index by 1 to users - // self.player_num_text.fragments_mut()[1].text = format!("<{}>", self.player_num + 1); - // self.update_sub_text_strings(); - // } - // } - fn update_sub_text_strings(&mut self) { // TODO } @@ -655,7 +394,7 @@ impl InputConfigMenu { pub fn draw(&mut self, ctx: &mut Context) { let window_dimensions = graphics::size(ctx); - for (index, item) in self.vec_menu_items_text.iter().enumerate() { + for (index, item) in self.vec_menu_items_main.iter().enumerate() { self.draw_text( ctx, &item.text, @@ -668,7 +407,7 @@ impl InputConfigMenu { // display nothing special on InputConfigMenuOption::Back, so just draw the extra stuff when it's not on InputConfigMenuOption::Back // and then later determine which of the other InputConfigMenuOption's it is for the specifics - if self.vec_menu_items_text[self.selection].trigger != MenuItemTrigger::Back { + if self.vec_menu_items_main[self.selection].trigger != MenuItemTrigger::Back { // draw a rectangle containing the subtexts for choosing controls // with a color based on whether or not the user is editing controls let editing_indicator_rectangle: graphics::Mesh; @@ -705,7 +444,7 @@ impl InputConfigMenu { } graphics::draw(ctx, &editing_indicator_rectangle, (Point2::new(0.0, 0.0),)).unwrap(); - if self.vec_menu_items_text[self.selection].trigger != MenuItemTrigger::SubSelection { + if self.vec_menu_items_main[self.selection].trigger != MenuItemTrigger::SubSelection { if self.keycode_conflict_flag { self.draw_text(ctx, &self.keycode_conflict_text, 0.43, &window_dimensions); } @@ -719,12 +458,6 @@ impl InputConfigMenu { &window_dimensions, ); } - // self.draw_text(ctx, &self.k_left_text, 0.5, &window_dimensions); - // self.draw_text(ctx, &self.k_right_text, 0.55, &window_dimensions); - // self.draw_text(ctx, &self.k_down_text, 0.6, &window_dimensions); - // self.draw_text(ctx, &self.k_rotate_cw_text, 0.65, &window_dimensions); - // self.draw_text(ctx, &self.k_rotate_ccw_text, 0.7, &window_dimensions); - // self.draw_text(ctx, &self.k_start_text, 0.75, &window_dimensions); } else if self.arr_split_controls[self.player_num as usize].1 { self.draw_text(ctx, &self.is_gamepad_text, 0.63, &window_dimensions); } else { From 61e2bc20c6e0140520a44d2cbbb7a88d11dc3ba1 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Fri, 9 Apr 2021 23:57:26 -0400 Subject: [PATCH 32/59] supposedly better input logic, but can't test it well because the menu changes broke multiplayer gameoptions --- src/game.rs | 97 +++++++++++++++++++++++++++-------------- src/game/player.rs | 6 --- src/inputs.rs | 3 -- src/menu.rs | 1 - src/menu/inputconfig.rs | 1 - 5 files changed, 64 insertions(+), 44 deletions(-) diff --git a/src/game.rs b/src/game.rs index 968f367..db4f1e5 100644 --- a/src/game.rs +++ b/src/game.rs @@ -101,7 +101,6 @@ impl From<&MenuGameOptions> for GameOptions { ctrls.down, ctrls.rotate_cw, ctrls.rotate_ccw, - KeyCode::Escape, )), false, )); @@ -136,7 +135,9 @@ pub struct Game { starting_level: u8, num_cleared_lines: u16, score: u64, - pause_flag: (bool, bool), + keycode_down_flags: (bool, bool), + keycode_escape_flags: (bool, bool), + pause_flags: (bool, bool), rotate_board_cw: (bool, bool), rotate_board_ccw: (bool, bool), gravity_direction: Movement, @@ -305,7 +306,9 @@ impl Game { starting_level: game_options.starting_level, num_cleared_lines: 0u16, score: 0u64, - pause_flag: (false, false), + keycode_down_flags: (false, false), + keycode_escape_flags: (false, false), + pause_flags: (false, false), rotate_board_cw: (false, false), rotate_board_ccw: (false, false), gravity_direction: Movement::Down, @@ -339,40 +342,52 @@ impl Game { if self.game_over_flag { if self.game_over_delay == 0 { // GAME OVER LOGIC + if self.keycode_escape_flags.1 { + return ProgramState::Menu; + } for player in &mut self.vec_players { // should we quit to main menu? if player.input.keydown_start.1 { return ProgramState::Menu; } - player.input.was_just_pressed_setfalse(); - self.rotate_board_cw.1 = false; - self.rotate_board_ccw.1 = false; } + self.was_just_pressed_setfalse_all_players(); } else { self.game_over_delay -= 1; } - } else if self.pause_flag.0 { + } else if self.pause_flags.0 { // PAUSE LOGIC - if self.pause_flag.1 { + if self.pause_flags.1 { // if the pause flag was just set, reset all inputs to false in case focus was lost or keyboard hardware is acting up somehow or another - self.pause_flag.1 = false; + self.pause_flags.1 = false; for player in &mut self.vec_players { player.input.reset_all(); } } else { + // down arrow key held and escape key results in quit to menu + if self.keycode_escape_flags.1 && self.keycode_down_flags.0 { + return ProgramState::Menu; + } + // this loop is mostly due to gamepad/keyboard controls meshing together weirdly for player in &mut self.vec_players { - // should we quit to main menu? - if player.input.keydown_down.0 && player.input.keydown_start.1 { + // should we quit to main menu? (down and start, but start on keyboard is Escape and not specific to a player, so check if players holding down are using keyboard) + if player.input.keydown_down.0 + && (player.input.keydown_start.1 + || ((player.control_scheme.0).is_some() && self.keycode_escape_flags.1)) + { return ProgramState::Menu; } // should we resume? if player.input.keydown_start.1 { - self.pause_flag = (false, false); - player.input.was_just_pressed_setfalse(); - self.rotate_board_cw.1 = false; - self.rotate_board_ccw.1 = false; + self.pause_flags = (false, false); + break; } } + // should we resume because of escape press? + if self.keycode_escape_flags.1 { + self.pause_flags = (false, false); + } + self.was_just_pressed_setfalse_all_players(); } } else { // GAME LOGIC @@ -439,8 +454,8 @@ impl Game { continue; } - // rotatris - // BOARD ROTATION + // rotatris specific + // board rotations if self.rotate_board_cw.1 { if self.bh.attempt_rotate_board(Movement::RotateCw) { self.gravity_direction = @@ -454,7 +469,7 @@ impl Game { Movement::from(((self.gravity_direction as u8) + 3) % 4); } } - // rotatris end + // rotatris specific end // piece movement // LEFT / RIGHT @@ -582,17 +597,15 @@ impl Game { } if player.input.keydown_start.1 { - self.pause_flag = (true, true); - player.input.was_just_pressed_setfalse(); - self.rotate_board_cw.1 = false; - self.rotate_board_ccw.1 = false; + self.pause_flags = (true, true); } + } - // update controls (always do after all player player input for each player) - player.input.was_just_pressed_setfalse(); - self.rotate_board_cw.1 = false; - self.rotate_board_ccw.1 = false; + // update controls so that the logic realizes next frame that the button inputs made were run through the logic + if self.keycode_escape_flags.1 { + self.pause_flags = (true, true); } + self.was_just_pressed_setfalse_all_players(); // attempt to line clear (go through the vector of FullLine's and decrement clear_delay if > 0, clear and return (lines_cleared, score) for <= 0) let (returned_lines, returned_score) = self.bh.attempt_clear(self.level); @@ -622,22 +635,40 @@ impl Game { ProgramState::Game } + fn was_just_pressed_setfalse_all_players(&mut self) { + for player in self.vec_players.iter_mut() { + player.input.was_just_pressed_setfalse(); + } + self.keycode_down_flags.1 = false; + self.keycode_escape_flags.1 = false; + } + pub fn key_down_event(&mut self, keycode: KeyCode, repeat: bool) { if !repeat { + if keycode == KeyCode::Escape { + self.keycode_escape_flags = (true, true); + return; + } else if keycode == KeyCode::Down { + println!("set down to (true, true)"); + self.keycode_down_flags = (true, true); + return; + } for player in &mut self.vec_players { if player.update_input_keydown(keycode) { return; } } - if keycode == KeyCode::Z { - self.rotate_board_ccw = (true, true); - } else if keycode == KeyCode::X { - self.rotate_board_cw = (true, true); - } } } pub fn key_up_event(&mut self, keycode: KeyCode) { + if keycode == KeyCode::Escape { + self.keycode_escape_flags = (false, false); + return; + } else if keycode == KeyCode::Down { + self.keycode_down_flags = (false, false); + return; + } for player in &mut self.vec_players { if player.update_input_keyup(keycode) { return; @@ -743,7 +774,7 @@ impl Game { 0.55, &(window_width, window_height), ); - } else if self.pause_flag.0 { + } else if self.pause_flags.0 { // DRAW PAUSE self.draw_text(ctx, &self.pause_text, 0.4, &(window_width, window_height)); } else { @@ -1060,7 +1091,7 @@ impl Game { pub fn focus_event(&mut self, gained: bool) { if !gained { - self.pause_flag = (true, true); + self.pause_flags = (true, true); } } } diff --git a/src/game/player.rs b/src/game/player.rs index 984339e..80ee5d2 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -88,9 +88,6 @@ impl Player { self.input.keydown_rotate_ccw = (true, true); return true; } - } else if input == k_ctrls.start && !self.input.keydown_start.0 { - self.input.keydown_start = (true, true); - return true; } } @@ -124,9 +121,6 @@ impl Player { } else if input == k_ctrls.rotate_ccw { self.input.keydown_rotate_ccw = (false, false); return true; - } else if input == k_ctrls.start { - self.input.keydown_start = (false, false); - return true; } } diff --git a/src/inputs.rs b/src/inputs.rs index b66dba6..ba7f42d 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -74,7 +74,6 @@ pub struct KeyboardControlScheme { pub down: KeyCode, pub rotate_cw: KeyCode, pub rotate_ccw: KeyCode, - pub start: KeyCode, } impl KeyboardControlScheme { @@ -84,7 +83,6 @@ impl KeyboardControlScheme { down: KeyCode, rotate_cw: KeyCode, rotate_ccw: KeyCode, - start: KeyCode, ) -> Self { Self { left, @@ -92,7 +90,6 @@ impl KeyboardControlScheme { down, rotate_cw, rotate_ccw, - start, } } diff --git a/src/menu.rs b/src/menu.rs index 1e5c1eb..5177e66 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -211,7 +211,6 @@ impl MenuGameOptions { k_ctrls.4.expect( "[!] attempted to create KeyboardControlScheme with RotateCcw == None", ), - KeyCode::Escape, )), false, ); diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 4bf7252..f3b3612 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -16,7 +16,6 @@ use crate::menu::TEXT_SCALE_DOWN; use crate::menu::DARK_GRAY; use crate::menu::HELP_RED; use crate::menu::LIGHT_GRAY; -use crate::menu::SELECT_GREEN; static KEY_UNEXPECTEDLY_NONE: &str = "[!] KeyCode of most recently pressed key is unexpectedly None"; From ea7ae21a053810873628dee1c26c5c294f28c302 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sat, 10 Apr 2021 01:36:22 -0400 Subject: [PATCH 33/59] yay only 9 errors --- src/game.rs | 27 ++++++-- src/game/board.rs | 3 +- src/game/piece.rs | 30 +-------- src/game/player.rs | 144 ++++++++++++++++++++++++---------------- src/inputs.rs | 99 +++++++++++++++++++-------- src/main.rs | 1 + src/menu.rs | 2 +- src/menu/inputconfig.rs | 45 +++++++++---- src/movement.rs | 28 ++++++++ 9 files changed, 243 insertions(+), 136 deletions(-) create mode 100644 src/movement.rs diff --git a/src/game.rs b/src/game.rs index db4f1e5..c9c55bd 100644 --- a/src/game.rs +++ b/src/game.rs @@ -7,6 +7,7 @@ use ggez::Context; use rand::random; use crate::control::ProgramState; +use crate::movement::Movement; mod player; use crate::game::player::{Player, SPAWN_DELAY}; @@ -16,7 +17,7 @@ use crate::game::tile::TileGraphic; use crate::game::tile::NUM_PIXEL_ROWS_PER_TILEGRAPHIC; mod piece; -use crate::game::piece::{Movement, NextPiece, Shapes}; +use crate::game::piece::{NextPiece, Shapes}; mod board; use crate::game::board::BoardHandler; @@ -75,6 +76,8 @@ pub const INITIAL_HANG_FRAMES: u8 = 180; pub const DETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.5; pub const UNDETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.2; +static INVALID_LAST_USED_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; + pub enum Modes { Classic, Rotatris, @@ -95,12 +98,22 @@ impl From<&MenuGameOptions> for GameOptions { for controls in menu_game_options.arr_controls.iter() { if let Some(ctrls) = controls.0 { vec_controls.push(( - Some(KeyboardControlScheme::new( - ctrls.left, - ctrls.right, - ctrls.down, - ctrls.rotate_cw, - ctrls.rotate_ccw, + Some(KeyboardControlScheme::new_classic( + ctrls + .keycode_from_movement(Movement::Left) + .expect(INVALID_LAST_USED_CONTROLS), + ctrls + .keycode_from_movement(Movement::Right) + .expect(INVALID_LAST_USED_CONTROLS), + ctrls + .keycode_from_movement(Movement::Down) + .expect(INVALID_LAST_USED_CONTROLS), + ctrls + .keycode_from_movement(Movement::RotateCw) + .expect(INVALID_LAST_USED_CONTROLS), + ctrls + .keycode_from_movement(Movement::RotateCcw) + .expect(INVALID_LAST_USED_CONTROLS), )), false, )); diff --git a/src/game/board.rs b/src/game/board.rs index fe49af8..71ba676 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -1,10 +1,11 @@ -use crate::game::piece::{Movement, Piece, Shapes}; +use crate::game::piece::{Piece, Shapes}; use crate::game::tile::Tile; use crate::game::Modes; use crate::game::{ CLEAR_DELAY_CLASSIC, FALL_DELAY_VALUES_CLASSIC, FALL_DELAY_VALUES_ROTATRIS, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, SCORE_TRIPLE_BASE, }; +use crate::movement::Movement; static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong option"; diff --git a/src/game/piece.rs b/src/game/piece.rs index a676683..40ebf00 100644 --- a/src/game/piece.rs +++ b/src/game/piece.rs @@ -1,4 +1,5 @@ use crate::game::board::Gravity; +use crate::movement::Movement; #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] @@ -28,35 +29,6 @@ impl From for Shapes { } } -#[repr(u8)] -#[derive(PartialEq, Eq, Copy, Clone)] -pub enum Movement { - Down, - Left, - Up, - Right, - RotateCw, - RotateCcw, - DoubleRotate, - None, -} - -impl From for Movement { - fn from(value: u8) -> Movement { - match value { - 0 => Movement::Down, - 1 => Movement::Left, - 2 => Movement::Up, - 3 => Movement::Right, - 4 => Movement::RotateCw, - 5 => Movement::RotateCcw, - 6 => Movement::DoubleRotate, - 7 => Movement::None, - _ => panic!("[!] Unknown Movement value: {}", value), - } - } -} - #[derive(Copy, Clone)] pub struct Piece { pub shape: Shapes, diff --git a/src/game/player.rs b/src/game/player.rs index 80ee5d2..0ebed4a 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -1,9 +1,11 @@ +use ggez::event::{Axis, Button, KeyCode}; +use rand::random; + use crate::game::piece::Shapes; use crate::game::{DAS_THRESHOLD_BIG, FORCE_FALL_DELAY, INITIAL_HANG_FRAMES}; use crate::game::{DETECT_GAMEPAD_AXIS_THRESHOLD, UNDETECT_GAMEPAD_AXIS_THRESHOLD}; use crate::inputs::{Input, KeyboardControlScheme}; -use ggez::event::{Axis, Button, KeyCode}; -use rand::random; +use crate::movement::Movement; pub const SPAWN_DELAY: i16 = 20i16; @@ -58,35 +60,51 @@ impl Player { } pub fn update_input_keydown(&mut self, input: KeyCode) -> bool { - if let Some(k_ctrls) = self.control_scheme.0 { - if input == k_ctrls.left { - if !self.input.keydown_left.0 { - self.input.keydown_left = (true, true); - // for auto-shift reasons and controller reasons... - self.input.keydown_right.0 = false; - return true; - } - } else if input == k_ctrls.right { - if !self.input.keydown_right.0 { - self.input.keydown_right = (true, true); - // for auto-shift reasons and controller reasons... - self.input.keydown_left.0 = false; - return true; - } - } else if input == k_ctrls.down { - if !self.input.keydown_down.0 { - self.input.keydown_down = (true, true); - return true; - } - } else if input == k_ctrls.rotate_cw { - if !self.input.keydown_rotate_cw.0 { - self.input.keydown_rotate_cw = (true, true); - return true; - } - } else if input == k_ctrls.rotate_ccw { - if !self.input.keydown_rotate_ccw.0 { - self.input.keydown_rotate_ccw = (true, true); - return true; + if let Some(k_ctrl_scheme) = self.control_scheme.0 { + let movement_opt = k_ctrl_scheme.movement_from_keycode(input); + if let Some(movement) = movement_opt { + match movement { + Movement::Down => { + if !self.input.keydown_down.0 { + self.input.keydown_down = (true, true); + return true; + } + } + Movement::Left => { + if !self.input.keydown_left.0 { + self.input.keydown_left = (true, true); + // for auto-shift reasons and controller reasons... + self.input.keydown_right.0 = false; + return true; + } + } + Movement::Up => { + if !self.input.keydown_up.0 { + self.input.keydown_up = (true, true); + return true; + } + } + Movement::Right => { + if !self.input.keydown_right.0 { + self.input.keydown_right = (true, true); + // for auto-shift reasons and controller reasons... + self.input.keydown_left.0 = false; + return true; + } + } + Movement::RotateCw => { + if !self.input.keydown_rotate_cw.0 { + self.input.keydown_rotate_cw = (true, true); + return true; + } + } + Movement::RotateCcw => { + if !self.input.keydown_rotate_ccw.0 { + self.input.keydown_rotate_ccw = (true, true); + return true; + } + } + _ => {} } } } @@ -95,32 +113,46 @@ impl Player { } pub fn update_input_keyup(&mut self, input: KeyCode) -> bool { - if let Some(k_ctrls) = self.control_scheme.0 { - if input == k_ctrls.left { - // for auto-shift reasons - if self.input.keydown_left.0 { - self.das_countdown = DAS_THRESHOLD_BIG; - self.waiting_to_shift = false; - } - self.input.keydown_left = (false, false); - return true; - } else if input == k_ctrls.right { - // for auto-shift reasons - if self.input.keydown_right.0 { - self.das_countdown = DAS_THRESHOLD_BIG; - self.waiting_to_shift = false; + if let Some(k_ctrl_scheme) = self.control_scheme.0 { + let movement_opt = k_ctrl_scheme.movement_from_keycode(input); + if let Some(movement) = movement_opt { + match movement { + Movement::Down => { + self.input.keydown_down = (false, false); + return true; + } + Movement::Left => { + // for auto-shift reasons + if self.input.keydown_left.0 { + self.das_countdown = DAS_THRESHOLD_BIG; + self.waiting_to_shift = false; + } + self.input.keydown_left = (false, false); + return true; + } + Movement::Up => { + self.input.keydown_up = (false, false); + return true; + } + Movement::Right => { + // for auto-shift reasons + if self.input.keydown_right.0 { + self.das_countdown = DAS_THRESHOLD_BIG; + self.waiting_to_shift = false; + } + self.input.keydown_right = (false, false); + return true; + } + Movement::RotateCw => { + self.input.keydown_rotate_cw = (false, false); + return true; + } + Movement::RotateCcw => { + self.input.keydown_rotate_ccw = (false, false); + return true; + } + _ => {} } - self.input.keydown_right = (false, false); - return true; - } else if input == k_ctrls.down { - self.input.keydown_down = (false, false); - return true; - } else if input == k_ctrls.rotate_cw { - self.input.keydown_rotate_cw = (false, false); - return true; - } else if input == k_ctrls.rotate_ccw { - self.input.keydown_rotate_ccw = (false, false); - return true; } } diff --git a/src/inputs.rs b/src/inputs.rs index ba7f42d..c31a778 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -1,3 +1,4 @@ +use crate::movement::Movement; use ggez::event::KeyCode; // (is pressed down, was pressed this frame) @@ -44,7 +45,7 @@ impl Input { self.keydown_start = (false, false); } - pub fn _print_inputs(&self) { + pub fn _debug_print_inputs(&self) { println!("Left: ({}, {})", self.keydown_left.0, self.keydown_left.1); println!( "Right: ({}, {})", @@ -67,47 +68,87 @@ impl Input { } } -#[derive(Copy, Clone)] pub struct KeyboardControlScheme { - pub left: KeyCode, - pub right: KeyCode, - pub down: KeyCode, - pub rotate_cw: KeyCode, - pub rotate_ccw: KeyCode, + pub vec_keycode_movement_pair: Vec<(KeyCode, Movement)>, // pub left: KeyCode, + // pub right: KeyCode, + // pub down: KeyCode, + // pub rotate_cw: KeyCode, + // pub rotate_ccw: KeyCode } impl KeyboardControlScheme { - pub fn new( + pub fn new_classic( left: KeyCode, right: KeyCode, down: KeyCode, rotate_cw: KeyCode, rotate_ccw: KeyCode, ) -> Self { + let mut vec_keycode_movement_pair: Vec<(KeyCode, Movement)> = Vec::with_capacity(5); + vec_keycode_movement_pair.push((left, Movement::Left)); + vec_keycode_movement_pair.push((right, Movement::Right)); + vec_keycode_movement_pair.push((down, Movement::Down)); + vec_keycode_movement_pair.push((rotate_cw, Movement::RotateCw)); + vec_keycode_movement_pair.push((rotate_ccw, Movement::RotateCcw)); Self { - left, - right, - down, - rotate_cw, - rotate_ccw, + vec_keycode_movement_pair, } } - pub fn split( - &self, - ) -> ( - Option, - Option, - Option, - Option, - Option, - ) { - ( - Some(self.left), - Some(self.right), - Some(self.down), - Some(self.rotate_cw), - Some(self.rotate_ccw), - ) + pub fn index_from_movement(&self, m: Movement) -> Option { + for (index, pair) in self.vec_keycode_movement_pair.iter().enumerate() { + if pair.1 == m { + return Some(index); + } + } + + None + } + + pub fn keycode_from_movement(&self, m: Movement) -> Option { + for (index, pair) in self.vec_keycode_movement_pair.iter().enumerate() { + if pair.1 == m { + return Some(pair.0); + } + } + + None + } + + pub fn movement_from_keycode(&self, k: KeyCode) -> Option { + for (index, pair) in self.vec_keycode_movement_pair.iter().enumerate() { + if pair.0 == k { + return Some(pair.1); + } + } + + None + } + + // pub fn split( + // &self, + // ) -> ( + // Option, + // Option, + // Option, + // Option, + // Option, + // ) { + // ( + // Some(self.left), + // Some(self.right), + // Some(self.down), + // Some(self.rotate_cw), + // Some(self.rotate_ccw), + // ) + // } +} + +impl Default for KeyboardControlScheme { + fn default() -> Self { + let vec_keycode_movement_pair: Vec<(KeyCode, Movement)> = vec![]; + Self { + vec_keycode_movement_pair, + } } } diff --git a/src/main.rs b/src/main.rs index 3b039b3..924399f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ mod game; mod menu; mod inputs; +mod movement; use ggez::input::gamepad::GilrsGamepadContext; diff --git a/src/menu.rs b/src/menu.rs index 5177e66..69f1378 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -195,7 +195,7 @@ impl MenuGameOptions { for (idx, ctrls) in arr_split_controls.iter().enumerate() { if let Some(k_ctrls) = ctrls.0 { arr_controls[idx] = ( - Some(KeyboardControlScheme::new( + Some(KeyboardControlScheme::new_classic( k_ctrls.0.expect( "[!] attempted to create KeyboardControlScheme with Left == None", ), diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index f3b3612..90ccbb0 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -5,6 +5,7 @@ use ggez::nalgebra::Point2; use ggez::Context; use crate::inputs::{Input, KeyboardControlScheme}; +use crate::movement::Movement; use crate::menu::{MenuItem, MenuItemTrigger, MenuItemValueType}; @@ -17,6 +18,7 @@ use crate::menu::DARK_GRAY; use crate::menu::HELP_RED; use crate::menu::LIGHT_GRAY; +static INVALID_LAST_USED_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; static KEY_UNEXPECTEDLY_NONE: &str = "[!] KeyCode of most recently pressed key is unexpectedly None"; @@ -65,13 +67,10 @@ impl InputConfigMenu { bool, ); MAX_NUM_PLAYERS as usize] = [(None, false); MAX_NUM_PLAYERS as usize]; for (idx, ctrls) in last_used_arr_controls.iter().enumerate() { - if let Some(k_ctrls) = ctrls.0 { - arr_split_controls[idx].0 = Some(k_ctrls.split()); - vec_used_keycode.push(k_ctrls.left); - vec_used_keycode.push(k_ctrls.right); - vec_used_keycode.push(k_ctrls.down); - vec_used_keycode.push(k_ctrls.rotate_cw); - vec_used_keycode.push(k_ctrls.rotate_ccw); + if let Some(k_ctrl_scheme) = &ctrls.0 { + for key_move_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { + vec_used_keycode.push(key_move_pair.0); + } } arr_split_controls[idx].1 = ctrls.1; } @@ -105,13 +104,33 @@ impl InputConfigMenu { Option, Option, ); - if let Some(ctrls) = last_used_arr_controls[0].0 { + if let Some(ctrls) = &last_used_arr_controls[0].0 { first_player_previous_controls = ( - Some(ctrls.left), - Some(ctrls.right), - Some(ctrls.down), - Some(ctrls.rotate_cw), - Some(ctrls.rotate_ccw), + Some( + ctrls + .keycode_from_movement(Movement::Left) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::Right) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::Down) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::RotateCw) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::RotateCcw) + .expect(INVALID_LAST_USED_CONTROLS), + ), ); } else { first_player_previous_controls = (None, None, None, None, None); diff --git a/src/movement.rs b/src/movement.rs new file mode 100644 index 0000000..e1a4cdb --- /dev/null +++ b/src/movement.rs @@ -0,0 +1,28 @@ +#[repr(u8)] +#[derive(PartialEq, Eq, Copy, Clone)] +pub enum Movement { + Down, + Left, + Up, + Right, + RotateCw, + RotateCcw, + DoubleRotate, + None, +} + +impl From for Movement { + fn from(value: u8) -> Movement { + match value { + 0 => Movement::Down, + 1 => Movement::Left, + 2 => Movement::Up, + 3 => Movement::Right, + 4 => Movement::RotateCw, + 5 => Movement::RotateCcw, + 6 => Movement::DoubleRotate, + 7 => Movement::None, + _ => panic!("[!] Unknown Movement value: {}", value), + } + } +} From e33a20788706a81a799d48cd8ca1e18bd40981ce Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Sun, 11 Apr 2021 20:31:20 -0400 Subject: [PATCH 34/59] can we get rid of these stupid tuples already? 5 errors remain --- src/control.rs | 2 +- src/game.rs | 76 +++++++++----- src/game/player.rs | 4 +- src/inputs.rs | 33 +++++-- src/menu.rs | 25 ++--- src/menu/inputconfig.rs | 212 +++++++++++++++------------------------- src/movement.rs | 18 ++++ 7 files changed, 183 insertions(+), 187 deletions(-) diff --git a/src/control.rs b/src/control.rs index 7346e88..d6345ff 100644 --- a/src/control.rs +++ b/src/control.rs @@ -36,7 +36,7 @@ impl Control { } } - pub fn change_state(&mut self, ctx: &mut Context, new_state: ProgramState) { + fn change_state(&mut self, ctx: &mut Context, new_state: ProgramState) { self.state = match new_state { ProgramState::Menu => { self.menu = Some(Menu::new(ctx, &self.game_options)); diff --git a/src/game.rs b/src/game.rs index c9c55bd..4fcd866 100644 --- a/src/game.rs +++ b/src/game.rs @@ -96,7 +96,7 @@ impl From<&MenuGameOptions> for GameOptions { Vec::with_capacity(menu_game_options.arr_controls.len()); let mut counted_active_controls: u8 = 0; for controls in menu_game_options.arr_controls.iter() { - if let Some(ctrls) = controls.0 { + if let Some(ctrls) = &controls.0 { vec_controls.push(( Some(KeyboardControlScheme::new_classic( ctrls @@ -181,40 +181,64 @@ impl Game { Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, }; let mut vec_players: Vec = Vec::with_capacity(game_options.num_players as usize); - // spawn columns - // first half, not including middle player if there's an odd number of players - for player in 0..(game_options.num_players) / 2 { - vec_players.push(Player::new( - player, - game_options.vec_controls[player as usize], + for player in 0..game_options.num_players { + // spawn_column + let spawn_column: u8 = if player < game_options.num_players / 2 { + // first half, not including middle player if there's an odd number of players (player as f32 * (board_width as f32 / game_options.num_players as f32) + board_width as f32 / (2.0 * game_options.num_players as f32)) as u8 - + 1, - )); - } - // middle player, for an odd number of players - if game_options.num_players % 2 == 1 { - let player = game_options.num_players / 2; - vec_players.push(Player::new( - player, - game_options.vec_controls[player as usize], - board_width / 2, - )); - } - // second half, not including the middle player if there's an odd number of players - for player in (game_options.num_players + 1) / 2..game_options.num_players { - vec_players.push(Player::new( - player, - game_options.vec_controls[player as usize], + + 1 + } else if player == game_options.num_players && game_options.num_players % 2 == 1 { + // middle player, for an odd number of players + board_width / 2 + } else { + // second half, not including the middle player if there's an odd number of players board_width - 1 - ((game_options.num_players - 1 - player) as f32 * (board_width as f32 / game_options.num_players as f32) + board_width as f32 / (2.0 * game_options.num_players as f32)) - as u8, - )); + as u8 + }; + // control_scheme; we need to create a copy of game_options.vec_controls, but to do that, we must "manually" copy the keyboard controls for the player if they exist (since that has a vector) + let control_scheme = match &game_options.vec_controls[player as usize].0 { + Some(k_ctrl_scheme) => (Some(k_ctrl_scheme.copy()), false), + None => (None, true), + }; + + vec_players.push(Player::new(player, control_scheme, spawn_column)); } + // for player in 0..(game_options.num_players) / 2 { + // vec_players.push(Player::new( + // player, + // game_options.vec_controls[player as usize], + // (player as f32 * (board_width as f32 / game_options.num_players as f32) + // + board_width as f32 / (2.0 * game_options.num_players as f32)) + // as u8 + // + 1, + // )); + // } + // if game_options.num_players % 2 == 1 { + // let player = game_options.num_players / 2; + // vec_players.push(Player::new( + // player, + // game_options.vec_controls[player as usize], + // board_width / 2, + // )); + // } + // for player in (game_options.num_players + 1) / 2..game_options.num_players { + // vec_players.push(Player::new( + // player, + // game_options.vec_controls[player as usize], + // board_width + // - 1 + // - ((game_options.num_players - 1 - player) as f32 + // * (board_width as f32 / game_options.num_players as f32) + // + board_width as f32 / (2.0 * game_options.num_players as f32)) + // as u8, + // )); + // } let mut batch_empty_tile = spritebatch::SpriteBatch::new(TileGraphic::new_empty(ctx).image); // the emtpy tile batch will be constant once the game starts with the player tile batches drawing on top of it, so just set that up here for x in 0..board_width { diff --git a/src/game/player.rs b/src/game/player.rs index 0ebed4a..6072c94 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -60,7 +60,7 @@ impl Player { } pub fn update_input_keydown(&mut self, input: KeyCode) -> bool { - if let Some(k_ctrl_scheme) = self.control_scheme.0 { + if let Some(k_ctrl_scheme) = &self.control_scheme.0 { let movement_opt = k_ctrl_scheme.movement_from_keycode(input); if let Some(movement) = movement_opt { match movement { @@ -113,7 +113,7 @@ impl Player { } pub fn update_input_keyup(&mut self, input: KeyCode) -> bool { - if let Some(k_ctrl_scheme) = self.control_scheme.0 { + if let Some(k_ctrl_scheme) = &self.control_scheme.0 { let movement_opt = k_ctrl_scheme.movement_from_keycode(input); if let Some(movement) = movement_opt { match movement { diff --git a/src/inputs.rs b/src/inputs.rs index c31a778..7e1654b 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -69,14 +69,29 @@ impl Input { } pub struct KeyboardControlScheme { - pub vec_keycode_movement_pair: Vec<(KeyCode, Movement)>, // pub left: KeyCode, - // pub right: KeyCode, - // pub down: KeyCode, - // pub rotate_cw: KeyCode, - // pub rotate_ccw: KeyCode + pub vec_keycode_movement_pair: Vec<(KeyCode, Movement)>, } impl KeyboardControlScheme { + pub fn copy(&self) -> Self { + let mut copy_vec_keycode_movement_pair: Vec<(KeyCode, Movement)> = + Vec::with_capacity(self.vec_keycode_movement_pair.capacity()); + for item in self.vec_keycode_movement_pair.iter() { + copy_vec_keycode_movement_pair.push(*item); + } + Self { + vec_keycode_movement_pair: copy_vec_keycode_movement_pair, + } + } + + pub fn len(&self) -> usize { + self.vec_keycode_movement_pair.len() + } + + pub fn clear(&mut self) { + self.vec_keycode_movement_pair.clear(); + } + pub fn new_classic( left: KeyCode, right: KeyCode, @@ -106,7 +121,7 @@ impl KeyboardControlScheme { } pub fn keycode_from_movement(&self, m: Movement) -> Option { - for (index, pair) in self.vec_keycode_movement_pair.iter().enumerate() { + for pair in self.vec_keycode_movement_pair.iter() { if pair.1 == m { return Some(pair.0); } @@ -116,7 +131,7 @@ impl KeyboardControlScheme { } pub fn movement_from_keycode(&self, k: KeyCode) -> Option { - for (index, pair) in self.vec_keycode_movement_pair.iter().enumerate() { + for pair in self.vec_keycode_movement_pair.iter() { if pair.0 == k { return Some(pair.1); } @@ -125,6 +140,10 @@ impl KeyboardControlScheme { None } + pub fn add_pair(&mut self, k: KeyCode, m: Movement) { + self.vec_keycode_movement_pair.push((k, m)); + } + // pub fn split( // &self, // ) -> ( diff --git a/src/menu.rs b/src/menu.rs index 69f1378..3209820 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -31,7 +31,7 @@ pub enum MenuItemValueType { KeyCode, } -#[derive(Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum MenuItemTrigger { None, StartGame, @@ -179,19 +179,9 @@ impl MenuGameOptions { fn new( num_players: u8, starting_level: u8, - arr_split_controls: [( - Option<( - Option, - Option, - Option, - Option, - Option, - )>, - bool, - ); MAX_NUM_PLAYERS as usize], + arr_controls: &[(Option, bool); MAX_NUM_PLAYERS as usize], ) -> Self { - let mut arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize] = - [(None, false); MAX_NUM_PLAYERS as usize]; + let arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize]; for (idx, ctrls) in arr_split_controls.iter().enumerate() { if let Some(k_ctrls) = ctrls.0 { arr_controls[idx] = ( @@ -216,6 +206,8 @@ impl MenuGameOptions { ); } else if ctrls.1 { arr_controls[idx] = (None, true); + } else { + arr_controls[idx] = (None, false); } } Self { @@ -258,7 +250,7 @@ impl Menu { ), input_config_menu: InputConfigMenu::new( window_dimensions, - menu_game_options.arr_controls, + Some(&menu_game_options.arr_controls), ), } } else { @@ -267,10 +259,7 @@ impl Menu { input: Input::new(), state: MenuState::Start, start_menu: StartMenu::new(window_dimensions, 1, 0), - input_config_menu: InputConfigMenu::new( - window_dimensions, - [(None, false); MAX_NUM_PLAYERS as usize], - ), + input_config_menu: InputConfigMenu::new(window_dimensions, None), } } } diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 90ccbb0..a35c679 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -31,16 +31,7 @@ pub struct InputConfigMenu { pub most_recently_pressed_key: Option, vec_used_keycode: Vec, keycode_conflict_flag: bool, - pub arr_split_controls: [( - Option<( - Option, - Option, - Option, - Option, - Option, - )>, - bool, - ); MAX_NUM_PLAYERS as usize], + pub arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize], // text vec_menu_items_main: Vec, // subtext @@ -53,7 +44,9 @@ pub struct InputConfigMenu { impl InputConfigMenu { pub fn new( window_dimensions: (f32, f32), - last_used_arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize], + opt_last_used_arr_controls: Option< + &[(Option, bool); MAX_NUM_PLAYERS as usize], + >, ) -> Self { let mut vec_used_keycode: Vec = vec![]; let mut arr_split_controls: [( @@ -66,13 +59,15 @@ impl InputConfigMenu { )>, bool, ); MAX_NUM_PLAYERS as usize] = [(None, false); MAX_NUM_PLAYERS as usize]; - for (idx, ctrls) in last_used_arr_controls.iter().enumerate() { - if let Some(k_ctrl_scheme) = &ctrls.0 { - for key_move_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { - vec_used_keycode.push(key_move_pair.0); + if let Some(last_used_arr_controls) = opt_last_used_arr_controls { + for (idx, ctrls) in last_used_arr_controls.iter().enumerate() { + if let Some(k_ctrl_scheme) = &ctrls.0 { + for key_move_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { + vec_used_keycode.push(key_move_pair.0); + } } + arr_split_controls[idx].1 = ctrls.1; } - arr_split_controls[idx].1 = ctrls.1; } // main MenuItems let mut vec_menu_items_main: Vec = Vec::with_capacity(2); @@ -104,34 +99,38 @@ impl InputConfigMenu { Option, Option, ); - if let Some(ctrls) = &last_used_arr_controls[0].0 { - first_player_previous_controls = ( - Some( - ctrls - .keycode_from_movement(Movement::Left) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::Right) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::Down) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::RotateCw) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::RotateCcw) - .expect(INVALID_LAST_USED_CONTROLS), - ), - ); + if let Some(last_used_arr_controls) = opt_last_used_arr_controls { + if let Some(ctrls) = &last_used_arr_controls[0].0 { + first_player_previous_controls = ( + Some( + ctrls + .keycode_from_movement(Movement::Left) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::Right) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::Down) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::RotateCw) + .expect(INVALID_LAST_USED_CONTROLS), + ), + Some( + ctrls + .keycode_from_movement(Movement::RotateCcw) + .expect(INVALID_LAST_USED_CONTROLS), + ), + ); + } else { + first_player_previous_controls = (None, None, None, None, None); + } } else { first_player_previous_controls = (None, None, None, None, None); } @@ -189,7 +188,7 @@ impl InputConfigMenu { most_recently_pressed_key: None, vec_used_keycode, keycode_conflict_flag: false, - arr_split_controls, + arr_controls, // text vec_menu_items_main, // subtext @@ -244,10 +243,10 @@ impl InputConfigMenu { if input.keydown_rotate_cw.1 && self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { - self.arr_split_controls[self.player_num as usize].1 = true; - if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { - self.remove_from_used_keycodes(&ctrls); - self.arr_split_controls[self.player_num as usize].0 = None; + self.arr_controls[self.player_num as usize].1 = true; + if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + self.remove_from_used_keycodes(&k_ctrl_scheme); + self.arr_controls[self.player_num as usize].0 = None; } } @@ -260,13 +259,11 @@ impl InputConfigMenu { == MenuItemTrigger::SubSelection { self.most_recently_pressed_key = None; - if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { - self.remove_from_used_keycodes(&ctrls); + if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + self.remove_from_used_keycodes(&k_ctrl_scheme); } - self.arr_split_controls[self.player_num as usize].0 = None; - - self.arr_split_controls[self.player_num as usize].0 = - Some((None, None, None, None, None)); + self.arr_controls[self.player_num as usize].0 = + Some(KeyboardControlScheme::default()); self.update_sub_text_strings(); self.sub_selection_keyboard_flag = true; self.vec_menu_items_main[self.selection].set_select(true); @@ -278,11 +275,11 @@ impl InputConfigMenu { if self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { // remove input stuff from selection if we are on the SubSelection option - if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { - self.remove_from_used_keycodes(&ctrls); - self.arr_split_controls[self.player_num as usize].0 = None; + if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + self.remove_from_used_keycodes(&k_ctrl_scheme); + self.arr_controls[self.player_num as usize].0 = None; } - self.arr_split_controls[self.player_num as usize].1 = false; + self.arr_controls[self.player_num as usize].1 = false; self.most_recently_pressed_key = None; } else { return true; @@ -297,24 +294,12 @@ impl InputConfigMenu { self.sub_selection_keyboard = 0; self.sub_selection_keyboard_flag = false; // the user was in the middle of creating keyboard controls when they hit 'Escape', so pop however many KeyCode's off vec_used_keycode that the user set up - if let Some(ctrls) = self.arr_split_controls[self.player_num as usize].0 { - if (ctrls.3).is_some() { - for _ in 1..=4 { - self.vec_used_keycode.pop(); - } - } else if (ctrls.2).is_some() { - for _ in 1..=3 { - self.vec_used_keycode.pop(); - } - } else if (ctrls.1).is_some() { - for _ in 1..=2 { - self.vec_used_keycode.pop(); - } - } else if (ctrls.0).is_some() { + if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + for _ in 0..k_ctrl_scheme.len() { self.vec_used_keycode.pop(); } } - self.arr_split_controls[self.player_num as usize].0 = None; + self.arr_controls[self.player_num as usize].0 = None; self.most_recently_pressed_key = None; } else if self .vec_used_keycode @@ -325,43 +310,16 @@ impl InputConfigMenu { } else { // no conflict, enter KeyCode of key pressed self.keycode_conflict_flag = false; - match (self.arr_split_controls[self.player_num as usize].0).as_mut() { - Some(mut ctrls) => match self.vec_menu_items_keycode - [self.sub_selection_keyboard] - .trigger - { - MenuItemTrigger::KeyLeft => { - ctrls.0 = self.most_recently_pressed_key; - self.vec_used_keycode - .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); - } - MenuItemTrigger::KeyRight => { - ctrls.1 = self.most_recently_pressed_key; - self.vec_used_keycode - .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); - } - MenuItemTrigger::KeyDown => { - ctrls.2 = self.most_recently_pressed_key; - self.vec_used_keycode - .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); - } - MenuItemTrigger::KeyRotateCw => { - ctrls.3 = self.most_recently_pressed_key; - self.vec_used_keycode - .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); - } - MenuItemTrigger::KeyRotateCcw => { - ctrls.4 = self.most_recently_pressed_key; - self.vec_used_keycode - .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); - } - _ => println!("[!] weird MenuItemTrigger in vec_menu_items_keycode item"), - }, - None => { - println!( - "[!] arr_split_controls[{}].0 was unexpectedly None", - self.player_num + match (self.arr_controls[self.player_num as usize].0).as_mut() { + Some(mut k_ctrl_scheme) => { + k_ctrl_scheme.add_pair( + self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE), + Movement::from( + self.vec_menu_items_keycode[self.sub_selection_keyboard].trigger, + ), ); + self.vec_used_keycode + .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); } } self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(false); @@ -377,29 +335,17 @@ impl InputConfigMenu { false } - fn remove_from_used_keycodes( - &mut self, - ctrls: &( - Option, - Option, - Option, - Option, - Option, - ), - ) { + fn remove_from_used_keycodes(&mut self, k_ctrl_scheme: &KeyboardControlScheme) { let mut items_removed = 0; for used_key_idx in 0..self.vec_used_keycode.len() { - if Some(self.vec_used_keycode[used_key_idx - items_removed]) == ctrls.0 - || Some(self.vec_used_keycode[used_key_idx - items_removed]) == ctrls.1 - || Some(self.vec_used_keycode[used_key_idx - items_removed]) == ctrls.2 - || Some(self.vec_used_keycode[used_key_idx - items_removed]) == ctrls.3 - || Some(self.vec_used_keycode[used_key_idx - items_removed]) == ctrls.4 - { - self.vec_used_keycode.remove(used_key_idx - items_removed); - items_removed += 1; - // we only need to get rid of self.vec_menu_items_keycode.len() - if items_removed >= self.vec_menu_items_keycode.len() { - return; + for k_m_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { + if k_m_pair.0 == self.vec_used_keycode[used_key_idx - items_removed] { + self.vec_used_keycode.remove(used_key_idx - items_removed); + items_removed += 1; + // we only need to get rid of k_ctrl_scheme.len() + if items_removed >= k_ctrl_scheme.len() { + return; + } } } } @@ -467,7 +413,7 @@ impl InputConfigMenu { self.draw_text(ctx, &self.keycode_conflict_text, 0.43, &window_dimensions); } - if (self.arr_split_controls[self.player_num as usize].0).is_some() { + if (self.arr_controls[self.player_num as usize].0).is_some() { for (index, item) in self.vec_menu_items_keycode.iter().enumerate() { self.draw_text( ctx, @@ -476,7 +422,7 @@ impl InputConfigMenu { &window_dimensions, ); } - } else if self.arr_split_controls[self.player_num as usize].1 { + } else if self.arr_controls[self.player_num as usize].1 { self.draw_text(ctx, &self.is_gamepad_text, 0.63, &window_dimensions); } else { self.draw_text(ctx, &self.input_uninitialized_text, 0.5, &window_dimensions); diff --git a/src/movement.rs b/src/movement.rs index e1a4cdb..8608661 100644 --- a/src/movement.rs +++ b/src/movement.rs @@ -1,3 +1,5 @@ +use crate::menu::MenuItemTrigger; + #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] pub enum Movement { @@ -26,3 +28,19 @@ impl From for Movement { } } } + +impl From for Movement { + fn from(value: MenuItemTrigger) -> Movement { + match value { + MenuItemTrigger::KeyLeft => Movement::Left, + MenuItemTrigger::KeyRight => Movement::Right, + MenuItemTrigger::KeyDown => Movement::Down, + MenuItemTrigger::KeyRotateCw => Movement::RotateCw, + MenuItemTrigger::KeyRotateCcw => Movement::RotateCcw, + _ => panic!( + "[!] Unexpected value converting MenuItemTrigger to Movement: {:?}", + value + ), + } + } +} From b95f81d5152c93859f9281f98848f2b38882d3f4 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Tue, 20 Apr 2021 00:42:14 -0400 Subject: [PATCH 35/59] stuck on weird box initialization issue --- src/control.rs | 28 ++++---- src/menu.rs | 112 ++++++++++---------------------- src/menu/inputconfig.rs | 139 +++++++++++++++------------------------- 3 files changed, 96 insertions(+), 183 deletions(-) diff --git a/src/control.rs b/src/control.rs index d6345ff..57f700e 100644 --- a/src/control.rs +++ b/src/control.rs @@ -23,16 +23,17 @@ pub struct Control { state: ProgramState, menu: Option, game: Option, - game_options: Option, + game_options: MenuGameOptions, } impl Control { pub fn new(ctx: &mut Context) -> Control { + let menu_game_options = MenuGameOptions::new(); Self { state: ProgramState::Menu, - menu: Some(Menu::new(ctx, &None)), + menu: Some(Menu::new(ctx, &menu_game_options)), game: None, - game_options: None, + game_options: menu_game_options, } } @@ -43,14 +44,7 @@ impl Control { ProgramState::Menu } ProgramState::Game => { - self.game = Some(Game::new( - ctx, - &GameOptions::from( - self.game_options - .as_ref() - .expect("[!] attempted to start Game with no GameOptions"), - ), - )); + self.game = Some(Game::new(ctx, &GameOptions::from(&self.game_options))); ProgramState::Game } }; @@ -66,12 +60,14 @@ impl EventHandler for Control { match self.state { ProgramState::Menu => { // update the menu and get the state with GameOptions if ProgramState is changing - if let Some(state_and_gameoptions) = - self.menu.as_mut().expect(STATE_MENU_BUT_MENU_NONE).update() + if let Some(new_state) = self + .menu + .as_mut() + .expect(STATE_MENU_BUT_MENU_NONE) + .update(&mut self.game_options) { self.menu = None; - self.game_options = Some(state_and_gameoptions.1); - self.change_state(ctx, state_and_gameoptions.0); + self.change_state(ctx, new_state); } } ProgramState::Game => { @@ -165,7 +161,7 @@ impl EventHandler for Control { .menu .as_mut() .expect(STATE_MENU_BUT_MENU_NONE) - .draw(ctx), + .draw(ctx, &self.game_options), ProgramState::Game => self .game .as_mut() diff --git a/src/menu.rs b/src/menu.rs index 3209820..4b67d3e 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -172,48 +172,15 @@ impl MenuItem { pub struct MenuGameOptions { pub num_players: u8, pub starting_level: u8, - pub arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize], + pub arr_controls: Box<[(Option, bool); MAX_NUM_PLAYERS as usize]>, } impl MenuGameOptions { - fn new( - num_players: u8, - starting_level: u8, - arr_controls: &[(Option, bool); MAX_NUM_PLAYERS as usize], - ) -> Self { - let arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize]; - for (idx, ctrls) in arr_split_controls.iter().enumerate() { - if let Some(k_ctrls) = ctrls.0 { - arr_controls[idx] = ( - Some(KeyboardControlScheme::new_classic( - k_ctrls.0.expect( - "[!] attempted to create KeyboardControlScheme with Left == None", - ), - k_ctrls.1.expect( - "[!] attempted to create KeyboardControlScheme with Right == None", - ), - k_ctrls.2.expect( - "[!] attempted to create KeyboardControlScheme with Down == None", - ), - k_ctrls.3.expect( - "[!] attempted to create KeyboardControlScheme with RotateCw == None", - ), - k_ctrls.4.expect( - "[!] attempted to create KeyboardControlScheme with RotateCcw == None", - ), - )), - false, - ); - } else if ctrls.1 { - arr_controls[idx] = (None, true); - } else { - arr_controls[idx] = (None, false); - } - } + pub fn new() -> Self { Self { - num_players, - starting_level, - arr_controls, + num_players: 1, + starting_level: 0, + arr_controls: vec![(None, false); MAX_NUM_PLAYERS as usize].into_boxed_slice(), } } } @@ -229,6 +196,7 @@ enum MenuState { pub struct Menu { // logic input: Input, + num_required_keycode_movement_pairs: usize, // states state: MenuState, start_menu: start::StartMenu, @@ -236,51 +204,31 @@ pub struct Menu { } impl Menu { - pub fn new(ctx: &mut Context, last_used_game_options: &Option) -> Self { + pub fn new(ctx: &mut Context, game_options: &MenuGameOptions) -> Self { let window_dimensions = graphics::size(ctx); - if let Some(menu_game_options) = last_used_game_options { - // previously used - Self { - input: Input::new(), - state: MenuState::Start, - start_menu: StartMenu::new( - window_dimensions, - menu_game_options.num_players, - menu_game_options.starting_level, - ), - input_config_menu: InputConfigMenu::new( - window_dimensions, - Some(&menu_game_options.arr_controls), - ), - } - } else { - // defaults - Self { - input: Input::new(), - state: MenuState::Start, - start_menu: StartMenu::new(window_dimensions, 1, 0), - input_config_menu: InputConfigMenu::new(window_dimensions, None), - } + Self { + input: Input::new(), + num_required_keycode_movement_pairs: 5, // TODO + state: MenuState::Start, + start_menu: StartMenu::new( + window_dimensions, + game_options.num_players, + game_options.starting_level, + ), + input_config_menu: InputConfigMenu::new(window_dimensions, game_options), } } - pub fn update(&mut self) -> Option<(ProgramState, MenuGameOptions)> { + pub fn update(&mut self, game_options: &mut MenuGameOptions) -> Option { match self.state { MenuState::Start => { let trigger: MenuItemTrigger = self.start_menu.update(&self.input); match trigger { MenuItemTrigger::StartGame => { - if self.ensure_enough_controls() { + if self.ensure_enough_controls(game_options) { let (num_players, starting_level) = self.start_menu.find_important_values(); - return Some(( - ProgramState::Game, - MenuGameOptions::new( - num_players, - starting_level, - self.input_config_menu.arr_split_controls, - ), - )); + return Some(ProgramState::Game); } else { self.start_menu.not_enough_controls_flag = true; } @@ -298,7 +246,7 @@ impl Menu { } } MenuState::InputConfig => { - if self.input_config_menu.update(&self.input) { + if self.input_config_menu.update(&self.input, game_options) { self.state = MenuState::Start; } } @@ -308,11 +256,19 @@ impl Menu { None } - fn ensure_enough_controls(&self) -> bool { + fn ensure_enough_controls(&self, game_options: &MenuGameOptions) -> bool { let mut ctrls_count = 0; - for ctrls in self.input_config_menu.arr_split_controls.iter() { - if ctrls.0.is_some() || ctrls.1 { + for ctrls in game_options.arr_controls.iter() { + if ctrls.1 { ctrls_count += 1; + } else { + if let Some(k_ctrl_scheme) = &ctrls.0 { + if k_ctrl_scheme.vec_keycode_movement_pair.len() + <= self.num_required_keycode_movement_pairs + { + ctrls_count += 1; + } + } } } ctrls_count >= self.start_menu.find_important_values().0 @@ -374,12 +330,12 @@ impl Menu { } } - pub fn draw(&mut self, ctx: &mut Context) { + pub fn draw(&mut self, ctx: &mut Context, game_options: &MenuGameOptions) { graphics::clear(ctx, GRAY); match self.state { MenuState::Start => self.start_menu.draw(ctx), - MenuState::InputConfig => self.input_config_menu.draw(ctx), + MenuState::InputConfig => self.input_config_menu.draw(ctx, game_options), } } diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index a35c679..de2d3e0 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -7,7 +7,7 @@ use ggez::Context; use crate::inputs::{Input, KeyboardControlScheme}; use crate::movement::Movement; -use crate::menu::{MenuItem, MenuItemTrigger, MenuItemValueType}; +use crate::menu::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; use crate::menu::MAX_NUM_PLAYERS; @@ -31,7 +31,6 @@ pub struct InputConfigMenu { pub most_recently_pressed_key: Option, vec_used_keycode: Vec, keycode_conflict_flag: bool, - pub arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize], // text vec_menu_items_main: Vec, // subtext @@ -42,31 +41,15 @@ pub struct InputConfigMenu { } impl InputConfigMenu { - pub fn new( - window_dimensions: (f32, f32), - opt_last_used_arr_controls: Option< - &[(Option, bool); MAX_NUM_PLAYERS as usize], - >, - ) -> Self { + pub fn new(window_dimensions: (f32, f32), game_options: &MenuGameOptions) -> Self { let mut vec_used_keycode: Vec = vec![]; - let mut arr_split_controls: [( - Option<( - Option, - Option, - Option, - Option, - Option, - )>, - bool, - ); MAX_NUM_PLAYERS as usize] = [(None, false); MAX_NUM_PLAYERS as usize]; - if let Some(last_used_arr_controls) = opt_last_used_arr_controls { - for (idx, ctrls) in last_used_arr_controls.iter().enumerate() { - if let Some(k_ctrl_scheme) = &ctrls.0 { - for key_move_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { - vec_used_keycode.push(key_move_pair.0); - } + let arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize]; + // gather what the starting used keycodes should be + for (idx, ctrls) in game_options.arr_controls.iter().enumerate() { + if let Some(k_ctrl_scheme) = &ctrls.0 { + for key_move_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { + vec_used_keycode.push(key_move_pair.0); } - arr_split_controls[idx].1 = ctrls.1; } } // main MenuItems @@ -92,54 +75,15 @@ impl InputConfigMenu { vec_menu_items_main[0].set_select(true); // keycode MenuItems - let first_player_previous_controls: ( - Option, - Option, - Option, - Option, - Option, - ); - if let Some(last_used_arr_controls) = opt_last_used_arr_controls { - if let Some(ctrls) = &last_used_arr_controls[0].0 { - first_player_previous_controls = ( - Some( - ctrls - .keycode_from_movement(Movement::Left) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::Right) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::Down) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::RotateCw) - .expect(INVALID_LAST_USED_CONTROLS), - ), - Some( - ctrls - .keycode_from_movement(Movement::RotateCcw) - .expect(INVALID_LAST_USED_CONTROLS), - ), - ); - } else { - first_player_previous_controls = (None, None, None, None, None); - } - } else { - first_player_previous_controls = (None, None, None, None, None); - } let mut vec_menu_items_keycode: Vec = Vec::with_capacity(6); vec_menu_items_keycode.push(MenuItem::new( "Left: ", MenuItemValueType::KeyCode, 0, - first_player_previous_controls.0, + match &game_options.arr_controls[0].0 { + Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::Left), + None => None, + }, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyLeft, @@ -148,7 +92,10 @@ impl InputConfigMenu { "Right: ", MenuItemValueType::KeyCode, 0, - first_player_previous_controls.1, + match &game_options.arr_controls[0].0 { + Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::Right), + None => None, + }, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyRight, @@ -157,7 +104,10 @@ impl InputConfigMenu { "Down: ", MenuItemValueType::KeyCode, 0, - first_player_previous_controls.2, + match &game_options.arr_controls[0].0 { + Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::Down), + None => None, + }, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyDown, @@ -166,7 +116,10 @@ impl InputConfigMenu { "RotateCw: ", MenuItemValueType::KeyCode, 0, - first_player_previous_controls.3, + match &game_options.arr_controls[0].0 { + Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::RotateCw), + None => None, + }, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCw, @@ -175,7 +128,10 @@ impl InputConfigMenu { "RotateCcw: ", MenuItemValueType::KeyCode, 0, - first_player_previous_controls.4, + match &game_options.arr_controls[0].0 { + Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::RotateCcw), + None => None, + }, window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCcw, @@ -188,7 +144,6 @@ impl InputConfigMenu { most_recently_pressed_key: None, vec_used_keycode, keycode_conflict_flag: false, - arr_controls, // text vec_menu_items_main, // subtext @@ -211,7 +166,7 @@ impl InputConfigMenu { } } - pub fn update(&mut self, input: &Input) -> bool { + pub fn update(&mut self, input: &Input, game_options: &mut MenuGameOptions) -> bool { if !self.sub_selection_keyboard_flag { if input.keydown_right.1 { self.vec_menu_items_main[self.selection].inc_or_dec(true); @@ -243,10 +198,11 @@ impl InputConfigMenu { if input.keydown_rotate_cw.1 && self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { - self.arr_controls[self.player_num as usize].1 = true; - if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + game_options.arr_controls[self.player_num as usize].1 = true; + if let Some(k_ctrl_scheme) = &game_options.arr_controls[self.player_num as usize].0 + { self.remove_from_used_keycodes(&k_ctrl_scheme); - self.arr_controls[self.player_num as usize].0 = None; + game_options.arr_controls[self.player_num as usize].0 = None; } } @@ -259,10 +215,12 @@ impl InputConfigMenu { == MenuItemTrigger::SubSelection { self.most_recently_pressed_key = None; - if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + if let Some(k_ctrl_scheme) = + &game_options.arr_controls[self.player_num as usize].0 + { self.remove_from_used_keycodes(&k_ctrl_scheme); } - self.arr_controls[self.player_num as usize].0 = + game_options.arr_controls[self.player_num as usize].0 = Some(KeyboardControlScheme::default()); self.update_sub_text_strings(); self.sub_selection_keyboard_flag = true; @@ -275,11 +233,13 @@ impl InputConfigMenu { if self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { // remove input stuff from selection if we are on the SubSelection option - if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + if let Some(k_ctrl_scheme) = + &game_options.arr_controls[self.player_num as usize].0 + { self.remove_from_used_keycodes(&k_ctrl_scheme); - self.arr_controls[self.player_num as usize].0 = None; + game_options.arr_controls[self.player_num as usize].0 = None; } - self.arr_controls[self.player_num as usize].1 = false; + game_options.arr_controls[self.player_num as usize].1 = false; self.most_recently_pressed_key = None; } else { return true; @@ -294,12 +254,13 @@ impl InputConfigMenu { self.sub_selection_keyboard = 0; self.sub_selection_keyboard_flag = false; // the user was in the middle of creating keyboard controls when they hit 'Escape', so pop however many KeyCode's off vec_used_keycode that the user set up - if let Some(k_ctrl_scheme) = self.arr_controls[self.player_num as usize].0 { + if let Some(k_ctrl_scheme) = &game_options.arr_controls[self.player_num as usize].0 + { for _ in 0..k_ctrl_scheme.len() { self.vec_used_keycode.pop(); } } - self.arr_controls[self.player_num as usize].0 = None; + game_options.arr_controls[self.player_num as usize].0 = None; self.most_recently_pressed_key = None; } else if self .vec_used_keycode @@ -310,8 +271,8 @@ impl InputConfigMenu { } else { // no conflict, enter KeyCode of key pressed self.keycode_conflict_flag = false; - match (self.arr_controls[self.player_num as usize].0).as_mut() { - Some(mut k_ctrl_scheme) => { + match (game_options.arr_controls[self.player_num as usize].0).as_mut() { + Some(k_ctrl_scheme) => { k_ctrl_scheme.add_pair( self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE), Movement::from( @@ -355,7 +316,7 @@ impl InputConfigMenu { // TODO } - pub fn draw(&mut self, ctx: &mut Context) { + pub fn draw(&mut self, ctx: &mut Context, game_options: &MenuGameOptions) { let window_dimensions = graphics::size(ctx); for (index, item) in self.vec_menu_items_main.iter().enumerate() { @@ -413,7 +374,7 @@ impl InputConfigMenu { self.draw_text(ctx, &self.keycode_conflict_text, 0.43, &window_dimensions); } - if (self.arr_controls[self.player_num as usize].0).is_some() { + if (game_options.arr_controls[self.player_num as usize].0).is_some() { for (index, item) in self.vec_menu_items_keycode.iter().enumerate() { self.draw_text( ctx, @@ -422,7 +383,7 @@ impl InputConfigMenu { &window_dimensions, ); } - } else if self.arr_controls[self.player_num as usize].1 { + } else if game_options.arr_controls[self.player_num as usize].1 { self.draw_text(ctx, &self.is_gamepad_text, 0.63, &window_dimensions); } else { self.draw_text(ctx, &self.input_uninitialized_text, 0.5, &window_dimensions); From 37c69dc506e8c3aaa3e02bb91638e21abc1bb47a Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Wed, 21 Apr 2021 00:07:20 -0400 Subject: [PATCH 36/59] it builds; it's very broken though --- src/game.rs | 28 +++++------ src/inputs.rs | 11 +++++ src/menu.rs | 17 +++---- src/menu/inputconfig.rs | 102 +++++++++++++++------------------------- 4 files changed, 71 insertions(+), 87 deletions(-) diff --git a/src/game.rs b/src/game.rs index 4fcd866..28a9dda 100644 --- a/src/game.rs +++ b/src/game.rs @@ -76,7 +76,7 @@ pub const INITIAL_HANG_FRAMES: u8 = 180; pub const DETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.5; pub const UNDETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.2; -static INVALID_LAST_USED_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; +static INVALID_MENU_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; pub enum Modes { Classic, @@ -95,30 +95,30 @@ impl From<&MenuGameOptions> for GameOptions { let mut vec_controls: Vec<(Option, bool)> = Vec::with_capacity(menu_game_options.arr_controls.len()); let mut counted_active_controls: u8 = 0; - for controls in menu_game_options.arr_controls.iter() { - if let Some(ctrls) = &controls.0 { + for ctrls in menu_game_options.arr_controls.iter() { + if !(ctrls.0).is_empty() { vec_controls.push(( Some(KeyboardControlScheme::new_classic( - ctrls + (ctrls.0) .keycode_from_movement(Movement::Left) - .expect(INVALID_LAST_USED_CONTROLS), - ctrls + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) .keycode_from_movement(Movement::Right) - .expect(INVALID_LAST_USED_CONTROLS), - ctrls + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) .keycode_from_movement(Movement::Down) - .expect(INVALID_LAST_USED_CONTROLS), - ctrls + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) .keycode_from_movement(Movement::RotateCw) - .expect(INVALID_LAST_USED_CONTROLS), - ctrls + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) .keycode_from_movement(Movement::RotateCcw) - .expect(INVALID_LAST_USED_CONTROLS), + .expect(INVALID_MENU_CONTROLS), )), false, )); counted_active_controls += 1; - } else if controls.1 { + } else if ctrls.1 { vec_controls.push((None, true)); counted_active_controls += 1; } diff --git a/src/inputs.rs b/src/inputs.rs index 7e1654b..0617238 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -68,6 +68,7 @@ impl Input { } } +#[derive(Clone)] pub struct KeyboardControlScheme { pub vec_keycode_movement_pair: Vec<(KeyCode, Movement)>, } @@ -88,6 +89,10 @@ impl KeyboardControlScheme { self.vec_keycode_movement_pair.len() } + pub fn is_empty(&self) -> bool { + self.vec_keycode_movement_pair.is_empty() + } + pub fn clear(&mut self) { self.vec_keycode_movement_pair.clear(); } @@ -171,3 +176,9 @@ impl Default for KeyboardControlScheme { } } } + +// impl Clone for KeyboardControlScheme { +// fn clone(&self) -> KeyboardControlScheme { +// *self +// } +// } diff --git a/src/menu.rs b/src/menu.rs index 4b67d3e..e733483 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -172,15 +172,18 @@ impl MenuItem { pub struct MenuGameOptions { pub num_players: u8, pub starting_level: u8, - pub arr_controls: Box<[(Option, bool); MAX_NUM_PLAYERS as usize]>, + pub arr_controls: Vec<(KeyboardControlScheme, bool)>, } impl MenuGameOptions { pub fn new() -> Self { + let mut arr_controls: Vec<(KeyboardControlScheme, bool)> = + vec![(KeyboardControlScheme::default(), false); MAX_NUM_PLAYERS as usize]; + // arr_controls.append(&vec![); Self { num_players: 1, starting_level: 0, - arr_controls: vec![(None, false); MAX_NUM_PLAYERS as usize].into_boxed_slice(), + arr_controls, } } } @@ -261,14 +264,8 @@ impl Menu { for ctrls in game_options.arr_controls.iter() { if ctrls.1 { ctrls_count += 1; - } else { - if let Some(k_ctrl_scheme) = &ctrls.0 { - if k_ctrl_scheme.vec_keycode_movement_pair.len() - <= self.num_required_keycode_movement_pairs - { - ctrls_count += 1; - } - } + } else if (ctrls.0).len() >= self.num_required_keycode_movement_pairs { + ctrls_count += 1; } } ctrls_count >= self.start_menu.find_important_values().0 diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index de2d3e0..65ed0b3 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -46,10 +46,8 @@ impl InputConfigMenu { let arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize]; // gather what the starting used keycodes should be for (idx, ctrls) in game_options.arr_controls.iter().enumerate() { - if let Some(k_ctrl_scheme) = &ctrls.0 { - for key_move_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { - vec_used_keycode.push(key_move_pair.0); - } + for key_move_pair in (ctrls.0).vec_keycode_movement_pair.iter() { + vec_used_keycode.push(key_move_pair.0); } } // main MenuItems @@ -80,10 +78,7 @@ impl InputConfigMenu { "Left: ", MenuItemValueType::KeyCode, 0, - match &game_options.arr_controls[0].0 { - Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::Left), - None => None, - }, + (game_options.arr_controls[0].0).keycode_from_movement(Movement::Left), window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyLeft, @@ -92,10 +87,7 @@ impl InputConfigMenu { "Right: ", MenuItemValueType::KeyCode, 0, - match &game_options.arr_controls[0].0 { - Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::Right), - None => None, - }, + (game_options.arr_controls[0].0).keycode_from_movement(Movement::Right), window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyRight, @@ -104,10 +96,7 @@ impl InputConfigMenu { "Down: ", MenuItemValueType::KeyCode, 0, - match &game_options.arr_controls[0].0 { - Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::Down), - None => None, - }, + (game_options.arr_controls[0].0).keycode_from_movement(Movement::Down), window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyDown, @@ -116,10 +105,7 @@ impl InputConfigMenu { "RotateCw: ", MenuItemValueType::KeyCode, 0, - match &game_options.arr_controls[0].0 { - Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::RotateCw), - None => None, - }, + (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCw), window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCw, @@ -128,10 +114,7 @@ impl InputConfigMenu { "RotateCcw: ", MenuItemValueType::KeyCode, 0, - match &game_options.arr_controls[0].0 { - Some(k_ctrl_scheme) => k_ctrl_scheme.keycode_from_movement(Movement::RotateCcw), - None => None, - }, + (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCcw), window_dimensions.1, TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCcw, @@ -199,11 +182,11 @@ impl InputConfigMenu { && self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { game_options.arr_controls[self.player_num as usize].1 = true; - if let Some(k_ctrl_scheme) = &game_options.arr_controls[self.player_num as usize].0 - { - self.remove_from_used_keycodes(&k_ctrl_scheme); - game_options.arr_controls[self.player_num as usize].0 = None; - } + self.remove_from_used_keycodes( + &game_options.arr_controls[self.player_num as usize].0, + ); + game_options.arr_controls[self.player_num as usize].0 = + KeyboardControlScheme::default(); } // 'Space' or 'Return' was pressed @@ -215,13 +198,11 @@ impl InputConfigMenu { == MenuItemTrigger::SubSelection { self.most_recently_pressed_key = None; - if let Some(k_ctrl_scheme) = - &game_options.arr_controls[self.player_num as usize].0 - { - self.remove_from_used_keycodes(&k_ctrl_scheme); - } + self.remove_from_used_keycodes( + &game_options.arr_controls[self.player_num as usize].0, + ); game_options.arr_controls[self.player_num as usize].0 = - Some(KeyboardControlScheme::default()); + KeyboardControlScheme::default(); self.update_sub_text_strings(); self.sub_selection_keyboard_flag = true; self.vec_menu_items_main[self.selection].set_select(true); @@ -233,12 +214,11 @@ impl InputConfigMenu { if self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { // remove input stuff from selection if we are on the SubSelection option - if let Some(k_ctrl_scheme) = - &game_options.arr_controls[self.player_num as usize].0 - { - self.remove_from_used_keycodes(&k_ctrl_scheme); - game_options.arr_controls[self.player_num as usize].0 = None; - } + self.remove_from_used_keycodes( + &game_options.arr_controls[self.player_num as usize].0, + ); + game_options.arr_controls[self.player_num as usize].0 = + KeyboardControlScheme::default(); game_options.arr_controls[self.player_num as usize].1 = false; self.most_recently_pressed_key = None; } else { @@ -254,13 +234,11 @@ impl InputConfigMenu { self.sub_selection_keyboard = 0; self.sub_selection_keyboard_flag = false; // the user was in the middle of creating keyboard controls when they hit 'Escape', so pop however many KeyCode's off vec_used_keycode that the user set up - if let Some(k_ctrl_scheme) = &game_options.arr_controls[self.player_num as usize].0 - { - for _ in 0..k_ctrl_scheme.len() { - self.vec_used_keycode.pop(); - } + for _ in 0..(game_options.arr_controls[self.player_num as usize].0).len() { + self.vec_used_keycode.pop(); } - game_options.arr_controls[self.player_num as usize].0 = None; + game_options.arr_controls[self.player_num as usize].0 = + KeyboardControlScheme::default(); self.most_recently_pressed_key = None; } else if self .vec_used_keycode @@ -271,18 +249,14 @@ impl InputConfigMenu { } else { // no conflict, enter KeyCode of key pressed self.keycode_conflict_flag = false; - match (game_options.arr_controls[self.player_num as usize].0).as_mut() { - Some(k_ctrl_scheme) => { - k_ctrl_scheme.add_pair( - self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE), - Movement::from( - self.vec_menu_items_keycode[self.sub_selection_keyboard].trigger, - ), - ); - self.vec_used_keycode - .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); - } - } + (game_options.arr_controls[self.player_num as usize].0).add_pair( + self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE), + Movement::from( + self.vec_menu_items_keycode[self.sub_selection_keyboard].trigger, + ), + ); + self.vec_used_keycode + .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(false); if self.sub_selection_keyboard < self.vec_menu_items_keycode.len() - 1 { self.sub_selection_keyboard += 1; @@ -297,15 +271,17 @@ impl InputConfigMenu { } fn remove_from_used_keycodes(&mut self, k_ctrl_scheme: &KeyboardControlScheme) { - let mut items_removed = 0; - for used_key_idx in 0..self.vec_used_keycode.len() { - for k_m_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { + for k_m_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { + let mut items_removed = 0; + for used_key_idx in 0..self.vec_used_keycode.len() { if k_m_pair.0 == self.vec_used_keycode[used_key_idx - items_removed] { self.vec_used_keycode.remove(used_key_idx - items_removed); items_removed += 1; // we only need to get rid of k_ctrl_scheme.len() if items_removed >= k_ctrl_scheme.len() { return; + } else { + break; } } } @@ -374,7 +350,7 @@ impl InputConfigMenu { self.draw_text(ctx, &self.keycode_conflict_text, 0.43, &window_dimensions); } - if (game_options.arr_controls[self.player_num as usize].0).is_some() { + if !(game_options.arr_controls[self.player_num as usize].0).is_empty() { for (index, item) in self.vec_menu_items_keycode.iter().enumerate() { self.draw_text( ctx, From 1aede1f4fb868455676087e19b13abf5544d76c3 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Wed, 12 May 2021 23:06:35 -0400 Subject: [PATCH 37/59] fixed it back to the way it was before but with better backend --- src/game.rs | 33 +---------------- src/inputs.rs | 38 ------------------- src/menu.rs | 28 ++++++++++---- src/menu/inputconfig.rs | 82 ++++++++++++++++++++++++++++++----------- src/menu/start.rs | 28 ++++++++------ 5 files changed, 98 insertions(+), 111 deletions(-) diff --git a/src/game.rs b/src/game.rs index 28a9dda..eed77a9 100644 --- a/src/game.rs +++ b/src/game.rs @@ -189,7 +189,7 @@ impl Game { + board_width as f32 / (2.0 * game_options.num_players as f32)) as u8 + 1 - } else if player == game_options.num_players && game_options.num_players % 2 == 1 { + } else if player == game_options.num_players / 2 && game_options.num_players % 2 == 1 { // middle player, for an odd number of players board_width / 2 } else { @@ -209,36 +209,6 @@ impl Game { vec_players.push(Player::new(player, control_scheme, spawn_column)); } - // for player in 0..(game_options.num_players) / 2 { - // vec_players.push(Player::new( - // player, - // game_options.vec_controls[player as usize], - // (player as f32 * (board_width as f32 / game_options.num_players as f32) - // + board_width as f32 / (2.0 * game_options.num_players as f32)) - // as u8 - // + 1, - // )); - // } - // if game_options.num_players % 2 == 1 { - // let player = game_options.num_players / 2; - // vec_players.push(Player::new( - // player, - // game_options.vec_controls[player as usize], - // board_width / 2, - // )); - // } - // for player in (game_options.num_players + 1) / 2..game_options.num_players { - // vec_players.push(Player::new( - // player, - // game_options.vec_controls[player as usize], - // board_width - // - 1 - // - ((game_options.num_players - 1 - player) as f32 - // * (board_width as f32 / game_options.num_players as f32) - // + board_width as f32 / (2.0 * game_options.num_players as f32)) - // as u8, - // )); - // } let mut batch_empty_tile = spritebatch::SpriteBatch::new(TileGraphic::new_empty(ctx).image); // the emtpy tile batch will be constant once the game starts with the player tile batches drawing on top of it, so just set that up here for x in 0..board_width { @@ -686,7 +656,6 @@ impl Game { self.keycode_escape_flags = (true, true); return; } else if keycode == KeyCode::Down { - println!("set down to (true, true)"); self.keycode_down_flags = (true, true); return; } diff --git a/src/inputs.rs b/src/inputs.rs index 0617238..1a86b4f 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -93,10 +93,6 @@ impl KeyboardControlScheme { self.vec_keycode_movement_pair.is_empty() } - pub fn clear(&mut self) { - self.vec_keycode_movement_pair.clear(); - } - pub fn new_classic( left: KeyCode, right: KeyCode, @@ -115,16 +111,6 @@ impl KeyboardControlScheme { } } - pub fn index_from_movement(&self, m: Movement) -> Option { - for (index, pair) in self.vec_keycode_movement_pair.iter().enumerate() { - if pair.1 == m { - return Some(index); - } - } - - None - } - pub fn keycode_from_movement(&self, m: Movement) -> Option { for pair in self.vec_keycode_movement_pair.iter() { if pair.1 == m { @@ -148,24 +134,6 @@ impl KeyboardControlScheme { pub fn add_pair(&mut self, k: KeyCode, m: Movement) { self.vec_keycode_movement_pair.push((k, m)); } - - // pub fn split( - // &self, - // ) -> ( - // Option, - // Option, - // Option, - // Option, - // Option, - // ) { - // ( - // Some(self.left), - // Some(self.right), - // Some(self.down), - // Some(self.rotate_cw), - // Some(self.rotate_ccw), - // ) - // } } impl Default for KeyboardControlScheme { @@ -176,9 +144,3 @@ impl Default for KeyboardControlScheme { } } } - -// impl Clone for KeyboardControlScheme { -// fn clone(&self) -> KeyboardControlScheme { -// *self -// } -// } diff --git a/src/menu.rs b/src/menu.rs index e733483..811f8ac 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -4,6 +4,7 @@ use ggez::Context; use crate::control::ProgramState; use crate::inputs::{Input, KeyboardControlScheme}; +use crate::movement::Movement; pub const MAX_STARTING_LEVEL: u8 = 29; // this is just the fastest speed, so yeah pub const MAX_NUM_PLAYERS: u8 = 62; // currently held back by board width being a u8 equal to 6 + 4 * num_players @@ -52,6 +53,7 @@ pub struct MenuItem { max_value: u8, pub value: u8, pub keycode: Option, + pub movement: Movement, value_show_increase: u8, text_scale_down: f32, pub trigger: MenuItemTrigger, @@ -94,7 +96,12 @@ impl MenuItem { ); } MenuItemValueType::KeyCode => { - text.add(TextFragment::new(format!("{:?}", keycode)).color(graphics::BLACK)); + match keycode { + Some(key) => { + text.add(TextFragment::new(format!("{:?}", key)).color(graphics::BLACK)) + } + None => text.add(TextFragment::new("None".to_string()).color(graphics::BLACK)), + }; } } text.set_font( @@ -107,6 +114,7 @@ impl MenuItem { max_value, value, keycode, + movement: Movement::None, value_show_increase, text_scale_down, trigger, @@ -158,7 +166,14 @@ impl MenuItem { pub fn set_keycode(&mut self, keycode: Option) { self.keycode = keycode; - self.text.fragments_mut()[1].text = format!("{:?}", keycode); + match self.keycode { + Some(key) => self.text.fragments_mut()[1].text = format!("{:?}", key), + None => self.text.fragments_mut()[1].text = "None".to_string(), + }; + } + + pub fn set_movement(&mut self, movement: Movement) { + self.movement = movement; } pub fn resize(&mut self, window_height: f32) { @@ -177,9 +192,8 @@ pub struct MenuGameOptions { impl MenuGameOptions { pub fn new() -> Self { - let mut arr_controls: Vec<(KeyboardControlScheme, bool)> = + let arr_controls: Vec<(KeyboardControlScheme, bool)> = vec![(KeyboardControlScheme::default(), false); MAX_NUM_PLAYERS as usize]; - // arr_controls.append(&vec![); Self { num_players: 1, starting_level: 0, @@ -225,12 +239,10 @@ impl Menu { pub fn update(&mut self, game_options: &mut MenuGameOptions) -> Option { match self.state { MenuState::Start => { - let trigger: MenuItemTrigger = self.start_menu.update(&self.input); + let trigger: MenuItemTrigger = self.start_menu.update(&self.input, game_options); match trigger { MenuItemTrigger::StartGame => { if self.ensure_enough_controls(game_options) { - let (num_players, starting_level) = - self.start_menu.find_important_values(); return Some(ProgramState::Game); } else { self.start_menu.not_enough_controls_flag = true; @@ -268,7 +280,7 @@ impl Menu { ctrls_count += 1; } } - ctrls_count >= self.start_menu.find_important_values().0 + ctrls_count >= game_options.num_players } pub fn key_down_event(&mut self, keycode: KeyCode, _repeat: bool) { diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 65ed0b3..5efb328 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -9,8 +9,6 @@ use crate::movement::Movement; use crate::menu::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; -use crate::menu::MAX_NUM_PLAYERS; - use crate::menu::SUB_TEXT_SCALE_DOWN; use crate::menu::TEXT_SCALE_DOWN; @@ -18,7 +16,6 @@ use crate::menu::DARK_GRAY; use crate::menu::HELP_RED; use crate::menu::LIGHT_GRAY; -static INVALID_LAST_USED_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; static KEY_UNEXPECTEDLY_NONE: &str = "[!] KeyCode of most recently pressed key is unexpectedly None"; @@ -43,9 +40,8 @@ pub struct InputConfigMenu { impl InputConfigMenu { pub fn new(window_dimensions: (f32, f32), game_options: &MenuGameOptions) -> Self { let mut vec_used_keycode: Vec = vec![]; - let arr_controls: [(Option, bool); MAX_NUM_PLAYERS as usize]; // gather what the starting used keycodes should be - for (idx, ctrls) in game_options.arr_controls.iter().enumerate() { + for ctrls in game_options.arr_controls.iter() { for key_move_pair in (ctrls.0).vec_keycode_movement_pair.iter() { vec_used_keycode.push(key_move_pair.0); } @@ -74,51 +70,61 @@ impl InputConfigMenu { // keycode MenuItems let mut vec_menu_items_keycode: Vec = Vec::with_capacity(6); + let mut items_pushed: usize = 0; vec_menu_items_keycode.push(MenuItem::new( "Left: ", MenuItemValueType::KeyCode, 0, (game_options.arr_controls[0].0).keycode_from_movement(Movement::Left), window_dimensions.1, - TEXT_SCALE_DOWN, + SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyLeft, )); + vec_menu_items_keycode[items_pushed].set_movement(Movement::Left); + items_pushed += 1; vec_menu_items_keycode.push(MenuItem::new( "Right: ", MenuItemValueType::KeyCode, 0, (game_options.arr_controls[0].0).keycode_from_movement(Movement::Right), window_dimensions.1, - TEXT_SCALE_DOWN, + SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyRight, )); + vec_menu_items_keycode[items_pushed].set_movement(Movement::Right); + items_pushed += 1; vec_menu_items_keycode.push(MenuItem::new( "Down: ", MenuItemValueType::KeyCode, 0, (game_options.arr_controls[0].0).keycode_from_movement(Movement::Down), window_dimensions.1, - TEXT_SCALE_DOWN, + SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyDown, )); + vec_menu_items_keycode[items_pushed].set_movement(Movement::Down); + items_pushed += 1; vec_menu_items_keycode.push(MenuItem::new( "RotateCw: ", MenuItemValueType::KeyCode, 0, (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCw), window_dimensions.1, - TEXT_SCALE_DOWN, + SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCw, )); + vec_menu_items_keycode[items_pushed].set_movement(Movement::RotateCw); + items_pushed += 1; vec_menu_items_keycode.push(MenuItem::new( "RotateCcw: ", MenuItemValueType::KeyCode, 0, (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCcw), window_dimensions.1, - TEXT_SCALE_DOWN, + SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCcw, )); + vec_menu_items_keycode[items_pushed].set_movement(Movement::RotateCcw); Self { selection: 0, player_num: 0, @@ -151,12 +157,18 @@ impl InputConfigMenu { pub fn update(&mut self, input: &Input, game_options: &mut MenuGameOptions) -> bool { if !self.sub_selection_keyboard_flag { + // NOT the input box + if input.keydown_right.1 { self.vec_menu_items_main[self.selection].inc_or_dec(true); + self.player_num = self.get_player_num(); + self.update_all_sub_text_strings(game_options); } if input.keydown_left.1 { self.vec_menu_items_main[self.selection].inc_or_dec(false); + self.player_num = self.get_player_num(); + self.update_all_sub_text_strings(game_options); } if input.keydown_down.1 { @@ -203,9 +215,9 @@ impl InputConfigMenu { ); game_options.arr_controls[self.player_num as usize].0 = KeyboardControlScheme::default(); - self.update_sub_text_strings(); + self.update_all_sub_text_strings(game_options); self.sub_selection_keyboard_flag = true; - self.vec_menu_items_main[self.selection].set_select(true); + self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(true); } } @@ -226,9 +238,12 @@ impl InputConfigMenu { } } } else if self.sub_selection_keyboard_flag && self.most_recently_pressed_key.is_some() { + // input box with some key press + // first check if the KeyCode is 'Escape', and if it is, just delete the layout entry and go out of the subselection section // second check if the KeyCode was already used. If it was, set the error message flag to true if input.keydown_rotate_ccw.1 { + // escape self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(false); self.keycode_conflict_flag = false; self.sub_selection_keyboard = 0; @@ -239,7 +254,6 @@ impl InputConfigMenu { } game_options.arr_controls[self.player_num as usize].0 = KeyboardControlScheme::default(); - self.most_recently_pressed_key = None; } else if self .vec_used_keycode .contains(&self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)) @@ -249,14 +263,15 @@ impl InputConfigMenu { } else { // no conflict, enter KeyCode of key pressed self.keycode_conflict_flag = false; + let key: KeyCode = self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE); (game_options.arr_controls[self.player_num as usize].0).add_pair( - self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE), + key, Movement::from( self.vec_menu_items_keycode[self.sub_selection_keyboard].trigger, ), ); - self.vec_used_keycode - .push(self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE)); + self.vec_menu_items_keycode[self.sub_selection_keyboard].set_keycode(Some(key)); + self.vec_used_keycode.push(key); self.vec_menu_items_keycode[self.sub_selection_keyboard].set_select(false); if self.sub_selection_keyboard < self.vec_menu_items_keycode.len() - 1 { self.sub_selection_keyboard += 1; @@ -266,10 +281,20 @@ impl InputConfigMenu { self.sub_selection_keyboard_flag = false; } } + self.most_recently_pressed_key = None; } false } + fn get_player_num(&self) -> u8 { + for item in self.vec_menu_items_main.iter() { + if item.value_type == MenuItemValueType::PlayerNum { + return item.value; + } + } + 0u8 + } + fn remove_from_used_keycodes(&mut self, k_ctrl_scheme: &KeyboardControlScheme) { for k_m_pair in k_ctrl_scheme.vec_keycode_movement_pair.iter() { let mut items_removed = 0; @@ -288,13 +313,26 @@ impl InputConfigMenu { } } - fn update_sub_text_strings(&mut self) { - // TODO + fn update_all_sub_text_strings(&mut self, game_options: &MenuGameOptions) { + for item in self.vec_menu_items_keycode.iter_mut() { + item.set_keycode(None); + for kmp in (game_options.arr_controls[self.player_num as usize].0) + .vec_keycode_movement_pair + .iter() + { + if kmp.1 == item.movement { + item.set_keycode(Some(kmp.0)); + break; + } + } + } } pub fn draw(&mut self, ctx: &mut Context, game_options: &MenuGameOptions) { let window_dimensions = graphics::size(ctx); + // always drawn stuff + for (index, item) in self.vec_menu_items_main.iter().enumerate() { self.draw_text( ctx, @@ -303,8 +341,6 @@ impl InputConfigMenu { &window_dimensions, ); } - // self.draw_text(ctx, &self.back_text, 0.1, &window_dimensions); - // self.draw_text(ctx, &self.player_num_text, 0.3, &window_dimensions); // display nothing special on InputConfigMenuOption::Back, so just draw the extra stuff when it's not on InputConfigMenuOption::Back // and then later determine which of the other InputConfigMenuOption's it is for the specifics @@ -345,12 +381,14 @@ impl InputConfigMenu { } graphics::draw(ctx, &editing_indicator_rectangle, (Point2::new(0.0, 0.0),)).unwrap(); - if self.vec_menu_items_main[self.selection].trigger != MenuItemTrigger::SubSelection { + if self.vec_menu_items_main[self.selection].trigger == MenuItemTrigger::SubSelection { if self.keycode_conflict_flag { self.draw_text(ctx, &self.keycode_conflict_text, 0.43, &window_dimensions); } - if !(game_options.arr_controls[self.player_num as usize].0).is_empty() { + if self.sub_selection_keyboard_flag + || !(game_options.arr_controls[self.player_num as usize].0).is_empty() + { for (index, item) in self.vec_menu_items_keycode.iter().enumerate() { self.draw_text( ctx, diff --git a/src/menu/start.rs b/src/menu/start.rs index 82d38c5..70d32d4 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -4,7 +4,7 @@ use ggez::nalgebra::Point2; use ggez::Context; use crate::inputs::Input; -use crate::menu::{MenuItem, MenuItemTrigger, MenuItemValueType}; +use crate::menu::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; use crate::menu::TEXT_SCALE_DOWN; @@ -71,7 +71,7 @@ impl StartMenu { } } - pub fn update(&mut self, input: &Input) -> MenuItemTrigger { + pub fn update(&mut self, input: &Input, game_options: &mut MenuGameOptions) -> MenuItemTrigger { if input.keydown_right.1 { self.vec_menu_items[self.selection].inc_or_dec(true); } @@ -80,6 +80,9 @@ impl StartMenu { self.vec_menu_items[self.selection].inc_or_dec(false); } + game_options.num_players = self.get_num_players() + 1; + game_options.starting_level = self.get_starting_level(); + if input.keydown_down.1 { self.vec_menu_items[self.selection].set_select(false); self.selection = (self.selection + 1) % self.vec_menu_items.len(); @@ -103,19 +106,22 @@ impl StartMenu { MenuItemTrigger::None } - // searches through self.vec_menu_items and gets the important values; returns (num_players, starting_level) - pub fn find_important_values(&self) -> (u8, u8) { - let mut num_players = 0; - let mut starting_level = 0; + fn get_num_players(&self) -> u8 { for item in self.vec_menu_items.iter() { - match item.value_type { - MenuItemValueType::NumPlayers => num_players = item.value + 1, - MenuItemValueType::StartingLevel => starting_level = item.value, - _ => {} + if item.value_type == MenuItemValueType::NumPlayers { + return item.value; } } + 0u8 + } - (num_players, starting_level) + fn get_starting_level(&self) -> u8 { + for item in self.vec_menu_items.iter() { + if item.value_type == MenuItemValueType::StartingLevel { + return item.value; + } + } + 0u8 } pub fn draw(&mut self, ctx: &mut Context) { From c97aedaace107bbcb02fe15bfdce62affad73ee6 Mon Sep 17 00:00:00 2001 From: "Brian Billman (Ubuntu20 Desktop)" Date: Thu, 13 May 2021 00:10:30 -0400 Subject: [PATCH 38/59] the nice thing about a fully dynamic game engine... --- src/menu.rs | 10 +-- src/menu/inputconfig.rs | 148 ++++++++++++++++++++++++++++------------ src/movement.rs | 2 + 3 files changed, 108 insertions(+), 52 deletions(-) diff --git a/src/menu.rs b/src/menu.rs index 811f8ac..97b0505 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -4,7 +4,7 @@ use ggez::Context; use crate::control::ProgramState; use crate::inputs::{Input, KeyboardControlScheme}; -use crate::movement::Movement; +// use crate::movement::Movement; pub const MAX_STARTING_LEVEL: u8 = 29; // this is just the fastest speed, so yeah pub const MAX_NUM_PLAYERS: u8 = 62; // currently held back by board width being a u8 equal to 6 + 4 * num_players @@ -45,6 +45,8 @@ pub enum MenuItemTrigger { KeyDown, KeyRotateCw, KeyRotateCcw, + KeyBoardCw, + KeyBoardCcw, } pub struct MenuItem { @@ -53,7 +55,6 @@ pub struct MenuItem { max_value: u8, pub value: u8, pub keycode: Option, - pub movement: Movement, value_show_increase: u8, text_scale_down: f32, pub trigger: MenuItemTrigger, @@ -114,7 +115,6 @@ impl MenuItem { max_value, value, keycode, - movement: Movement::None, value_show_increase, text_scale_down, trigger, @@ -172,10 +172,6 @@ impl MenuItem { }; } - pub fn set_movement(&mut self, movement: Movement) { - self.movement = movement; - } - pub fn resize(&mut self, window_height: f32) { self.text.set_font( Font::default(), diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 5efb328..4647125 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -16,6 +16,8 @@ use crate::menu::DARK_GRAY; use crate::menu::HELP_RED; use crate::menu::LIGHT_GRAY; +const MAX_NON_START_INPUTS_PER_PLAYER: usize = 8; + static KEY_UNEXPECTEDLY_NONE: &str = "[!] KeyCode of most recently pressed key is unexpectedly None"; @@ -69,9 +71,69 @@ impl InputConfigMenu { vec_menu_items_main[0].set_select(true); // keycode MenuItems - let mut vec_menu_items_keycode: Vec = Vec::with_capacity(6); - let mut items_pushed: usize = 0; - vec_menu_items_keycode.push(MenuItem::new( + let mut vec_menu_items_keycode: Vec = + Vec::with_capacity(MAX_NON_START_INPUTS_PER_PLAYER); + Self::setup_classic_mode_subtext( + &mut vec_menu_items_keycode, + game_options, + window_dimensions, + ); + // Self::setup_rotatris_mode_subtext(&mut vec_menu_items_keycode, game_options, window_dimensions); + Self { + selection: 0, + player_num: 0, + sub_selection_keyboard: 0, + sub_selection_keyboard_flag: false, + most_recently_pressed_key: None, + vec_used_keycode, + keycode_conflict_flag: false, + // text + vec_menu_items_main, + // subtext + vec_menu_items_keycode, + input_uninitialized_text: Text::new( + TextFragment::new("No Controls\nKeyboard: Space/Enter\nGamepad: 'G'") + .color(HELP_RED) + .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + ), + keycode_conflict_text: Text::new( + TextFragment::new("[!] Redundant KeyCode; ignoring") + .color(HELP_RED) + .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + ), + is_gamepad_text: Text::new( + TextFragment::new("Set to Gamepad\n\n\nSee README for help") + .color(graphics::BLACK) + .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), + ), + } + } + + fn setup_classic_mode_subtext( + vec_to_add_to: &mut Vec, + game_options: &MenuGameOptions, + window_dimensions: (f32, f32), + ) { + Self::setup_left_right_down_subtext(vec_to_add_to, game_options, window_dimensions); + Self::setup_rotate_piece_subtext(vec_to_add_to, game_options, window_dimensions); + } + + fn setup_rotatris_mode_subtext( + vec_to_add_to: &mut Vec, + game_options: &MenuGameOptions, + window_dimensions: (f32, f32), + ) { + Self::setup_left_right_down_subtext(vec_to_add_to, game_options, window_dimensions); + Self::setup_rotate_piece_subtext(vec_to_add_to, game_options, window_dimensions); + Self::setup_rotate_board_subtext(vec_to_add_to, game_options, window_dimensions); + } + + fn setup_left_right_down_subtext( + vec_to_add_to: &mut Vec, + game_options: &MenuGameOptions, + window_dimensions: (f32, f32), + ) { + vec_to_add_to.push(MenuItem::new( "Left: ", MenuItemValueType::KeyCode, 0, @@ -80,9 +142,7 @@ impl InputConfigMenu { SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyLeft, )); - vec_menu_items_keycode[items_pushed].set_movement(Movement::Left); - items_pushed += 1; - vec_menu_items_keycode.push(MenuItem::new( + vec_to_add_to.push(MenuItem::new( "Right: ", MenuItemValueType::KeyCode, 0, @@ -91,9 +151,7 @@ impl InputConfigMenu { SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyRight, )); - vec_menu_items_keycode[items_pushed].set_movement(Movement::Right); - items_pushed += 1; - vec_menu_items_keycode.push(MenuItem::new( + vec_to_add_to.push(MenuItem::new( "Down: ", MenuItemValueType::KeyCode, 0, @@ -102,9 +160,14 @@ impl InputConfigMenu { SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyDown, )); - vec_menu_items_keycode[items_pushed].set_movement(Movement::Down); - items_pushed += 1; - vec_menu_items_keycode.push(MenuItem::new( + } + + fn setup_rotate_piece_subtext( + vec_to_add_to: &mut Vec, + game_options: &MenuGameOptions, + window_dimensions: (f32, f32), + ) { + vec_to_add_to.push(MenuItem::new( "RotateCw: ", MenuItemValueType::KeyCode, 0, @@ -113,9 +176,7 @@ impl InputConfigMenu { SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCw, )); - vec_menu_items_keycode[items_pushed].set_movement(Movement::RotateCw); - items_pushed += 1; - vec_menu_items_keycode.push(MenuItem::new( + vec_to_add_to.push(MenuItem::new( "RotateCcw: ", MenuItemValueType::KeyCode, 0, @@ -124,35 +185,31 @@ impl InputConfigMenu { SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyRotateCcw, )); - vec_menu_items_keycode[items_pushed].set_movement(Movement::RotateCcw); - Self { - selection: 0, - player_num: 0, - sub_selection_keyboard: 0, - sub_selection_keyboard_flag: false, - most_recently_pressed_key: None, - vec_used_keycode, - keycode_conflict_flag: false, - // text - vec_menu_items_main, - // subtext - vec_menu_items_keycode, - input_uninitialized_text: Text::new( - TextFragment::new("No Controls\nKeyboard: Space/Enter\nGamepad: 'G'") - .color(HELP_RED) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ), - keycode_conflict_text: Text::new( - TextFragment::new("[!] Redundant KeyCode; ignoring") - .color(HELP_RED) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ), - is_gamepad_text: Text::new( - TextFragment::new("Set to Gamepad\n\n\nSee README for help") - .color(graphics::BLACK) - .scale(Scale::uniform(window_dimensions.1 / SUB_TEXT_SCALE_DOWN)), - ), - } + } + + fn setup_rotate_board_subtext( + vec_to_add_to: &mut Vec, + game_options: &MenuGameOptions, + window_dimensions: (f32, f32), + ) { + vec_to_add_to.push(MenuItem::new( + "BoardCw: ", + MenuItemValueType::KeyCode, + 0, + (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCw), + window_dimensions.1, + SUB_TEXT_SCALE_DOWN, + MenuItemTrigger::KeyBoardCw, + )); + vec_to_add_to.push(MenuItem::new( + "BoardCcw: ", + MenuItemValueType::KeyCode, + 0, + (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCcw), + window_dimensions.1, + SUB_TEXT_SCALE_DOWN, + MenuItemTrigger::KeyBoardCcw, + )); } pub fn update(&mut self, input: &Input, game_options: &mut MenuGameOptions) -> bool { @@ -315,12 +372,13 @@ impl InputConfigMenu { fn update_all_sub_text_strings(&mut self, game_options: &MenuGameOptions) { for item in self.vec_menu_items_keycode.iter_mut() { + let desired_movement = Movement::from(item.trigger); item.set_keycode(None); for kmp in (game_options.arr_controls[self.player_num as usize].0) .vec_keycode_movement_pair .iter() { - if kmp.1 == item.movement { + if kmp.1 == desired_movement { item.set_keycode(Some(kmp.0)); break; } diff --git a/src/movement.rs b/src/movement.rs index 8608661..c9f9558 100644 --- a/src/movement.rs +++ b/src/movement.rs @@ -37,6 +37,8 @@ impl From for Movement { MenuItemTrigger::KeyDown => Movement::Down, MenuItemTrigger::KeyRotateCw => Movement::RotateCw, MenuItemTrigger::KeyRotateCcw => Movement::RotateCcw, + MenuItemTrigger::KeyBoardCw => Movement::None, + MenuItemTrigger::KeyBoardCcw => Movement::None, _ => panic!( "[!] Unexpected value converting MenuItemTrigger to Movement: {:?}", value From 0dee84875122c1296e15530f9534c8b67303eb8d Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 28 May 2021 00:42:42 -0400 Subject: [PATCH 39/59] create menuhelper.rs for all the useful menu things --- src/control.rs | 2 +- src/game.rs | 2 +- src/menu.rs | 195 ++-------------------------------------- src/menu/inputconfig.rs | 11 +-- src/menu/menuhelpers.rs | 191 +++++++++++++++++++++++++++++++++++++++ src/menu/start.rs | 8 +- src/movement.rs | 2 +- 7 files changed, 205 insertions(+), 206 deletions(-) create mode 100644 src/menu/menuhelpers.rs diff --git a/src/control.rs b/src/control.rs index 57f700e..afbde55 100644 --- a/src/control.rs +++ b/src/control.rs @@ -5,7 +5,7 @@ use ggez::timer; use ggez::{Context, GameResult}; use crate::game::{Game, GameOptions}; -use crate::menu::{Menu, MenuGameOptions}; +use crate::menu::{menuhelpers::MenuGameOptions, Menu}; static STATE_MENU_BUT_MENU_NONE: &str = "[!] control.state == ProgramState::Menu but control.menu == None"; diff --git a/src/game.rs b/src/game.rs index eed77a9..0c5f8a6 100644 --- a/src/game.rs +++ b/src/game.rs @@ -23,7 +23,7 @@ mod board; use crate::game::board::BoardHandler; use crate::inputs::KeyboardControlScheme; -use crate::menu::MenuGameOptions; +use crate::menu::menuhelpers::MenuGameOptions; const BOARD_HEIGHT: u8 = 20u8; diff --git a/src/menu.rs b/src/menu.rs index 97b0505..b6b6186 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,203 +1,18 @@ use ggez::event::KeyCode; -use ggez::graphics::{self, Color, Font, Scale, Text, TextFragment}; +use ggez::graphics; use ggez::Context; use crate::control::ProgramState; -use crate::inputs::{Input, KeyboardControlScheme}; -// use crate::movement::Movement; - -pub const MAX_STARTING_LEVEL: u8 = 29; // this is just the fastest speed, so yeah -pub const MAX_NUM_PLAYERS: u8 = 62; // currently held back by board width being a u8 equal to 6 + 4 * num_players - -pub const TEXT_SCALE_DOWN: f32 = 15.0; -pub const SUB_TEXT_SCALE_DOWN: f32 = 25.0; - -const GRAY: Color = Color::new(0.4, 0.4, 0.4, 1.0); -pub const DARK_GRAY: Color = Color::new(0.3, 0.3, 0.3, 1.0); -pub const LIGHT_GRAY: Color = Color::new(0.6, 0.6, 0.6, 1.0); -pub const SELECT_GREEN: Color = Color::new(0.153, 0.839, 0.075, 1.0); -pub const HELP_RED: Color = Color::new(0.9, 0.11, 0.11, 1.0); +use crate::inputs::Input; mod inputconfig; +pub mod menuhelpers; mod start; use inputconfig::InputConfigMenu; +use menuhelpers::GRAY; +use menuhelpers::{MenuGameOptions, MenuItemTrigger}; use start::StartMenu; -#[derive(Eq, PartialEq)] -pub enum MenuItemValueType { - None, - NumPlayers, - StartingLevel, - PlayerNum, - KeyCode, -} - -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum MenuItemTrigger { - None, - StartGame, - SubMenu1, - SubMenu2, - Back, - SubSelection, - KeyLeft, - KeyRight, - KeyDown, - KeyRotateCw, - KeyRotateCcw, - KeyBoardCw, - KeyBoardCcw, -} - -pub struct MenuItem { - pub text: Text, - pub value_type: MenuItemValueType, - max_value: u8, - pub value: u8, - pub keycode: Option, - value_show_increase: u8, - text_scale_down: f32, - pub trigger: MenuItemTrigger, - selected: bool, -} - -impl MenuItem { - pub fn new( - item_start_str: &str, - value_type: MenuItemValueType, - value: u8, - keycode: Option, - window_height: f32, - text_scale_down: f32, - trigger: MenuItemTrigger, - ) -> Self { - let mut text = Text::new(TextFragment::new(item_start_str).color(graphics::BLACK)); - let mut max_value = 0; - let mut value_show_increase = 0; - match value_type { - MenuItemValueType::None => {} - MenuItemValueType::NumPlayers => { - max_value = MAX_NUM_PLAYERS; - value_show_increase = 1; - text.add( - TextFragment::new(format!(" {}", value + value_show_increase)) - .color(graphics::BLACK), - ); - } - MenuItemValueType::StartingLevel => { - max_value = MAX_STARTING_LEVEL; - text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); - } - MenuItemValueType::PlayerNum => { - max_value = MAX_NUM_PLAYERS; - value_show_increase = 1; - text.add( - TextFragment::new(format!(" {}", value + value_show_increase)) - .color(graphics::BLACK), - ); - } - MenuItemValueType::KeyCode => { - match keycode { - Some(key) => { - text.add(TextFragment::new(format!("{:?}", key)).color(graphics::BLACK)) - } - None => text.add(TextFragment::new("None".to_string()).color(graphics::BLACK)), - }; - } - } - text.set_font( - Font::default(), - Scale::uniform(window_height / text_scale_down), - ); - Self { - text, - value_type, - max_value, - value, - keycode, - value_show_increase, - text_scale_down, - trigger, - selected: false, - } - } - - pub fn set_select(&mut self, select: bool) { - self.selected = select; - self.text.fragments_mut()[0].color = Some(if select { - SELECT_GREEN - } else { - graphics::BLACK - }); - if self.value_type != MenuItemValueType::None { - self.text.fragments_mut()[1].color = Some(if select { - SELECT_GREEN - } else { - graphics::BLACK - }); - if self.value_type == MenuItemValueType::NumPlayers - || self.value_type == MenuItemValueType::StartingLevel - || self.value_type == MenuItemValueType::PlayerNum - { - self.text.fragments_mut()[1].text = if select { - format!("<{}>", self.value + self.value_show_increase) - } else { - format!(" {}", self.value + self.value_show_increase) - }; - } - } - } - - pub fn inc_or_dec(&mut self, inc: bool) { - if self.value_type == MenuItemValueType::NumPlayers - || self.value_type == MenuItemValueType::StartingLevel - || self.value_type == MenuItemValueType::PlayerNum - { - self.value = if inc { - (self.value + 1) % self.max_value - } else { - (self.value - 1 + self.max_value) % self.max_value - }; - // assume it's selected because it's being incremented/decremented - self.text.fragments_mut()[1].text = - format!("<{}>", self.value + self.value_show_increase); - } - } - - pub fn set_keycode(&mut self, keycode: Option) { - self.keycode = keycode; - match self.keycode { - Some(key) => self.text.fragments_mut()[1].text = format!("{:?}", key), - None => self.text.fragments_mut()[1].text = "None".to_string(), - }; - } - - pub fn resize(&mut self, window_height: f32) { - self.text.set_font( - Font::default(), - Scale::uniform(window_height / self.text_scale_down), - ); - } -} - -pub struct MenuGameOptions { - pub num_players: u8, - pub starting_level: u8, - pub arr_controls: Vec<(KeyboardControlScheme, bool)>, -} - -impl MenuGameOptions { - pub fn new() -> Self { - let arr_controls: Vec<(KeyboardControlScheme, bool)> = - vec![(KeyboardControlScheme::default(), false); MAX_NUM_PLAYERS as usize]; - Self { - num_players: 1, - starting_level: 0, - arr_controls, - } - } -} - #[repr(u8)] #[derive(PartialEq, Eq)] enum MenuState { diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 4647125..8457bcb 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -7,14 +7,9 @@ use ggez::Context; use crate::inputs::{Input, KeyboardControlScheme}; use crate::movement::Movement; -use crate::menu::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; - -use crate::menu::SUB_TEXT_SCALE_DOWN; -use crate::menu::TEXT_SCALE_DOWN; - -use crate::menu::DARK_GRAY; -use crate::menu::HELP_RED; -use crate::menu::LIGHT_GRAY; +use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; +use crate::menu::menuhelpers::{DARK_GRAY, HELP_RED, LIGHT_GRAY}; +use crate::menu::menuhelpers::{SUB_TEXT_SCALE_DOWN, TEXT_SCALE_DOWN}; const MAX_NON_START_INPUTS_PER_PLAYER: usize = 8; diff --git a/src/menu/menuhelpers.rs b/src/menu/menuhelpers.rs new file mode 100644 index 0000000..436eeb5 --- /dev/null +++ b/src/menu/menuhelpers.rs @@ -0,0 +1,191 @@ +use ggez::event::KeyCode; +use ggez::graphics::{self, Color, Font, Scale, Text, TextFragment}; + +use crate::inputs::KeyboardControlScheme; + +pub const MAX_STARTING_LEVEL: u8 = 29; // this is just the fastest speed, so yeah +pub const MAX_NUM_PLAYERS: u8 = 62; // currently held back by board width being a u8 equal to 6 + 4 * num_players + +pub const GRAY: Color = Color::new(0.4, 0.4, 0.4, 1.0); +pub const DARK_GRAY: Color = Color::new(0.3, 0.3, 0.3, 1.0); +pub const LIGHT_GRAY: Color = Color::new(0.6, 0.6, 0.6, 1.0); +pub const SELECT_GREEN: Color = Color::new(0.153, 0.839, 0.075, 1.0); +pub const HELP_RED: Color = Color::new(0.9, 0.11, 0.11, 1.0); + +pub const TEXT_SCALE_DOWN: f32 = 15.0; +pub const SUB_TEXT_SCALE_DOWN: f32 = 25.0; + +#[derive(Eq, PartialEq)] +pub enum MenuItemValueType { + None, + NumPlayers, + StartingLevel, + PlayerNum, + KeyCode, +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum MenuItemTrigger { + None, + StartGame, + SubMenu1, + SubMenu2, + Back, + SubSelection, + KeyLeft, + KeyRight, + KeyDown, + KeyRotateCw, + KeyRotateCcw, + KeyBoardCw, + KeyBoardCcw, +} + +pub struct MenuItem { + pub text: Text, + pub value_type: MenuItemValueType, + max_value: u8, + pub value: u8, + pub keycode: Option, + value_show_increase: u8, + text_scale_down: f32, + pub trigger: MenuItemTrigger, + selected: bool, +} + +impl MenuItem { + pub fn new( + item_start_str: &str, + value_type: MenuItemValueType, + value: u8, + keycode: Option, + window_height: f32, + text_scale_down: f32, + trigger: MenuItemTrigger, + ) -> Self { + let mut text = Text::new(TextFragment::new(item_start_str).color(graphics::BLACK)); + let mut max_value = 0; + let mut value_show_increase = 0; + match value_type { + MenuItemValueType::None => {} + MenuItemValueType::NumPlayers => { + max_value = MAX_NUM_PLAYERS; + value_show_increase = 1; + text.add( + TextFragment::new(format!(" {}", value + value_show_increase)) + .color(graphics::BLACK), + ); + } + MenuItemValueType::StartingLevel => { + max_value = MAX_STARTING_LEVEL; + text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); + } + MenuItemValueType::PlayerNum => { + max_value = MAX_NUM_PLAYERS; + value_show_increase = 1; + text.add( + TextFragment::new(format!(" {}", value + value_show_increase)) + .color(graphics::BLACK), + ); + } + MenuItemValueType::KeyCode => { + match keycode { + Some(key) => { + text.add(TextFragment::new(format!("{:?}", key)).color(graphics::BLACK)) + } + None => text.add(TextFragment::new("None".to_string()).color(graphics::BLACK)), + }; + } + } + text.set_font( + Font::default(), + Scale::uniform(window_height / text_scale_down), + ); + Self { + text, + value_type, + max_value, + value, + keycode, + value_show_increase, + text_scale_down, + trigger, + selected: false, + } + } + + pub fn set_select(&mut self, select: bool) { + self.selected = select; + self.text.fragments_mut()[0].color = Some(if select { + SELECT_GREEN + } else { + graphics::BLACK + }); + if self.value_type != MenuItemValueType::None { + self.text.fragments_mut()[1].color = Some(if select { + SELECT_GREEN + } else { + graphics::BLACK + }); + if self.value_type == MenuItemValueType::NumPlayers + || self.value_type == MenuItemValueType::StartingLevel + || self.value_type == MenuItemValueType::PlayerNum + { + self.text.fragments_mut()[1].text = if select { + format!("<{}>", self.value + self.value_show_increase) + } else { + format!(" {}", self.value + self.value_show_increase) + }; + } + } + } + + pub fn inc_or_dec(&mut self, inc: bool) { + if self.value_type == MenuItemValueType::NumPlayers + || self.value_type == MenuItemValueType::StartingLevel + || self.value_type == MenuItemValueType::PlayerNum + { + self.value = if inc { + (self.value + 1) % self.max_value + } else { + (self.value - 1 + self.max_value) % self.max_value + }; + // assume it's selected because it's being incremented/decremented + self.text.fragments_mut()[1].text = + format!("<{}>", self.value + self.value_show_increase); + } + } + + pub fn set_keycode(&mut self, keycode: Option) { + self.keycode = keycode; + match self.keycode { + Some(key) => self.text.fragments_mut()[1].text = format!("{:?}", key), + None => self.text.fragments_mut()[1].text = "None".to_string(), + }; + } + + pub fn resize(&mut self, window_height: f32) { + self.text.set_font( + Font::default(), + Scale::uniform(window_height / self.text_scale_down), + ); + } +} + +pub struct MenuGameOptions { + pub num_players: u8, + pub starting_level: u8, + pub arr_controls: Vec<(KeyboardControlScheme, bool)>, +} + +impl MenuGameOptions { + pub fn new() -> Self { + let arr_controls: Vec<(KeyboardControlScheme, bool)> = + vec![(KeyboardControlScheme::default(), false); MAX_NUM_PLAYERS as usize]; + Self { + num_players: 1, + starting_level: 0, + arr_controls, + } + } +} diff --git a/src/menu/start.rs b/src/menu/start.rs index 70d32d4..2831259 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -4,11 +4,9 @@ use ggez::nalgebra::Point2; use ggez::Context; use crate::inputs::Input; -use crate::menu::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; - -use crate::menu::TEXT_SCALE_DOWN; - -use crate::menu::HELP_RED; +use crate::menu::menuhelpers::HELP_RED; +use crate::menu::menuhelpers::TEXT_SCALE_DOWN; +use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; pub struct StartMenu { // logic diff --git a/src/movement.rs b/src/movement.rs index c9f9558..674b07f 100644 --- a/src/movement.rs +++ b/src/movement.rs @@ -1,4 +1,4 @@ -use crate::menu::MenuItemTrigger; +use crate::menu::menuhelpers::MenuItemTrigger; #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] From d4d22b056df2624f96480d8fbb7d4fbb6aeb3ca4 Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 28 May 2021 01:17:28 -0400 Subject: [PATCH 40/59] start menu/choosemode.rs --- src/menu/choosemode.rs | 112 ++++++++++++++++++++++++++++++++++++++++ src/menu/inputconfig.rs | 3 +- src/menu/menuhelpers.rs | 25 ++++++--- src/menu/start.rs | 6 +-- 4 files changed, 132 insertions(+), 14 deletions(-) diff --git a/src/menu/choosemode.rs b/src/menu/choosemode.rs index e69de29..0cba382 100644 --- a/src/menu/choosemode.rs +++ b/src/menu/choosemode.rs @@ -0,0 +1,112 @@ + + +struct ChooseModeMenu { + // logic + selection: usize, + // drawing + vec_menu_items: Vec, +} + +impl StartMenu { + pub fn new(window_dimensions: (f32, f32), num_players: u8, starting_level: u8) -> Self { + let mut vec_menu_items: Vec = Vec::with_capacity(1); + vec_menu_items.push(MenuItem::new( + "Mode: ", + MenuItemValueType::Custom, + 0, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::StartGame, + )); + vec_menu_items[0].set_num_values(2); + vec_menu_items[0].set_select(true); + Self { + // logic + selection: 0, + not_enough_controls_flag: false, + vec_menu_items, + // drawing + not_enough_controls_text: Text::new( + TextFragment::new("[!] Not enough Controls Setup to Start").color(HELP_RED), + ), + } + } + + pub fn update(&mut self, input: &Input, game_options: &mut MenuGameOptions) -> MenuItemTrigger { + if input.keydown_right.1 { + self.vec_menu_items[self.selection].inc_or_dec(true); + } + + if input.keydown_left.1 { + self.vec_menu_items[self.selection].inc_or_dec(false); + } + + game_options.num_players = self.get_num_players() + 1; + game_options.starting_level = self.get_starting_level(); + + if input.keydown_down.1 { + self.vec_menu_items[self.selection].set_select(false); + self.selection = (self.selection + 1) % self.vec_menu_items.len(); + self.vec_menu_items[self.selection].set_select(true); + } + + if input.keydown_up.1 { + self.vec_menu_items[self.selection].set_select(false); + self.selection = if self.selection == 0 { + self.vec_menu_items.len() - 1 + } else { + self.selection - 1 + }; + self.vec_menu_items[self.selection].set_select(true); + } + + if input.keydown_start.1 { + return self.vec_menu_items[self.selection].trigger; + } + + MenuItemTrigger::None + } + + pub fn draw(&mut self, ctx: &mut Context) { + let window_dimensions = graphics::size(ctx); + let num_menu_items_to_draw = self.vec_menu_items.len(); + + if self.not_enough_controls_flag { + self.draw_text(ctx, &self.not_enough_controls_text, 0.1, &window_dimensions); + } + for (index, item) in self.vec_menu_items.iter().enumerate() { + self.draw_text( + ctx, + &item.text, + (1.0 * (index + 1) as f32) / (num_menu_items_to_draw + 1) as f32, + &window_dimensions, + ); + } + } + + fn draw_text( + &self, + ctx: &mut Context, + text_var: &Text, + vertical_position: f32, + window_dimensions: &(f32, f32), + ) { + let (text_width, text_height) = text_var.dimensions(ctx); + graphics::draw( + ctx, + text_var, + DrawParam::new().dest(Point2::new( + (window_dimensions.0 - text_width as f32) / 2.0, + (window_dimensions.1 - text_height as f32) * vertical_position, + )), + ) + .unwrap(); + } + + pub fn resize_event(&mut self, height: f32) { + for item in self.vec_menu_items.iter_mut() { + item.resize(height); + } + } +} diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 8457bcb..603bb84 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -1,6 +1,5 @@ use ggez::event::KeyCode; -use ggez::graphics::{self, DrawParam}; -use ggez::graphics::{Scale, Text, TextFragment}; +use ggez::graphics::{self, DrawParam, Scale, Text, TextFragment}; use ggez::nalgebra::Point2; use ggez::Context; diff --git a/src/menu/menuhelpers.rs b/src/menu/menuhelpers.rs index 436eeb5..18e551b 100644 --- a/src/menu/menuhelpers.rs +++ b/src/menu/menuhelpers.rs @@ -22,6 +22,7 @@ pub enum MenuItemValueType { StartingLevel, PlayerNum, KeyCode, + Custom, } #[derive(Debug, Eq, PartialEq, Copy, Clone)] @@ -44,7 +45,7 @@ pub enum MenuItemTrigger { pub struct MenuItem { pub text: Text, pub value_type: MenuItemValueType, - max_value: u8, + num_values: u8, pub value: u8, pub keycode: Option, value_show_increase: u8, @@ -64,12 +65,12 @@ impl MenuItem { trigger: MenuItemTrigger, ) -> Self { let mut text = Text::new(TextFragment::new(item_start_str).color(graphics::BLACK)); - let mut max_value = 0; + let mut num_values = 0; let mut value_show_increase = 0; match value_type { MenuItemValueType::None => {} MenuItemValueType::NumPlayers => { - max_value = MAX_NUM_PLAYERS; + num_values = MAX_NUM_PLAYERS; value_show_increase = 1; text.add( TextFragment::new(format!(" {}", value + value_show_increase)) @@ -77,11 +78,11 @@ impl MenuItem { ); } MenuItemValueType::StartingLevel => { - max_value = MAX_STARTING_LEVEL; + num_values = MAX_STARTING_LEVEL + 1; // level indexes by 0, so we have one more than max starting level text.add(TextFragment::new(format!(" {}", value)).color(graphics::BLACK)); } MenuItemValueType::PlayerNum => { - max_value = MAX_NUM_PLAYERS; + num_values = MAX_NUM_PLAYERS; value_show_increase = 1; text.add( TextFragment::new(format!(" {}", value + value_show_increase)) @@ -96,6 +97,9 @@ impl MenuItem { None => text.add(TextFragment::new("None".to_string()).color(graphics::BLACK)), }; } + MenuItemValueType::Custom => { + text.add(TextFragment::new("".to_string()).color(graphics::BLACK)); + } } text.set_font( Font::default(), @@ -104,7 +108,7 @@ impl MenuItem { Self { text, value_type, - max_value, + num_values, value, keycode, value_show_increase, @@ -144,11 +148,12 @@ impl MenuItem { if self.value_type == MenuItemValueType::NumPlayers || self.value_type == MenuItemValueType::StartingLevel || self.value_type == MenuItemValueType::PlayerNum + || self.value_type == MenuItemValueType::Custom { self.value = if inc { - (self.value + 1) % self.max_value + (self.value + 1) % self.num_values } else { - (self.value - 1 + self.max_value) % self.max_value + (self.value - 1 + self.num_values) % self.num_values }; // assume it's selected because it's being incremented/decremented self.text.fragments_mut()[1].text = @@ -170,6 +175,10 @@ impl MenuItem { Scale::uniform(window_height / self.text_scale_down), ); } + + pub fn set_num_values(&mut self, num_vals: u8) { + self.num_values = num_vals; + } } pub struct MenuGameOptions { diff --git a/src/menu/start.rs b/src/menu/start.rs index 2831259..088c969 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -1,12 +1,10 @@ -use ggez::graphics::{self, DrawParam}; -use ggez::graphics::{Text, TextFragment}; +use ggez::graphics::{self, DrawParam, Text, TextFragment}; use ggez::nalgebra::Point2; use ggez::Context; use crate::inputs::Input; -use crate::menu::menuhelpers::HELP_RED; -use crate::menu::menuhelpers::TEXT_SCALE_DOWN; use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; +use crate::menu::menuhelpers::{HELP_RED, TEXT_SCALE_DOWN}; pub struct StartMenu { // logic From 52271f012cae1a6dcd881a2673c3a888942bf2e9 Mon Sep 17 00:00:00 2001 From: Brian Billman Date: Mon, 7 Jun 2021 22:42:08 -0400 Subject: [PATCH 41/59] switched back to old logic so that rotations can be buffered between piece spawns --- src/game.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/game.rs b/src/game.rs index 0c5f8a6..6c111bd 100644 --- a/src/game.rs +++ b/src/game.rs @@ -606,13 +606,16 @@ impl Game { if player.input.keydown_start.1 { self.pause_flags = (true, true); } + + // reset player controls in memory for next frame consistency + player.input.was_just_pressed_setfalse(); } // update controls so that the logic realizes next frame that the button inputs made were run through the logic if self.keycode_escape_flags.1 { self.pause_flags = (true, true); } - self.was_just_pressed_setfalse_all_players(); + self.was_just_pressed_setfalse_common(); // attempt to line clear (go through the vector of FullLine's and decrement clear_delay if > 0, clear and return (lines_cleared, score) for <= 0) let (returned_lines, returned_score) = self.bh.attempt_clear(self.level); @@ -646,6 +649,10 @@ impl Game { for player in self.vec_players.iter_mut() { player.input.was_just_pressed_setfalse(); } + self.was_just_pressed_setfalse_common(); + } + + fn was_just_pressed_setfalse_common(&mut self) { self.keycode_down_flags.1 = false; self.keycode_escape_flags.1 = false; } From 723d1ccbed17d94615e293339c53a401bc3519f3 Mon Sep 17 00:00:00 2001 From: Brian Billman Date: Mon, 7 Jun 2021 23:30:32 -0400 Subject: [PATCH 42/59] functionalize some things about das --- src/game.rs | 40 +++++++++++----------------------------- src/game/player.rs | 11 +++++++++++ 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/game.rs b/src/game.rs index 6c111bd..02d355a 100644 --- a/src/game.rs +++ b/src/game.rs @@ -428,6 +428,7 @@ impl Game { player.spawn_piece_flag = false; // set das_countdown to the smaller das value if input left or right is pressed as the piece spawns in if player.input.keydown_left.0 || player.input.keydown_right.0 { + println!("setting das threshold to little"); player.das_countdown = DAS_THRESHOLD_LITTLE; } // set next piece to random; reroll once if it chooses the same piece as it just was @@ -491,6 +492,7 @@ impl Game { player.player_num, ) .0; + println!("setting das threshold to big"); player.das_countdown = DAS_THRESHOLD_BIG; } if player.input.keydown_right.1 { @@ -504,50 +506,30 @@ impl Game { player.player_num, ) .0; + println!("setting das threshold to big"); player.das_countdown = DAS_THRESHOLD_BIG; } - if player.input.keydown_left.0 && !player.input.keydown_left.1 { - if player.das_countdown > 0 { - player.das_countdown -= 1; - } - if player.das_countdown == 0 || player.waiting_to_shift { - if self - .bh - .attempt_piece_movement( - Movement::from( - (Movement::Left as u8 + self.gravity_direction as u8) % 4, - ), - player.player_num, - ) - .0 - { - player.das_countdown = - std::cmp::max(DAS_THRESHOLD_LITTLE, player.das_countdown); - player.waiting_to_shift = false; - } else { - player.waiting_to_shift = true; - }; - } - } - if player.input.keydown_right.0 && !player.input.keydown_right.1 { - if player.das_countdown > 0 { - player.das_countdown -= 1; - } - if player.das_countdown == 0 || player.waiting_to_shift { + if (player.input.keydown_left.0 && !player.input.keydown_left.1) || (player.input.keydown_right.0 && !player.input.keydown_right.1) { + let movement: Movement = if player.input.keydown_left.0 {Movement::Left} else {Movement::Right}; + if player.tick_das_countdown() { + // if the das countdown hit zero, we try to move the piece if self .bh .attempt_piece_movement( Movement::from( - (Movement::Right as u8 + self.gravity_direction as u8) % 4, + (movement as u8 + self.gravity_direction as u8) % 4, ), player.player_num, ) .0 { + // if the piece moved, set variables accordingly + println!("setting das threshold to max of little ({}) and player.das_countdown ({})", DAS_THRESHOLD_LITTLE, player.das_countdown); player.das_countdown = std::cmp::max(DAS_THRESHOLD_LITTLE, player.das_countdown); player.waiting_to_shift = false; } else { + // failed to move piece, so we are waiting to shift the piece player.waiting_to_shift = true; }; } diff --git a/src/game/player.rs b/src/game/player.rs index 6072c94..39b728e 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -59,6 +59,17 @@ impl Player { } } + pub fn tick_das_countdown(&mut self) -> bool { + if self.das_countdown > 0 { + self.das_countdown -= 1; + } + if self.das_countdown == 0 || self.waiting_to_shift { + return true; + } + + false + } + pub fn update_input_keydown(&mut self, input: KeyCode) -> bool { if let Some(k_ctrl_scheme) = &self.control_scheme.0 { let movement_opt = k_ctrl_scheme.movement_from_keycode(input); From 7bc0620f540e04286f3975f324501a4a0ed5ed0b Mon Sep 17 00:00:00 2001 From: brian Date: Tue, 15 Jun 2021 23:14:41 -0400 Subject: [PATCH 43/59] perfect; ship it --- src/control.rs | 2 +- src/game.rs | 53 +++++++++++++------ src/game/board.rs | 72 +++++++++++++------------- src/menu.rs | 36 ++++++++++--- src/menu/choosemode.rs | 29 ++++++----- src/menu/start.rs | 112 ++++++++++++++++++++++++++++------------- 6 files changed, 196 insertions(+), 108 deletions(-) diff --git a/src/control.rs b/src/control.rs index afbde55..d0fc6c8 100644 --- a/src/control.rs +++ b/src/control.rs @@ -182,7 +182,7 @@ impl EventHandler for Control { .menu .as_mut() .expect(STATE_MENU_BUT_MENU_NONE) - .resize_event(height), + .resize_event((width, height)), ProgramState::Game => self .game .as_mut() diff --git a/src/game.rs b/src/game.rs index 02d355a..3c4aa85 100644 --- a/src/game.rs +++ b/src/game.rs @@ -78,11 +78,32 @@ pub const UNDETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.2; static INVALID_MENU_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; -pub enum Modes { +#[derive(PartialEq, Eq, Copy, Clone)] +pub enum GameMode { Classic, Rotatris, } +impl GameMode { + pub fn num_required_inputs_from(mode: Self) -> usize { + match mode { + Self::Classic => 5, + Self::Rotatris => 7, + } + } + + pub fn from_usize(num: usize) -> Self { + match num { + 0 => Self::Classic, + 1 => Self::Rotatris, + _ => { + println!("[!] Invalid conversion attempt usize({}) -> GameMode", num); + Self::Classic + } + } + } +} + // this struct is for the Menu class so that it can return what game options to start the game with pub struct GameOptions { pub num_players: u8, @@ -171,14 +192,14 @@ pub struct Game { impl Game { pub fn new(ctx: &mut Context, game_options: &GameOptions) -> Game { - let mode = Modes::Classic; + let mode = GameMode::Classic; let board_width = match mode { - Modes::Classic => 6 + 4 * game_options.num_players, - Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, + GameMode::Classic => 6 + 4 * game_options.num_players, + GameMode::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, }; let board_height = match mode { - Modes::Classic => BOARD_HEIGHT, - Modes::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, + GameMode::Classic => BOARD_HEIGHT, + GameMode::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, }; let mut vec_players: Vec = Vec::with_capacity(game_options.num_players as usize); for player in 0..game_options.num_players { @@ -253,12 +274,12 @@ impl Game { )); } let mut game_info_text = match mode { - Modes::Classic => Text::new( + GameMode::Classic => Text::new( TextFragment::new("Lines: ") .color(graphics::WHITE) .scale(Scale::uniform(LITTLE_TEXT_SCALE)), ), - Modes::Rotatris => Text::new( + GameMode::Rotatris => Text::new( TextFragment::new("Rings: ") .color(graphics::WHITE) .scale(Scale::uniform(LITTLE_TEXT_SCALE)), @@ -509,16 +530,20 @@ impl Game { println!("setting das threshold to big"); player.das_countdown = DAS_THRESHOLD_BIG; } - if (player.input.keydown_left.0 && !player.input.keydown_left.1) || (player.input.keydown_right.0 && !player.input.keydown_right.1) { - let movement: Movement = if player.input.keydown_left.0 {Movement::Left} else {Movement::Right}; + if (player.input.keydown_left.0 && !player.input.keydown_left.1) + || (player.input.keydown_right.0 && !player.input.keydown_right.1) + { + let movement: Movement = if player.input.keydown_left.0 { + Movement::Left + } else { + Movement::Right + }; if player.tick_das_countdown() { // if the das countdown hit zero, we try to move the piece if self .bh .attempt_piece_movement( - Movement::from( - (movement as u8 + self.gravity_direction as u8) % 4, - ), + Movement::from((movement as u8 + self.gravity_direction as u8) % 4), player.player_num, ) .0 @@ -588,7 +613,7 @@ impl Game { if player.input.keydown_start.1 { self.pause_flags = (true, true); } - + // reset player controls in memory for next frame consistency player.input.was_just_pressed_setfalse(); } diff --git a/src/game/board.rs b/src/game/board.rs index 71ba676..7e44da4 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -1,6 +1,6 @@ use crate::game::piece::{Piece, Shapes}; use crate::game::tile::Tile; -use crate::game::Modes; +use crate::game::GameMode; use crate::game::{ CLEAR_DELAY_CLASSIC, FALL_DELAY_VALUES_CLASSIC, FALL_DELAY_VALUES_ROTATRIS, SCORE_DOUBLE_BASE, SCORE_QUADRUPLE_BASE, SCORE_SINGLE_BASE, SCORE_TRIPLE_BASE, @@ -45,25 +45,25 @@ impl From for Gravity { // abstract the board and the possible gamemodes into one struct pub struct BoardHandler { - pub mode: Modes, + pub mode: GameMode, pub classic: Option, pub rotatris: Option, } impl BoardHandler { - pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: Modes) -> Self { + pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: GameMode) -> Self { // determine some rules based on gamemode let (board_height_buffer, spawn_row) = match mode { - Modes::Rotatris => (0, (board_height - 1) / 2), - Modes::Classic => (2, 0), + GameMode::Rotatris => (0, (board_height - 1) / 2), + GameMode::Classic => (2, 0), }; let mut classic: Option = None; let mut rotatris: Option = None; match mode { - Modes::Rotatris => { + GameMode::Rotatris => { rotatris = Some(BoardRotatris::new(board_width, spawn_row, num_players)) } - Modes::Classic => { + GameMode::Classic => { classic = Some(BoardClassic::new( board_width, board_height, @@ -83,31 +83,31 @@ impl BoardHandler { // get... pub fn get_width(&mut self) -> u8 { match self.mode { - Modes::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).width, - Modes::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, + GameMode::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).width, + GameMode::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, } } pub fn get_height(&mut self) -> u8 { match self.mode { - Modes::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height, - Modes::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, + GameMode::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height, + GameMode::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, } } pub fn get_height_buffer(&mut self) -> u8 { match self.mode { - Modes::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height_buffer, - Modes::Rotatris => 0u8, + GameMode::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height_buffer, + GameMode::Rotatris => 0u8, } } pub fn get_active_from_pos(&mut self, y: u8, x: u8) -> bool { match self.mode { - Modes::Classic => { + GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active } - Modes::Rotatris => { + GameMode::Rotatris => { self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active } } @@ -115,10 +115,10 @@ impl BoardHandler { pub fn get_empty_from_pos(&mut self, y: u8, x: u8) -> bool { match self.mode { - Modes::Classic => { + GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty } - Modes::Rotatris => { + GameMode::Rotatris => { self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty } } @@ -126,10 +126,10 @@ impl BoardHandler { pub fn get_player_from_pos(&mut self, y: u8, x: u8) -> u8 { match self.mode { - Modes::Classic => { + GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player } - Modes::Rotatris => { + GameMode::Rotatris => { self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player } } @@ -137,10 +137,10 @@ impl BoardHandler { pub fn get_shape_from_pos(&mut self, y: u8, x: u8) -> Shapes { match self.mode { - Modes::Classic => { + GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape } - Modes::Rotatris => { + GameMode::Rotatris => { self.rotatris.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape } } @@ -148,10 +148,10 @@ impl BoardHandler { pub fn get_shape_from_player(&mut self, player: u8) -> Shapes { match self.mode { - Modes::Classic => { + GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).vec_active_piece[player as usize].shape } - Modes::Rotatris => { + GameMode::Rotatris => { self.rotatris .as_mut() .expect(BH_WRONG_MODE) @@ -163,8 +163,8 @@ impl BoardHandler { pub fn get_fall_delay_from_level(&mut self, level: u8) -> u8 { match self.mode { - Modes::Classic => FALL_DELAY_VALUES_CLASSIC[level as usize], - Modes::Rotatris => FALL_DELAY_VALUES_ROTATRIS[level as usize], + GameMode::Classic => FALL_DELAY_VALUES_CLASSIC[level as usize], + GameMode::Rotatris => FALL_DELAY_VALUES_ROTATRIS[level as usize], } } @@ -176,12 +176,12 @@ impl BoardHandler { spawn_piece_shape: Shapes, ) -> (bool, bool) { match self.mode { - Modes::Classic => self + GameMode::Classic => self .classic .as_mut() .expect(BH_WRONG_MODE) .attempt_piece_spawn(player, spawn_col, spawn_piece_shape), - Modes::Rotatris => self + GameMode::Rotatris => self .rotatris .as_mut() .expect(BH_WRONG_MODE) @@ -191,12 +191,12 @@ impl BoardHandler { pub fn attempt_clear(&mut self, level: u8) -> (u8, u32) { match self.mode { - Modes::Classic => self + GameMode::Classic => self .classic .as_mut() .expect(BH_WRONG_MODE) .attempt_clear_lines(level), - Modes::Rotatris => self + GameMode::Rotatris => self .rotatris .as_mut() .expect(BH_WRONG_MODE) @@ -206,12 +206,12 @@ impl BoardHandler { pub fn attempt_piece_movement(&mut self, m: Movement, p: u8) -> (bool, bool) { match self.mode { - Modes::Classic => self + GameMode::Classic => self .classic .as_mut() .expect(BH_WRONG_MODE) .attempt_piece_movement(m, p), - Modes::Rotatris => self + GameMode::Rotatris => self .rotatris .as_mut() .expect(BH_WRONG_MODE) @@ -221,11 +221,11 @@ impl BoardHandler { pub fn attempt_rotate_board(&mut self, rd: Movement) -> bool { match self.mode { - Modes::Classic => { - println!("[!] `attempt_rotate_board` called but mode is Modes::Classic"); + GameMode::Classic => { + println!("[!] `attempt_rotate_board` called but mode is GameMode::Classic"); false } - Modes::Rotatris => self + GameMode::Rotatris => self .rotatris .as_mut() .expect(BH_WRONG_MODE) @@ -235,12 +235,12 @@ impl BoardHandler { pub fn playerify_piece(&mut self, player: u8) { match self.mode { - Modes::Classic => self + GameMode::Classic => self .classic .as_mut() .expect(BH_WRONG_MODE) .playerify_piece(player), - Modes::Rotatris => self + GameMode::Rotatris => self .rotatris .as_mut() .expect(BH_WRONG_MODE) diff --git a/src/menu.rs b/src/menu.rs index b6b6186..63d48f4 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -3,11 +3,14 @@ use ggez::graphics; use ggez::Context; use crate::control::ProgramState; +use crate::game::GameMode; use crate::inputs::Input; +mod choosemode; mod inputconfig; pub mod menuhelpers; mod start; +use choosemode::ChooseModeMenu; use inputconfig::InputConfigMenu; use menuhelpers::GRAY; use menuhelpers::{MenuGameOptions, MenuItemTrigger}; @@ -16,6 +19,7 @@ use start::StartMenu; #[repr(u8)] #[derive(PartialEq, Eq)] enum MenuState { + ChooseMode, Start, InputConfig, } @@ -27,8 +31,11 @@ pub struct Menu { num_required_keycode_movement_pairs: usize, // states state: MenuState, - start_menu: start::StartMenu, - input_config_menu: inputconfig::InputConfigMenu, + choose_mode_menu: ChooseModeMenu, + start_menu: StartMenu, + input_config_menu: InputConfigMenu, + // window size + window_dimensions: (f32, f32), } impl Menu { @@ -36,19 +43,33 @@ impl Menu { let window_dimensions = graphics::size(ctx); Self { input: Input::new(), - num_required_keycode_movement_pairs: 5, // TODO - state: MenuState::Start, + num_required_keycode_movement_pairs: GameMode::num_required_inputs_from( + GameMode::Classic, + ), + state: MenuState::ChooseMode, + choose_mode_menu: ChooseModeMenu::new(window_dimensions), start_menu: StartMenu::new( window_dimensions, game_options.num_players, game_options.starting_level, ), input_config_menu: InputConfigMenu::new(window_dimensions, game_options), + window_dimensions, } } pub fn update(&mut self, game_options: &mut MenuGameOptions) -> Option { match self.state { + MenuState::ChooseMode => { + if self.choose_mode_menu.update(&self.input) == MenuItemTrigger::SubMenu1 { + self.state = MenuState::Start; + self.start_menu.set_game_mode( + self.choose_mode_menu.game_mode, + game_options, + self.window_dimensions, + ); + } + } MenuState::Start => { let trigger: MenuItemTrigger = self.start_menu.update(&self.input, game_options); match trigger { @@ -154,14 +175,17 @@ impl Menu { graphics::clear(ctx, GRAY); match self.state { + MenuState::ChooseMode => self.choose_mode_menu.draw(ctx), MenuState::Start => self.start_menu.draw(ctx), MenuState::InputConfig => self.input_config_menu.draw(ctx, game_options), } } - pub fn resize_event(&mut self, height: f32) { + pub fn resize_event(&mut self, window_dims: (f32, f32)) { + self.window_dimensions = window_dims; match self.state { - MenuState::Start => self.start_menu.resize_event(height), + MenuState::ChooseMode => {} + MenuState::Start => self.start_menu.resize_event(window_dims.1), MenuState::InputConfig => {} } } diff --git a/src/menu/choosemode.rs b/src/menu/choosemode.rs index 0cba382..2c17aad 100644 --- a/src/menu/choosemode.rs +++ b/src/menu/choosemode.rs @@ -1,14 +1,23 @@ +use ggez::event::KeyCode; +use ggez::graphics::{self, DrawParam, Scale, Text, TextFragment}; +use ggez::nalgebra::Point2; +use ggez::Context; +use crate::game::GameMode; +use crate::inputs::Input; +use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; +use crate::menu::menuhelpers::{HELP_RED, TEXT_SCALE_DOWN}; -struct ChooseModeMenu { +pub struct ChooseModeMenu { // logic selection: usize, + pub game_mode: GameMode, // drawing vec_menu_items: Vec, } -impl StartMenu { - pub fn new(window_dimensions: (f32, f32), num_players: u8, starting_level: u8) -> Self { +impl ChooseModeMenu { + pub fn new(window_dimensions: (f32, f32)) -> Self { let mut vec_menu_items: Vec = Vec::with_capacity(1); vec_menu_items.push(MenuItem::new( "Mode: ", @@ -24,16 +33,12 @@ impl StartMenu { Self { // logic selection: 0, - not_enough_controls_flag: false, + game_mode: GameMode::from_usize(0), vec_menu_items, - // drawing - not_enough_controls_text: Text::new( - TextFragment::new("[!] Not enough Controls Setup to Start").color(HELP_RED), - ), } } - pub fn update(&mut self, input: &Input, game_options: &mut MenuGameOptions) -> MenuItemTrigger { + pub fn update(&mut self, input: &Input) -> MenuItemTrigger { if input.keydown_right.1 { self.vec_menu_items[self.selection].inc_or_dec(true); } @@ -42,9 +47,6 @@ impl StartMenu { self.vec_menu_items[self.selection].inc_or_dec(false); } - game_options.num_players = self.get_num_players() + 1; - game_options.starting_level = self.get_starting_level(); - if input.keydown_down.1 { self.vec_menu_items[self.selection].set_select(false); self.selection = (self.selection + 1) % self.vec_menu_items.len(); @@ -72,9 +74,6 @@ impl StartMenu { let window_dimensions = graphics::size(ctx); let num_menu_items_to_draw = self.vec_menu_items.len(); - if self.not_enough_controls_flag { - self.draw_text(ctx, &self.not_enough_controls_text, 0.1, &window_dimensions); - } for (index, item) in self.vec_menu_items.iter().enumerate() { self.draw_text( ctx, diff --git a/src/menu/start.rs b/src/menu/start.rs index 088c969..82d119e 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -2,6 +2,7 @@ use ggez::graphics::{self, DrawParam, Text, TextFragment}; use ggez::nalgebra::Point2; use ggez::Context; +use crate::game::GameMode; use crate::inputs::Input; use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; use crate::menu::menuhelpers::{HELP_RED, TEXT_SCALE_DOWN}; @@ -9,56 +10,30 @@ use crate::menu::menuhelpers::{HELP_RED, TEXT_SCALE_DOWN}; pub struct StartMenu { // logic selection: usize, + game_mode: GameMode, pub not_enough_controls_flag: bool, - // drawing vec_menu_items: Vec, + // drawing not_enough_controls_text: Text, } impl StartMenu { pub fn new(window_dimensions: (f32, f32), num_players: u8, starting_level: u8) -> Self { let mut vec_menu_items: Vec = Vec::with_capacity(4); - vec_menu_items.push(MenuItem::new( - "Start", - MenuItemValueType::None, - 0, - None, - window_dimensions.1, - TEXT_SCALE_DOWN, - MenuItemTrigger::StartGame, - )); - vec_menu_items.push(MenuItem::new( - "Number of Players: ", - MenuItemValueType::NumPlayers, - num_players - 1, - None, - window_dimensions.1, - TEXT_SCALE_DOWN, - MenuItemTrigger::StartGame, - )); - vec_menu_items.push(MenuItem::new( - "Starting Level: ", - MenuItemValueType::StartingLevel, + let game_mode = GameMode::Classic; + Self::fill_vec_menu_items( + &mut vec_menu_items, + game_mode, + num_players, starting_level, - None, - window_dimensions.1, - TEXT_SCALE_DOWN, - MenuItemTrigger::StartGame, - )); - vec_menu_items.push(MenuItem::new( - "Controls", - MenuItemValueType::None, - 0, - None, - window_dimensions.1, - TEXT_SCALE_DOWN, - MenuItemTrigger::SubMenu1, - )); + window_dimensions, + ); vec_menu_items[0].set_select(true); Self { // logic selection: 0, not_enough_controls_flag: false, + game_mode, vec_menu_items, // drawing not_enough_controls_text: Text::new( @@ -102,6 +77,71 @@ impl StartMenu { MenuItemTrigger::None } + pub fn set_game_mode( + &mut self, + mode: GameMode, + game_options: &MenuGameOptions, + window_dimensions: (f32, f32), + ) { + if self.game_mode != mode { + self.vec_menu_items.clear(); + Self::fill_vec_menu_items( + &mut self.vec_menu_items, + mode, + game_options.num_players, + game_options.starting_level, + window_dimensions, + ); + } + } + + fn fill_vec_menu_items( + vec_menu_items: &mut Vec, + mode: GameMode, + num_players: u8, + starting_level: u8, + window_dimensions: (f32, f32), + ) { + vec_menu_items.push(MenuItem::new( + "Start", + MenuItemValueType::None, + 0, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::StartGame, + )); + if mode == GameMode::Classic { + vec_menu_items.push(MenuItem::new( + "Number of Players: ", + MenuItemValueType::NumPlayers, + num_players - 1, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::StartGame, + )); + } + vec_menu_items.push(MenuItem::new( + "Starting Level: ", + MenuItemValueType::StartingLevel, + starting_level, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::StartGame, + )); + vec_menu_items.push(MenuItem::new( + "Controls", + MenuItemValueType::None, + 0, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::SubMenu1, + )); + } + fn get_num_players(&self) -> u8 { for item in self.vec_menu_items.iter() { if item.value_type == MenuItemValueType::NumPlayers { From b5dd626de2d66e799b4b7533f388957cc2170500 Mon Sep 17 00:00:00 2001 From: brian Date: Sun, 4 Jul 2021 20:13:12 -0400 Subject: [PATCH 44/59] very close; still need to deal with keyboard controls --- src/game.rs | 6 ++++-- src/menu.rs | 4 ++-- src/menu/choosemode.rs | 41 ++++++++++++++--------------------------- src/menu/menuhelpers.rs | 9 ++++++--- src/menu/start.rs | 16 +++++++++++++++- 5 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/game.rs b/src/game.rs index 3c4aa85..f90057d 100644 --- a/src/game.rs +++ b/src/game.rs @@ -78,7 +78,7 @@ pub const UNDETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.2; static INVALID_MENU_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; -#[derive(PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum GameMode { Classic, Rotatris, @@ -91,8 +91,10 @@ impl GameMode { Self::Rotatris => 7, } } +} - pub fn from_usize(num: usize) -> Self { +impl From for GameMode { + fn from(num: usize) -> Self { match num { 0 => Self::Classic, 1 => Self::Rotatris, diff --git a/src/menu.rs b/src/menu.rs index 63d48f4..86213e6 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -86,7 +86,7 @@ impl Menu { self.state = MenuState::InputConfig; } MenuItemTrigger::Back => { - println!("[!] what? 1"); + self.state = MenuState::ChooseMode; } MenuItemTrigger::None => {} _ => println!("[!] Wrong menu?"), @@ -184,7 +184,7 @@ impl Menu { pub fn resize_event(&mut self, window_dims: (f32, f32)) { self.window_dimensions = window_dims; match self.state { - MenuState::ChooseMode => {} + MenuState::ChooseMode => self.choose_mode_menu.resize_event(window_dims.1), MenuState::Start => self.start_menu.resize_event(window_dims.1), MenuState::InputConfig => {} } diff --git a/src/menu/choosemode.rs b/src/menu/choosemode.rs index 2c17aad..d04f265 100644 --- a/src/menu/choosemode.rs +++ b/src/menu/choosemode.rs @@ -1,12 +1,11 @@ -use ggez::event::KeyCode; -use ggez::graphics::{self, DrawParam, Scale, Text, TextFragment}; +use ggez::graphics::{self, DrawParam, Text}; use ggez::nalgebra::Point2; use ggez::Context; use crate::game::GameMode; use crate::inputs::Input; -use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; -use crate::menu::menuhelpers::{HELP_RED, TEXT_SCALE_DOWN}; +use crate::menu::menuhelpers::TEXT_SCALE_DOWN; +use crate::menu::menuhelpers::{MenuItem, MenuItemTrigger, MenuItemValueType}; pub struct ChooseModeMenu { // logic @@ -26,41 +25,29 @@ impl ChooseModeMenu { None, window_dimensions.1, TEXT_SCALE_DOWN, - MenuItemTrigger::StartGame, + MenuItemTrigger::SubMenu1, )); vec_menu_items[0].set_num_values(2); + vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", GameMode::Classic); vec_menu_items[0].set_select(true); Self { // logic selection: 0, - game_mode: GameMode::from_usize(0), + game_mode: GameMode::Classic, vec_menu_items, } } pub fn update(&mut self, input: &Input) -> MenuItemTrigger { - if input.keydown_right.1 { + if input.keydown_left.1 + || input.keydown_right.1 + || input.keydown_down.1 + || input.keydown_up.1 + { self.vec_menu_items[self.selection].inc_or_dec(true); - } - - if input.keydown_left.1 { - self.vec_menu_items[self.selection].inc_or_dec(false); - } - - if input.keydown_down.1 { - self.vec_menu_items[self.selection].set_select(false); - self.selection = (self.selection + 1) % self.vec_menu_items.len(); - self.vec_menu_items[self.selection].set_select(true); - } - - if input.keydown_up.1 { - self.vec_menu_items[self.selection].set_select(false); - self.selection = if self.selection == 0 { - self.vec_menu_items.len() - 1 - } else { - self.selection - 1 - }; - self.vec_menu_items[self.selection].set_select(true); + self.game_mode = GameMode::from(self.vec_menu_items[self.selection].value as usize); + self.vec_menu_items[self.selection].text.fragments_mut()[1].text = + format!("{:?}", self.game_mode); } if input.keydown_start.1 { diff --git a/src/menu/menuhelpers.rs b/src/menu/menuhelpers.rs index 18e551b..3b37748 100644 --- a/src/menu/menuhelpers.rs +++ b/src/menu/menuhelpers.rs @@ -1,6 +1,7 @@ use ggez::event::KeyCode; use ggez::graphics::{self, Color, Font, Scale, Text, TextFragment}; +use crate::game::GameMode; use crate::inputs::KeyboardControlScheme; pub const MAX_STARTING_LEVEL: u8 = 29; // this is just the fastest speed, so yeah @@ -155,9 +156,11 @@ impl MenuItem { } else { (self.value - 1 + self.num_values) % self.num_values }; - // assume it's selected because it's being incremented/decremented - self.text.fragments_mut()[1].text = - format!("<{}>", self.value + self.value_show_increase); + if self.value_type != MenuItemValueType::Custom { + // assume it's selected because it's being incremented/decremented + self.text.fragments_mut()[1].text = + format!("<{}>", self.value + self.value_show_increase); + } } } diff --git a/src/menu/start.rs b/src/menu/start.rs index 82d119e..178c7ea 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -21,9 +21,11 @@ impl StartMenu { pub fn new(window_dimensions: (f32, f32), num_players: u8, starting_level: u8) -> Self { let mut vec_menu_items: Vec = Vec::with_capacity(4); let game_mode = GameMode::Classic; + let mut selection = 0; Self::fill_vec_menu_items( &mut vec_menu_items, game_mode, + &mut selection, num_players, starting_level, window_dimensions, @@ -31,7 +33,7 @@ impl StartMenu { vec_menu_items[0].set_select(true); Self { // logic - selection: 0, + selection, not_enough_controls_flag: false, game_mode, vec_menu_items, @@ -43,6 +45,12 @@ impl StartMenu { } pub fn update(&mut self, input: &Input, game_options: &mut MenuGameOptions) -> MenuItemTrigger { + if input.keydown_rotate_ccw.1 { + // escape was pressed; go back to the choosemode menu after resetting some things + self.not_enough_controls_flag = false; + return MenuItemTrigger::Back; + } + if input.keydown_right.1 { self.vec_menu_items[self.selection].inc_or_dec(true); } @@ -84,10 +92,12 @@ impl StartMenu { window_dimensions: (f32, f32), ) { if self.game_mode != mode { + self.game_mode = mode; self.vec_menu_items.clear(); Self::fill_vec_menu_items( &mut self.vec_menu_items, mode, + &mut self.selection, game_options.num_players, game_options.starting_level, window_dimensions, @@ -98,6 +108,7 @@ impl StartMenu { fn fill_vec_menu_items( vec_menu_items: &mut Vec, mode: GameMode, + selection: &mut usize, num_players: u8, starting_level: u8, window_dimensions: (f32, f32), @@ -111,6 +122,7 @@ impl StartMenu { TEXT_SCALE_DOWN, MenuItemTrigger::StartGame, )); + if mode == GameMode::Classic { vec_menu_items.push(MenuItem::new( "Number of Players: ", @@ -140,6 +152,8 @@ impl StartMenu { TEXT_SCALE_DOWN, MenuItemTrigger::SubMenu1, )); + *selection = 0; + vec_menu_items[0].set_select(true); } fn get_num_players(&self) -> u8 { From 5de47f2d0ed42988ec17ca23748d421d4264f3f4 Mon Sep 17 00:00:00 2001 From: brian Date: Sun, 15 Aug 2021 21:58:24 -0400 Subject: [PATCH 45/59] LICENSE update --- LICENSE | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/LICENSE b/LICENSE index ee0713d..3121d0a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,17 +1,16 @@ -MIT/X Consortium License +Custom License @ 2020 Brian "Catcow" B Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: +to deal in the Software without restriction except for selling the Software +or otherwise distributing the Software for gain. Distribution (with no gain), +modifications, and merge requests are welcomed. The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software unless there is written approval -in the form of an email from the author(s). +all copies or substantial portions of the Software unless there is written +approval in the form of an email from the author(s). THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, From 1ed104be67db237bdf1dd79055ae4538b1e6f347 Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 16 Aug 2021 00:17:47 -0400 Subject: [PATCH 46/59] improve some menu logic --- src/control.rs | 2 +- src/menu.rs | 7 ++++++- src/menu/inputconfig.rs | 20 +++++++++++++++++++- src/menu/menuhelpers.rs | 6 ++++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/control.rs b/src/control.rs index d0fc6c8..870f0c7 100644 --- a/src/control.rs +++ b/src/control.rs @@ -28,7 +28,7 @@ pub struct Control { impl Control { pub fn new(ctx: &mut Context) -> Control { - let menu_game_options = MenuGameOptions::new(); + let menu_game_options = MenuGameOptions::default(); Self { state: ProgramState::Menu, menu: Some(Menu::new(ctx, &menu_game_options)), diff --git a/src/menu.rs b/src/menu.rs index 86213e6..726939c 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -53,7 +53,7 @@ impl Menu { game_options.num_players, game_options.starting_level, ), - input_config_menu: InputConfigMenu::new(window_dimensions, game_options), + input_config_menu: InputConfigMenu::new(game_options, window_dimensions), window_dimensions, } } @@ -68,6 +68,11 @@ impl Menu { game_options, self.window_dimensions, ); + if game_options.game_mode != self.choose_mode_menu.game_mode { + game_options.game_mode = self.choose_mode_menu.game_mode; + self.input_config_menu + .set_game_mode(game_options.game_mode, self.window_dimensions); + } } } MenuState::Start => { diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 603bb84..970c19e 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -3,6 +3,7 @@ use ggez::graphics::{self, DrawParam, Scale, Text, TextFragment}; use ggez::nalgebra::Point2; use ggez::Context; +use crate::game::GameMode; use crate::inputs::{Input, KeyboardControlScheme}; use crate::movement::Movement; @@ -34,7 +35,7 @@ pub struct InputConfigMenu { } impl InputConfigMenu { - pub fn new(window_dimensions: (f32, f32), game_options: &MenuGameOptions) -> Self { + pub fn new(game_options: &MenuGameOptions, window_dimensions: (f32, f32)) -> Self { let mut vec_used_keycode: Vec = vec![]; // gather what the starting used keycodes should be for ctrls in game_options.arr_controls.iter() { @@ -103,6 +104,23 @@ impl InputConfigMenu { } } + pub fn set_game_mode(&mut self, game_mode: GameMode, window_dimensions: (f32, f32)) { + self.vec_menu_items_keycode.clear(); + let game_options = MenuGameOptions::default(); + match game_mode { + GameMode::Classic => Self::setup_classic_mode_subtext( + &mut self.vec_menu_items_keycode, + &game_options, + window_dimensions, + ), + GameMode::Rotatris => Self::setup_rotatris_mode_subtext( + &mut self.vec_menu_items_keycode, + &game_options, + window_dimensions, + ), + } + } + fn setup_classic_mode_subtext( vec_to_add_to: &mut Vec, game_options: &MenuGameOptions, diff --git a/src/menu/menuhelpers.rs b/src/menu/menuhelpers.rs index 3b37748..92ceeb5 100644 --- a/src/menu/menuhelpers.rs +++ b/src/menu/menuhelpers.rs @@ -187,16 +187,18 @@ impl MenuItem { pub struct MenuGameOptions { pub num_players: u8, pub starting_level: u8, + pub game_mode: GameMode, pub arr_controls: Vec<(KeyboardControlScheme, bool)>, } -impl MenuGameOptions { - pub fn new() -> Self { +impl Default for MenuGameOptions { + fn default() -> Self { let arr_controls: Vec<(KeyboardControlScheme, bool)> = vec![(KeyboardControlScheme::default(), false); MAX_NUM_PLAYERS as usize]; Self { num_players: 1, starting_level: 0, + game_mode: GameMode::Classic, arr_controls, } } From 31591157e836fedac7a6f8084245b64a04a7ad87 Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 16 Aug 2021 00:23:41 -0400 Subject: [PATCH 47/59] rm das debugs; rm info debugs --- src/game.rs | 4 ---- src/main.rs | 1 - 2 files changed, 5 deletions(-) diff --git a/src/game.rs b/src/game.rs index f90057d..9dfdf8f 100644 --- a/src/game.rs +++ b/src/game.rs @@ -451,7 +451,6 @@ impl Game { player.spawn_piece_flag = false; // set das_countdown to the smaller das value if input left or right is pressed as the piece spawns in if player.input.keydown_left.0 || player.input.keydown_right.0 { - println!("setting das threshold to little"); player.das_countdown = DAS_THRESHOLD_LITTLE; } // set next piece to random; reroll once if it chooses the same piece as it just was @@ -515,7 +514,6 @@ impl Game { player.player_num, ) .0; - println!("setting das threshold to big"); player.das_countdown = DAS_THRESHOLD_BIG; } if player.input.keydown_right.1 { @@ -529,7 +527,6 @@ impl Game { player.player_num, ) .0; - println!("setting das threshold to big"); player.das_countdown = DAS_THRESHOLD_BIG; } if (player.input.keydown_left.0 && !player.input.keydown_left.1) @@ -551,7 +548,6 @@ impl Game { .0 { // if the piece moved, set variables accordingly - println!("setting das threshold to max of little ({}) and player.das_countdown ({})", DAS_THRESHOLD_LITTLE, player.das_countdown); player.das_countdown = std::cmp::max(DAS_THRESHOLD_LITTLE, player.das_countdown); player.waiting_to_shift = false; diff --git a/src/main.rs b/src/main.rs index 924399f..27ccd31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,6 @@ fn main() { if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") { let mut path = path::PathBuf::from(manifest_dir); path.push("resources"); - println!("[+] Adding path {:?}", path); context = context.add_resource_path(path); } From a4014f4107a15860a7fe7dac7800089161c7dc5d Mon Sep 17 00:00:00 2001 From: brian Date: Sun, 12 Sep 2021 16:46:08 -0400 Subject: [PATCH 48/59] better implementation things --- src/control.rs | 1 - src/game.rs | 103 +++++++++++++++++++++++++++++----------- src/inputs.rs | 22 +++++++++ src/menu.rs | 10 ++-- src/menu/inputconfig.rs | 4 +- src/movement.rs | 10 ++-- 6 files changed, 110 insertions(+), 40 deletions(-) diff --git a/src/control.rs b/src/control.rs index 870f0c7..b78d70d 100644 --- a/src/control.rs +++ b/src/control.rs @@ -172,7 +172,6 @@ impl EventHandler for Control { graphics::present(ctx) } - // this seems unused but is called somewhere in ggez to ultimately make things scale and get placed correctly when changing window size fn resize_event(&mut self, ctx: &mut Context, width: f32, height: f32) { let new_rect = graphics::Rect::new(0.0, 0.0, width, height); graphics::set_screen_coordinates(ctx, new_rect).unwrap(); diff --git a/src/game.rs b/src/game.rs index 9dfdf8f..2f8b3b1 100644 --- a/src/game.rs +++ b/src/game.rs @@ -110,6 +110,7 @@ impl From for GameMode { pub struct GameOptions { pub num_players: u8, pub starting_level: u8, + pub game_mode: GameMode, pub vec_controls: Vec<(Option, bool)>, } @@ -118,41 +119,85 @@ impl From<&MenuGameOptions> for GameOptions { let mut vec_controls: Vec<(Option, bool)> = Vec::with_capacity(menu_game_options.arr_controls.len()); let mut counted_active_controls: u8 = 0; - for ctrls in menu_game_options.arr_controls.iter() { - if !(ctrls.0).is_empty() { - vec_controls.push(( - Some(KeyboardControlScheme::new_classic( - (ctrls.0) - .keycode_from_movement(Movement::Left) - .expect(INVALID_MENU_CONTROLS), - (ctrls.0) - .keycode_from_movement(Movement::Right) - .expect(INVALID_MENU_CONTROLS), - (ctrls.0) - .keycode_from_movement(Movement::Down) - .expect(INVALID_MENU_CONTROLS), - (ctrls.0) - .keycode_from_movement(Movement::RotateCw) - .expect(INVALID_MENU_CONTROLS), - (ctrls.0) - .keycode_from_movement(Movement::RotateCcw) - .expect(INVALID_MENU_CONTROLS), - )), - false, - )); - counted_active_controls += 1; - } else if ctrls.1 { - vec_controls.push((None, true)); - counted_active_controls += 1; + match menu_game_options.game_mode { + GameMode::Classic => { + for ctrls in menu_game_options.arr_controls.iter() { + if !(ctrls.0).is_empty() { + vec_controls.push(( + Some(KeyboardControlScheme::new_classic( + (ctrls.0) + .keycode_from_movement(Movement::Left) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::Right) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::Down) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::RotateCw) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::RotateCcw) + .expect(INVALID_MENU_CONTROLS), + )), + false, + )); + counted_active_controls += 1; + } else if ctrls.1 { + vec_controls.push((None, true)); + counted_active_controls += 1; + } + if counted_active_controls == menu_game_options.num_players { + break; + } + } } - if counted_active_controls == menu_game_options.num_players { - break; + GameMode::Rotatris => { + for ctrls in menu_game_options.arr_controls.iter() { + if !(ctrls.0).is_empty() { + vec_controls.push(( + Some(KeyboardControlScheme::new_rotatris( + (ctrls.0) + .keycode_from_movement(Movement::Left) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::Right) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::Down) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::RotateCw) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::RotateCcw) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::BoardCw) + .expect(INVALID_MENU_CONTROLS), + (ctrls.0) + .keycode_from_movement(Movement::BoardCcw) + .expect(INVALID_MENU_CONTROLS), + )), + false, + )); + counted_active_controls += 1; + } else if ctrls.1 { + vec_controls.push((None, true)); + counted_active_controls += 1; + } + if counted_active_controls == menu_game_options.num_players { + break; + } + } } } Self { num_players: menu_game_options.num_players, starting_level: menu_game_options.starting_level, + game_mode: menu_game_options.game_mode, vec_controls, } } @@ -194,7 +239,7 @@ pub struct Game { impl Game { pub fn new(ctx: &mut Context, game_options: &GameOptions) -> Game { - let mode = GameMode::Classic; + let mode = game_options.game_mode; let board_width = match mode { GameMode::Classic => 6 + 4 * game_options.num_players, GameMode::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, diff --git a/src/inputs.rs b/src/inputs.rs index 1a86b4f..9d73433 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -111,6 +111,28 @@ impl KeyboardControlScheme { } } + pub fn new_rotatris( + left: KeyCode, + right: KeyCode, + down: KeyCode, + rotate_cw: KeyCode, + rotate_ccw: KeyCode, + rotate_board_cw: KeyCode, + rotate_board_ccw: KeyCode, + ) -> Self { + let mut vec_keycode_movement_pair: Vec<(KeyCode, Movement)> = Vec::with_capacity(7); + vec_keycode_movement_pair.push((left, Movement::Left)); + vec_keycode_movement_pair.push((right, Movement::Right)); + vec_keycode_movement_pair.push((down, Movement::Down)); + vec_keycode_movement_pair.push((rotate_cw, Movement::RotateCw)); + vec_keycode_movement_pair.push((rotate_ccw, Movement::RotateCcw)); + vec_keycode_movement_pair.push((rotate_board_cw, Movement::BoardCw)); + vec_keycode_movement_pair.push((rotate_board_ccw, Movement::BoardCcw)); + Self { + vec_keycode_movement_pair, + } + } + pub fn keycode_from_movement(&self, m: Movement) -> Option { for pair in self.vec_keycode_movement_pair.iter() { if pair.1 == m { diff --git a/src/menu.rs b/src/menu.rs index 726939c..6ed1f60 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -63,13 +63,13 @@ impl Menu { MenuState::ChooseMode => { if self.choose_mode_menu.update(&self.input) == MenuItemTrigger::SubMenu1 { self.state = MenuState::Start; - self.start_menu.set_game_mode( - self.choose_mode_menu.game_mode, - game_options, - self.window_dimensions, - ); if game_options.game_mode != self.choose_mode_menu.game_mode { game_options.game_mode = self.choose_mode_menu.game_mode; + self.start_menu.set_game_mode( + self.choose_mode_menu.game_mode, + game_options, + self.window_dimensions, + ); self.input_config_menu .set_game_mode(game_options.game_mode, self.window_dimensions); } diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 970c19e..33eda55 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -208,7 +208,7 @@ impl InputConfigMenu { "BoardCw: ", MenuItemValueType::KeyCode, 0, - (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCw), + (game_options.arr_controls[0].0).keycode_from_movement(Movement::BoardCw), window_dimensions.1, SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyBoardCw, @@ -217,7 +217,7 @@ impl InputConfigMenu { "BoardCcw: ", MenuItemValueType::KeyCode, 0, - (game_options.arr_controls[0].0).keycode_from_movement(Movement::RotateCcw), + (game_options.arr_controls[0].0).keycode_from_movement(Movement::BoardCcw), window_dimensions.1, SUB_TEXT_SCALE_DOWN, MenuItemTrigger::KeyBoardCcw, diff --git a/src/movement.rs b/src/movement.rs index 674b07f..2a1187c 100644 --- a/src/movement.rs +++ b/src/movement.rs @@ -10,6 +10,8 @@ pub enum Movement { RotateCw, RotateCcw, DoubleRotate, + BoardCw, + BoardCcw, None, } @@ -23,7 +25,9 @@ impl From for Movement { 4 => Movement::RotateCw, 5 => Movement::RotateCcw, 6 => Movement::DoubleRotate, - 7 => Movement::None, + 7 => Movement::BoardCw, + 8 => Movement::BoardCcw, + 9 => Movement::None, _ => panic!("[!] Unknown Movement value: {}", value), } } @@ -37,8 +41,8 @@ impl From for Movement { MenuItemTrigger::KeyDown => Movement::Down, MenuItemTrigger::KeyRotateCw => Movement::RotateCw, MenuItemTrigger::KeyRotateCcw => Movement::RotateCcw, - MenuItemTrigger::KeyBoardCw => Movement::None, - MenuItemTrigger::KeyBoardCcw => Movement::None, + MenuItemTrigger::KeyBoardCw => Movement::BoardCw, + MenuItemTrigger::KeyBoardCcw => Movement::BoardCcw, _ => panic!( "[!] Unexpected value converting MenuItemTrigger to Movement: {:?}", value From 52543d5db075d07ea09893d28b843ece27998e62 Mon Sep 17 00:00:00 2001 From: brian Date: Sun, 12 Sep 2021 19:14:31 -0400 Subject: [PATCH 49/59] it's literally so close dude rip --- src/game.rs | 9 ++++----- src/game/player.rs | 20 ++++++++++++++++++++ src/inputs.rs | 20 ++++++++++++++++++-- src/menu.rs | 16 ++++++++-------- src/menu/choosemode.rs | 13 ++++++++----- src/menu/inputconfig.rs | 26 +++++++++++++++++++------- src/menu/menuhelpers.rs | 3 +-- src/menu/start.rs | 10 +++++++--- 8 files changed, 85 insertions(+), 32 deletions(-) diff --git a/src/game.rs b/src/game.rs index 2f8b3b1..9e9d83a 100644 --- a/src/game.rs +++ b/src/game.rs @@ -85,8 +85,8 @@ pub enum GameMode { } impl GameMode { - pub fn num_required_inputs_from(mode: Self) -> usize { - match mode { + pub fn num_required_inputs(&self) -> usize { + match self { Self::Classic => 5, Self::Rotatris => 7, } @@ -531,14 +531,14 @@ impl Game { // rotatris specific // board rotations - if self.rotate_board_cw.1 { + if player.input.keydown_board_cw.1 { if self.bh.attempt_rotate_board(Movement::RotateCw) { self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 1) % 4); } } - if self.rotate_board_ccw.1 { + if player.input.keydown_board_ccw.1 { if self.bh.attempt_rotate_board(Movement::RotateCcw) { self.gravity_direction = Movement::from(((self.gravity_direction as u8) + 3) % 4); @@ -714,7 +714,6 @@ impl Game { return; } else if keycode == KeyCode::Down { self.keycode_down_flags = (true, true); - return; } for player in &mut self.vec_players { if player.update_input_keydown(keycode) { diff --git a/src/game/player.rs b/src/game/player.rs index 39b728e..6887ea1 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -115,6 +115,18 @@ impl Player { return true; } } + Movement::BoardCw => { + if !self.input.keydown_board_cw.0 { + self.input.keydown_board_cw = (true, true); + return true; + } + } + Movement::BoardCcw => { + if !self.input.keydown_board_ccw.0 { + self.input.keydown_board_ccw = (true, true); + return true; + } + } _ => {} } } @@ -162,6 +174,14 @@ impl Player { self.input.keydown_rotate_ccw = (false, false); return true; } + Movement::BoardCw => { + self.input.keydown_board_cw = (false, false); + return true; + } + Movement::BoardCcw => { + self.input.keydown_board_ccw = (false, false); + return true; + } _ => {} } } diff --git a/src/inputs.rs b/src/inputs.rs index 9d73433..8bf16cd 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -9,6 +9,8 @@ pub struct Input { pub keydown_up: (bool, bool), pub keydown_rotate_cw: (bool, bool), pub keydown_rotate_ccw: (bool, bool), + pub keydown_board_cw: (bool, bool), + pub keydown_board_ccw: (bool, bool), pub keydown_start: (bool, bool), } @@ -21,6 +23,8 @@ impl Input { keydown_up: (false, false), keydown_rotate_cw: (false, false), keydown_rotate_ccw: (false, false), + keydown_board_cw: (false, false), + keydown_board_ccw: (false, false), keydown_start: (false, false), } } @@ -32,6 +36,8 @@ impl Input { self.keydown_up.1 = false; self.keydown_rotate_cw.1 = false; self.keydown_rotate_ccw.1 = false; + self.keydown_board_cw.1 = false; + self.keydown_board_ccw.1 = false; self.keydown_start.1 = false; } @@ -42,6 +48,8 @@ impl Input { self.keydown_up = (false, false); self.keydown_rotate_cw = (false, false); self.keydown_rotate_ccw = (false, false); + self.keydown_board_cw = (false, false); + self.keydown_board_ccw = (false, false); self.keydown_start = (false, false); } @@ -54,13 +62,21 @@ impl Input { println!("Down: ({}, {})", self.keydown_down.0, self.keydown_down.1); println!("Up: ({}, {})", self.keydown_up.0, self.keydown_up.1); println!( - "Cw: ({}, {})", + "RotateCw: ({}, {})", self.keydown_rotate_cw.0, self.keydown_rotate_cw.1 ); println!( - "Ccw: ({}, {})", + "RotateCcw: ({}, {})", self.keydown_rotate_ccw.0, self.keydown_rotate_ccw.1 ); + println!( + "BoardCw: ({}, {})", + self.keydown_board_cw.0, self.keydown_board_cw.1 + ); + println!( + "BoardCcw: ({}, {})", + self.keydown_board_ccw.0, self.keydown_board_ccw.1 + ); println!( "Start: ({}, {})", self.keydown_start.0, self.keydown_start.1 diff --git a/src/menu.rs b/src/menu.rs index 6ed1f60..3d19fa5 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -3,7 +3,6 @@ use ggez::graphics; use ggez::Context; use crate::control::ProgramState; -use crate::game::GameMode; use crate::inputs::Input; mod choosemode; @@ -43,15 +42,14 @@ impl Menu { let window_dimensions = graphics::size(ctx); Self { input: Input::new(), - num_required_keycode_movement_pairs: GameMode::num_required_inputs_from( - GameMode::Classic, - ), + num_required_keycode_movement_pairs: game_options.game_mode.num_required_inputs(), state: MenuState::ChooseMode, - choose_mode_menu: ChooseModeMenu::new(window_dimensions), + choose_mode_menu: ChooseModeMenu::new(game_options.game_mode, window_dimensions), start_menu: StartMenu::new( window_dimensions, game_options.num_players, game_options.starting_level, + game_options.game_mode, ), input_config_menu: InputConfigMenu::new(game_options, window_dimensions), window_dimensions, @@ -61,17 +59,19 @@ impl Menu { pub fn update(&mut self, game_options: &mut MenuGameOptions) -> Option { match self.state { MenuState::ChooseMode => { - if self.choose_mode_menu.update(&self.input) == MenuItemTrigger::SubMenu1 { + if self.choose_mode_menu.update(&self.input) == MenuItemTrigger::SubMenu { self.state = MenuState::Start; if game_options.game_mode != self.choose_mode_menu.game_mode { game_options.game_mode = self.choose_mode_menu.game_mode; + self.num_required_keycode_movement_pairs = + game_options.game_mode.num_required_inputs(); self.start_menu.set_game_mode( self.choose_mode_menu.game_mode, game_options, self.window_dimensions, ); self.input_config_menu - .set_game_mode(game_options.game_mode, self.window_dimensions); + .update_game_mode(self.window_dimensions, game_options); } } } @@ -85,7 +85,7 @@ impl Menu { self.start_menu.not_enough_controls_flag = true; } } - MenuItemTrigger::SubMenu1 => { + MenuItemTrigger::SubMenu => { // InputConfig menu self.start_menu.not_enough_controls_flag = false; self.state = MenuState::InputConfig; diff --git a/src/menu/choosemode.rs b/src/menu/choosemode.rs index d04f265..f59a3ba 100644 --- a/src/menu/choosemode.rs +++ b/src/menu/choosemode.rs @@ -16,24 +16,27 @@ pub struct ChooseModeMenu { } impl ChooseModeMenu { - pub fn new(window_dimensions: (f32, f32)) -> Self { + pub fn new(game_mode: GameMode, window_dimensions: (f32, f32)) -> Self { let mut vec_menu_items: Vec = Vec::with_capacity(1); vec_menu_items.push(MenuItem::new( "Mode: ", MenuItemValueType::Custom, - 0, + match game_mode { + GameMode::Classic => 0, + GameMode::Rotatris => 1, + }, None, window_dimensions.1, TEXT_SCALE_DOWN, - MenuItemTrigger::SubMenu1, + MenuItemTrigger::SubMenu, )); vec_menu_items[0].set_num_values(2); - vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", GameMode::Classic); + vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", game_mode); vec_menu_items[0].set_select(true); Self { // logic selection: 0, - game_mode: GameMode::Classic, + game_mode: game_mode, vec_menu_items, } } diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 33eda55..3fd11e8 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -68,11 +68,18 @@ impl InputConfigMenu { // keycode MenuItems let mut vec_menu_items_keycode: Vec = Vec::with_capacity(MAX_NON_START_INPUTS_PER_PLAYER); - Self::setup_classic_mode_subtext( - &mut vec_menu_items_keycode, - game_options, - window_dimensions, - ); + match game_options.game_mode { + GameMode::Classic => Self::setup_classic_mode_subtext( + &mut vec_menu_items_keycode, + &game_options, + window_dimensions, + ), + GameMode::Rotatris => Self::setup_rotatris_mode_subtext( + &mut vec_menu_items_keycode, + &game_options, + window_dimensions, + ), + } // Self::setup_rotatris_mode_subtext(&mut vec_menu_items_keycode, game_options, window_dimensions); Self { selection: 0, @@ -104,9 +111,14 @@ impl InputConfigMenu { } } - pub fn set_game_mode(&mut self, game_mode: GameMode, window_dimensions: (f32, f32)) { + pub fn update_game_mode( + &mut self, + window_dimensions: (f32, f32), + game_options: &mut MenuGameOptions, + ) { self.vec_menu_items_keycode.clear(); - let game_options = MenuGameOptions::default(); + let game_mode = game_options.game_mode; + *game_options = MenuGameOptions::default(); match game_mode { GameMode::Classic => Self::setup_classic_mode_subtext( &mut self.vec_menu_items_keycode, diff --git a/src/menu/menuhelpers.rs b/src/menu/menuhelpers.rs index 92ceeb5..bcf06af 100644 --- a/src/menu/menuhelpers.rs +++ b/src/menu/menuhelpers.rs @@ -30,8 +30,7 @@ pub enum MenuItemValueType { pub enum MenuItemTrigger { None, StartGame, - SubMenu1, - SubMenu2, + SubMenu, Back, SubSelection, KeyLeft, diff --git a/src/menu/start.rs b/src/menu/start.rs index 178c7ea..29d734f 100644 --- a/src/menu/start.rs +++ b/src/menu/start.rs @@ -18,9 +18,13 @@ pub struct StartMenu { } impl StartMenu { - pub fn new(window_dimensions: (f32, f32), num_players: u8, starting_level: u8) -> Self { + pub fn new( + window_dimensions: (f32, f32), + num_players: u8, + starting_level: u8, + game_mode: GameMode, + ) -> Self { let mut vec_menu_items: Vec = Vec::with_capacity(4); - let game_mode = GameMode::Classic; let mut selection = 0; Self::fill_vec_menu_items( &mut vec_menu_items, @@ -150,7 +154,7 @@ impl StartMenu { None, window_dimensions.1, TEXT_SCALE_DOWN, - MenuItemTrigger::SubMenu1, + MenuItemTrigger::SubMenu, )); *selection = 0; vec_menu_items[0].set_select(true); From 4135498ff08f32908fd76797dc18fb7609cfa8cd Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 14:55:00 -0400 Subject: [PATCH 50/59] fix a lot of rotatris problems and some menu issues --- src/game.rs | 8 +++----- src/game/board.rs | 24 ++++++++++++------------ src/menu/inputconfig.rs | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/game.rs b/src/game.rs index 9e9d83a..52a1937 100644 --- a/src/game.rs +++ b/src/game.rs @@ -98,10 +98,7 @@ impl From for GameMode { match num { 0 => Self::Classic, 1 => Self::Rotatris, - _ => { - println!("[!] Invalid conversion attempt usize({}) -> GameMode", num); - Self::Classic - } + _ => Self::Classic, } } } @@ -1102,7 +1099,8 @@ impl Game { .unwrap(); } } - // score text; TODO: perhaps make a separate function for something based on the bottom, or just figure out how to do this better so we don't divide out by the window_height + // score text; TODO: perhaps make a separate function for something based on the bottom, + // or just figure out how to do this better so we don't divide out by the window_height self.draw_text( ctx, &self.game_info_text, diff --git a/src/game/board.rs b/src/game/board.rs index 7e44da4..68fdf1d 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -168,7 +168,7 @@ impl BoardHandler { } } - // board logic functions + // board logic... pub fn attempt_piece_spawn( &mut self, player: u8, @@ -659,8 +659,8 @@ impl BoardRotatris { } let matrix = vec![vec![Tile::default(); board_size as usize]; board_size as usize]; - // DEBUG - // for a in 0..4 { + // DEBUG TILES ADDED + // for a in 0..10 { // for b in 0..board_size as usize { // matrix[a][b] = Tile::new(false, false, 0, Shapes::I); // matrix[b][a] = Tile::new(false, false, 0, Shapes::I); @@ -672,6 +672,8 @@ impl BoardRotatris { // matrix[board_size as usize / 2][board_size as usize - 2] = Tile::default(); // matrix[board_size as usize / 2][board_size as usize - 3] = Tile::default(); // matrix[board_size as usize / 2][board_size as usize - 4] = Tile::default(); + // matrix[board_size as usize / 2][board_size as usize - 5] = Tile::default(); + // matrix[board_size as usize / 2][board_size as usize - 6] = Tile::default(); Self { gravity: Gravity::Down, @@ -989,26 +991,24 @@ impl BoardRotatris { pub fn attempt_clear_rings(&mut self, level: u8) -> (u8, u32) { let mut num_cleared_rings = 0; let mut score_from_cleared_rings = 0; - let num_rings_to_check = self.board_size / 2 - 2; + let num_rings_to_check = self.board_size / 2; - // go from inner rings to outer rings checking if any ring is full, avoiding the middle 4 rings + // go from inner rings to outer rings checking if any ring is full, avoiding the very middle for z in (0..num_rings_to_check).rev() { if self.rotatris_check_single_ring(z) { num_cleared_rings += 1; // clear and pull inner stuff out - for j in (z + 1)..num_rings_to_check { + if z == num_rings_to_check - 1 { + self.emptify_single_ring(z); + continue; + } + for j in (z + 1)..=self.board_size / 2 { self.rotatris_pull_single_ring_out(j); } - } else { } } if num_cleared_rings > 0 { - // get rid of all tiles in the middle area - for z in num_rings_to_check..=((self.board_size - 1) / 2) { - self.emptify_single_ring(z); - } - score_from_cleared_rings += match num_cleared_rings { 1 => SCORE_SINGLE_BASE as u32 * (level as u32 + 1), 2 => SCORE_DOUBLE_BASE as u32 * (level as u32 + 1), diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 3fd11e8..23a6c2b 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -118,7 +118,7 @@ impl InputConfigMenu { ) { self.vec_menu_items_keycode.clear(); let game_mode = game_options.game_mode; - *game_options = MenuGameOptions::default(); + // TODO match game_mode { GameMode::Classic => Self::setup_classic_mode_subtext( &mut self.vec_menu_items_keycode, From 224cdafb5c25d34d85ea54b2a6997d13fde88e1d Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 15:13:36 -0400 Subject: [PATCH 51/59] some state machine fixes --- src/inputs.rs | 4 ++++ src/menu/inputconfig.rs | 3 ++- src/menu/menuhelpers.rs | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/inputs.rs b/src/inputs.rs index 8bf16cd..a526874 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -101,6 +101,10 @@ impl KeyboardControlScheme { } } + pub fn clear(&mut self) { + self.vec_keycode_movement_pair.clear(); + } + pub fn len(&self) -> usize { self.vec_keycode_movement_pair.len() } diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 23a6c2b..0d41872 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -118,7 +118,8 @@ impl InputConfigMenu { ) { self.vec_menu_items_keycode.clear(); let game_mode = game_options.game_mode; - // TODO + game_options.reset_controls(); + self.vec_used_keycode.clear(); match game_mode { GameMode::Classic => Self::setup_classic_mode_subtext( &mut self.vec_menu_items_keycode, diff --git a/src/menu/menuhelpers.rs b/src/menu/menuhelpers.rs index bcf06af..901f2e9 100644 --- a/src/menu/menuhelpers.rs +++ b/src/menu/menuhelpers.rs @@ -202,3 +202,12 @@ impl Default for MenuGameOptions { } } } + +impl MenuGameOptions { + pub fn reset_controls(&mut self) { + for ctrls in self.arr_controls.iter_mut() { + ctrls.0.clear(); + ctrls.1 = false; + } + } +} From 51a338ba501d702d5b117f1e13ee105874a6cedf Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 18:26:09 -0400 Subject: [PATCH 52/59] choosemode menu only on startup; gamepad compatibility with rotatris --- src/game.rs | 7 +++++++ src/game/board.rs | 19 ++++++++++++++++++- src/game/player.rs | 8 ++++++++ src/menu.rs | 6 +++++- src/menu/choosemode.rs | 12 ++++++++++-- src/menu/inputconfig.rs | 4 ++++ src/menu/menuhelpers.rs | 4 +++- 7 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/game.rs b/src/game.rs index 52a1937..f120bef 100644 --- a/src/game.rs +++ b/src/game.rs @@ -77,9 +77,11 @@ pub const DETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.5; pub const UNDETECT_GAMEPAD_AXIS_THRESHOLD: f32 = 0.2; static INVALID_MENU_CONTROLS: &str = "[!] last used controls was Some() but invalid data"; +static GAME_MODE_NONE: &str = "[!] GameMode unexpectedly None"; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum GameMode { + None, Classic, Rotatris, } @@ -87,6 +89,7 @@ pub enum GameMode { impl GameMode { pub fn num_required_inputs(&self) -> usize { match self { + Self::None => 0, Self::Classic => 5, Self::Rotatris => 7, } @@ -117,6 +120,7 @@ impl From<&MenuGameOptions> for GameOptions { Vec::with_capacity(menu_game_options.arr_controls.len()); let mut counted_active_controls: u8 = 0; match menu_game_options.game_mode { + GameMode::None => panic!(GAME_MODE_NONE), GameMode::Classic => { for ctrls in menu_game_options.arr_controls.iter() { if !(ctrls.0).is_empty() { @@ -238,10 +242,12 @@ impl Game { pub fn new(ctx: &mut Context, game_options: &GameOptions) -> Game { let mode = game_options.game_mode; let board_width = match mode { + GameMode::None => panic!(GAME_MODE_NONE), GameMode::Classic => 6 + 4 * game_options.num_players, GameMode::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, }; let board_height = match mode { + GameMode::None => panic!(GAME_MODE_NONE), GameMode::Classic => BOARD_HEIGHT, GameMode::Rotatris => ROTATRIS_BOARD_SIDE_LENGTH, }; @@ -318,6 +324,7 @@ impl Game { )); } let mut game_info_text = match mode { + GameMode::None => panic!(GAME_MODE_NONE), GameMode::Classic => Text::new( TextFragment::new("Lines: ") .color(graphics::WHITE) diff --git a/src/game/board.rs b/src/game/board.rs index 68fdf1d..197388a 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -7,7 +7,8 @@ use crate::game::{ }; use crate::movement::Movement; -static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong option"; +static BH_WRONG_MODE: &str = "[!] BoardHandler has wrong GameMode setup"; +static BH_MODE_NONE: &str = "[!] BoardHandler has GameMode None"; #[repr(u8)] #[derive(Copy, Clone, Eq, PartialEq)] @@ -54,12 +55,14 @@ impl BoardHandler { pub fn new(board_width: u8, board_height: u8, num_players: u8, mode: GameMode) -> Self { // determine some rules based on gamemode let (board_height_buffer, spawn_row) = match mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Rotatris => (0, (board_height - 1) / 2), GameMode::Classic => (2, 0), }; let mut classic: Option = None; let mut rotatris: Option = None; match mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Rotatris => { rotatris = Some(BoardRotatris::new(board_width, spawn_row, num_players)) } @@ -83,6 +86,7 @@ impl BoardHandler { // get... pub fn get_width(&mut self) -> u8 { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).width, GameMode::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, } @@ -90,6 +94,7 @@ impl BoardHandler { pub fn get_height(&mut self) -> u8 { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height, GameMode::Rotatris => self.rotatris.as_mut().expect(BH_WRONG_MODE).board_size, } @@ -97,6 +102,7 @@ impl BoardHandler { pub fn get_height_buffer(&mut self) -> u8 { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => self.classic.as_mut().expect(BH_WRONG_MODE).height_buffer, GameMode::Rotatris => 0u8, } @@ -104,6 +110,7 @@ impl BoardHandler { pub fn get_active_from_pos(&mut self, y: u8, x: u8) -> bool { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].active } @@ -115,6 +122,7 @@ impl BoardHandler { pub fn get_empty_from_pos(&mut self, y: u8, x: u8) -> bool { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].empty } @@ -126,6 +134,7 @@ impl BoardHandler { pub fn get_player_from_pos(&mut self, y: u8, x: u8) -> u8 { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].player } @@ -137,6 +146,7 @@ impl BoardHandler { pub fn get_shape_from_pos(&mut self, y: u8, x: u8) -> Shapes { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).matrix[y as usize][x as usize].shape } @@ -148,6 +158,7 @@ impl BoardHandler { pub fn get_shape_from_player(&mut self, player: u8) -> Shapes { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => { self.classic.as_mut().expect(BH_WRONG_MODE).vec_active_piece[player as usize].shape } @@ -163,6 +174,7 @@ impl BoardHandler { pub fn get_fall_delay_from_level(&mut self, level: u8) -> u8 { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => FALL_DELAY_VALUES_CLASSIC[level as usize], GameMode::Rotatris => FALL_DELAY_VALUES_ROTATRIS[level as usize], } @@ -176,6 +188,7 @@ impl BoardHandler { spawn_piece_shape: Shapes, ) -> (bool, bool) { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => self .classic .as_mut() @@ -191,6 +204,7 @@ impl BoardHandler { pub fn attempt_clear(&mut self, level: u8) -> (u8, u32) { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => self .classic .as_mut() @@ -206,6 +220,7 @@ impl BoardHandler { pub fn attempt_piece_movement(&mut self, m: Movement, p: u8) -> (bool, bool) { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => self .classic .as_mut() @@ -221,6 +236,7 @@ impl BoardHandler { pub fn attempt_rotate_board(&mut self, rd: Movement) -> bool { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => { println!("[!] `attempt_rotate_board` called but mode is GameMode::Classic"); false @@ -235,6 +251,7 @@ impl BoardHandler { pub fn playerify_piece(&mut self, player: u8) { match self.mode { + GameMode::None => panic!(BH_MODE_NONE), GameMode::Classic => self .classic .as_mut() diff --git a/src/game/player.rs b/src/game/player.rs index 6887ea1..dab2d45 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -203,6 +203,10 @@ impl Player { self.input.keydown_rotate_cw = (true, true); } else if btn == Button::South { self.input.keydown_rotate_ccw = (true, true); + } else if btn == Button::North { + self.input.keydown_board_cw = (true, true); + } else if btn == Button::West { + self.input.keydown_board_ccw = (true, true); } else if btn == Button::Start { self.input.keydown_start = (true, true); } @@ -229,6 +233,10 @@ impl Player { self.input.keydown_rotate_cw = (false, false); } else if btn == Button::South { self.input.keydown_rotate_ccw = (false, false); + } else if btn == Button::North { + self.input.keydown_board_cw = (false, false); + } else if btn == Button::West { + self.input.keydown_board_ccw = (false, false); } else if btn == Button::Start { self.input.keydown_start = (false, false); } diff --git a/src/menu.rs b/src/menu.rs index 3d19fa5..9b29bec 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -3,6 +3,7 @@ use ggez::graphics; use ggez::Context; use crate::control::ProgramState; +use crate::game::GameMode; use crate::inputs::Input; mod choosemode; @@ -43,7 +44,10 @@ impl Menu { Self { input: Input::new(), num_required_keycode_movement_pairs: game_options.game_mode.num_required_inputs(), - state: MenuState::ChooseMode, + state: match game_options.game_mode { + GameMode::None => MenuState::ChooseMode, + _ => MenuState::Start, + }, choose_mode_menu: ChooseModeMenu::new(game_options.game_mode, window_dimensions), start_menu: StartMenu::new( window_dimensions, diff --git a/src/menu/choosemode.rs b/src/menu/choosemode.rs index f59a3ba..99abc9c 100644 --- a/src/menu/choosemode.rs +++ b/src/menu/choosemode.rs @@ -22,6 +22,7 @@ impl ChooseModeMenu { "Mode: ", MenuItemValueType::Custom, match game_mode { + GameMode::None => 0, GameMode::Classic => 0, GameMode::Rotatris => 1, }, @@ -31,12 +32,19 @@ impl ChooseModeMenu { MenuItemTrigger::SubMenu, )); vec_menu_items[0].set_num_values(2); - vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", game_mode); + if game_mode == GameMode::None { + vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", GameMode::Classic); + } else { + vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", game_mode); + } vec_menu_items[0].set_select(true); Self { // logic selection: 0, - game_mode: game_mode, + game_mode: match game_mode { + GameMode::None => GameMode::Classic, + _ => game_mode, + }, vec_menu_items, } } diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 0d41872..90964fd 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -7,6 +7,7 @@ use crate::game::GameMode; use crate::inputs::{Input, KeyboardControlScheme}; use crate::movement::Movement; +use crate::menu::menuhelpers::GAME_MODE_UNEXPECTEDLY_NONE; use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; use crate::menu::menuhelpers::{DARK_GRAY, HELP_RED, LIGHT_GRAY}; use crate::menu::menuhelpers::{SUB_TEXT_SCALE_DOWN, TEXT_SCALE_DOWN}; @@ -68,7 +69,9 @@ impl InputConfigMenu { // keycode MenuItems let mut vec_menu_items_keycode: Vec = Vec::with_capacity(MAX_NON_START_INPUTS_PER_PLAYER); + match game_options.game_mode { + GameMode::None => {} GameMode::Classic => Self::setup_classic_mode_subtext( &mut vec_menu_items_keycode, &game_options, @@ -121,6 +124,7 @@ impl InputConfigMenu { game_options.reset_controls(); self.vec_used_keycode.clear(); match game_mode { + GameMode::None => panic!(GAME_MODE_UNEXPECTEDLY_NONE), GameMode::Classic => Self::setup_classic_mode_subtext( &mut self.vec_menu_items_keycode, &game_options, diff --git a/src/menu/menuhelpers.rs b/src/menu/menuhelpers.rs index 901f2e9..71d2d46 100644 --- a/src/menu/menuhelpers.rs +++ b/src/menu/menuhelpers.rs @@ -16,6 +16,8 @@ pub const HELP_RED: Color = Color::new(0.9, 0.11, 0.11, 1.0); pub const TEXT_SCALE_DOWN: f32 = 15.0; pub const SUB_TEXT_SCALE_DOWN: f32 = 25.0; +pub static GAME_MODE_UNEXPECTEDLY_NONE: &str = "[!] GameMode unexpectedly None"; + #[derive(Eq, PartialEq)] pub enum MenuItemValueType { None, @@ -197,7 +199,7 @@ impl Default for MenuGameOptions { Self { num_players: 1, starting_level: 0, - game_mode: GameMode::Classic, + game_mode: GameMode::None, arr_controls, } } From 82c289532d9485097b2bcab6cdb6e08fad7c333a Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 19:48:59 -0400 Subject: [PATCH 53/59] touch up README and line up controls properly --- README.md | 10 ++++++---- src/game.rs | 10 ++++++---- src/game/board.rs | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8a9ead6..e454189 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,13 @@ In case a gamepad is not detected or a different layout is desired, here is a cr Then the gamepad should be recognized when the program is opened again. When creating a gamepad mapping, consider which buttons the program has set to do which action: ``` -Axis::LeftAxisX- and Button::DPadLeft -> Left -Axis::LeftAxisX+ and Button::DPadRight -> Right -Axis::LeftAxisY- and Button::DPadDown -> Down -Button::West -> RotateCw +Axis::LeftAxisX-/Button::DPadLeft -> Left +Axis::LeftAxisX+/Button::DPadRight -> Right +Axis::LeftAxisY-/Button::DPadDown -> Down +Button::East -> RotateCw Button::South -> RotateCcw +Button::North -> RotateBoardCw +Button::West -> RotateBoardCcw Button::Start -> Start ``` where, in the graphic, `Button::Start` is the small button just to the right of the circle button in the middle, and the compass directions refer to the four buttons on the right. diff --git a/src/game.rs b/src/game.rs index f120bef..eb6fbb2 100644 --- a/src/game.rs +++ b/src/game.rs @@ -536,16 +536,18 @@ impl Game { // rotatris specific // board rotations if player.input.keydown_board_cw.1 { - if self.bh.attempt_rotate_board(Movement::RotateCw) { + // this is flipped because singleplayer and multiplayer will be different someday; TODO + if self.bh.attempt_rotate_board(Movement::RotateCcw) { self.gravity_direction = - Movement::from(((self.gravity_direction as u8) + 1) % 4); + Movement::from(((self.gravity_direction as u8) + 3) % 4); } } if player.input.keydown_board_ccw.1 { - if self.bh.attempt_rotate_board(Movement::RotateCcw) { + // this is flipped because singleplayer and multiplayer will be different someday; TODO + if self.bh.attempt_rotate_board(Movement::RotateCw) { self.gravity_direction = - Movement::from(((self.gravity_direction as u8) + 3) % 4); + Movement::from(((self.gravity_direction as u8) + 1) % 4); } } // rotatris specific end diff --git a/src/game/board.rs b/src/game/board.rs index 197388a..4ef0fc8 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -728,7 +728,7 @@ impl BoardRotatris { } } _ => { - println!("[!] Sent some non-rotation Movement to `attempt_rotate_board()`, a method of `BoardHandler`"); + println!("[!] Sent some non-rotation Movement to `attempt_rotate_board`, a method of `BoardHandler`"); return false; } } From 315fb0b848a7ef8b2fb55e7746138e123e51eb28 Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 19:54:44 -0400 Subject: [PATCH 54/59] readme spacing fix --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e454189..e7de4a9 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ In case a gamepad is not detected or a different layout is desired, here is a cr Then the gamepad should be recognized when the program is opened again. When creating a gamepad mapping, consider which buttons the program has set to do which action: ``` -Axis::LeftAxisX-/Button::DPadLeft -> Left +Axis::LeftAxisX-/Button::DPadLeft -> Left Axis::LeftAxisX+/Button::DPadRight -> Right -Axis::LeftAxisY-/Button::DPadDown -> Down +Axis::LeftAxisY-/Button::DPadDown -> Down Button::East -> RotateCw Button::South -> RotateCcw Button::North -> RotateBoardCw From de2db0c68f581b8ba855fc1c9a18f8f27451360b Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 22:27:36 -0400 Subject: [PATCH 55/59] use vec\! macro where applicable --- src/inputs.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/inputs.rs b/src/inputs.rs index a526874..8207a88 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -120,14 +120,14 @@ impl KeyboardControlScheme { rotate_cw: KeyCode, rotate_ccw: KeyCode, ) -> Self { - let mut vec_keycode_movement_pair: Vec<(KeyCode, Movement)> = Vec::with_capacity(5); - vec_keycode_movement_pair.push((left, Movement::Left)); - vec_keycode_movement_pair.push((right, Movement::Right)); - vec_keycode_movement_pair.push((down, Movement::Down)); - vec_keycode_movement_pair.push((rotate_cw, Movement::RotateCw)); - vec_keycode_movement_pair.push((rotate_ccw, Movement::RotateCcw)); Self { - vec_keycode_movement_pair, + vec_keycode_movement_pair: vec![ + (left, Movement::Left), + (right, Movement::Right), + (down, Movement::Down), + (rotate_cw, Movement::RotateCw), + (rotate_ccw, Movement::RotateCcw), + ], } } @@ -140,16 +140,16 @@ impl KeyboardControlScheme { rotate_board_cw: KeyCode, rotate_board_ccw: KeyCode, ) -> Self { - let mut vec_keycode_movement_pair: Vec<(KeyCode, Movement)> = Vec::with_capacity(7); - vec_keycode_movement_pair.push((left, Movement::Left)); - vec_keycode_movement_pair.push((right, Movement::Right)); - vec_keycode_movement_pair.push((down, Movement::Down)); - vec_keycode_movement_pair.push((rotate_cw, Movement::RotateCw)); - vec_keycode_movement_pair.push((rotate_ccw, Movement::RotateCcw)); - vec_keycode_movement_pair.push((rotate_board_cw, Movement::BoardCw)); - vec_keycode_movement_pair.push((rotate_board_ccw, Movement::BoardCcw)); Self { - vec_keycode_movement_pair, + vec_keycode_movement_pair: vec![ + (left, Movement::Left), + (right, Movement::Right), + (down, Movement::Down), + (rotate_cw, Movement::RotateCw), + (rotate_ccw, Movement::RotateCcw), + (rotate_board_cw, Movement::BoardCw), + (rotate_board_ccw, Movement::BoardCcw), + ], } } From e91ba7205367e9fba7ed7f41f962c680d56f438e Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 22:29:38 -0400 Subject: [PATCH 56/59] derive Default trait instead of manual implementation --- src/inputs.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/inputs.rs b/src/inputs.rs index 8207a88..4a4d4cc 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -84,7 +84,7 @@ impl Input { } } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct KeyboardControlScheme { pub vec_keycode_movement_pair: Vec<(KeyCode, Movement)>, } @@ -177,12 +177,3 @@ impl KeyboardControlScheme { self.vec_keycode_movement_pair.push((k, m)); } } - -impl Default for KeyboardControlScheme { - fn default() -> Self { - let vec_keycode_movement_pair: Vec<(KeyCode, Movement)> = vec![]; - Self { - vec_keycode_movement_pair, - } - } -} From b8d897d821e00e81b29cd55a6799e4704f465de4 Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 23:10:41 -0400 Subject: [PATCH 57/59] more readiability and slight efficiency improvement --- src/menu.rs | 4 +--- src/menu/choosemode.rs | 9 +++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/menu.rs b/src/menu.rs index 9b29bec..8e6d473 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -115,9 +115,7 @@ impl Menu { fn ensure_enough_controls(&self, game_options: &MenuGameOptions) -> bool { let mut ctrls_count = 0; for ctrls in game_options.arr_controls.iter() { - if ctrls.1 { - ctrls_count += 1; - } else if (ctrls.0).len() >= self.num_required_keycode_movement_pairs { + if ctrls.1 || (ctrls.0).len() >= self.num_required_keycode_movement_pairs { ctrls_count += 1; } } diff --git a/src/menu/choosemode.rs b/src/menu/choosemode.rs index 99abc9c..60c6070 100644 --- a/src/menu/choosemode.rs +++ b/src/menu/choosemode.rs @@ -32,11 +32,12 @@ impl ChooseModeMenu { MenuItemTrigger::SubMenu, )); vec_menu_items[0].set_num_values(2); - if game_mode == GameMode::None { - vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", GameMode::Classic); + let game_mode_for_text = if game_mode == GameMode::None { + GameMode::Classic } else { - vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", game_mode); - } + game_mode + }; + vec_menu_items[0].text.fragments_mut()[1].text = format!("{:?}", game_mode_for_text); vec_menu_items[0].set_select(true); Self { // logic From 4af3944750b5cc06105f222fba71f627c835094a Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 11 Oct 2021 23:58:53 -0400 Subject: [PATCH 58/59] use vec macro --- src/menu/inputconfig.rs | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index 90964fd..fc87d00 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -45,25 +45,26 @@ impl InputConfigMenu { } } // main MenuItems - let mut vec_menu_items_main: Vec = Vec::with_capacity(2); - vec_menu_items_main.push(MenuItem::new( - "Back", - MenuItemValueType::None, - 0, - None, - window_dimensions.1, - TEXT_SCALE_DOWN, - MenuItemTrigger::Back, - )); - vec_menu_items_main.push(MenuItem::new( - "Player Number: ", - MenuItemValueType::PlayerNum, - 0, - None, - window_dimensions.1, - TEXT_SCALE_DOWN, - MenuItemTrigger::SubSelection, - )); + let mut vec_menu_items_main: Vec = vec![ + MenuItem::new( + "Back", + MenuItemValueType::None, + 0, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::Back, + ), + MenuItem::new( + "Player Number: ", + MenuItemValueType::PlayerNum, + 0, + None, + window_dimensions.1, + TEXT_SCALE_DOWN, + MenuItemTrigger::SubSelection, + ), + ]; vec_menu_items_main[0].set_select(true); // keycode MenuItems From 791a231434143e39d4dab364531bae11b625da2b Mon Sep 17 00:00:00 2001 From: brian Date: Tue, 12 Oct 2021 00:26:51 -0400 Subject: [PATCH 59/59] yay error handling instead of panics everywhere --- src/game.rs | 29 +++++++++++++------- src/game/board.rs | 4 +-- src/menu/inputconfig.rs | 11 +++++--- src/movement.rs | 60 +++++++++++++++++++++++------------------ 4 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/game.rs b/src/game.rs index eb6fbb2..efd9562 100644 --- a/src/game.rs +++ b/src/game.rs @@ -8,6 +8,9 @@ use rand::random; use crate::control::ProgramState; use crate::movement::Movement; +use crate::movement::CONVERSION_FAILED_MOVEMENT_FROM_U8; + +use std::convert::TryFrom; mod player; use crate::game::player::{Player, SPAWN_DELAY}; @@ -539,7 +542,8 @@ impl Game { // this is flipped because singleplayer and multiplayer will be different someday; TODO if self.bh.attempt_rotate_board(Movement::RotateCcw) { self.gravity_direction = - Movement::from(((self.gravity_direction as u8) + 3) % 4); + Movement::try_from(((self.gravity_direction as u8) + 3) % 4) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_U8); } } @@ -547,7 +551,8 @@ impl Game { // this is flipped because singleplayer and multiplayer will be different someday; TODO if self.bh.attempt_rotate_board(Movement::RotateCw) { self.gravity_direction = - Movement::from(((self.gravity_direction as u8) + 1) % 4); + Movement::try_from(((self.gravity_direction as u8) + 1) % 4) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_U8); } } // rotatris specific end @@ -559,9 +564,10 @@ impl Game { player.waiting_to_shift = !self .bh .attempt_piece_movement( - Movement::from( + Movement::try_from( (Movement::Left as u8 + self.gravity_direction as u8) % 4, - ), + ) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_U8), player.player_num, ) .0; @@ -572,9 +578,10 @@ impl Game { player.waiting_to_shift = !self .bh .attempt_piece_movement( - Movement::from( + Movement::try_from( (Movement::Right as u8 + self.gravity_direction as u8) % 4, - ), + ) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_U8), player.player_num, ) .0; @@ -593,7 +600,10 @@ impl Game { if self .bh .attempt_piece_movement( - Movement::from((movement as u8 + self.gravity_direction as u8) % 4), + Movement::try_from( + (movement as u8 + self.gravity_direction as u8) % 4, + ) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_U8), player.player_num, ) .0 @@ -625,9 +635,10 @@ impl Game { { let (moved_flag, caused_full_line_flag): (bool, bool) = self.bh.attempt_piece_movement( - Movement::from( + Movement::try_from( (Movement::Down as u8 + self.gravity_direction as u8) % 4, - ), + ) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_U8), player.player_num, ); // if the piece got locked, piece.shape gets set to Shapes::None, so set the spawn piece flag diff --git a/src/game/board.rs b/src/game/board.rs index 4ef0fc8..efa09eb 100644 --- a/src/game/board.rs +++ b/src/game/board.rs @@ -21,13 +21,13 @@ pub enum Gravity { } impl From for Gravity { - fn from(value: u8) -> Gravity { + fn from(value: u8) -> Self { match value { 0 => Gravity::Down, 1 => Gravity::Left, 2 => Gravity::Up, 3 => Gravity::Right, - _ => panic!("[!] Unknown Gravity value: {}", value), + _ => Gravity::Invalid, } } } diff --git a/src/menu/inputconfig.rs b/src/menu/inputconfig.rs index fc87d00..b6789b4 100644 --- a/src/menu/inputconfig.rs +++ b/src/menu/inputconfig.rs @@ -6,12 +6,15 @@ use ggez::Context; use crate::game::GameMode; use crate::inputs::{Input, KeyboardControlScheme}; use crate::movement::Movement; +use crate::movement::CONVERSION_FAILED_MOVEMENT_FROM_MENUITEMTRIGGER; use crate::menu::menuhelpers::GAME_MODE_UNEXPECTEDLY_NONE; use crate::menu::menuhelpers::{MenuGameOptions, MenuItem, MenuItemTrigger, MenuItemValueType}; use crate::menu::menuhelpers::{DARK_GRAY, HELP_RED, LIGHT_GRAY}; use crate::menu::menuhelpers::{SUB_TEXT_SCALE_DOWN, TEXT_SCALE_DOWN}; +use std::convert::TryFrom; + const MAX_NON_START_INPUTS_PER_PLAYER: usize = 8; static KEY_UNEXPECTEDLY_NONE: &str = @@ -353,9 +356,10 @@ impl InputConfigMenu { let key: KeyCode = self.most_recently_pressed_key.expect(KEY_UNEXPECTEDLY_NONE); (game_options.arr_controls[self.player_num as usize].0).add_pair( key, - Movement::from( + Movement::try_from( self.vec_menu_items_keycode[self.sub_selection_keyboard].trigger, - ), + ) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_MENUITEMTRIGGER), ); self.vec_menu_items_keycode[self.sub_selection_keyboard].set_keycode(Some(key)); self.vec_used_keycode.push(key); @@ -402,7 +406,8 @@ impl InputConfigMenu { fn update_all_sub_text_strings(&mut self, game_options: &MenuGameOptions) { for item in self.vec_menu_items_keycode.iter_mut() { - let desired_movement = Movement::from(item.trigger); + let desired_movement = Movement::try_from(item.trigger) + .expect(CONVERSION_FAILED_MOVEMENT_FROM_MENUITEMTRIGGER); item.set_keycode(None); for kmp in (game_options.arr_controls[self.player_num as usize].0) .vec_keycode_movement_pair diff --git a/src/movement.rs b/src/movement.rs index 2a1187c..d9dbdcb 100644 --- a/src/movement.rs +++ b/src/movement.rs @@ -1,5 +1,7 @@ use crate::menu::menuhelpers::MenuItemTrigger; +use std::convert::TryFrom; + #[repr(u8)] #[derive(PartialEq, Eq, Copy, Clone)] pub enum Movement { @@ -15,38 +17,44 @@ pub enum Movement { None, } -impl From for Movement { - fn from(value: u8) -> Movement { +pub static CONVERSION_FAILED_MOVEMENT_FROM_U8: &str = "[!] Failed to get Movement value from u8"; + +impl TryFrom for Movement { + type Error = &'static str; + + fn try_from(value: u8) -> Result { match value { - 0 => Movement::Down, - 1 => Movement::Left, - 2 => Movement::Up, - 3 => Movement::Right, - 4 => Movement::RotateCw, - 5 => Movement::RotateCcw, - 6 => Movement::DoubleRotate, - 7 => Movement::BoardCw, - 8 => Movement::BoardCcw, - 9 => Movement::None, - _ => panic!("[!] Unknown Movement value: {}", value), + 0 => Ok(Movement::Down), + 1 => Ok(Movement::Left), + 2 => Ok(Movement::Up), + 3 => Ok(Movement::Right), + 4 => Ok(Movement::RotateCw), + 5 => Ok(Movement::RotateCcw), + 6 => Ok(Movement::DoubleRotate), + 7 => Ok(Movement::BoardCw), + 8 => Ok(Movement::BoardCcw), + 9 => Ok(Movement::None), + _ => Err("Invalid u8 value"), } } } -impl From for Movement { - fn from(value: MenuItemTrigger) -> Movement { +pub static CONVERSION_FAILED_MOVEMENT_FROM_MENUITEMTRIGGER: &str = + "[!] Failed to get Movement value from MenuItemTrigger"; + +impl TryFrom for Movement { + type Error = &'static str; + + fn try_from(value: MenuItemTrigger) -> Result { match value { - MenuItemTrigger::KeyLeft => Movement::Left, - MenuItemTrigger::KeyRight => Movement::Right, - MenuItemTrigger::KeyDown => Movement::Down, - MenuItemTrigger::KeyRotateCw => Movement::RotateCw, - MenuItemTrigger::KeyRotateCcw => Movement::RotateCcw, - MenuItemTrigger::KeyBoardCw => Movement::BoardCw, - MenuItemTrigger::KeyBoardCcw => Movement::BoardCcw, - _ => panic!( - "[!] Unexpected value converting MenuItemTrigger to Movement: {:?}", - value - ), + MenuItemTrigger::KeyLeft => Ok(Movement::Left), + MenuItemTrigger::KeyRight => Ok(Movement::Right), + MenuItemTrigger::KeyDown => Ok(Movement::Down), + MenuItemTrigger::KeyRotateCw => Ok(Movement::RotateCw), + MenuItemTrigger::KeyRotateCcw => Ok(Movement::RotateCcw), + MenuItemTrigger::KeyBoardCw => Ok(Movement::BoardCw), + MenuItemTrigger::KeyBoardCcw => Ok(Movement::BoardCcw), + _ => Err("Invalid MenuItemTrigger value"), } } }