Skip to content

Commit

Permalink
Merge pull request #57 from Brian-Catcow-B/ghost-pieces
Browse files Browse the repository at this point in the history
Ghost pieces
  • Loading branch information
wcampbell0x2a authored Nov 2, 2021
2 parents 584f438 + e9e6b69 commit 023c4c6
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 19 deletions.
60 changes: 54 additions & 6 deletions src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,17 +224,17 @@ pub struct Game {
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,
game_over_flag: bool,
game_over_delay: i8,
determine_ghost_tile_locations: bool,
// drawing
tile_size: f32,
batch_empty_tile: spritebatch::SpriteBatch,
batch_highlight_active_tile: spritebatch::SpriteBatch,
batch_highlight_clearing_standard_tile: spritebatch::SpriteBatch,
batch_highlight_clearing_tetrisnt_tile: spritebatch::SpriteBatch,
batch_highlight_ghost_tile: spritebatch::SpriteBatch,
vec_batch_player_piece: Vec<spritebatch::SpriteBatch>,
vec_batch_next_piece: Vec<spritebatch::SpriteBatch>,
game_info_text: Text,
Expand Down Expand Up @@ -393,11 +393,10 @@ impl Game {
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,
game_over_flag: false,
game_over_delay: GAME_OVER_DELAY,
determine_ghost_tile_locations: true,
tile_size: TileGraphic::get_size(
window_width,
window_height,
Expand All @@ -414,6 +413,9 @@ impl Game {
batch_highlight_clearing_tetrisnt_tile: spritebatch::SpriteBatch::new(
TileGraphic::new_clear_tetrisnt_highlight(ctx).image,
),
batch_highlight_ghost_tile: spritebatch::SpriteBatch::new(
TileGraphic::new_ghost_highlight(ctx).image,
),
vec_batch_player_piece,
vec_batch_next_piece,
game_info_text,
Expand Down Expand Up @@ -480,8 +482,6 @@ impl Game {
&& self.bh.get_shape_from_player(player.player_num) == Shapes::None
{
player.input.was_just_pressed_setfalse();
self.rotate_board_cw.1 = false;
self.rotate_board_ccw.1 = false;
continue;
}

Expand Down Expand Up @@ -862,6 +862,38 @@ impl Game {
self.draw_text(ctx, &self.pause_text, 0.4, &(window_width, window_height));
} else {
// DRAW GAME

// ghost tile highlights
if self.determine_ghost_tile_locations {
self.batch_highlight_ghost_tile.clear();
for piece_positions in self.bh.get_ghost_highlight_positions().iter() {
for pos in piece_positions.iter().take(4) {
let center = width / 2;
let is_center_even: u8 = (center + 1) % 2;
let (y_draw_pos, x_draw_pos) = match self.gravity_direction {
// account for the gravity direction in how to draw it (rotatris)
Movement::Down => (pos.0, pos.1),
Movement::Left => (center * 2 - pos.1 - is_center_even, pos.0),
Movement::Up => (
center * 2 - pos.0 - is_center_even,
center * 2 - pos.1 - is_center_even,
),
Movement::Right => (pos.1, center * 2 - pos.0 - is_center_even),
_ => unreachable!(
"[!] Error: self.gravity_direction is {}",
self.gravity_direction as u8
),
};
self.batch_highlight_ghost_tile
.add(graphics::DrawParam::new().dest(Point2::from_slice(&[
x_draw_pos as f32 * NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32,
(y_draw_pos - height_buffer) as f32
* NUM_PIXEL_ROWS_PER_TILEGRAPHIC as f32,
])));
}
}
}

// add each non-empty tile to the correct SpriteBatch
for x in 0..width {
for y in 0..height {
Expand Down Expand Up @@ -909,11 +941,13 @@ impl Game {
}
}
}

// line clear highlights
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 - height_buffer) as usize;
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
Expand Down Expand Up @@ -943,6 +977,7 @@ impl Game {
}
} else {
// tetrisnt clear animation

let y = (full_line.row - height_buffer) as usize;
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
Expand Down Expand Up @@ -973,6 +1008,7 @@ impl Game {
}
}
}

// next pieces
let mut color_number_singleplayer = 2;
let next_piece = self.vec_next_piece[0].shape;
Expand Down Expand Up @@ -1042,6 +1078,18 @@ impl Game {
.scale(Vector2::from_slice(&[scaled_tile_size, scaled_tile_size])),
)
.unwrap();
// ghost pieces
graphics::draw(
ctx,
&self.batch_highlight_ghost_tile,
DrawParam::new()
.dest(Point2::from_slice(&[
board_top_left_corner,
NON_BOARD_SPACE_U as f32 * self.tile_size,
]))
.scale(Vector2::from_slice(&[scaled_tile_size, scaled_tile_size])),
)
.unwrap();
// player tiles
for player in 0..std::cmp::max(self.num_players, 3) {
graphics::draw(
Expand Down
109 changes: 108 additions & 1 deletion src/game/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,30 @@ impl BoardHandler {
}
}

pub fn get_fall_delay_from_level(&mut self, level: u8) -> u8 {
pub fn get_fall_delay_from_level(&self, level: u8) -> u8 {
match self.mode {
GameMode::None => unreachable!("{}", BH_MODE_NONE),
GameMode::Classic => FALL_DELAY_VALUES_CLASSIC[level as usize],
GameMode::Rotatris => FALL_DELAY_VALUES_ROTATRIS[level as usize],
}
}

pub fn get_ghost_highlight_positions(&self) -> Vec<[(u8, u8); 4]> {
match self.mode {
GameMode::None => unreachable!("{}", BH_MODE_NONE),
GameMode::Classic => self
.classic
.as_ref()
.expect(BH_WRONG_MODE)
.get_ghost_highlight_positions(),
GameMode::Rotatris => self
.rotatris
.as_ref()
.expect(BH_WRONG_MODE)
.get_ghost_highlight_positions(),
}
}

// board logic...
pub fn attempt_piece_spawn(
&mut self,
Expand Down Expand Up @@ -350,6 +366,39 @@ impl BoardClassic {
}
}

pub fn get_ghost_highlight_positions(&self) -> Vec<[(u8, u8); 4]> {
let mut ghost_highlight_positions: Vec<[(u8, u8); 4]> = vec![];

for (idx, piece) in self.vec_active_piece.iter().enumerate() {
if piece.shape == Shapes::None {
continue;
}
let mut projection: [(u8, u8); 4] = piece.positions;
'project_down: loop {
for pos in projection.iter().take(4) {
if pos.0 + 1 >= self.height + self.height_buffer {
ghost_highlight_positions.push(projection);
break 'project_down;
} else if !self.matrix[1 + pos.0 as usize][pos.1 as usize].empty {
if self.matrix[1 + pos.0 as usize][pos.1 as usize].active {
if self.matrix[1 + pos.0 as usize][pos.1 as usize].player != idx as u8 {
break 'project_down;
}
} else {
ghost_highlight_positions.push(projection);
break 'project_down;
}
}
}
for pos in projection.iter_mut().take(4) {
pos.0 += 1;
}
}
}

ghost_highlight_positions
}

// returns (bool, bool) based on (blocked, blocked by some !active tile)
pub fn attempt_piece_spawn(
&mut self,
Expand Down Expand Up @@ -701,6 +750,64 @@ impl BoardRotatris {
}
}

pub fn get_ghost_highlight_positions(&self) -> Vec<[(u8, u8); 4]> {
let mut ghost_highlight_positions: Vec<[(u8, u8); 4]> = vec![];

let piece_projection_movement: (isize, isize) = match self.gravity {
Gravity::Down => (1, 0),
Gravity::Left => (0, -1),
Gravity::Up => (-1, 0),
Gravity::Right => (0, 1),
_ => unreachable!(
"Called BoardRotatris::get_ghost_highlight_positions with invalid Gravity"
),
};

for (idx, piece) in self.vec_active_piece.iter().enumerate() {
if piece.shape == Shapes::None {
continue;
}
let mut projection: [(u8, u8); 4] = piece.positions;
'project_gravity_direction: loop {
for pos in projection.iter().take(4) {
if pos.0 as isize + piece_projection_movement.0 >= self.board_size as isize
|| pos.0 as isize + piece_projection_movement.0 < 0
|| pos.1 as isize + piece_projection_movement.1 >= self.board_size as isize
|| pos.1 as isize + piece_projection_movement.1 < 0
{
ghost_highlight_positions.push(projection);
break 'project_gravity_direction;
} else if !self.matrix[(piece_projection_movement.0 + pos.0 as isize) as usize]
[(piece_projection_movement.1 + pos.1 as isize) as usize]
.empty
{
if self.matrix[(piece_projection_movement.0 + pos.0 as isize) as usize]
[(piece_projection_movement.1 + pos.1 as isize) as usize]
.active
{
if self.matrix[(piece_projection_movement.0 + pos.0 as isize) as usize]
[(piece_projection_movement.1 + pos.1 as isize) as usize]
.player
!= idx as u8
{
break 'project_gravity_direction;
}
} else {
ghost_highlight_positions.push(projection);
break 'project_gravity_direction;
}
}
}
for pos in projection.iter_mut().take(4) {
pos.0 += piece_projection_movement.0 as u8;
pos.1 += piece_projection_movement.1 as u8;
}
}
}

ghost_highlight_positions
}

// return bool is if rotate was successful
pub fn attempt_rotate_board(&mut self, rotate_direction: Movement) -> bool {
let center: u8 = self.board_size / 2;
Expand Down
20 changes: 10 additions & 10 deletions src/game/piece.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,33 +261,33 @@ impl Piece {
}
}

// returns the position based on the given Movement type
pub fn piece_pos(&self, r#move: Movement) -> [(u8, u8); 4] {
// returns the resulting positions based on the given Movement type
pub fn piece_pos(&self, movement: Movement) -> [(u8, u8); 4] {
// for movements and rotations, we don't have to worry about integer underflow because we will assume the board width is nowhere close to 0xff
if r#move == Movement::None {
if movement == Movement::None {
self.positions
} else if r#move == Movement::Left {
} else if movement == Movement::Left {
[
(self.positions[0].0, self.positions[0].1 - 1),
(self.positions[1].0, self.positions[1].1 - 1),
(self.positions[2].0, self.positions[2].1 - 1),
(self.positions[3].0, self.positions[3].1 - 1),
]
} else if r#move == Movement::Right {
} else if movement == Movement::Right {
[
(self.positions[0].0, self.positions[0].1 + 1),
(self.positions[1].0, self.positions[1].1 + 1),
(self.positions[2].0, self.positions[2].1 + 1),
(self.positions[3].0, self.positions[3].1 + 1),
]
} else if r#move == Movement::Down {
} else if movement == Movement::Down {
[
(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 {
} else if movement == Movement::Up {
return [
(self.positions[0].0 - 1, self.positions[0].1),
(self.positions[1].0 - 1, self.positions[1].1),
Expand All @@ -297,17 +297,17 @@ impl Piece {
} else {
// T, L, J
if self.num_rotations == 4 {
if r#move == Movement::RotateCw {
if movement == Movement::RotateCw {
self.rotate(true)
} else if r#move == Movement::RotateCcw {
} else if movement == 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 r#move != Movement::DoubleRotate {
if movement != Movement::DoubleRotate {
if self.rotation == 0 {
self.rotate(false)
} else {
Expand Down
Loading

0 comments on commit 023c4c6

Please sign in to comment.